| // 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. |
| |
| #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/metrics/user_metrics.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/date_time_configuration_changed_event.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/starboard_stats_tracker.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_on_offline_event.h" |
| #include "cobalt/base/window_on_online_event.h" |
| #include "cobalt/base/window_size_changed_event.h" |
| #include "cobalt/browser/client_hint_headers.h" |
| #include "cobalt/browser/device_authentication.h" |
| #include "cobalt/browser/memory_settings/auto_mem_settings.h" |
| #include "cobalt/browser/metrics/cobalt_metrics_services_manager.h" |
| #include "cobalt/browser/switches.h" |
| #include "cobalt/browser/user_agent_platform_info.h" |
| #include "cobalt/browser/user_agent_string.h" |
| #include "cobalt/cache/cache.h" |
| #include "cobalt/configuration/configuration.h" |
| #include "cobalt/h5vcc/h5vcc_crash_log.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 "cobalt/watchdog/watchdog.h" |
| #include "components/metrics/metrics_service.h" |
| #include "starboard/common/device_type.h" |
| #include "starboard/common/metrics/stats_tracker.h" |
| #include "starboard/common/system_property.h" |
| #include "starboard/configuration.h" |
| #include "starboard/event.h" |
| #include "starboard/extension/crash_handler.h" |
| #include "starboard/extension/installation_manager.h" |
| #include "starboard/system.h" |
| #include "starboard/time.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) |
| |
| const char kPersistentSettingsJson[] = "settings.json"; |
| |
| // The watchdog client name used to represent Stats. |
| const char kWatchdogName[] = "stats"; |
| // The watchdog time interval in microseconds allowed between pings before |
| // triggering violations. |
| const int64_t kWatchdogTimeInterval = 10000000; |
| // The watchdog time wait in microseconds to initially wait before triggering |
| // violations. |
| const int64_t kWatchdogTimeWait = 2000000; |
| |
| 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; |
| ip_v6 = SbSocketIsIpv6Supported(); |
| // Default to INADDR_ANY |
| std::string listen_ip(ip_v6 ? "::" : "0.0.0.0"); |
| |
| #if SB_API_VERSION < 15 |
| // Desktop PCs default to loopback. |
| if (SbSystemGetDeviceType() == kSbSystemDeviceTypeDesktopPC) { |
| listen_ip = ip_v6 ? "::1" : "127.0.0.1"; |
| } |
| #else |
| if (starboard::GetSystemPropertyString(kSbSystemPropertyDeviceType) == |
| starboard::kSystemDeviceTypeDesktopPC) { |
| listen_ip = ip_v6 ? "::1" : "127.0.0.1"; |
| } |
| #endif |
| |
| #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; |
| } |
| #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 (!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); |
| } |
| } |
| |
| 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() { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kMinLogLevel)) { |
| return command_line->GetSwitchValueASCII(switches::kMinLogLevel); |
| } |
| #if defined(OFFICIAL_BUILD) |
| return "fatal"; |
| #else |
| return "info"; |
| #endif |
| } |
| |
| 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; |
| #if defined(OFFICIAL_BUILD) |
| return logging::LOG_FATAL; |
| #else |
| return logging::LOG_INFO; |
| #endif |
| } |
| } |
| |
| 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; |
| network::CORSPolicy cors_policy; |
| }; |
| |
| // |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; |
| |
| void AddCrashHandlerAnnotations(const UserAgentPlatformInfo& platform_info) { |
| auto crash_handler_extension = |
| static_cast<const CobaltExtensionCrashHandlerApi*>( |
| SbSystemGetExtension(kCobaltExtensionCrashHandlerName)); |
| if (!crash_handler_extension) { |
| LOG(INFO) << "No crash handler extension, not sending annotations."; |
| return; |
| } |
| |
| 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'); |
| |
| bool result = true; |
| if (crash_handler_extension->version == 1) { |
| CrashpadAnnotations crashpad_annotations; |
| memset(&crashpad_annotations, 0, sizeof(CrashpadAnnotations)); |
| strncpy(crashpad_annotations.user_agent_string, user_agent.c_str(), |
| USER_AGENT_STRING_MAX_SIZE); |
| strncpy(crashpad_annotations.product, product.c_str(), |
| CRASHPAD_ANNOTATION_DEFAULT_LENGTH); |
| strncpy(crashpad_annotations.version, version.c_str(), |
| CRASHPAD_ANNOTATION_DEFAULT_LENGTH); |
| result = crash_handler_extension->OverrideCrashpadAnnotations( |
| &crashpad_annotations); |
| } else if (crash_handler_extension->version > 1) { |
| // These particular annotation key names (e.g., ver) are expected by |
| // Crashpad. |
| // TODO(b/201538792): figure out a clean way to define these constants once |
| // and use them everywhere. |
| if (!crash_handler_extension->SetString("user_agent_string", |
| user_agent.c_str())) { |
| result = false; |
| } |
| // TODO(b/265339522): move crashpad prod and ver setter to starboard. |
| if (!crash_handler_extension->SetString("prod", product.c_str())) { |
| result = false; |
| } |
| if (!crash_handler_extension->SetString("ver", version.c_str())) { |
| result = false; |
| } |
| } else { |
| result = false; |
| } // Invalid extension version |
| if (result) { |
| LOG(INFO) << "Sent annotations to crash handler"; |
| } else { |
| LOG(ERROR) << "Could not send some annotation(s) to crash handler."; |
| } |
| } |
| |
| void AddCrashLogApplicationState(base::ApplicationState state) { |
| std::string application_state = std::string(GetApplicationStateString(state)); |
| application_state.push_back('\0'); |
| |
| auto crash_handler_extension = |
| static_cast<const CobaltExtensionCrashHandlerApi*>( |
| SbSystemGetExtension(kCobaltExtensionCrashHandlerName)); |
| if (crash_handler_extension && crash_handler_extension->version >= 2) { |
| if (!crash_handler_extension->SetString("application_state", |
| application_state.c_str())) { |
| LOG(ERROR) << "Could not send application state to crash handler."; |
| } |
| return; |
| } |
| |
| // Crash handler is not supported, fallback to crash log dictionary. |
| h5vcc::CrashLogDictionary::GetInstance()->SetString("application_state", |
| application_state); |
| } |
| |
| } // namespace |
| |
| // Static user logs |
| ssize_t Application::available_memory_ = 0; |
| int64 Application::lifetime_in_ms_ = 0; |
| |
| Application::Application(const base::Closure& quit_closure, bool should_preload, |
| SbTimeMonotonic timestamp) |
| : message_loop_(base::MessageLoop::current()), quit_closure_(quit_closure) { |
| DCHECK(!quit_closure_.is_null()); |
| if (should_preload) { |
| preload_timestamp_ = timestamp; |
| } else { |
| start_timestamp_ = timestamp; |
| } |
| // 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); |
| |
| // 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"); |
| |
| starboard::StatsTrackerContainer::GetInstance()->set_stats_tracker( |
| std::make_unique<StarboardStatsTracker>()); |
| |
| // Initializes persistent settings. |
| persistent_settings_ = |
| std::make_unique<persistent_storage::PersistentSettings>( |
| kPersistentSettingsJson); |
| |
| // Initialize telemetry/metrics. |
| InitMetrics(); |
| |
| // Initializes Watchdog. |
| watchdog::Watchdog* watchdog = |
| watchdog::Watchdog::CreateInstance(persistent_settings_.get()); |
| DCHECK(watchdog); |
| |
| // Registers Stats as a watchdog client. |
| if (watchdog) |
| watchdog->Register(kWatchdogName, kWatchdogName, |
| base::kApplicationStateStarted, kWatchdogTimeInterval, |
| kWatchdogTimeWait, watchdog::NONE); |
| |
| cobalt::cache::Cache::GetInstance()->set_persistent_settings( |
| persistent_settings_.get()); |
| |
| 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_; |
| |
| network::NetworkModule::Options network_module_options; |
| // Create the main components of our browser. |
| BrowserModule::Options options; |
| network_module_options.preferred_language = language; |
| network_module_options.persistent_settings = persistent_settings_.get(); |
| options.persistent_settings = persistent_settings_.get(); |
| 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.web_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)) { |
| network_module_options.storage_manager_options.savegame_options.factory = |
| &storage::SavegameFake::Create; |
| } |
| |
| if (command_line->HasSwitch(browser::switches::kDisableOnScreenKeyboard)) { |
| options.enable_on_screen_keyboard = false; |
| } |
| |
| #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); |
| } |
| network_module_options.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) { |
| network_module_options.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.web_options.extra_web_file_dir = |
| GetExtraWebFileDir(); |
| SecurityFlags security_flags{csp::kCSPRequired, network::kHTTPSRequired, |
| network::kCORSRequired}; |
| // Set callback to be notified when a navigation occurs that destroys the |
| // underlying WebModule. |
| options.web_module_created_callback = |
| base::Bind(&Application::MainWebModuleCreated, 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::kAllowAllCrossOrigin)) { |
| security_flags.cors_policy = network::kCORSOptional; |
| } |
| |
| if (command_line->HasSwitch(browser::switches::kProd)) { |
| security_flags.https_requirement = network::kHTTPSRequired; |
| security_flags.csp_header_policy = csp::kCSPRequired; |
| security_flags.cors_policy = network::kCORSRequired; |
| } |
| |
| 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 (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) |
| |
| #if defined(COBALT_FORCE_CORS) |
| security_flags.cors_policy = network::kCORSRequired; |
| #endif // defined(COBALT_FORCE_CORS) |
| |
| network_module_options.https_requirement = security_flags.https_requirement; |
| network_module_options.cors_policy = security_flags.cors_policy; |
| options.web_module_options.csp_header_policy = |
| security_flags.csp_header_policy; |
| options.web_module_options.csp_enforcement_type = web::kCspEnforcementEnable; |
| |
| options.requested_viewport_size = requested_viewport_size; |
| |
| // Set callback to collect unload event time before firing document's unload |
| // event. |
| options.web_module_options.collect_unload_event_time_callback = base::Bind( |
| &Application::CollectUnloadEventTimingInfo, base::Unretained(this)); |
| |
| cobalt::browser::UserAgentPlatformInfo platform_info; |
| |
| network_module_.reset(new network::NetworkModule( |
| CreateUserAgentString(platform_info), GetClientHintHeaders(platform_info), |
| &event_dispatcher_, network_module_options)); |
| // This is not necessary, but since some platforms need a lot of time to read |
| // the savegame, start the storage manager as soon as possible here. |
| network_module_->EnsureStorageManagerStarted(); |
| |
| AddCrashHandlerAnnotations(platform_info); |
| |
| #if SB_IS(EVERGREEN) |
| if (SbSystemGetExtension(kCobaltExtensionInstallationManagerName) && |
| !command_line->HasSwitch(switches::kDisableUpdaterModule)) { |
| uint64_t update_check_delay_sec = |
| cobalt::updater::kDefaultUpdateCheckDelaySeconds; |
| if (command_line->HasSwitch(browser::switches::kUpdateCheckDelaySeconds)) { |
| std::string seconds_value = command_line->GetSwitchValueASCII( |
| browser::switches::kUpdateCheckDelaySeconds); |
| if (!base::StringToUint64(seconds_value, &update_check_delay_sec)) { |
| LOG(WARNING) << "Invalid delay specified for the update check: " |
| << seconds_value << ". Using default value: " |
| << cobalt::updater::kDefaultUpdateCheckDelaySeconds; |
| update_check_delay_sec = |
| cobalt::updater::kDefaultUpdateCheckDelaySeconds; |
| } |
| } |
| updater_module_.reset(new updater::UpdaterModule(network_module_.get(), |
| update_check_delay_sec)); |
| } |
| #endif |
| browser_module_.reset( |
| new BrowserModule(initial_url, |
| (should_preload ? base::kApplicationStateConcealed |
| : base::kApplicationStateStarted), |
| &event_dispatcher_, network_module_.get(), |
| #if SB_IS(EVERGREEN) |
| updater_module_.get(), |
| #endif |
| options)); |
| |
| UpdateUserAgent(); |
| |
| // Register event callbacks. |
| window_size_change_event_callback_ = base::Bind( |
| &Application::OnWindowSizeChangedEvent, base::Unretained(this)); |
| event_dispatcher_.AddEventCallback(base::WindowSizeChangedEvent::TypeId(), |
| window_size_change_event_callback_); |
| 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_); |
| 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_); |
| 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_); |
| on_window_on_online_event_callback_ = |
| base::Bind(&Application::OnWindowOnOnlineEvent, base::Unretained(this)); |
| event_dispatcher_.AddEventCallback(base::WindowOnOnlineEvent::TypeId(), |
| on_window_on_online_event_callback_); |
| on_window_on_offline_event_callback_ = |
| base::Bind(&Application::OnWindowOnOfflineEvent, base::Unretained(this)); |
| event_dispatcher_.AddEventCallback(base::WindowOnOfflineEvent::TypeId(), |
| on_window_on_offline_event_callback_); |
| on_date_time_configuration_changed_event_callback_ = |
| base::Bind(&Application::OnDateTimeConfigurationChangedEvent, |
| base::Unretained(this)); |
| event_dispatcher_.AddEventCallback( |
| base::DateTimeConfigurationChangedEvent::TypeId(), |
| on_date_time_configuration_changed_event_callback_); |
| |
| #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(), GetDevServersListenIp(), |
| 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) |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableWebDebugger)) { |
| LOG(INFO) << "Remote web debugger disabled."; |
| } else { |
| int remote_debugging_port = GetRemoteDebuggingPort(); |
| if (remote_debugging_port == 0) { |
| LOG(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. |
| watchdog::Watchdog::DeleteInstance(); |
| |
| // Explicitly delete the global metrics services manager here to give it |
| // an opportunity to clean up late logs and persist metrics. |
| metrics::CobaltMetricsServicesManager::DeleteInstance(); |
| |
| // Unregister event callbacks. |
| event_dispatcher_.RemoveEventCallback(base::WindowSizeChangedEvent::TypeId(), |
| window_size_change_event_callback_); |
| 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_); |
| event_dispatcher_.RemoveEventCallback( |
| base::OnScreenKeyboardSuggestionsUpdatedEvent::TypeId(), |
| on_screen_keyboard_suggestions_updated_event_callback_); |
| event_dispatcher_.RemoveEventCallback( |
| base::AccessibilityCaptionSettingsChangedEvent::TypeId(), |
| on_caption_settings_changed_event_callback_); |
| event_dispatcher_.RemoveEventCallback( |
| base::DateTimeConfigurationChangedEvent::TypeId(), |
| on_date_time_configuration_changed_event_callback_); |
| browser_module_.reset(); |
| network_module_.reset(); |
| } |
| |
| void Application::Start(SbTimeMonotonic timestamp) { |
| if (base::MessageLoop::current() != message_loop_) { |
| message_loop_->task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&Application::Start, base::Unretained(this), timestamp)); |
| return; |
| } |
| DCHECK_CALLED_ON_VALID_THREAD(application_event_thread_checker_); |
| |
| if (!start_timestamp_.has_value()) { |
| start_timestamp_ = timestamp; |
| } |
| OnApplicationEvent(kSbEventTypeStart, timestamp); |
| } |
| |
| void Application::Quit() { |
| if (base::MessageLoop::current() != message_loop_) { |
| message_loop_->task_runner()->PostTask( |
| FROM_HERE, base::Bind(&Application::Quit, base::Unretained(this))); |
| return; |
| } |
| DCHECK_CALLED_ON_VALID_THREAD(application_event_thread_checker_); |
| |
| quit_closure_.Run(); |
| } |
| |
| 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) { |
| case kSbEventTypeBlur: |
| case kSbEventTypeFocus: |
| case kSbEventTypeConceal: |
| case kSbEventTypeReveal: |
| case kSbEventTypeFreeze: |
| case kSbEventTypeUnfreeze: |
| case kSbEventTypeLowMemory: |
| OnApplicationEvent(starboard_event->type, starboard_event->timestamp); |
| break; |
| case kSbEventTypeWindowSizeChanged: |
| DispatchEventInternal(new base::WindowSizeChangedEvent( |
| static_cast<SbEventWindowSizeChangedData*>(starboard_event->data) |
| ->window, |
| static_cast<SbEventWindowSizeChangedData*>(starboard_event->data) |
| ->size)); |
| break; |
| 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; |
| case kSbEventTypeOnScreenKeyboardSuggestionsUpdated: |
| DispatchEventInternal(new base::OnScreenKeyboardSuggestionsUpdatedEvent( |
| *static_cast<int*>(starboard_event->data))); |
| break; |
| case kSbEventTypeLink: { |
| DispatchDeepLink(static_cast<const char*>(starboard_event->data), |
| starboard_event->timestamp); |
| break; |
| } |
| case kSbEventTypeAccessibilitySettingsChanged: |
| DispatchEventInternal(new base::AccessibilitySettingsChangedEvent()); |
| break; |
| case kSbEventTypeAccessibilityCaptionSettingsChanged: |
| DispatchEventInternal( |
| new base::AccessibilityCaptionSettingsChangedEvent()); |
| break; |
| case kSbEventTypeAccessibilityTextToSpeechSettingsChanged: |
| DispatchEventInternal( |
| new base::AccessibilityTextToSpeechSettingsChangedEvent()); |
| break; |
| case kSbEventTypeOsNetworkDisconnected: |
| DispatchEventInternal(new base::WindowOnOfflineEvent()); |
| break; |
| case kSbEventTypeOsNetworkConnected: |
| DispatchEventInternal(new base::WindowOnOnlineEvent()); |
| break; |
| case kSbEventDateTimeConfigurationChanged: |
| DispatchEventInternal(new base::DateTimeConfigurationChangedEvent()); |
| break; |
| // 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: |
| 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, |
| SbTimeMonotonic timestamp) { |
| TRACE_EVENT0("cobalt::browser", "Application::OnApplicationEvent()"); |
| DCHECK_CALLED_ON_VALID_THREAD(application_event_thread_checker_); |
| |
| watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance(); |
| |
| switch (event_type) { |
| case kSbEventTypeStop: |
| LOG(INFO) << "Got quit event."; |
| if (watchdog) watchdog->UpdateState(base::kApplicationStateStopped); |
| AddCrashLogApplicationState(base::kApplicationStateStopped); |
| Quit(); |
| LOG(INFO) << "Finished quitting."; |
| break; |
| case kSbEventTypeStart: |
| LOG(INFO) << "Got start event."; |
| browser_module_->Reveal(timestamp); |
| browser_module_->Focus(timestamp); |
| LOG(INFO) << "Finished starting."; |
| break; |
| case kSbEventTypeBlur: |
| LOG(INFO) << "Got blur event."; |
| browser_module_->Blur(timestamp); |
| LOG(INFO) << "Finished blurring."; |
| break; |
| case kSbEventTypeFocus: |
| LOG(INFO) << "Got focus event."; |
| browser_module_->Focus(timestamp); |
| LOG(INFO) << "Finished focusing."; |
| break; |
| case kSbEventTypeConceal: |
| LOG(INFO) << "Got conceal event."; |
| browser_module_->Conceal(timestamp); |
| LOG(INFO) << "Finished concealing."; |
| break; |
| case kSbEventTypeReveal: |
| DCHECK(SbSystemSupportsResume()); |
| LOG(INFO) << "Got reveal event."; |
| browser_module_->Reveal(timestamp); |
| LOG(INFO) << "Finished revealing."; |
| break; |
| case kSbEventTypeFreeze: |
| LOG(INFO) << "Got freeze event."; |
| browser_module_->Freeze(timestamp); |
| #if SB_IS(EVERGREEN) |
| if (updater_module_) updater_module_->Suspend(); |
| #endif |
| LOG(INFO) << "Finished freezing."; |
| break; |
| case kSbEventTypeUnfreeze: |
| LOG(INFO) << "Got unfreeze event."; |
| browser_module_->Unfreeze(timestamp); |
| #if SB_IS(EVERGREEN) |
| if (updater_module_) updater_module_->Resume(); |
| #endif |
| LOG(INFO) << "Finished unfreezing."; |
| break; |
| 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: |
| case kSbEventTypeWindowSizeChanged: |
| case kSbEventTypeAccessibilityCaptionSettingsChanged: |
| case kSbEventTypeAccessibilityTextToSpeechSettingsChanged: |
| case kSbEventTypeOnScreenKeyboardBlurred: |
| case kSbEventTypeOnScreenKeyboardFocused: |
| case kSbEventTypeOnScreenKeyboardHidden: |
| case kSbEventTypeOnScreenKeyboardShown: |
| case kSbEventTypeOnScreenKeyboardSuggestionsUpdated: |
| case kSbEventTypeAccessibilitySettingsChanged: |
| case kSbEventTypeInput: |
| case kSbEventTypeLink: |
| case kSbEventTypeScheduled: |
| case kSbEventTypeUser: |
| case kSbEventTypeVerticalSync: |
| case kSbEventTypeOsNetworkDisconnected: |
| case kSbEventTypeOsNetworkConnected: |
| case kSbEventDateTimeConfigurationChanged: |
| NOTREACHED() << "Unexpected event type: " << event_type; |
| return; |
| } |
| if (watchdog) watchdog->UpdateState(browser_module_->GetApplicationState()); |
| AddCrashLogApplicationState(browser_module_->GetApplicationState()); |
| } |
| |
| 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(); |
| float diagonal = |
| SbWindowGetDiagonalSizeInInches(window_size_change_event->window()); |
| |
| // 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); |
| } |
| |
| 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)); |
| } |
| |
| void Application::OnOnScreenKeyboardSuggestionsUpdatedEvent( |
| const base::Event* event) { |
| TRACE_EVENT0("cobalt::browser", |
| "Application::OnOnScreenKeyboardSuggestionsUpdatedEvent()"); |
| browser_module_->OnOnScreenKeyboardSuggestionsUpdated( |
| base::polymorphic_downcast< |
| const base::OnScreenKeyboardSuggestionsUpdatedEvent*>(event)); |
| } |
| |
| void Application::OnCaptionSettingsChangedEvent(const base::Event* event) { |
| TRACE_EVENT0("cobalt::browser", |
| "Application::OnCaptionSettingsChangedEvent()"); |
| browser_module_->OnCaptionSettingsChanged( |
| base::polymorphic_downcast< |
| const base::AccessibilityCaptionSettingsChangedEvent*>(event)); |
| } |
| |
| void Application::OnWindowOnOnlineEvent(const base::Event* event) { |
| browser_module_->OnWindowOnOnlineEvent( |
| base::polymorphic_downcast<const base::WindowOnOnlineEvent*>(event)); |
| } |
| void Application::OnWindowOnOfflineEvent(const base::Event* event) { |
| browser_module_->OnWindowOnOfflineEvent( |
| base::polymorphic_downcast<const base::WindowOnOfflineEvent*>(event)); |
| } |
| |
| void Application::OnDateTimeConfigurationChangedEvent( |
| const base::Event* event) { |
| TRACE_EVENT0("cobalt::browser", |
| "Application::OnDateTimeConfigurationChangedEvent()"); |
| browser_module_->OnDateTimeConfigurationChanged( |
| base::polymorphic_downcast< |
| const base::DateTimeConfigurationChangedEvent*>(event)); |
| } |
| |
| void Application::MainWebModuleCreated(WebModule* web_module) { |
| TRACE_EVENT0("cobalt::browser", "Application::MainWebModuleCreated()"); |
| // Note: This is a callback function that runs in the MainWebModule thread. |
| DCHECK(web_module); |
| if (preload_timestamp_.has_value()) { |
| web_module->SetApplicationStartOrPreloadTimestamp( |
| true, preload_timestamp_.value()); |
| } |
| if (start_timestamp_.has_value()) { |
| web_module->SetApplicationStartOrPreloadTimestamp(false, |
| start_timestamp_.value()); |
| } |
| DispatchDeepLinkIfNotConsumed(); |
| #if defined(ENABLE_WEBDRIVER) |
| if (web_driver_module_) { |
| web_driver_module_->OnWindowRecreated(); |
| } |
| #endif |
| if (!unload_event_start_time_.is_null() || |
| !unload_event_end_time_.is_null()) { |
| web_module->SetUnloadEventTimingInfo(unload_event_start_time_, |
| unload_event_end_time_); |
| } |
| } |
| |
| void Application::CollectUnloadEventTimingInfo(base::TimeTicks start_time, |
| base::TimeTicks end_time) { |
| unload_event_start_time_ = start_time; |
| unload_event_end_time_ = end_time; |
| } |
| |
| 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(); |
| |
| // Pings watchdog. |
| watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance(); |
| if (watchdog) { |
| #if defined(_DEBUG) |
| // Injects delay for watchdog debugging. |
| watchdog->MaybeInjectDebugDelay(kWatchdogName); |
| #endif // defined(_DEBUG) |
| watchdog->Ping(kWatchdogName); |
| } |
| |
| 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)); |
| } |
| |
| // 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, |
| SbTimeMonotonic timestamp) { |
| 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_; |
| deep_link_timestamp_ = timestamp; |
| } |
| |
| LOG(INFO) << "Dispatching deep link: " << deep_link; |
| DispatchEventInternal(new base::DeepLinkEvent( |
| deep_link, base::Bind(&Application::OnDeepLinkConsumedCallback, |
| base::Unretained(this), deep_link))); |
| if (browser_module_) { |
| browser_module_->SetDeepLinkTimestamp(timestamp); |
| } |
| } |
| |
| void Application::DispatchDeepLinkIfNotConsumed() { |
| std::string deep_link; |
| SbTimeMonotonic timestamp; |
| // 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_; |
| timestamp = deep_link_timestamp_; |
| } |
| |
| 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))); |
| } |
| if (browser_module_) { |
| browser_module_->SetDeepLinkTimestamp(timestamp); |
| } |
| } |
| |
| void Application::InitMetrics() { |
| // Must be called early as it initializes global state which is then read by |
| // all threads without synchronization. |
| // RecordAction task runner must be called before metric initialization. |
| base::SetRecordActionTaskRunner(base::ThreadTaskRunnerHandle::Get()); |
| metrics_services_manager_ = |
| metrics::CobaltMetricsServicesManager::GetInstance(); |
| // Before initializing metrics manager, set any persisted settings like if |
| // it's enabled or upload interval. |
| bool is_metrics_enabled = persistent_settings_->GetPersistentSettingAsBool( |
| metrics::kMetricEnabledSettingName, false); |
| auto metric_event_interval = persistent_settings_->GetPersistentSettingAsInt( |
| metrics::kMetricEventIntervalSettingName, 300); |
| metrics_services_manager_->SetUploadInterval(metric_event_interval); |
| metrics_services_manager_->ToggleMetricsEnabled(is_metrics_enabled); |
| // Metric recording state initialization _must_ happen before we bootstrap |
| // otherwise we crash. |
| metrics_services_manager_->GetMetricsService() |
| ->InitializeMetricsRecordingState(); |
| // UpdateUploadPermissions bootstraps the whole metric reporting, scheduling, |
| // and uploading cycle. |
| metrics_services_manager_->UpdateUploadPermissions(is_metrics_enabled); |
| LOG(INFO) |
| << "Cobalt Telemetry initialized with settings: is_metrics_enabled: " |
| << is_metrics_enabled |
| << ", metric_event_interval: " << metric_event_interval; |
| } |
| |
| } // namespace browser |
| } // namespace cobalt |
| |
| const char* GetCobaltUserAgentString() { |
| static std::string ua = cobalt::browser::CreateUserAgentString( |
| cobalt::browser::GetUserAgentPlatformInfoFromSystem()); |
| return ua.c_str(); |
| } |