blob: 0b7e216d5cef5b017dc890c8e4ad45f98a686f06 [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(STARBOARD)
#include "starboard/client_porting/poem/stdio_leaks_poem.h"
#endif
#include "cobalt/browser/application.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/statistics_recorder.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "cobalt/base/accessibility_caption_settings_changed_event.h"
#include "cobalt/base/accessibility_settings_changed_event.h"
#include "cobalt/base/accessibility_text_to_speech_settings_changed_event.h"
#include "cobalt/base/cobalt_paths.h"
#include "cobalt/base/deep_link_event.h"
#include "cobalt/base/get_application_key.h"
#include "cobalt/base/init_cobalt.h"
#include "cobalt/base/language.h"
#include "cobalt/base/localized_strings.h"
#include "cobalt/base/on_screen_keyboard_blurred_event.h"
#include "cobalt/base/on_screen_keyboard_focused_event.h"
#include "cobalt/base/on_screen_keyboard_hidden_event.h"
#include "cobalt/base/on_screen_keyboard_shown_event.h"
#include "cobalt/base/on_screen_keyboard_suggestions_updated_event.h"
#include "cobalt/base/startup_timer.h"
#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
#include "cobalt/base/version_compatibility.h"
#endif // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
#include "cobalt/base/window_size_changed_event.h"
#include "cobalt/browser/device_authentication.h"
#include "cobalt/browser/memory_settings/auto_mem_settings.h"
#include "cobalt/browser/memory_tracker/tool.h"
#include "cobalt/browser/storage_upgrade_handler.h"
#include "cobalt/browser/switches.h"
#include "cobalt/browser/user_agent_string.h"
#include "cobalt/configuration/configuration.h"
#include "cobalt/extension/crash_handler.h"
#include "cobalt/extension/installation_manager.h"
#include "cobalt/loader/image/image_decoder.h"
#include "cobalt/math/size.h"
#include "cobalt/script/javascript_engine.h"
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
#include "cobalt/storage/savegame_fake.h"
#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
#include "cobalt/system_window/input_event.h"
#include "cobalt/trace_event/scoped_trace_to_file.h"
#include "starboard/configuration.h"
#include "starboard/system.h"
#include "url/gurl.h"
#if SB_IS(EVERGREEN)
#include "cobalt/updater/utils.h"
#endif
using cobalt::cssom::ViewportSize;
namespace cobalt {
namespace browser {
namespace {
const int kStatUpdatePeriodMs = 1000;
const char kDefaultURL[] = "https://www.youtube.com/tv";
#if defined(ENABLE_ABOUT_SCHEME)
const char kAboutBlankURL[] = "about:blank";
#endif // defined(ENABLE_ABOUT_SCHEME)
bool IsStringNone(const std::string& str) {
return !base::strcasecmp(str.c_str(), "none");
}
#if defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
std::string GetDevServersListenIp() {
bool ip_v6;
#if SB_API_VERSION >= 12
ip_v6 = SbSocketIsIpv6Supported();
#elif SB_HAS(IPV6)
ip_v6 = true;
#else
ip_v6 = false;
#endif
// Default to INADDR_ANY
std::string listen_ip(ip_v6 ? "::" : "0.0.0.0");
// Desktop PCs default to loopback.
if (SbSystemGetDeviceType() == kSbSystemDeviceTypeDesktopPC) {
listen_ip = ip_v6 ? "::1" : "127.0.0.1";
}
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kDevServersListenIp)) {
listen_ip =
command_line->GetSwitchValueASCII(switches::kDevServersListenIp);
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return listen_ip;
}
#endif // defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
#if defined(ENABLE_DEBUGGER)
int GetRemoteDebuggingPort() {
#if defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT)
const unsigned int kDefaultRemoteDebuggingPort =
SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT;
#else
const unsigned int kDefaultRemoteDebuggingPort = 9222;
#endif // defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT)
unsigned int remote_debugging_port = kDefaultRemoteDebuggingPort;
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) {
std::string switch_value =
command_line->GetSwitchValueASCII(switches::kRemoteDebuggingPort);
if (!base::StringToUint(switch_value, &remote_debugging_port)) {
DLOG(ERROR) << "Invalid port specified for remote debug server: "
<< switch_value
<< ". Using default port: " << kDefaultRemoteDebuggingPort;
remote_debugging_port = kDefaultRemoteDebuggingPort;
}
}
DCHECK(remote_debugging_port != 0 ||
!command_line->HasSwitch(switches::kWaitForWebDebugger))
<< switches::kWaitForWebDebugger << " switch can't be used when "
<< switches::kRemoteDebuggingPort << " is 0 (disabled).";
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return uint16_t(remote_debugging_port);
}
#endif // ENABLE_DEBUGGER
#if defined(ENABLE_WEBDRIVER)
int GetWebDriverPort() {
// The default port on which the webdriver server should listen for incoming
// connections.
#if defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
const int kDefaultWebDriverPort = SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT;
#else
const int kDefaultWebDriverPort = 4444;
#endif // defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
int webdriver_port = kDefaultWebDriverPort;
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kWebDriverPort)) {
if (!base::StringToInt(
command_line->GetSwitchValueASCII(switches::kWebDriverPort),
&webdriver_port)) {
DLOG(ERROR) << "Invalid port specified for WebDriver server: "
<< command_line->GetSwitchValueASCII(switches::kWebDriverPort)
<< ". Using default port: " << kDefaultWebDriverPort;
webdriver_port = kDefaultWebDriverPort;
}
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return webdriver_port;
}
std::string GetWebDriverListenIp() {
// The default IP on which the webdriver server should listen for incoming
// connections.
std::string webdriver_listen_ip = GetDevServersListenIp();
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
DLOG(WARNING) << "The \"--" << switches::kWebDriverListenIp
<< "\" switch is deprecated; please use \"--"
<< switches::kDevServersListenIp << "\" instead.";
webdriver_listen_ip =
command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return webdriver_listen_ip;
}
#endif // ENABLE_WEBDRIVER
GURL GetInitialURL(bool should_preload) {
GURL initial_url = GURL(kDefaultURL);
// Allow the user to override the default URL via a command line parameter.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kInitialURL)) {
std::string url_switch =
command_line->GetSwitchValueASCII(switches::kInitialURL);
#if defined(ENABLE_ABOUT_SCHEME)
// Check the switch itself since some non-empty strings parse to empty URLs.
if (url_switch.empty()) {
LOG(ERROR) << "URL from parameter is empty, using " << kAboutBlankURL;
return GURL(kAboutBlankURL);
}
#endif // defined(ENABLE_ABOUT_SCHEME)
GURL url = GURL(url_switch);
if (url.is_valid()) {
initial_url = url;
} else {
LOG(ERROR) << "URL \"" << url_switch
<< "\" from parameter is not valid, using default URL "
<< initial_url;
}
}
if (should_preload) {
std::string query = initial_url.query();
if (!query.empty()) {
query += "&";
}
query += "launch=preload";
GURL::Replacements replacements;
replacements.SetQueryStr(query);
initial_url = initial_url.ReplaceComponents(replacements);
}
#if SB_API_VERSION >= 11
if (!command_line->HasSwitch(
switches::kOmitDeviceAuthenticationQueryParameters)) {
// Append the device authentication query parameters based on the platform's
// certification secret to the initial URL.
std::string query = initial_url.query();
std::string device_authentication_query_string =
GetDeviceAuthenticationSignedURLQueryString();
if (!query.empty() && !device_authentication_query_string.empty()) {
query += "&";
}
query += device_authentication_query_string;
if (!query.empty()) {
GURL::Replacements replacements;
replacements.SetQueryStr(query);
initial_url = initial_url.ReplaceComponents(replacements);
}
}
#endif // SB_API_VERSION >= 11
return initial_url;
}
bool ValidateSplashScreen(const base::Optional<GURL>& url) {
if (url->is_valid() &&
(url->SchemeIsFile() || url->SchemeIs("h5vcc-embedded"))) {
return true;
}
LOG(FATAL) << "Ignoring invalid fallback splash screen: " << url->spec();
return false;
}
base::Optional<GURL> GetFallbackSplashScreenURL() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string fallback_splash_screen_string;
if (command_line->HasSwitch(switches::kFallbackSplashScreenURL)) {
fallback_splash_screen_string =
command_line->GetSwitchValueASCII(switches::kFallbackSplashScreenURL);
} else {
fallback_splash_screen_string = configuration::Configuration::GetInstance()
->CobaltFallbackSplashScreenUrl();
}
if (IsStringNone(fallback_splash_screen_string)) {
return base::Optional<GURL>();
}
base::Optional<GURL> fallback_splash_screen_url =
GURL(fallback_splash_screen_string);
ValidateSplashScreen(fallback_splash_screen_url);
return fallback_splash_screen_url;
}
// Parses the fallback_splash_screen_topics command line parameter
// and maps topics to full file url locations, if valid.
void ParseFallbackSplashScreenTopics(
const base::Optional<GURL>& default_fallback_splash_screen_url,
std::map<std::string, GURL>* fallback_splash_screen_topic_map) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string topics;
if (command_line->HasSwitch(switches::kFallbackSplashScreenTopics)) {
topics = command_line->GetSwitchValueASCII(
switches::kFallbackSplashScreenTopics);
} else {
topics = configuration::Configuration::GetInstance()
->CobaltFallbackSplashScreenTopics();
}
// Note: values in topics_map may be either file paths or filenames.
std::map<std::string, std::string> topics_map;
BrowserModule::GetParamMap(topics, topics_map);
for (auto iterator = topics_map.begin(); iterator != topics_map.end();
iterator++) {
std::string topic = iterator->first;
std::string location = iterator->second;
base::Optional<GURL> topic_fallback_url = GURL(location);
// If not a valid url, check whether it is a valid filename in the
// same directory as the default fallback url.
if (!topic_fallback_url->is_valid()) {
if (default_fallback_splash_screen_url) {
topic_fallback_url = GURL(
default_fallback_splash_screen_url->GetWithoutFilename().spec() +
location);
} else {
break;
}
}
if (ValidateSplashScreen(topic_fallback_url)) {
(*fallback_splash_screen_topic_map)[topic] = topic_fallback_url.value();
}
}
}
base::TimeDelta GetTimedTraceDuration() {
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
int duration_in_seconds = 0;
if (command_line->HasSwitch(switches::kTimedTrace) &&
base::StringToInt(
command_line->GetSwitchValueASCII(switches::kTimedTrace),
&duration_in_seconds)) {
return base::TimeDelta::FromSeconds(duration_in_seconds);
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return base::TimeDelta();
}
base::FilePath GetExtraWebFileDir() {
// Default is empty, command line can override.
base::FilePath result;
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kExtraWebFileDir)) {
result = base::FilePath(
command_line->GetSwitchValueASCII(switches::kExtraWebFileDir));
if (!result.IsAbsolute()) {
// Non-absolute paths are relative to the executable directory.
base::FilePath content_path;
base::PathService::Get(base::DIR_EXE, &content_path);
result = content_path.DirName().DirName().Append(result);
}
DLOG(INFO) << "Extra web file dir: " << result.value();
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return result;
}
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
void EnableUsingStubImageDecoderIfRequired() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kStubImageDecoder)) {
loader::image::ImageDecoder::UseStubImageDecoder();
}
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
base::Optional<cssom::ViewportSize> GetRequestedViewportSize(
base::CommandLine* command_line) {
DCHECK(command_line);
if (!command_line->HasSwitch(browser::switches::kViewport)) {
return base::nullopt;
}
std::string switch_value =
command_line->GetSwitchValueASCII(browser::switches::kViewport);
std::vector<std::string> lengths = base::SplitString(
switch_value, "x", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (lengths.empty()) {
DLOG(ERROR) << "Viewport " << switch_value << " is invalid.";
return base::nullopt;
}
int width = 0;
if (!base::StringToInt(lengths[0], &width)) {
DLOG(ERROR) << "Viewport " << switch_value << " has invalid width.";
return base::nullopt;
}
if (lengths.size() < 2) {
// Allow shorthand specification of the viewport by only giving the
// width. This calculates the height at 4:3 aspect ratio for smaller
// viewport widths, and 16:9 for viewports 1280 pixels wide or larger.
if (width >= 1280) {
return ViewportSize(width, 9 * width / 16);
}
return ViewportSize(width, 3 * width / 4);
}
int height = 0;
if (!base::StringToInt(lengths[1], &height)) {
DLOG(ERROR) << "Viewport " << switch_value << " has invalid height.";
return base::nullopt;
}
if (lengths.size() < 3) {
return ViewportSize(width, height);
}
double screen_diagonal_inches = 0.0f;
if (lengths.size() >= 3) {
if (!base::StringToDouble(lengths[2], &screen_diagonal_inches)) {
DLOG(ERROR) << "Viewport " << switch_value
<< " has invalid screen_diagonal_inches.";
return base::nullopt;
}
}
double video_pixel_ratio = 1.0f;
if (lengths.size() >= 4) {
if (!base::StringToDouble(lengths[3], &video_pixel_ratio)) {
DLOG(ERROR) << "Viewport " << switch_value
<< " has invalid video_pixel_ratio.";
return base::nullopt;
}
}
return ViewportSize(width, height, static_cast<float>(video_pixel_ratio),
static_cast<float>(screen_diagonal_inches));
}
std::string GetMinLogLevelString() {
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kMinLogLevel)) {
return command_line->GetSwitchValueASCII(switches::kMinLogLevel);
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return "info";
}
int StringToLogLevel(const std::string& log_level) {
if (log_level == "info") {
return logging::LOG_INFO;
} else if (log_level == "warning") {
return logging::LOG_WARNING;
} else if (log_level == "error") {
return logging::LOG_ERROR;
} else if (log_level == "fatal") {
return logging::LOG_FATAL;
} else {
NOTREACHED() << "Unrecognized logging level: " << log_level;
return logging::LOG_INFO;
}
}
void SetIntegerIfSwitchIsSet(const char* switch_name, int* output) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) {
int32 out;
if (base::StringToInt32(
base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
switch_name),
&out)) {
LOG(INFO) << "Command line switch '" << switch_name << "': Modifying "
<< *output << " -> " << out;
*output = out;
} else {
LOG(ERROR) << "Invalid value for command line setting: " << switch_name;
}
}
}
void ApplyCommandLineSettingsToRendererOptions(
renderer::RendererModule::Options* options) {
SetIntegerIfSwitchIsSet(browser::switches::kScratchSurfaceCacheSizeInBytes,
&options->scratch_surface_cache_size_in_bytes);
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
auto command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(browser::switches::kDisableRasterizerCaching) ||
command_line->HasSwitch(
browser::switches::kForceDeterministicRendering)) {
options->force_deterministic_rendering = true;
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
}
struct NonTrivialStaticFields {
NonTrivialStaticFields() : system_language(base::GetSystemLanguage()) {}
const std::string system_language;
std::string user_agent;
private:
DISALLOW_COPY_AND_ASSIGN(NonTrivialStaticFields);
};
struct SecurityFlags {
csp::CSPHeaderPolicy csp_header_policy;
network::HTTPSRequirement https_requirement;
};
// |non_trivial_static_fields| will be lazily created on the first time it's
// accessed.
base::LazyInstance<NonTrivialStaticFields>::DestructorAtExit
non_trivial_static_fields = LAZY_INSTANCE_INITIALIZER;
#if defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
const char kMemoryTrackerCommand[] = "memory_tracker";
const char kMemoryTrackerCommandShortHelp[] = "Create a memory tracker.";
const char kMemoryTrackerCommandLongHelp[] =
"Create a memory tracker of the given type. Use an empty string to see the "
"available trackers.";
#endif // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
#if SB_API_VERSION >= 11
bool AddCrashHandlerAnnotations() {
auto crash_handler_extension =
SbSystemGetExtension(kCobaltExtensionCrashHandlerName);
if (!crash_handler_extension) {
LOG(INFO) << "No crash handler extension, not sending annotations.";
return false;
}
auto platform_info = cobalt::browser::GetUserAgentPlatformInfoFromSystem();
std::string user_agent =
cobalt::browser::CreateUserAgentString(platform_info);
std::string version = "";
#if SB_IS(EVERGREEN)
version = cobalt::updater::GetCurrentEvergreenVersion();
std::string product = "Cobalt_Evergreen";
#else
std::string product = "Cobalt";
#endif
if (version.empty()) {
base::StringAppendF(&version, "%s.%s-%s",
platform_info.cobalt_version.c_str(),
platform_info.cobalt_build_version_number.c_str(),
platform_info.build_configuration.c_str());
}
user_agent.push_back('\0');
product.push_back('\0');
version.push_back('\0');
CrashpadAnnotations crashpad_annotations;
SbMemorySet(&crashpad_annotations, sizeof(CrashpadAnnotations), 0);
SbStringCopy(crashpad_annotations.user_agent_string, user_agent.c_str(),
USER_AGENT_STRING_MAX_SIZE);
SbStringCopy(crashpad_annotations.product, product.c_str(),
CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
SbStringCopy(crashpad_annotations.version, version.c_str(),
CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
bool result = static_cast<const CobaltExtensionCrashHandlerApi*>(
crash_handler_extension)
->OverrideCrashpadAnnotations(&crashpad_annotations);
if (result) {
LOG(INFO) << "Sent annotations to crash handler";
} else {
LOG(ERROR) << "Could not send annotations to crash handler.";
}
return result;
}
#endif // SB_API_VERSION >= 11
} // namespace
// Helper stub to disable histogram tracking in StatisticsRecorder
struct RecordCheckerStub : public base::RecordHistogramChecker {
bool ShouldRecord(uint64_t) const override { return false; }
};
// Static user logs
ssize_t Application::available_memory_ = 0;
int64 Application::lifetime_in_ms_ = 0;
Application::AppStatus Application::app_status_ =
Application::kUninitializedAppStatus;
Application::Application(const base::Closure& quit_closure, bool should_preload)
: message_loop_(base::MessageLoop::current()),
quit_closure_(quit_closure)
#if defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
,
ALLOW_THIS_IN_INITIALIZER_LIST(memory_tracker_command_handler_(
kMemoryTrackerCommand,
base::Bind(&Application::OnMemoryTrackerCommand,
base::Unretained(this)),
kMemoryTrackerCommandShortHelp, kMemoryTrackerCommandLongHelp))
#endif // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
{
DCHECK(!quit_closure_.is_null());
// Check to see if a timed_trace has been set, indicating that we should
// begin a timed trace upon startup.
base::TimeDelta trace_duration = GetTimedTraceDuration();
if (trace_duration != base::TimeDelta()) {
trace_event::TraceToFileForDuration(
base::FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration);
}
TRACE_EVENT0("cobalt::browser", "Application::Application()");
DCHECK(base::MessageLoop::current());
DCHECK_EQ(
base::MessageLoop::TYPE_UI,
static_cast<base::MessageLoop*>(base::MessageLoop::current())->type());
DETACH_FROM_THREAD(network_event_thread_checker_);
DETACH_FROM_THREAD(application_event_thread_checker_);
// Set the minimum logging level, if specified on the command line.
logging::SetMinLogLevel(StringToLogLevel(GetMinLogLevelString()));
stats_update_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(kStatUpdatePeriodMs),
base::Bind(&Application::UpdatePeriodicStats, base::Unretained(this)));
// Get the initial URL.
GURL initial_url = GetInitialURL(should_preload);
DLOG(INFO) << "Initial URL: " << initial_url;
// Get the fallback splash screen URL.
base::Optional<GURL> fallback_splash_screen_url =
GetFallbackSplashScreenURL();
DLOG(INFO) << "Fallback splash screen URL: "
<< (fallback_splash_screen_url ? fallback_splash_screen_url->spec()
: "none");
// Get the system language and initialize our localized strings.
std::string language = base::GetSystemLanguage();
base::LocalizedStrings::GetInstance()->Initialize(language);
// Disable histogram tracking before TaskScheduler creates StatisticsRecorder
// instances.
auto record_checker = std::make_unique<RecordCheckerStub>();
base::StatisticsRecorder::SetRecordChecker(std::move(record_checker));
// A one-per-process task scheduler is needed for usage of APIs in
// base/post_task.h which will be used by some net APIs like
// URLRequestContext;
base::TaskScheduler::CreateAndStartWithDefaultParams("Cobalt TaskScheduler");
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::Optional<cssom::ViewportSize> requested_viewport_size =
GetRequestedViewportSize(command_line);
unconsumed_deep_link_ = GetInitialDeepLink();
DLOG(INFO) << "Initial deep link: " << unconsumed_deep_link_;
WebModule::Options web_options;
storage::StorageManager::Options storage_manager_options;
network::NetworkModule::Options network_module_options;
// Create the main components of our browser.
BrowserModule::Options options(web_options);
options.web_module_options.name = "MainWebModule";
network_module_options.preferred_language = language;
options.command_line_auto_mem_settings =
memory_settings::GetSettings(*command_line);
options.build_auto_mem_settings = memory_settings::GetDefaultBuildSettings();
options.fallback_splash_screen_url = fallback_splash_screen_url;
ParseFallbackSplashScreenTopics(fallback_splash_screen_url,
&options.fallback_splash_screen_topic_map);
if (command_line->HasSwitch(browser::switches::kFPSPrint)) {
options.renderer_module_options.enable_fps_stdout = true;
}
if (command_line->HasSwitch(browser::switches::kFPSOverlay)) {
options.renderer_module_options.enable_fps_overlay = true;
}
ApplyCommandLineSettingsToRendererOptions(&options.renderer_module_options);
if (command_line->HasSwitch(browser::switches::kDisableJavaScriptJit)) {
options.web_module_options.javascript_engine_options.disable_jit = true;
}
if (command_line->HasSwitch(
browser::switches::kRetainRemoteTypefaceCacheDuringSuspend)) {
options.web_module_options.should_retain_remote_typeface_cache_on_freeze =
true;
}
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
if (command_line->HasSwitch(browser::switches::kNullSavegame)) {
storage_manager_options.savegame_options.factory =
&storage::SavegameFake::Create;
}
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
if (command_line->HasSwitch(browser::switches::kDisableOnScreenKeyboard)) {
options.enable_on_screen_keyboard = false;
}
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
constexpr int kDefaultMinCompatibilityVersion = 1;
int minimum_version = kDefaultMinCompatibilityVersion;
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
if (command_line->HasSwitch(browser::switches::kMinCompatibilityVersion)) {
std::string switch_value =
command_line->GetSwitchValueASCII(switches::kMinCompatibilityVersion);
if (!base::StringToInt(switch_value, &minimum_version)) {
DLOG(ERROR) << "Invalid min_compatibility_version provided.";
}
}
#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::VersionCompatibility::GetInstance()->SetMinimumVersion(minimum_version);
#endif // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
base::Optional<std::string> partition_key;
if (command_line->HasSwitch(browser::switches::kLocalStoragePartitionUrl)) {
std::string local_storage_partition_url = command_line->GetSwitchValueASCII(
browser::switches::kLocalStoragePartitionUrl);
partition_key = base::GetApplicationKey(GURL(local_storage_partition_url));
CHECK(partition_key) << "local_storage_partition_url is not a valid URL.";
} else {
partition_key = base::GetApplicationKey(initial_url);
}
storage_manager_options.savegame_options.id = partition_key;
base::Optional<std::string> default_key =
base::GetApplicationKey(GURL(kDefaultURL));
if (command_line->HasSwitch(
browser::switches::kForceMigrationForStoragePartitioning) ||
partition_key == default_key) {
storage_manager_options.savegame_options.fallback_to_default_id = true;
}
// User can specify an extra search path entry for files loaded via file://.
options.web_module_options.extra_web_file_dir = GetExtraWebFileDir();
SecurityFlags security_flags{csp::kCSPRequired, network::kHTTPSRequired};
// Set callback to be notified when a navigation occurs that destroys the
// underlying WebModule.
options.web_module_created_callback =
base::Bind(&Application::WebModuleCreated, base::Unretained(this));
// The main web module's stat tracker tracks event stats.
options.web_module_options.track_event_stats = true;
if (command_line->HasSwitch(browser::switches::kProxy)) {
network_module_options.custom_proxy =
command_line->GetSwitchValueASCII(browser::switches::kProxy);
}
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
#if defined(ENABLE_IGNORE_CERTIFICATE_ERRORS)
if (command_line->HasSwitch(browser::switches::kIgnoreCertificateErrors)) {
network_module_options.ignore_certificate_errors = true;
}
#endif // defined(ENABLE_IGNORE_CERTIFICATE_ERRORS)
if (!command_line->HasSwitch(switches::kRequireHTTPSLocation)) {
security_flags.https_requirement = network::kHTTPSOptional;
}
if (!command_line->HasSwitch(browser::switches::kRequireCSP)) {
security_flags.csp_header_policy = csp::kCSPOptional;
}
if (command_line->HasSwitch(browser::switches::kProd)) {
security_flags.https_requirement = network::kHTTPSRequired;
security_flags.csp_header_policy = csp::kCSPRequired;
}
if (command_line->HasSwitch(switches::kVideoPlaybackRateMultiplier)) {
double playback_rate = 1.0;
base::StringToDouble(command_line->GetSwitchValueASCII(
switches::kVideoPlaybackRateMultiplier),
&playback_rate);
options.web_module_options.video_playback_rate_multiplier =
static_cast<float>(playback_rate);
DLOG(INFO) << "Set video playback rate multiplier to "
<< options.web_module_options.video_playback_rate_multiplier;
}
EnableUsingStubImageDecoderIfRequired();
#if defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
if (command_line->HasSwitch(switches::kMemoryTracker)) {
std::string command_arg =
command_line->GetSwitchValueASCII(switches::kMemoryTracker);
memory_tracker_tool_ = memory_tracker::CreateMemoryTrackerTool(command_arg);
}
#endif // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
if (command_line->HasSwitch(switches::kDisableImageAnimations)) {
options.web_module_options.enable_image_animations = false;
}
if (command_line->HasSwitch(switches::kDisableSplashScreenOnReloads)) {
options.enable_splash_screen_on_reloads = false;
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
// Production-builds override all switches to the most secure configuration.
#if defined(COBALT_FORCE_HTTPS)
security_flags.https_requirement = network::kHTTPSRequired;
#endif // defined(COBALT_FORCE_HTTPS)
#if defined(COBALT_FORCE_CSP)
security_flags.csp_header_policy = csp::kCSPRequired;
#endif // defined(COBALT_FORCE_CSP)
network_module_options.https_requirement = security_flags.https_requirement;
options.web_module_options.require_csp = security_flags.csp_header_policy;
options.web_module_options.csp_enforcement_mode = dom::kCspEnforcementEnable;
options.requested_viewport_size = requested_viewport_size;
account_manager_.reset(new account::AccountManager());
storage_manager_.reset(new storage::StorageManager(
std::unique_ptr<storage::StorageManager::UpgradeHandler>(
new StorageUpgradeHandler(initial_url)),
storage_manager_options));
network_module_.reset(new network::NetworkModule(
CreateUserAgentString(GetUserAgentPlatformInfoFromSystem()),
storage_manager_.get(), &event_dispatcher_, network_module_options));
#if SB_API_VERSION >= 11
AddCrashHandlerAnnotations();
#endif
#if SB_IS(EVERGREEN)
if (SbSystemGetExtension(kCobaltExtensionInstallationManagerName)) {
updater_module_.reset(new updater::UpdaterModule(network_module_.get()));
}
#endif
browser_module_.reset(new BrowserModule(
initial_url,
(should_preload ? base::kApplicationStateConcealed
: base::kApplicationStateStarted),
&event_dispatcher_, account_manager_.get(), network_module_.get(),
#if SB_IS(EVERGREEN)
updater_module_.get(),
#endif
options));
UpdateUserAgent();
app_status_ = (should_preload ? kConcealedAppStatus : kRunningAppStatus);
// Register event callbacks.
#if SB_API_VERSION >= 8
window_size_change_event_callback_ = base::Bind(
&Application::OnWindowSizeChangedEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(base::WindowSizeChangedEvent::TypeId(),
window_size_change_event_callback_);
#endif // SB_API_VERSION >= 8
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
on_screen_keyboard_shown_event_callback_ = base::Bind(
&Application::OnOnScreenKeyboardShownEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(base::OnScreenKeyboardShownEvent::TypeId(),
on_screen_keyboard_shown_event_callback_);
on_screen_keyboard_hidden_event_callback_ = base::Bind(
&Application::OnOnScreenKeyboardHiddenEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(
base::OnScreenKeyboardHiddenEvent::TypeId(),
on_screen_keyboard_hidden_event_callback_);
on_screen_keyboard_focused_event_callback_ = base::Bind(
&Application::OnOnScreenKeyboardFocusedEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(
base::OnScreenKeyboardFocusedEvent::TypeId(),
on_screen_keyboard_focused_event_callback_);
on_screen_keyboard_blurred_event_callback_ = base::Bind(
&Application::OnOnScreenKeyboardBlurredEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(
base::OnScreenKeyboardBlurredEvent::TypeId(),
on_screen_keyboard_blurred_event_callback_);
#if SB_API_VERSION >= 11
on_screen_keyboard_suggestions_updated_event_callback_ =
base::Bind(&Application::OnOnScreenKeyboardSuggestionsUpdatedEvent,
base::Unretained(this));
event_dispatcher_.AddEventCallback(
base::OnScreenKeyboardSuggestionsUpdatedEvent::TypeId(),
on_screen_keyboard_suggestions_updated_event_callback_);
#endif // SB_API_VERSION >= 11
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
#if SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
on_caption_settings_changed_event_callback_ = base::Bind(
&Application::OnCaptionSettingsChangedEvent, base::Unretained(this));
event_dispatcher_.AddEventCallback(
base::AccessibilityCaptionSettingsChangedEvent::TypeId(),
on_caption_settings_changed_event_callback_);
#endif // SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
#if defined(ENABLE_WEBDRIVER)
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
bool create_webdriver_module =
!command_line->HasSwitch(switches::kDisableWebDriver);
#else
bool create_webdriver_module = true;
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
if (create_webdriver_module) {
web_driver_module_.reset(new webdriver::WebDriverModule(
GetWebDriverPort(), GetWebDriverListenIp(),
base::Bind(&BrowserModule::CreateSessionDriver,
base::Unretained(browser_module_.get())),
base::Bind(&BrowserModule::RequestScreenshotToMemory,
base::Unretained(browser_module_.get())),
base::Bind(&BrowserModule::SetProxy,
base::Unretained(browser_module_.get())),
base::Bind(&Application::Quit, base::Unretained(this))));
}
#endif // ENABLE_WEBDRIVER
#if defined(ENABLE_DEBUGGER)
int remote_debugging_port = GetRemoteDebuggingPort();
if (remote_debugging_port == 0) {
DLOG(INFO) << "Remote web debugger disabled because "
<< switches::kRemoteDebuggingPort << " is 0.";
} else {
debug_web_server_.reset(new debug::remote::DebugWebServer(
remote_debugging_port, GetDevServersListenIp(),
base::Bind(&BrowserModule::CreateDebugClient,
base::Unretained(browser_module_.get()))));
}
#endif // ENABLE_DEBUGGER
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
int duration_in_seconds = 0;
if (command_line->HasSwitch(switches::kShutdownAfter) &&
base::StringToInt(
command_line->GetSwitchValueASCII(switches::kShutdownAfter),
&duration_in_seconds)) {
// If the "shutdown_after" command line option is specified, setup a delayed
// message to quit the application after the specified number of seconds
// have passed.
message_loop_->task_runner()->PostDelayedTask(
FROM_HERE, quit_closure_,
base::TimeDelta::FromSeconds(duration_in_seconds));
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
}
Application::~Application() {
// explicitly reset here because the destruction of the object is complex
// and involves a thread join. If this were to hang the app then having
// the destruction at this point gives a real file-line number and a place
// for the debugger to land.
#if defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
memory_tracker_tool_.reset(NULL);
#endif // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
// Unregister event callbacks.
#if SB_API_VERSION >= 8
event_dispatcher_.RemoveEventCallback(base::WindowSizeChangedEvent::TypeId(),
window_size_change_event_callback_);
#endif // SB_API_VERSION >= 8
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
event_dispatcher_.RemoveEventCallback(
base::OnScreenKeyboardShownEvent::TypeId(),
on_screen_keyboard_shown_event_callback_);
event_dispatcher_.RemoveEventCallback(
base::OnScreenKeyboardHiddenEvent::TypeId(),
on_screen_keyboard_hidden_event_callback_);
event_dispatcher_.RemoveEventCallback(
base::OnScreenKeyboardFocusedEvent::TypeId(),
on_screen_keyboard_focused_event_callback_);
event_dispatcher_.RemoveEventCallback(
base::OnScreenKeyboardBlurredEvent::TypeId(),
on_screen_keyboard_blurred_event_callback_);
#if SB_API_VERSION >= 11
event_dispatcher_.RemoveEventCallback(
base::OnScreenKeyboardSuggestionsUpdatedEvent::TypeId(),
on_screen_keyboard_suggestions_updated_event_callback_);
#endif // SB_API_VERSION >= 11
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
#if SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
event_dispatcher_.RemoveEventCallback(
base::AccessibilityCaptionSettingsChangedEvent::TypeId(),
on_caption_settings_changed_event_callback_);
#endif // SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
app_status_ = kShutDownAppStatus;
}
void Application::Start() {
if (base::MessageLoop::current() != message_loop_) {
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&Application::Start, base::Unretained(this)));
return;
}
if (app_status_ != kConcealedAppStatus) {
NOTREACHED() << __FUNCTION__ << ": Redundant call.";
return;
}
OnApplicationEvent(kSbEventTypeStart);
}
void Application::Quit() {
if (base::MessageLoop::current() != message_loop_) {
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&Application::Quit, base::Unretained(this)));
return;
}
quit_closure_.Run();
app_status_ = kQuitAppStatus;
}
void Application::HandleStarboardEvent(const SbEvent* starboard_event) {
DCHECK(starboard_event);
DCHECK_EQ(base::MessageLoop::current(), message_loop_);
// Forward input events to |SystemWindow|.
if (starboard_event->type == kSbEventTypeInput) {
system_window::HandleInputEvent(starboard_event);
return;
}
// Create a Cobalt event from the Starboard event, if recognized.
switch (starboard_event->type) {
#if SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION || \
SB_HAS(CONCEALED_STATE)
case kSbEventTypeBlur:
case kSbEventTypeFocus:
case kSbEventTypeConceal:
case kSbEventTypeReveal:
case kSbEventTypeFreeze:
case kSbEventTypeUnfreeze:
case kSbEventTypeLowMemory:
OnApplicationEvent(starboard_event->type);
break;
#else
case kSbEventTypePause:
case kSbEventTypeUnpause:
case kSbEventTypeSuspend:
case kSbEventTypeResume:
case kSbEventTypeLowMemory:
OnApplicationEvent(starboard_event->type);
break;
#endif // SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION ||
// SB_HAS(CONCEALED_STATE)
#if SB_API_VERSION >= 8
case kSbEventTypeWindowSizeChanged:
DispatchEventInternal(new base::WindowSizeChangedEvent(
static_cast<SbEventWindowSizeChangedData*>(starboard_event->data)
->window,
static_cast<SbEventWindowSizeChangedData*>(starboard_event->data)
->size));
break;
#endif // SB_API_VERSION >= 8
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
case kSbEventTypeOnScreenKeyboardShown:
DCHECK(starboard_event->data);
DispatchEventInternal(new base::OnScreenKeyboardShownEvent(
*static_cast<int*>(starboard_event->data)));
break;
case kSbEventTypeOnScreenKeyboardHidden:
DispatchEventInternal(new base::OnScreenKeyboardHiddenEvent(
*static_cast<int*>(starboard_event->data)));
break;
case kSbEventTypeOnScreenKeyboardFocused:
DCHECK(starboard_event->data);
DispatchEventInternal(new base::OnScreenKeyboardFocusedEvent(
*static_cast<int*>(starboard_event->data)));
break;
case kSbEventTypeOnScreenKeyboardBlurred:
DispatchEventInternal(new base::OnScreenKeyboardBlurredEvent(
*static_cast<int*>(starboard_event->data)));
break;
#if SB_API_VERSION >= 11
case kSbEventTypeOnScreenKeyboardSuggestionsUpdated:
DispatchEventInternal(new base::OnScreenKeyboardSuggestionsUpdatedEvent(
*static_cast<int*>(starboard_event->data)));
break;
#endif // SB_API_VERSION >= 11
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
case kSbEventTypeLink: {
DispatchDeepLink(static_cast<const char*>(starboard_event->data));
break;
}
case kSbEventTypeAccessiblitySettingsChanged:
DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
#if SB_API_VERSION < 12
// Also dispatch the newer text-to-speech settings changed event since
// the specific kSbEventTypeAccessiblityTextToSpeechSettingsChanged
// event is not available in this starboard version.
DispatchEventInternal(
new base::AccessibilityTextToSpeechSettingsChangedEvent());
#endif
break;
#if SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
case kSbEventTypeAccessibilityCaptionSettingsChanged:
DispatchEventInternal(
new base::AccessibilityCaptionSettingsChangedEvent());
break;
#endif // SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
#if SB_API_VERSION >= 12
case kSbEventTypeAccessiblityTextToSpeechSettingsChanged:
DispatchEventInternal(
new base::AccessibilityTextToSpeechSettingsChangedEvent());
break;
#endif
// Explicitly list unhandled cases here so that the compiler can give a
// warning when a value is added, but not handled.
case kSbEventTypeInput:
case kSbEventTypePreload:
#if SB_API_VERSION < 11
case kSbEventTypeNetworkConnect:
case kSbEventTypeNetworkDisconnect:
#endif // SB_API_VERSION < 11
case kSbEventTypeScheduled:
case kSbEventTypeStart:
case kSbEventTypeStop:
case kSbEventTypeUser:
case kSbEventTypeVerticalSync:
DLOG(WARNING) << "Unhandled Starboard event of type: "
<< starboard_event->type;
}
}
void Application::OnApplicationEvent(SbEventType event_type) {
TRACE_EVENT0("cobalt::browser", "Application::OnApplicationEvent()");
DCHECK_CALLED_ON_VALID_THREAD(application_event_thread_checker_);
switch (event_type) {
case kSbEventTypeStop:
DLOG(INFO) << "Got quit event.";
app_status_ = kWillQuitAppStatus;
Quit();
DLOG(INFO) << "Finished quitting.";
break;
case kSbEventTypeStart:
DLOG(INFO) << "Got start event.";
app_status_ = kRunningAppStatus;
browser_module_->Reveal();
browser_module_->Focus();
DLOG(INFO) << "Finished starting.";
break;
#if SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION || \
SB_HAS(CONCEALED_STATE)
case kSbEventTypeBlur:
DLOG(INFO) << "Got blur event.";
app_status_ = kBlurredAppStatus;
browser_module_->Blur();
DLOG(INFO) << "Finished blurring.";
break;
case kSbEventTypeFocus:
DLOG(INFO) << "Got focus event.";
app_status_ = kRunningAppStatus;
browser_module_->Focus();
DLOG(INFO) << "Finished focusing.";
break;
case kSbEventTypeConceal:
DLOG(INFO) << "Got conceal event.";
app_status_ = kConcealedAppStatus;
browser_module_->Conceal();
DLOG(INFO) << "Finished concealing.";
break;
case kSbEventTypeReveal:
DCHECK(SbSystemSupportsResume());
DLOG(INFO) << "Got reveal event.";
app_status_ = kBlurredAppStatus;
browser_module_->Reveal();
DLOG(INFO) << "Finished revealing.";
break;
case kSbEventTypeFreeze:
DLOG(INFO) << "Got freeze event.";
app_status_ = kFrozenAppStatus;
browser_module_->Freeze();
#if SB_IS(EVERGREEN)
if (updater_module_) updater_module_->Suspend();
#endif
DLOG(INFO) << "Finished freezing.";
break;
case kSbEventTypeUnfreeze:
DLOG(INFO) << "Got unfreeze event.";
app_status_ = kConcealedAppStatus;
browser_module_->Unfreeze();
#if SB_IS(EVERGREEN)
if (updater_module_) updater_module_->Resume();
#endif
DLOG(INFO) << "Finished unfreezing.";
break;
#else
case kSbEventTypePause:
DLOG(INFO) << "Got pause event.";
app_status_ = kBlurredAppStatus;
browser_module_->Blur();
DLOG(INFO) << "Finished pausing.";
break;
case kSbEventTypeUnpause:
DLOG(INFO) << "Got unpause event.";
app_status_ = kRunningAppStatus;
browser_module_->Focus();
DLOG(INFO) << "Finished unpausing.";
break;
case kSbEventTypeSuspend:
DLOG(INFO) << "Got suspend event.";
app_status_ = kConcealedAppStatus;
browser_module_->Conceal();
app_status_ = kFrozenAppStatus;
browser_module_->Freeze();
#if SB_IS(EVERGREEN)
if (updater_module_) updater_module_->Suspend();
#endif
DLOG(INFO) << "Finished suspending.";
break;
case kSbEventTypeResume:
DCHECK(SbSystemSupportsResume());
DLOG(INFO) << "Got resume event.";
app_status_ = kConcealedAppStatus;
browser_module_->Unfreeze();
app_status_ = kBlurredAppStatus;
browser_module_->Reveal();
#if SB_IS(EVERGREEN)
if (updater_module_) updater_module_->Resume();
#endif
DLOG(INFO) << "Finished resuming.";
break;
#endif // SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION ||
// SB_HAS(CONCEALED_STATE)
case kSbEventTypeLowMemory:
DLOG(INFO) << "Got low memory event.";
browser_module_->ReduceMemory();
DLOG(INFO) << "Finished reducing memory usage.";
break;
// All of the remaining event types are unexpected:
case kSbEventTypePreload:
#if SB_API_VERSION >= 8
case kSbEventTypeWindowSizeChanged:
#endif
#if SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
case kSbEventTypeAccessibilityCaptionSettingsChanged:
#endif // SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
#if SB_API_VERSION >= 12
case kSbEventTypeAccessiblityTextToSpeechSettingsChanged:
#endif // SB_API_VERSION >= 12
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
case kSbEventTypeOnScreenKeyboardBlurred:
case kSbEventTypeOnScreenKeyboardFocused:
case kSbEventTypeOnScreenKeyboardHidden:
case kSbEventTypeOnScreenKeyboardShown:
#if SB_API_VERSION >= 11
case kSbEventTypeOnScreenKeyboardSuggestionsUpdated:
#endif // SB_API_VERSION >= 11
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
case kSbEventTypeAccessiblitySettingsChanged:
case kSbEventTypeInput:
case kSbEventTypeLink:
#if SB_API_VERSION < 11
case kSbEventTypeNetworkConnect:
case kSbEventTypeNetworkDisconnect:
#endif // SB_API_VERSION < 11
case kSbEventTypeScheduled:
case kSbEventTypeUser:
case kSbEventTypeVerticalSync:
NOTREACHED() << "Unexpected event type: " << event_type;
return;
}
}
#if SB_API_VERSION >= 8
void Application::OnWindowSizeChangedEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser", "Application::OnWindowSizeChangedEvent()");
const base::WindowSizeChangedEvent* window_size_change_event =
base::polymorphic_downcast<const base::WindowSizeChangedEvent*>(event);
const auto& size = window_size_change_event->size();
#if SB_API_VERSION >= 11
float diagonal =
SbWindowGetDiagonalSizeInInches(window_size_change_event->window());
#else
float diagonal = 0.0f; // Special value meaning diagonal size is not known.
#endif
// A value of 0.0 for the video pixel ratio means that the ratio could not be
// determined. In that case it should be assumed to be the same as the
// graphics resolution, which corresponds to a device pixel ratio of 1.0.
float device_pixel_ratio =
(size.video_pixel_ratio == 0) ? 1.0f : size.video_pixel_ratio;
cssom::ViewportSize viewport_size(size.width, size.height, diagonal,
device_pixel_ratio);
browser_module_->OnWindowSizeChanged(viewport_size);
}
#endif // SB_API_VERSION >= 8
#if SB_API_VERSION >= 12 || SB_HAS(ON_SCREEN_KEYBOARD)
void Application::OnOnScreenKeyboardShownEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnOnScreenKeyboardShownEvent()");
browser_module_->OnOnScreenKeyboardShown(
base::polymorphic_downcast<const base::OnScreenKeyboardShownEvent*>(
event));
}
void Application::OnOnScreenKeyboardHiddenEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnOnScreenKeyboardHiddenEvent()");
browser_module_->OnOnScreenKeyboardHidden(
base::polymorphic_downcast<const base::OnScreenKeyboardHiddenEvent*>(
event));
}
void Application::OnOnScreenKeyboardFocusedEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnOnScreenKeyboardFocusedEvent()");
browser_module_->OnOnScreenKeyboardFocused(
base::polymorphic_downcast<const base::OnScreenKeyboardFocusedEvent*>(
event));
}
void Application::OnOnScreenKeyboardBlurredEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnOnScreenKeyboardBlurredEvent()");
browser_module_->OnOnScreenKeyboardBlurred(
base::polymorphic_downcast<const base::OnScreenKeyboardBlurredEvent*>(
event));
}
#if SB_API_VERSION >= 11
void Application::OnOnScreenKeyboardSuggestionsUpdatedEvent(
const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnOnScreenKeyboardSuggestionsUpdatedEvent()");
browser_module_->OnOnScreenKeyboardSuggestionsUpdated(
base::polymorphic_downcast<
const base::OnScreenKeyboardSuggestionsUpdatedEvent*>(event));
}
#endif // SB_API_VERSION >= 11
#endif // SB_API_VERSION >= 12 ||
// SB_HAS(ON_SCREEN_KEYBOARD)
#if SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
void Application::OnCaptionSettingsChangedEvent(const base::Event* event) {
TRACE_EVENT0("cobalt::browser",
"Application::OnCaptionSettingsChangedEvent()");
browser_module_->OnCaptionSettingsChanged(
base::polymorphic_downcast<
const base::AccessibilityCaptionSettingsChangedEvent*>(event));
}
#endif // SB_API_VERSION >= 12 || SB_HAS(CAPTIONS)
void Application::WebModuleCreated() {
TRACE_EVENT0("cobalt::browser", "Application::WebModuleCreated()");
DispatchDeepLinkIfNotConsumed();
#if defined(ENABLE_WEBDRIVER)
if (web_driver_module_) {
web_driver_module_->OnWindowRecreated();
}
#endif
}
Application::CValStats::CValStats()
: free_cpu_memory("Memory.CPU.Free", 0,
"Total free application CPU memory remaining."),
used_cpu_memory("Memory.CPU.Used", 0,
"Total CPU memory allocated via the app's allocators."),
app_start_time("Time.Cobalt.Start",
base::StartupTimer::StartTime().ToInternalValue(),
"Start time of the application in microseconds."),
app_lifetime("Cobalt.Lifetime", base::TimeDelta(),
"Application lifetime in microseconds.") {
if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
free_gpu_memory.emplace("Memory.GPU.Free", 0,
"Total free application GPU memory remaining.");
used_gpu_memory.emplace("Memory.GPU.Used", 0,
"Total GPU memory allocated by the application.");
}
}
// NOTE: UserAgent registration is handled separately, as the value is not
// available when the app is first being constructed. Registration must happen
// each time the user agent is modified, because the string may be pointing
// to a new location on the heap.
void Application::UpdateUserAgent() {
non_trivial_static_fields.Get().user_agent = browser_module_->GetUserAgent();
LOG(INFO) << "User Agent: " << non_trivial_static_fields.Get().user_agent;
}
void Application::UpdatePeriodicStats() {
TRACE_EVENT0("cobalt::browser", "Application::UpdatePeriodicStats()");
c_val_stats_.app_lifetime = base::StartupTimer::TimeElapsed();
int64_t used_cpu_memory = SbSystemGetUsedCPUMemory();
base::Optional<int64_t> used_gpu_memory;
if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
used_gpu_memory = SbSystemGetUsedGPUMemory();
}
available_memory_ =
static_cast<ssize_t>(SbSystemGetTotalCPUMemory() - used_cpu_memory);
c_val_stats_.free_cpu_memory = available_memory_;
c_val_stats_.used_cpu_memory = used_cpu_memory;
if (used_gpu_memory) {
*c_val_stats_.free_gpu_memory =
SbSystemGetTotalGPUMemory() - *used_gpu_memory;
*c_val_stats_.used_gpu_memory = *used_gpu_memory;
}
browser_module_->UpdateJavaScriptHeapStatistics();
browser_module_->CheckMemory(used_cpu_memory, used_gpu_memory);
}
void Application::DispatchEventInternal(base::Event* event) {
event_dispatcher_.DispatchEvent(std::unique_ptr<base::Event>(event));
}
#if defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
void Application::OnMemoryTrackerCommand(const std::string& message) {
if (base::MessageLoop::current() != message_loop_) {
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&Application::OnMemoryTrackerCommand,
base::Unretained(this), message));
return;
}
if (memory_tracker_tool_) {
LOG(ERROR) << "Can not create a memory tracker when one is already active.";
return;
}
LOG(WARNING) << "Creating \"" << message << "\" memory tracker.";
memory_tracker_tool_ = memory_tracker::CreateMemoryTrackerTool(message);
}
#endif // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
// Called to handle deep link consumed events.
void Application::OnDeepLinkConsumedCallback(const std::string& link) {
LOG(INFO) << "Got deep link consumed callback: " << link;
base::AutoLock auto_lock(unconsumed_deep_link_lock_);
if (link == unconsumed_deep_link_) {
unconsumed_deep_link_.clear();
}
}
void Application::DispatchDeepLink(const char* link) {
if (!link || *link == 0) {
return;
}
std::string deep_link;
// This block exists to ensure that the lock is held while accessing
// unconsumed_deep_link_.
{
base::AutoLock auto_lock(unconsumed_deep_link_lock_);
// Stash the deep link so that if it is not consumed, it can be dispatched
// again after the next WebModule is created.
unconsumed_deep_link_ = link;
deep_link = unconsumed_deep_link_;
}
LOG(INFO) << "Dispatching deep link: " << deep_link;
DispatchEventInternal(new base::DeepLinkEvent(
deep_link, base::Bind(&Application::OnDeepLinkConsumedCallback,
base::Unretained(this), deep_link)));
}
void Application::DispatchDeepLinkIfNotConsumed() {
std::string deep_link;
// This block exists to ensure that the lock is held while accessing
// unconsumed_deep_link_.
{
base::AutoLock auto_lock(unconsumed_deep_link_lock_);
deep_link = unconsumed_deep_link_;
}
if (!deep_link.empty()) {
LOG(INFO) << "Dispatching deep link: " << deep_link;
DispatchEventInternal(new base::DeepLinkEvent(
deep_link, base::Bind(&Application::OnDeepLinkConsumedCallback,
base::Unretained(this), deep_link)));
}
}
} // namespace browser
} // namespace cobalt
const char* GetCobaltUserAgentString() {
static std::string ua = cobalt::browser::CreateUserAgentString(
cobalt::browser::GetUserAgentPlatformInfoFromSystem());
return ua.c_str();
}