| // Copyright 2015 Google Inc. 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 <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/optional.h" |
| #include "base/path_service.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/time.h" |
| #include "build/build_config.h" |
| #include "cobalt/base/accessibility_settings_changed_event.h" |
| #include "cobalt/base/application_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/startup_timer.h" |
| #include "cobalt/base/user_log.h" |
| #include "cobalt/browser/memory_settings/auto_mem_settings.h" |
| #include "cobalt/browser/memory_tracker/tool.h" |
| #include "cobalt/browser/switches.h" |
| #include "cobalt/loader/image/image_decoder.h" |
| #include "cobalt/math/size.h" |
| #include "cobalt/network/network_event.h" |
| #include "cobalt/script/javascript_engine.h" |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| #include "cobalt/storage/savegame_fake.h" |
| #endif |
| #include "cobalt/system_window/input_event.h" |
| #include "cobalt/trace_event/scoped_trace_to_file.h" |
| #include "googleurl/src/gurl.h" |
| #include "starboard/configuration.h" |
| |
| namespace cobalt { |
| namespace browser { |
| |
| namespace { |
| const int kStatUpdatePeriodMs = 1000; |
| |
| const char kDefaultURL[] = "https://www.youtube.com/tv"; |
| |
| bool IsStringNone(const std::string& str) { |
| return !base::strcasecmp(str.c_str(), "none"); |
| } |
| |
| #if defined(ENABLE_REMOTE_DEBUGGING) |
| int GetRemoteDebuggingPort() { |
| #if defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT) |
| const int kDefaultRemoteDebuggingPort = |
| SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT; |
| #else |
| const int kDefaultRemoteDebuggingPort = 9222; |
| #endif // defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT) |
| int remote_debugging_port = kDefaultRemoteDebuggingPort; |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) { |
| const std::string switchValue = |
| command_line->GetSwitchValueASCII(switches::kRemoteDebuggingPort); |
| if (!base::StringToInt(switchValue, &remote_debugging_port)) { |
| DLOG(ERROR) << "Invalid port specified for remote debug server: " |
| << switchValue |
| << ". Using default port: " << kDefaultRemoteDebuggingPort; |
| remote_debugging_port = kDefaultRemoteDebuggingPort; |
| } |
| } |
| #endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES |
| return remote_debugging_port; |
| } |
| #endif // ENABLE_REMOTE_DEBUGGING |
| |
| #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) |
| CommandLine* command_line = 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 = |
| webdriver::WebDriverModule::kDefaultListenIp; |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kWebDriverListenIp)) { |
| webdriver_listen_ip = |
| command_line->GetSwitchValueASCII(switches::kWebDriverListenIp); |
| } |
| #endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES |
| return webdriver_listen_ip; |
| } |
| #endif // ENABLE_WEBDRIVER |
| |
| GURL GetInitialURL() { |
| // Allow the user to override the default URL via a command line parameter. |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kInitialURL)) { |
| GURL url = GURL(command_line->GetSwitchValueASCII(switches::kInitialURL)); |
| if (url.is_valid()) { |
| return url; |
| } else { |
| DLOG(ERROR) << "URL from parameter " << command_line |
| << " is not valid, using default URL."; |
| } |
| } |
| |
| return GURL(kDefaultURL); |
| } |
| |
| base::optional<GURL> GetFallbackSplashScreenURL() { |
| CommandLine* command_line = 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 = COBALT_FALLBACK_SPLASH_SCREEN_URL; |
| } |
| if (IsStringNone(fallback_splash_screen_string)) { |
| return base::optional<GURL>(); |
| } |
| base::optional<GURL> fallback_splash_screen_url = |
| GURL(fallback_splash_screen_string); |
| if (!fallback_splash_screen_url->is_valid() || |
| !(fallback_splash_screen_url->SchemeIsFile() || |
| fallback_splash_screen_url->SchemeIs("h5vcc-embedded"))) { |
| LOG(FATAL) << "Ignoring invalid fallback splash screen: " |
| << fallback_splash_screen_string; |
| } |
| return fallback_splash_screen_url; |
| } |
| |
| base::TimeDelta GetTimedTraceDuration() { |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| CommandLine* command_line = 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(); |
| } |
| |
| FilePath GetExtraWebFileDir() { |
| // Default is empty, command line can override. |
| FilePath result; |
| |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kExtraWebFileDir)) { |
| result = |
| FilePath(command_line->GetSwitchValueASCII(switches::kExtraWebFileDir)); |
| if (!result.IsAbsolute()) { |
| // Non-absolute paths are relative to the executable directory. |
| FilePath content_path; |
| 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() { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kStubImageDecoder)) { |
| loader::image::ImageDecoder::UseStubImageDecoder(); |
| } |
| } |
| #endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES |
| |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| base::optional<math::Size> GetVideoOutputResolutionOverride( |
| CommandLine* command_line) { |
| DCHECK(command_line); |
| if (command_line->HasSwitch(switches::kVideoContainerSizeOverride)) { |
| std::string size_override = command_line->GetSwitchValueASCII( |
| browser::switches::kVideoContainerSizeOverride); |
| DLOG(INFO) << "Set video container size override from command line to " |
| << size_override; |
| // Override string should be something like "1920x1080". |
| int32 width, height; |
| std::vector<std::string> tokens; |
| base::SplitString(size_override, 'x', &tokens); |
| if (tokens.size() == 2 && base::StringToInt32(tokens[0], &width) && |
| base::StringToInt32(tokens[1], &height)) { |
| return math::Size(width, height); |
| } |
| |
| DLOG(WARNING) << "Invalid size specified for video container: " |
| << size_override; |
| } |
| |
| return base::nullopt; |
| } |
| #endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES |
| |
| // Represents a parsed int. |
| struct ParsedIntValue { |
| public: |
| ParsedIntValue() : value_(0), error_(false) {} |
| ParsedIntValue(const ParsedIntValue& other) |
| : value_(other.value_), error_(other.error_) {} |
| int value_; |
| bool error_; // true if there was a parse error. |
| }; |
| |
| // Parses a string like "1234x5678" to vector of parsed int values. |
| std::vector<ParsedIntValue> ParseDimensions(const std::string& value_str) { |
| std::vector<ParsedIntValue> output; |
| |
| std::vector<std::string> lengths; |
| base::SplitString(value_str, 'x', &lengths); |
| |
| for (size_t i = 0; i < lengths.size(); ++i) { |
| ParsedIntValue parsed_value; |
| parsed_value.error_ = !base::StringToInt(lengths[i], &parsed_value.value_); |
| output.push_back(parsed_value); |
| } |
| return output; |
| } |
| |
| base::optional<math::Size> GetRequestedViewportSize(CommandLine* command_line) { |
| DCHECK(command_line); |
| if (!command_line->HasSwitch(browser::switches::kViewport)) { |
| return base::nullopt; |
| } |
| |
| const std::string switchValue = |
| command_line->GetSwitchValueASCII(browser::switches::kViewport); |
| std::vector<ParsedIntValue> parsed_ints = ParseDimensions(switchValue); |
| if (parsed_ints.size() < 1) { |
| return base::nullopt; |
| } |
| |
| const ParsedIntValue parsed_width = parsed_ints[0]; |
| if (parsed_width.error_) { |
| DLOG(ERROR) << "Invalid value specified for viewport width: " << switchValue |
| << ". Using default viewport size."; |
| return base::nullopt; |
| } |
| |
| const ParsedIntValue* parsed_height_ptr = NULL; |
| if (parsed_ints.size() >= 2) { |
| parsed_height_ptr = &parsed_ints[1]; |
| } |
| |
| if (!parsed_height_ptr) { |
| // 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 (parsed_width.value_ >= 1280) { |
| return math::Size(parsed_width.value_, 9 * parsed_width.value_ / 16); |
| } |
| |
| return math::Size(parsed_width.value_, 3 * parsed_width.value_ / 4); |
| } |
| |
| if (parsed_height_ptr->error_) { |
| DLOG(ERROR) << "Invalid value specified for viewport height: " |
| << switchValue << ". Using default viewport size."; |
| return base::nullopt; |
| } |
| |
| return math::Size(parsed_width.value_, parsed_height_ptr->value_); |
| } |
| |
| std::string GetMinLogLevelString() { |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| CommandLine* command_line = 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 (CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) { |
| int32 out; |
| if (base::StringToInt32( |
| 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::kSurfaceCacheSizeInBytes, |
| &options->surface_cache_size_in_bytes); |
| SetIntegerIfSwitchIsSet(browser::switches::kScratchSurfaceCacheSizeInBytes, |
| &options->scratch_surface_cache_size_in_bytes); |
| } |
| |
| // Restrict navigation to a couple of whitelisted URLs by default. |
| const char kYouTubeTvLocationPolicy[] = |
| "h5vcc-location-src " |
| "https://s.ytimg.com/yts/cobalt/ " |
| "https://www.youtube.com/tv " |
| "https://www.youtube.com/tv/ " |
| "https://web-green-qa.youtube.com/tv " |
| "https://web-green-qa.youtube.com/tv/ " |
| "https://web-release-qa.youtube.com/tv " |
| "https://web-release-qa.youtube.com/tv/ " |
| #if defined(ENABLE_ABOUT_SCHEME) |
| "about: " |
| #endif |
| "h5vcc:"; |
| |
| #if !defined(COBALT_FORCE_CSP) |
| dom::CspEnforcementType StringToCspMode(const std::string& mode) { |
| if (mode == "disable") { |
| return dom::kCspEnforcementDisable; |
| } else if (mode == "enable") { |
| return dom::kCspEnforcementEnable; |
| } else { |
| DLOG(INFO) << "Invalid CSP mode: " << mode << ": use [disable|enable]"; |
| return dom::kCspEnforcementEnable; |
| } |
| } |
| #endif // !defined(COBALT_FORCE_CSP) |
| |
| struct NonTrivialStaticFields { |
| NonTrivialStaticFields() : system_language(base::GetSystemLanguage()) {} |
| |
| const std::string system_language; |
| std::string user_agent; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(NonTrivialStaticFields); |
| }; |
| |
| // |non_trivial_static_fields| will be lazily created on the first time it's |
| // accessed. |
| base::LazyInstance<NonTrivialStaticFields> non_trivial_static_fields = |
| LAZY_INSTANCE_INITIALIZER; |
| } // namespace |
| |
| // Static user logs |
| ssize_t Application::available_memory_ = 0; |
| int64 Application::lifetime_in_ms_ = 0; |
| |
| Application::AppStatus Application::app_status_ = |
| Application::kUninitializedAppStatus; |
| int Application::app_pause_count_ = 0; |
| int Application::app_unpause_count_ = 0; |
| int Application::app_suspend_count_ = 0; |
| int Application::app_resume_count_ = 0; |
| |
| Application::NetworkStatus Application::network_status_ = |
| Application::kDisconnectedNetworkStatus; |
| int Application::network_connect_count_ = 0; |
| int Application::network_disconnect_count_ = 0; |
| |
| Application::Application(const base::Closure& quit_closure, bool should_preload) |
| : message_loop_(MessageLoop::current()), |
| quit_closure_(quit_closure), |
| stats_update_timer_(true, true) { |
| // 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( |
| FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration); |
| } |
| |
| TRACE_EVENT0("cobalt::browser", "Application::Application()"); |
| |
| DCHECK(MessageLoop::current()); |
| DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); |
| |
| network_event_thread_checker_.DetachFromThread(); |
| application_event_thread_checker_.DetachFromThread(); |
| |
| RegisterUserLogs(); |
| |
| // 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(); |
| 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); |
| |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| base::optional<math::Size> requested_viewport_size = |
| GetRequestedViewportSize(command_line); |
| |
| WebModule::Options web_options; |
| // Create the main components of our browser. |
| BrowserModule::Options options(web_options); |
| options.web_module_options.name = "MainWebModule"; |
| options.language = language; |
| options.initial_deep_link = GetInitialDeepLink(); |
| options.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; |
| |
| 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_options.disable_jit = true; |
| } |
| |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| if (command_line->HasSwitch(browser::switches::kNullSavegame)) { |
| options.storage_manager_options.savegame_options.factory = |
| &storage::SavegameFake::Create; |
| } |
| #endif |
| |
| base::optional<std::string> initial_key = |
| base::GetApplicationKey(initial_url); |
| options.storage_manager_options.savegame_options.id = initial_key; |
| |
| base::optional<std::string> default_key = |
| base::GetApplicationKey(GURL(kDefaultURL)); |
| if (initial_key == default_key) { |
| 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.extra_web_file_dir = GetExtraWebFileDir(); |
| options.web_module_options.location_policy = kYouTubeTvLocationPolicy; |
| // Set callback to be notified when a navigation occurs that destroys the |
| // underlying WebModule. |
| options.web_module_recreated_callback = |
| base::Bind(&Application::WebModuleRecreated, base::Unretained(this)); |
| |
| // The main web module's stat tracker tracks event stats. |
| options.web_module_options.track_event_stats = true; |
| |
| #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) |
| if (command_line->HasSwitch(browser::switches::kProxy)) { |
| options.network_module_options.custom_proxy = |
| command_line->GetSwitchValueASCII(browser::switches::kProxy); |
| } |
| |
| #if !defined(COBALT_FORCE_CSP) |
| if (command_line->HasSwitch(browser::switches::kCspMode)) { |
| options.web_module_options.csp_enforcement_mode = StringToCspMode( |
| command_line->GetSwitchValueASCII(browser::switches::kCspMode)); |
| } |
| if (options.web_module_options.csp_enforcement_mode != |
| dom::kCspEnforcementEnable) { |
| options.web_module_options.location_policy = "h5vcc-location-src *"; |
| } |
| #endif // !defined(COBALT_FORCE_CSP) |
| |
| #if defined(ENABLE_IGNORE_CERTIFICATE_ERRORS) |
| if (command_line->HasSwitch(browser::switches::kIgnoreCertificateErrors)) { |
| options.network_module_options.ignore_certificate_errors = true; |
| } |
| #endif // defined(ENABLE_IGNORE_CERTIFICATE_ERRORS) |
| |
| #if !defined(COBALT_FORCE_HTTPS) |
| if (command_line->HasSwitch(switches::kAllowHttp)) { |
| DLOG(INFO) << "Allowing insecure HTTP connections"; |
| options.network_module_options.require_https = false; |
| } |
| #endif // !defined(COBALT_FORCE_HTTPS) |
| |
| 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(browser::switches::kDisableWebmVp9)) { |
| DLOG(INFO) << "Webm/Vp9 disabled"; |
| options.media_module_options.disable_webm_vp9 = true; |
| } |
| if (command_line->HasSwitch(switches::kAudioDecoderStub)) { |
| DLOG(INFO) << "Use ShellRawAudioDecoderStub"; |
| options.media_module_options.use_audio_decoder_stub = true; |
| } |
| if (command_line->HasSwitch(switches::kNullAudioStreamer)) { |
| DLOG(INFO) << "Use null audio"; |
| options.media_module_options.use_null_audio_streamer = true; |
| } |
| if (command_line->HasSwitch(switches::kVideoDecoderStub)) { |
| DLOG(INFO) << "Use ShellRawVideoDecoderStub"; |
| options.media_module_options.use_video_decoder_stub = true; |
| } |
| options.media_module_options.output_resolution_override = |
| GetVideoOutputResolutionOverride(command_line); |
| if (command_line->HasSwitch(switches::kMemoryTracker)) { |
| std::string command_arg = |
| command_line->GetSwitchValueASCII(switches::kMemoryTracker); |
| memory_tracker_tool_ = |
| memory_tracker::CreateMemoryTrackerTool(command_arg); |
| } |
| |
| if (command_line->HasSwitch(switches::kDisableImageAnimations)) { |
| options.web_module_options.enable_image_animations = false; |
| } |
| #endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES |
| |
| if (command_line->HasSwitch(browser::switches::kDisableNavigationWhitelist)) { |
| LOG(ERROR) << "\n" |
| << " *** Disabling the default navigation whitelist! ***\n" |
| << " *** Do not run in this mode in production! ***"; |
| options.web_module_options.location_policy = "h5vcc-location-src *"; |
| } |
| |
| options.requested_viewport_size = requested_viewport_size; |
| account_manager_.reset(new account::AccountManager()); |
| browser_module_.reset( |
| new BrowserModule(initial_url, |
| (should_preload ? base::kApplicationStatePreloading |
| : base::kApplicationStateStarted), |
| &event_dispatcher_, account_manager_.get(), options)); |
| UpdateAndMaybeRegisterUserAgent(); |
| |
| app_status_ = (should_preload ? kPreloadingAppStatus : kRunningAppStatus); |
| |
| // Register event callbacks. |
| network_event_callback_ = |
| base::Bind(&Application::OnNetworkEvent, base::Unretained(this)); |
| event_dispatcher_.AddEventCallback(network::NetworkEvent::TypeId(), |
| network_event_callback_); |
| deep_link_event_callback_ = |
| base::Bind(&Application::OnDeepLinkEvent, base::Unretained(this)); |
| event_dispatcher_.AddEventCallback(base::DeepLinkEvent::TypeId(), |
| deep_link_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(), GetWebDriverListenIp(), |
| base::Bind(&BrowserModule::CreateSessionDriver, |
| base::Unretained(browser_module_.get())), |
| base::Bind(&BrowserModule::RequestScreenshotToBuffer, |
| 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_REMOTE_DEBUGGING) |
| int remote_debugging_port = GetRemoteDebuggingPort(); |
| debug_web_server_.reset(new debug::DebugWebServer( |
| remote_debugging_port, |
| base::Bind(&BrowserModule::GetDebugServer, |
| base::Unretained(browser_module_.get())))); |
| #endif // ENABLE_REMOTE_DEBUGGING |
| |
| #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_->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. |
| memory_tracker_tool_.reset(NULL); |
| |
| // Unregister event callbacks. |
| event_dispatcher_.RemoveEventCallback(network::NetworkEvent::TypeId(), |
| network_event_callback_); |
| event_dispatcher_.RemoveEventCallback( |
| base::DeepLinkEvent::TypeId(), deep_link_event_callback_); |
| |
| app_status_ = kShutDownAppStatus; |
| } |
| |
| void Application::Start() { |
| if (MessageLoop::current() != message_loop_) { |
| message_loop_->PostTask( |
| FROM_HERE, base::Bind(&Application::Start, base::Unretained(this))); |
| return; |
| } |
| |
| if (app_status_ != kPreloadingAppStatus) { |
| NOTREACHED() << __FUNCTION__ << ": Redundant call."; |
| return; |
| } |
| |
| OnApplicationEvent(kSbEventTypeStart); |
| } |
| |
| void Application::Quit() { |
| if (MessageLoop::current() != message_loop_) { |
| message_loop_->PostTask( |
| FROM_HERE, base::Bind(&Application::Quit, base::Unretained(this))); |
| return; |
| } |
| |
| DCHECK(!quit_closure_.is_null()); |
| if (!quit_closure_.is_null()) { |
| quit_closure_.Run(); |
| } |
| |
| app_status_ = kQuitAppStatus; |
| } |
| |
| void Application::HandleStarboardEvent(const SbEvent* starboard_event) { |
| DCHECK(starboard_event); |
| |
| // 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 kSbEventTypePause: |
| case kSbEventTypeUnpause: |
| case kSbEventTypeSuspend: |
| case kSbEventTypeResume: |
| #if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION |
| case kSbEventTypeLowMemory: |
| #endif // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION |
| OnApplicationEvent(starboard_event->type); |
| break; |
| case kSbEventTypeNetworkConnect: |
| DispatchEventInternal( |
| new network::NetworkEvent(network::NetworkEvent::kConnection)); |
| break; |
| case kSbEventTypeNetworkDisconnect: |
| DispatchEventInternal( |
| new network::NetworkEvent(network::NetworkEvent::kDisconnection)); |
| break; |
| case kSbEventTypeLink: { |
| const char* link = static_cast<const char*>(starboard_event->data); |
| DispatchEventInternal(new base::DeepLinkEvent(link)); |
| break; |
| } |
| case kSbEventTypeAccessiblitySettingsChanged: |
| DispatchEventInternal(new base::AccessibilitySettingsChangedEvent()); |
| break; |
| default: |
| DLOG(WARNING) << "Unhandled Starboard event of type: " |
| << starboard_event->type; |
| } |
| } |
| |
| void Application::OnNetworkEvent(const base::Event* event) { |
| TRACE_EVENT0("cobalt::browser", "Application::OnNetworkEvent()"); |
| DCHECK(network_event_thread_checker_.CalledOnValidThread()); |
| const network::NetworkEvent* network_event = |
| base::polymorphic_downcast<const network::NetworkEvent*>(event); |
| if (network_event->type() == network::NetworkEvent::kDisconnection) { |
| network_status_ = kDisconnectedNetworkStatus; |
| ++network_disconnect_count_; |
| browser_module_->Navigate(GURL("h5vcc://network-failure")); |
| } else if (network_event->type() == network::NetworkEvent::kConnection) { |
| network_status_ = kConnectedNetworkStatus; |
| ++network_connect_count_; |
| if (network_disconnect_count_ > 0) { |
| DLOG(INFO) << "Got network connection event, reloading browser."; |
| browser_module_->Reload(); |
| } else { |
| DLOG(INFO) << "Got network connection event, NOT reloading browser."; |
| } |
| } |
| } |
| |
| void Application::OnApplicationEvent(SbEventType event_type) { |
| TRACE_EVENT0("cobalt::browser", "Application::OnApplicationEvent()"); |
| DCHECK(application_event_thread_checker_.CalledOnValidThread()); |
| 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_->Start(); |
| DLOG(INFO) << "Finished starting."; |
| break; |
| case kSbEventTypePause: |
| DLOG(INFO) << "Got pause event."; |
| app_status_ = kPausedAppStatus; |
| ++app_pause_count_; |
| browser_module_->Pause(); |
| DLOG(INFO) << "Finished pausing."; |
| break; |
| case kSbEventTypeUnpause: |
| DLOG(INFO) << "Got unpause event."; |
| app_status_ = kRunningAppStatus; |
| ++app_unpause_count_; |
| browser_module_->Unpause(); |
| DLOG(INFO) << "Finished unpausing."; |
| break; |
| case kSbEventTypeSuspend: |
| DLOG(INFO) << "Got suspend event."; |
| app_status_ = kSuspendedAppStatus; |
| ++app_suspend_count_; |
| browser_module_->Suspend(); |
| DLOG(INFO) << "Finished suspending."; |
| break; |
| case kSbEventTypeResume: |
| DLOG(INFO) << "Got resume event."; |
| app_status_ = kPausedAppStatus; |
| ++app_resume_count_; |
| browser_module_->Resume(); |
| DLOG(INFO) << "Finished resuming."; |
| break; |
| #if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION |
| case kSbEventTypeLowMemory: |
| DLOG(INFO) << "Got low memory event."; |
| browser_module_->ReduceMemory(); |
| DLOG(INFO) << "Finished reducing memory usage."; |
| break; |
| #endif // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION |
| default: |
| NOTREACHED() << "Unexpected event type: " << event_type; |
| return; |
| } |
| } |
| |
| void Application::OnDeepLinkEvent(const base::Event* event) { |
| TRACE_EVENT0("cobalt::browser", "Application::OnDeepLinkEvent()"); |
| const base::DeepLinkEvent* deep_link_event = |
| base::polymorphic_downcast<const base::DeepLinkEvent*>(event); |
| // TODO: Remove this when terminal application states are properly handled. |
| if (deep_link_event->IsH5vccLink()) { |
| browser_module_->Navigate(GURL(deep_link_event->link())); |
| } |
| } |
| |
| void Application::WebModuleRecreated() { |
| TRACE_EVENT0("cobalt::browser", "Application::WebModuleRecreated()"); |
| #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."), |
| js_reserved_memory("Memory.JS", 0, |
| "The total memory that is reserved by the engine, " |
| "including the part that is actually occupied by " |
| "JS objects, and the part that is not yet."), |
| 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."); |
| } |
| } |
| |
| void Application::RegisterUserLogs() { |
| if (base::UserLog::IsRegistrationSupported()) { |
| base::UserLog::Register( |
| base::UserLog::kSystemLanguageStringIndex, "SystemLanguage", |
| non_trivial_static_fields.Get().system_language.c_str(), |
| non_trivial_static_fields.Get().system_language.size()); |
| |
| base::UserLog::Register(base::UserLog::kAvailableMemoryIndex, |
| "AvailableMemory", &available_memory_, |
| sizeof(available_memory_)); |
| base::UserLog::Register(base::UserLog::kAppLifetimeIndex, "Lifetime(ms)", |
| &lifetime_in_ms_, sizeof(lifetime_in_ms_)); |
| base::UserLog::Register(base::UserLog::kAppStatusIndex, "AppStatus", |
| &app_status_, sizeof(app_status_)); |
| base::UserLog::Register(base::UserLog::kAppPauseCountIndex, "PauseCnt", |
| &app_pause_count_, sizeof(app_pause_count_)); |
| base::UserLog::Register(base::UserLog::kAppUnpauseCountIndex, "UnpauseCnt", |
| &app_unpause_count_, sizeof(app_unpause_count_)); |
| base::UserLog::Register(base::UserLog::kAppSuspendCountIndex, "SuspendCnt", |
| &app_suspend_count_, sizeof(app_suspend_count_)); |
| base::UserLog::Register(base::UserLog::kAppResumeCountIndex, "ResumeCnt", |
| &app_resume_count_, sizeof(app_resume_count_)); |
| base::UserLog::Register(base::UserLog::kNetworkStatusIndex, |
| "NetworkStatus", &network_status_, |
| sizeof(network_status_)); |
| base::UserLog::Register(base::UserLog::kNetworkConnectCountIndex, |
| "ConnectCnt", &network_connect_count_, |
| sizeof(network_connect_count_)); |
| base::UserLog::Register(base::UserLog::kNetworkDisconnectCountIndex, |
| "DisconnectCnt", &network_disconnect_count_, |
| sizeof(network_disconnect_count_)); |
| } |
| } |
| |
| // 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::UpdateAndMaybeRegisterUserAgent() { |
| non_trivial_static_fields.Get().user_agent = browser_module_->GetUserAgent(); |
| DLOG(INFO) << "User Agent: " << non_trivial_static_fields.Get().user_agent; |
| if (base::UserLog::IsRegistrationSupported()) { |
| base::UserLog::Register(base::UserLog::kUserAgentStringIndex, "UserAgent", |
| non_trivial_static_fields.Get().user_agent.c_str(), |
| non_trivial_static_fields.Get().user_agent.size()); |
| } |
| } |
| |
| 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; |
| } |
| |
| c_val_stats_.js_reserved_memory = |
| script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved(); |
| |
| browser_module_->CheckMemory(used_cpu_memory, used_gpu_memory); |
| } |
| |
| void Application::DispatchEventInternal(base::Event* event) { |
| event_dispatcher_.DispatchEvent(make_scoped_ptr<base::Event>(event)); |
| } |
| |
| } // namespace browser |
| } // namespace cobalt |