web_engine_browser_main_parts.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "fuchsia_web/webengine/browser/web_engine_browser_main_parts.h"
  5. #include <fuchsia/ui/scenic/cpp/fidl.h>
  6. #include <fuchsia/web/cpp/fidl.h>
  7. #include <lib/sys/cpp/component_context.h>
  8. #include <lib/sys/cpp/outgoing_directory.h>
  9. #include <lib/sys/inspect/cpp/component.h>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/bind.h"
  13. #include "base/callback_helpers.h"
  14. #include "base/command_line.h"
  15. #include "base/files/file_path.h"
  16. #include "base/files/important_file_writer_cleaner.h"
  17. #include "base/fuchsia/file_utils.h"
  18. #include "base/fuchsia/fuchsia_logging.h"
  19. #include "base/fuchsia/intl_profile_watcher.h"
  20. #include "base/fuchsia/koid.h"
  21. #include "base/fuchsia/process_context.h"
  22. #include "base/i18n/rtl.h"
  23. #include "base/logging.h"
  24. #include "base/no_destructor.h"
  25. #include "base/strings/stringprintf.h"
  26. #include "base/system/sys_info.h"
  27. #include "base/threading/thread_restrictions.h"
  28. #include "base/threading/thread_task_runner_handle.h"
  29. #include "build/build_config.h"
  30. #include "components/fuchsia_component_support/inspect.h"
  31. #include "components/fuchsia_legacymetrics/legacymetrics_client.h"
  32. #include "content/public/browser/content_browser_client.h"
  33. #include "content/public/browser/gpu_data_manager.h"
  34. #include "content/public/browser/histogram_fetcher.h"
  35. #include "content/public/browser/network_quality_observer_factory.h"
  36. #include "content/public/browser/network_service_instance.h"
  37. #include "content/public/browser/render_frame_host.h"
  38. #include "content/public/browser/storage_partition.h"
  39. #include "content/public/common/main_function_params.h"
  40. #include "content/public/common/result_codes.h"
  41. #include "fuchsia_web/webengine/browser/context_impl.h"
  42. #include "fuchsia_web/webengine/browser/web_engine_browser_context.h"
  43. #include "fuchsia_web/webengine/browser/web_engine_devtools_controller.h"
  44. #include "fuchsia_web/webengine/browser/web_engine_memory_inspector.h"
  45. #include "fuchsia_web/webengine/common/cast_streaming.h"
  46. #include "fuchsia_web/webengine/switches.h"
  47. #include "gpu/command_buffer/service/gpu_switches.h"
  48. #include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
  49. #include "net/http/http_util.h"
  50. #include "services/network/public/cpp/network_quality_tracker.h"
  51. #include "services/network/public/mojom/network_context.mojom.h"
  52. #include "third_party/widevine/cdm/widevine_cdm_common.h"
  53. #include "ui/aura/screen_ozone.h"
  54. #include "ui/base/resource/resource_bundle.h"
  55. #include "ui/display/screen.h"
  56. #include "ui/gfx/switches.h"
  57. #include "ui/ozone/public/ozone_platform.h"
  58. #include "ui/ozone/public/ozone_switches.h"
  59. namespace {
  60. base::NoDestructor<fidl::InterfaceRequest<fuchsia::web::Context>>
  61. g_test_request;
  62. constexpr base::TimeDelta kMetricsReportingInterval = base::Minutes(1);
  63. constexpr base::TimeDelta kChildProcessHistogramFetchTimeout =
  64. base::Seconds(10);
  65. // Merge child process' histogram deltas into the browser process' histograms.
  66. void FetchHistogramsFromChildProcesses(
  67. base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>
  68. done_cb) {
  69. content::FetchHistogramsAsynchronously(
  70. base::ThreadTaskRunnerHandle::Get(),
  71. base::BindOnce(std::move(done_cb),
  72. std::vector<fuchsia::legacymetrics::Event>()),
  73. kChildProcessHistogramFetchTimeout);
  74. }
  75. template <typename KeySystemInterface>
  76. fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> ConnectToKeySystem() {
  77. static_assert(
  78. (std::is_same<KeySystemInterface, fuchsia::media::drm::Widevine>::value ||
  79. std::is_same<KeySystemInterface, fuchsia::media::drm::PlayReady>::value),
  80. "KeySystemInterface must be either fuchsia::media::drm::Widevine or "
  81. "fuchsia::media::drm::PlayReady");
  82. fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> key_system;
  83. base::ComponentContextForProcess()->svc()->Connect(key_system.NewRequest(),
  84. KeySystemInterface::Name_);
  85. return key_system;
  86. }
  87. std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
  88. media::FuchsiaCdmManager::CreateKeySystemCallbackMap
  89. create_key_system_callbacks;
  90. const auto* command_line = base::CommandLine::ForCurrentProcess();
  91. if (command_line->HasSwitch(switches::kEnableWidevine)) {
  92. create_key_system_callbacks.emplace(
  93. kWidevineKeySystem,
  94. base::BindRepeating(
  95. &ConnectToKeySystem<fuchsia::media::drm::Widevine>));
  96. }
  97. std::string playready_key_system =
  98. command_line->GetSwitchValueASCII(switches::kPlayreadyKeySystem);
  99. if (!playready_key_system.empty()) {
  100. create_key_system_callbacks.emplace(
  101. playready_key_system,
  102. base::BindRepeating(
  103. &ConnectToKeySystem<fuchsia::media::drm::PlayReady>));
  104. }
  105. std::string cdm_data_directory =
  106. command_line->GetSwitchValueASCII(switches::kCdmDataDirectory);
  107. absl::optional<uint64_t> cdm_data_quota_bytes;
  108. if (command_line->HasSwitch(switches::kCdmDataQuotaBytes)) {
  109. uint64_t value = 0;
  110. CHECK(base::StringToUint64(
  111. command_line->GetSwitchValueASCII(switches::kCdmDataQuotaBytes),
  112. &value));
  113. cdm_data_quota_bytes = value;
  114. }
  115. return std::make_unique<media::FuchsiaCdmManager>(
  116. std::move(create_key_system_callbacks),
  117. base::FilePath(cdm_data_directory), cdm_data_quota_bytes);
  118. }
  119. // Checks the supported ozone platform with Scenic if no arg is specified
  120. // already.
  121. void MaybeSetOzonePlatformArg(base::CommandLine* launch_args) {
  122. if (launch_args->HasSwitch(switches::kOzonePlatform))
  123. return;
  124. // TODO(crbug.com/1309100): Emulator on ARM hangs at the Scenic call because
  125. // there is no GPU emulation. We should add HEADLESS to those test
  126. // configurations and remove this.
  127. #if !defined(ARCH_CPU_ARM64)
  128. fuchsia::ui::scenic::ScenicSyncPtr scenic;
  129. zx_status_t status =
  130. base::ComponentContextForProcess()->svc()->Connect(scenic.NewRequest());
  131. ZX_CHECK(status == ZX_OK, status) << "Couldn't connect to Scenic.";
  132. bool scenic_uses_flatland = false;
  133. status = scenic->UsesFlatland(&scenic_uses_flatland);
  134. ZX_CHECK(status == ZX_OK, status) << "UsesFlatland()";
  135. launch_args->AppendSwitchNative(switches::kOzonePlatform,
  136. scenic_uses_flatland ? "flatland" : "scenic");
  137. #endif
  138. }
  139. } // namespace
  140. void FrameHostImpl::CreateFrameWithParams(
  141. fuchsia::web::CreateFrameParams params,
  142. fidl::InterfaceRequest<fuchsia::web::Frame> request) {
  143. context_.CreateFrameWithParams(std::move(params), std::move(request));
  144. }
  145. WebEngineBrowserMainParts::WebEngineBrowserMainParts(
  146. content::ContentBrowserClient* browser_client)
  147. : browser_client_(browser_client) {}
  148. WebEngineBrowserMainParts::~WebEngineBrowserMainParts() = default;
  149. std::vector<content::BrowserContext*>
  150. WebEngineBrowserMainParts::browser_contexts() const {
  151. std::vector<content::BrowserContext*> contexts;
  152. contexts.reserve(context_bindings_.size());
  153. for (auto& binding : context_bindings_.bindings())
  154. contexts.push_back(binding->impl()->browser_context());
  155. return contexts;
  156. }
  157. int WebEngineBrowserMainParts::PreEarlyInitialization() {
  158. MaybeSetOzonePlatformArg(base::CommandLine::ForCurrentProcess());
  159. return content::BrowserMainParts::PreEarlyInitialization();
  160. }
  161. void WebEngineBrowserMainParts::PostEarlyInitialization() {
  162. base::ImportantFileWriterCleaner::GetInstance().Initialize();
  163. }
  164. int WebEngineBrowserMainParts::PreMainMessageLoopRun() {
  165. DCHECK_EQ(context_bindings_.size(), 0u);
  166. // Initialize the |component_inspector_| to allow diagnostics to be published.
  167. component_inspector_ = std::make_unique<sys::ComponentInspector>(
  168. base::ComponentContextForProcess());
  169. fuchsia_component_support::PublishVersionInfoToInspect(
  170. component_inspector_.get());
  171. // Add a node providing memory details for this whole web instance.
  172. memory_inspector_ =
  173. std::make_unique<WebEngineMemoryInspector>(component_inspector_->root());
  174. const auto* command_line = base::CommandLine::ForCurrentProcess();
  175. // If Vulkan is not enabled then disable hardware acceleration. Otherwise gpu
  176. // process will be restarted several times trying to initialize GL before
  177. // falling back to software compositing.
  178. if (!command_line->HasSwitch(switches::kUseVulkan)) {
  179. content::GpuDataManager* gpu_data_manager =
  180. content::GpuDataManager::GetInstance();
  181. DCHECK(gpu_data_manager);
  182. gpu_data_manager->DisableHardwareAcceleration();
  183. }
  184. devtools_controller_ =
  185. WebEngineDevToolsController::CreateFromCommandLine(*command_line);
  186. if (command_line->HasSwitch(switches::kUseLegacyMetricsService)) {
  187. legacy_metrics_client_ =
  188. std::make_unique<fuchsia_legacymetrics::LegacyMetricsClient>();
  189. // Add a hook to asynchronously pull metrics from child processes just prior
  190. // to uploading.
  191. legacy_metrics_client_->SetReportAdditionalMetricsCallback(
  192. base::BindRepeating(&FetchHistogramsFromChildProcesses));
  193. legacy_metrics_client_->Start(kMetricsReportingInterval);
  194. }
  195. // Configure SysInfo to report total/free space under "/data" based on the
  196. // requested soft-quota, if any. This only affects persistent instances.
  197. if (command_line->HasSwitch(switches::kDataQuotaBytes)) {
  198. // Setting quota on "/data" is benign in incognito contexts, but indicates
  199. // that the client probably mis-configured this instance.
  200. DCHECK(!command_line->HasSwitch(switches::kIncognito))
  201. << "data_quota_bytes set for incognito instance.";
  202. uint64_t quota_bytes = 0;
  203. CHECK(base::StringToUint64(
  204. command_line->GetSwitchValueASCII(switches::kDataQuotaBytes),
  205. &quota_bytes));
  206. base::SysInfo::SetAmountOfTotalDiskSpace(
  207. base::FilePath(base::kPersistedDataDirectoryPath), quota_bytes);
  208. }
  209. // Watch for changes to the user's locale setting.
  210. intl_profile_watcher_ = std::make_unique<base::FuchsiaIntlProfileWatcher>(
  211. base::BindRepeating(&WebEngineBrowserMainParts::OnIntlProfileChanged,
  212. base::Unretained(this)));
  213. // Configure Ozone with an Aura implementation of the Screen abstraction.
  214. screen_ = std::make_unique<aura::ScopedScreenOzone>();
  215. // Create the FuchsiaCdmManager at startup rather than on-demand, to allow it
  216. // to perform potentially expensive startup work in the background.
  217. cdm_manager_ = CreateCdmManager();
  218. // Disable RenderFrameHost's Javascript injection restrictions so that the
  219. // Context and Frames can implement their own JS injection policy at a higher
  220. // level.
  221. content::RenderFrameHost::AllowInjectingJavaScript();
  222. // Make sure temporary files associated with this process are cleaned up.
  223. base::ImportantFileWriterCleaner::GetInstance().Start();
  224. // Publish the fuchsia.web.Context and fuchsia.web.FrameHost capabilities.
  225. base::ComponentContextForProcess()->outgoing()->AddPublicService(
  226. fidl::InterfaceRequestHandler<fuchsia::web::Context>(fit::bind_member(
  227. this, &WebEngineBrowserMainParts::HandleContextRequest)));
  228. base::ComponentContextForProcess()->outgoing()->AddPublicService(
  229. fidl::InterfaceRequestHandler<fuchsia::web::FrameHost>(fit::bind_member(
  230. this, &WebEngineBrowserMainParts::HandleFrameHostRequest)));
  231. // TODO(crbug.com/1315601): Create a base::ProcessLifecycle instance here, to
  232. // trigger graceful shutdown on component stop, when migrated to CFv2.
  233. // Manage network-quality signals and send them to renderers. Provides input
  234. // for networking-related Client Hints.
  235. network_quality_tracker_ = std::make_unique<network::NetworkQualityTracker>(
  236. base::BindRepeating(&content::GetNetworkService));
  237. network_quality_observer_ =
  238. content::CreateNetworkQualityObserver(network_quality_tracker_.get());
  239. // Now that all services have been published, it is safe to start processing
  240. // requests to the service directory.
  241. base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo();
  242. // TODO(crbug.com/1163073): Update tests to make a service connection to the
  243. // Context and remove this workaround.
  244. if (*g_test_request)
  245. HandleContextRequest(std::move(*g_test_request));
  246. return content::RESULT_CODE_NORMAL_EXIT;
  247. }
  248. void WebEngineBrowserMainParts::WillRunMainMessageLoop(
  249. std::unique_ptr<base::RunLoop>& run_loop) {
  250. quit_closure_ = run_loop->QuitClosure();
  251. }
  252. void WebEngineBrowserMainParts::PostMainMessageLoopRun() {
  253. // Main loop should quit only after all Context instances have been destroyed.
  254. DCHECK_EQ(context_bindings_.size(), 0u);
  255. // These resources must be freed while a MessageLoop is still available, so
  256. // that they may post cleanup tasks during teardown.
  257. // NOTE: Objects are destroyed in the reverse order of their creation.
  258. legacy_metrics_client_.reset();
  259. intl_profile_watcher_.reset();
  260. base::ImportantFileWriterCleaner::GetInstance().Stop();
  261. }
  262. // static
  263. void WebEngineBrowserMainParts::SetContextRequestForTest(
  264. fidl::InterfaceRequest<fuchsia::web::Context> request) {
  265. *g_test_request.get() = std::move(request);
  266. }
  267. ContextImpl* WebEngineBrowserMainParts::context_for_test() const {
  268. if (context_bindings_.size() == 0)
  269. return nullptr;
  270. return context_bindings_.bindings().front()->impl().get();
  271. }
  272. std::vector<FrameHostImpl*> WebEngineBrowserMainParts::frame_hosts_for_test()
  273. const {
  274. std::vector<FrameHostImpl*> frame_host_impls;
  275. for (auto& binding : frame_host_bindings_.bindings()) {
  276. frame_host_impls.push_back(binding->impl().get());
  277. }
  278. return frame_host_impls;
  279. }
  280. void WebEngineBrowserMainParts::HandleContextRequest(
  281. fidl::InterfaceRequest<fuchsia::web::Context> request) {
  282. if (context_bindings_.size() > 0) {
  283. request.Close(ZX_ERR_BAD_STATE);
  284. return;
  285. }
  286. // Create the BrowserContext for the fuchsia.web.Context, with persistence
  287. // configured as requested via the command-line.
  288. std::unique_ptr<WebEngineBrowserContext> browser_context;
  289. if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kIncognito)) {
  290. browser_context = WebEngineBrowserContext::CreateIncognito(
  291. network_quality_tracker_.get());
  292. } else {
  293. browser_context = WebEngineBrowserContext::CreatePersistent(
  294. base::FilePath(base::kPersistedDataDirectoryPath),
  295. network_quality_tracker_.get());
  296. }
  297. auto inspect_node_name =
  298. base::StringPrintf("context-%lu", *base::GetKoid(request.channel()));
  299. auto context_impl = std::make_unique<ContextImpl>(
  300. std::move(browser_context),
  301. component_inspector_->root().CreateChild(inspect_node_name),
  302. devtools_controller_.get());
  303. // If this web instance should allow CastStreaming then enable it in this
  304. // ContextImpl. CastStreaming will not be available in FrameHost contexts.
  305. if (IsCastStreamingEnabled())
  306. context_impl->SetCastStreamingEnabled();
  307. // Create the fuchsia.web.Context implementation using the BrowserContext and
  308. // configure it to terminate the process when the client goes away.
  309. context_bindings_.AddBinding(
  310. std::move(context_impl), std::move(request), /* dispatcher */ nullptr,
  311. // Quit the browser main loop when the Context connection is
  312. // dropped.
  313. [this](zx_status_t status) {
  314. ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
  315. << " Context disconnected.";
  316. BeginGracefulShutdown();
  317. });
  318. }
  319. void WebEngineBrowserMainParts::HandleFrameHostRequest(
  320. fidl::InterfaceRequest<fuchsia::web::FrameHost> request) {
  321. auto inspect_node_name =
  322. base::StringPrintf("framehost-%lu", *base::GetKoid(request.channel()));
  323. frame_host_bindings_.AddBinding(
  324. std::make_unique<FrameHostImpl>(
  325. component_inspector_->root().CreateChild(inspect_node_name),
  326. devtools_controller_.get(), network_quality_tracker_.get()),
  327. std::move(request));
  328. }
  329. void WebEngineBrowserMainParts::OnIntlProfileChanged(
  330. const fuchsia::intl::Profile& profile) {
  331. // Configure the ICU library in this process with the new primary locale.
  332. std::string primary_locale =
  333. base::FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(profile);
  334. base::i18n::SetICUDefaultLocale(primary_locale);
  335. {
  336. // Reloading locale-specific resources requires synchronous blocking.
  337. // Locale changes should not be frequent enough for this to cause jank.
  338. base::ScopedAllowBlocking allow_blocking;
  339. std::string loaded_locale =
  340. ui::ResourceBundle::GetSharedInstance().ReloadLocaleResources(
  341. base::i18n::GetConfiguredLocale());
  342. VLOG(1) << "Reloaded locale resources: " << loaded_locale;
  343. }
  344. // Reconfigure each web.Context's NetworkContext with the new setting.
  345. for (auto& binding : context_bindings_.bindings()) {
  346. content::BrowserContext* const browser_context =
  347. binding->impl()->browser_context();
  348. std::string accept_language = net::HttpUtil::GenerateAcceptLanguageHeader(
  349. browser_client_->GetAcceptLangs(browser_context));
  350. browser_context->GetDefaultStoragePartition()
  351. ->GetNetworkContext()
  352. ->SetAcceptLanguage(accept_language);
  353. }
  354. }
  355. void WebEngineBrowserMainParts::BeginGracefulShutdown() {
  356. if (quit_closure_)
  357. std::move(quit_closure_).Run();
  358. }