123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "fuchsia_web/webengine/browser/web_engine_browser_main_parts.h"
- #include <fuchsia/ui/scenic/cpp/fidl.h>
- #include <fuchsia/web/cpp/fidl.h>
- #include <lib/sys/cpp/component_context.h>
- #include <lib/sys/cpp/outgoing_directory.h>
- #include <lib/sys/inspect/cpp/component.h>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/command_line.h"
- #include "base/files/file_path.h"
- #include "base/files/important_file_writer_cleaner.h"
- #include "base/fuchsia/file_utils.h"
- #include "base/fuchsia/fuchsia_logging.h"
- #include "base/fuchsia/intl_profile_watcher.h"
- #include "base/fuchsia/koid.h"
- #include "base/fuchsia/process_context.h"
- #include "base/i18n/rtl.h"
- #include "base/logging.h"
- #include "base/no_destructor.h"
- #include "base/strings/stringprintf.h"
- #include "base/system/sys_info.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "components/fuchsia_component_support/inspect.h"
- #include "components/fuchsia_legacymetrics/legacymetrics_client.h"
- #include "content/public/browser/content_browser_client.h"
- #include "content/public/browser/gpu_data_manager.h"
- #include "content/public/browser/histogram_fetcher.h"
- #include "content/public/browser/network_quality_observer_factory.h"
- #include "content/public/browser/network_service_instance.h"
- #include "content/public/browser/render_frame_host.h"
- #include "content/public/browser/storage_partition.h"
- #include "content/public/common/main_function_params.h"
- #include "content/public/common/result_codes.h"
- #include "fuchsia_web/webengine/browser/context_impl.h"
- #include "fuchsia_web/webengine/browser/web_engine_browser_context.h"
- #include "fuchsia_web/webengine/browser/web_engine_devtools_controller.h"
- #include "fuchsia_web/webengine/browser/web_engine_memory_inspector.h"
- #include "fuchsia_web/webengine/common/cast_streaming.h"
- #include "fuchsia_web/webengine/switches.h"
- #include "gpu/command_buffer/service/gpu_switches.h"
- #include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h"
- #include "net/http/http_util.h"
- #include "services/network/public/cpp/network_quality_tracker.h"
- #include "services/network/public/mojom/network_context.mojom.h"
- #include "third_party/widevine/cdm/widevine_cdm_common.h"
- #include "ui/aura/screen_ozone.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/display/screen.h"
- #include "ui/gfx/switches.h"
- #include "ui/ozone/public/ozone_platform.h"
- #include "ui/ozone/public/ozone_switches.h"
- namespace {
- base::NoDestructor<fidl::InterfaceRequest<fuchsia::web::Context>>
- g_test_request;
- constexpr base::TimeDelta kMetricsReportingInterval = base::Minutes(1);
- constexpr base::TimeDelta kChildProcessHistogramFetchTimeout =
- base::Seconds(10);
- // Merge child process' histogram deltas into the browser process' histograms.
- void FetchHistogramsFromChildProcesses(
- base::OnceCallback<void(std::vector<fuchsia::legacymetrics::Event>)>
- done_cb) {
- content::FetchHistogramsAsynchronously(
- base::ThreadTaskRunnerHandle::Get(),
- base::BindOnce(std::move(done_cb),
- std::vector<fuchsia::legacymetrics::Event>()),
- kChildProcessHistogramFetchTimeout);
- }
- template <typename KeySystemInterface>
- fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> ConnectToKeySystem() {
- static_assert(
- (std::is_same<KeySystemInterface, fuchsia::media::drm::Widevine>::value ||
- std::is_same<KeySystemInterface, fuchsia::media::drm::PlayReady>::value),
- "KeySystemInterface must be either fuchsia::media::drm::Widevine or "
- "fuchsia::media::drm::PlayReady");
- fidl::InterfaceHandle<fuchsia::media::drm::KeySystem> key_system;
- base::ComponentContextForProcess()->svc()->Connect(key_system.NewRequest(),
- KeySystemInterface::Name_);
- return key_system;
- }
- std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() {
- media::FuchsiaCdmManager::CreateKeySystemCallbackMap
- create_key_system_callbacks;
- const auto* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kEnableWidevine)) {
- create_key_system_callbacks.emplace(
- kWidevineKeySystem,
- base::BindRepeating(
- &ConnectToKeySystem<fuchsia::media::drm::Widevine>));
- }
- std::string playready_key_system =
- command_line->GetSwitchValueASCII(switches::kPlayreadyKeySystem);
- if (!playready_key_system.empty()) {
- create_key_system_callbacks.emplace(
- playready_key_system,
- base::BindRepeating(
- &ConnectToKeySystem<fuchsia::media::drm::PlayReady>));
- }
- std::string cdm_data_directory =
- command_line->GetSwitchValueASCII(switches::kCdmDataDirectory);
- absl::optional<uint64_t> cdm_data_quota_bytes;
- if (command_line->HasSwitch(switches::kCdmDataQuotaBytes)) {
- uint64_t value = 0;
- CHECK(base::StringToUint64(
- command_line->GetSwitchValueASCII(switches::kCdmDataQuotaBytes),
- &value));
- cdm_data_quota_bytes = value;
- }
- return std::make_unique<media::FuchsiaCdmManager>(
- std::move(create_key_system_callbacks),
- base::FilePath(cdm_data_directory), cdm_data_quota_bytes);
- }
- // Checks the supported ozone platform with Scenic if no arg is specified
- // already.
- void MaybeSetOzonePlatformArg(base::CommandLine* launch_args) {
- if (launch_args->HasSwitch(switches::kOzonePlatform))
- return;
- // TODO(crbug.com/1309100): Emulator on ARM hangs at the Scenic call because
- // there is no GPU emulation. We should add HEADLESS to those test
- // configurations and remove this.
- #if !defined(ARCH_CPU_ARM64)
- fuchsia::ui::scenic::ScenicSyncPtr scenic;
- zx_status_t status =
- base::ComponentContextForProcess()->svc()->Connect(scenic.NewRequest());
- ZX_CHECK(status == ZX_OK, status) << "Couldn't connect to Scenic.";
- bool scenic_uses_flatland = false;
- status = scenic->UsesFlatland(&scenic_uses_flatland);
- ZX_CHECK(status == ZX_OK, status) << "UsesFlatland()";
- launch_args->AppendSwitchNative(switches::kOzonePlatform,
- scenic_uses_flatland ? "flatland" : "scenic");
- #endif
- }
- } // namespace
- void FrameHostImpl::CreateFrameWithParams(
- fuchsia::web::CreateFrameParams params,
- fidl::InterfaceRequest<fuchsia::web::Frame> request) {
- context_.CreateFrameWithParams(std::move(params), std::move(request));
- }
- WebEngineBrowserMainParts::WebEngineBrowserMainParts(
- content::ContentBrowserClient* browser_client)
- : browser_client_(browser_client) {}
- WebEngineBrowserMainParts::~WebEngineBrowserMainParts() = default;
- std::vector<content::BrowserContext*>
- WebEngineBrowserMainParts::browser_contexts() const {
- std::vector<content::BrowserContext*> contexts;
- contexts.reserve(context_bindings_.size());
- for (auto& binding : context_bindings_.bindings())
- contexts.push_back(binding->impl()->browser_context());
- return contexts;
- }
- int WebEngineBrowserMainParts::PreEarlyInitialization() {
- MaybeSetOzonePlatformArg(base::CommandLine::ForCurrentProcess());
- return content::BrowserMainParts::PreEarlyInitialization();
- }
- void WebEngineBrowserMainParts::PostEarlyInitialization() {
- base::ImportantFileWriterCleaner::GetInstance().Initialize();
- }
- int WebEngineBrowserMainParts::PreMainMessageLoopRun() {
- DCHECK_EQ(context_bindings_.size(), 0u);
- // Initialize the |component_inspector_| to allow diagnostics to be published.
- component_inspector_ = std::make_unique<sys::ComponentInspector>(
- base::ComponentContextForProcess());
- fuchsia_component_support::PublishVersionInfoToInspect(
- component_inspector_.get());
- // Add a node providing memory details for this whole web instance.
- memory_inspector_ =
- std::make_unique<WebEngineMemoryInspector>(component_inspector_->root());
- const auto* command_line = base::CommandLine::ForCurrentProcess();
- // If Vulkan is not enabled then disable hardware acceleration. Otherwise gpu
- // process will be restarted several times trying to initialize GL before
- // falling back to software compositing.
- if (!command_line->HasSwitch(switches::kUseVulkan)) {
- content::GpuDataManager* gpu_data_manager =
- content::GpuDataManager::GetInstance();
- DCHECK(gpu_data_manager);
- gpu_data_manager->DisableHardwareAcceleration();
- }
- devtools_controller_ =
- WebEngineDevToolsController::CreateFromCommandLine(*command_line);
- if (command_line->HasSwitch(switches::kUseLegacyMetricsService)) {
- legacy_metrics_client_ =
- std::make_unique<fuchsia_legacymetrics::LegacyMetricsClient>();
- // Add a hook to asynchronously pull metrics from child processes just prior
- // to uploading.
- legacy_metrics_client_->SetReportAdditionalMetricsCallback(
- base::BindRepeating(&FetchHistogramsFromChildProcesses));
- legacy_metrics_client_->Start(kMetricsReportingInterval);
- }
- // Configure SysInfo to report total/free space under "/data" based on the
- // requested soft-quota, if any. This only affects persistent instances.
- if (command_line->HasSwitch(switches::kDataQuotaBytes)) {
- // Setting quota on "/data" is benign in incognito contexts, but indicates
- // that the client probably mis-configured this instance.
- DCHECK(!command_line->HasSwitch(switches::kIncognito))
- << "data_quota_bytes set for incognito instance.";
- uint64_t quota_bytes = 0;
- CHECK(base::StringToUint64(
- command_line->GetSwitchValueASCII(switches::kDataQuotaBytes),
- "a_bytes));
- base::SysInfo::SetAmountOfTotalDiskSpace(
- base::FilePath(base::kPersistedDataDirectoryPath), quota_bytes);
- }
- // Watch for changes to the user's locale setting.
- intl_profile_watcher_ = std::make_unique<base::FuchsiaIntlProfileWatcher>(
- base::BindRepeating(&WebEngineBrowserMainParts::OnIntlProfileChanged,
- base::Unretained(this)));
- // Configure Ozone with an Aura implementation of the Screen abstraction.
- screen_ = std::make_unique<aura::ScopedScreenOzone>();
- // Create the FuchsiaCdmManager at startup rather than on-demand, to allow it
- // to perform potentially expensive startup work in the background.
- cdm_manager_ = CreateCdmManager();
- // Disable RenderFrameHost's Javascript injection restrictions so that the
- // Context and Frames can implement their own JS injection policy at a higher
- // level.
- content::RenderFrameHost::AllowInjectingJavaScript();
- // Make sure temporary files associated with this process are cleaned up.
- base::ImportantFileWriterCleaner::GetInstance().Start();
- // Publish the fuchsia.web.Context and fuchsia.web.FrameHost capabilities.
- base::ComponentContextForProcess()->outgoing()->AddPublicService(
- fidl::InterfaceRequestHandler<fuchsia::web::Context>(fit::bind_member(
- this, &WebEngineBrowserMainParts::HandleContextRequest)));
- base::ComponentContextForProcess()->outgoing()->AddPublicService(
- fidl::InterfaceRequestHandler<fuchsia::web::FrameHost>(fit::bind_member(
- this, &WebEngineBrowserMainParts::HandleFrameHostRequest)));
- // TODO(crbug.com/1315601): Create a base::ProcessLifecycle instance here, to
- // trigger graceful shutdown on component stop, when migrated to CFv2.
- // Manage network-quality signals and send them to renderers. Provides input
- // for networking-related Client Hints.
- network_quality_tracker_ = std::make_unique<network::NetworkQualityTracker>(
- base::BindRepeating(&content::GetNetworkService));
- network_quality_observer_ =
- content::CreateNetworkQualityObserver(network_quality_tracker_.get());
- // Now that all services have been published, it is safe to start processing
- // requests to the service directory.
- base::ComponentContextForProcess()->outgoing()->ServeFromStartupInfo();
- // TODO(crbug.com/1163073): Update tests to make a service connection to the
- // Context and remove this workaround.
- if (*g_test_request)
- HandleContextRequest(std::move(*g_test_request));
- return content::RESULT_CODE_NORMAL_EXIT;
- }
- void WebEngineBrowserMainParts::WillRunMainMessageLoop(
- std::unique_ptr<base::RunLoop>& run_loop) {
- quit_closure_ = run_loop->QuitClosure();
- }
- void WebEngineBrowserMainParts::PostMainMessageLoopRun() {
- // Main loop should quit only after all Context instances have been destroyed.
- DCHECK_EQ(context_bindings_.size(), 0u);
- // These resources must be freed while a MessageLoop is still available, so
- // that they may post cleanup tasks during teardown.
- // NOTE: Objects are destroyed in the reverse order of their creation.
- legacy_metrics_client_.reset();
- intl_profile_watcher_.reset();
- base::ImportantFileWriterCleaner::GetInstance().Stop();
- }
- // static
- void WebEngineBrowserMainParts::SetContextRequestForTest(
- fidl::InterfaceRequest<fuchsia::web::Context> request) {
- *g_test_request.get() = std::move(request);
- }
- ContextImpl* WebEngineBrowserMainParts::context_for_test() const {
- if (context_bindings_.size() == 0)
- return nullptr;
- return context_bindings_.bindings().front()->impl().get();
- }
- std::vector<FrameHostImpl*> WebEngineBrowserMainParts::frame_hosts_for_test()
- const {
- std::vector<FrameHostImpl*> frame_host_impls;
- for (auto& binding : frame_host_bindings_.bindings()) {
- frame_host_impls.push_back(binding->impl().get());
- }
- return frame_host_impls;
- }
- void WebEngineBrowserMainParts::HandleContextRequest(
- fidl::InterfaceRequest<fuchsia::web::Context> request) {
- if (context_bindings_.size() > 0) {
- request.Close(ZX_ERR_BAD_STATE);
- return;
- }
- // Create the BrowserContext for the fuchsia.web.Context, with persistence
- // configured as requested via the command-line.
- std::unique_ptr<WebEngineBrowserContext> browser_context;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kIncognito)) {
- browser_context = WebEngineBrowserContext::CreateIncognito(
- network_quality_tracker_.get());
- } else {
- browser_context = WebEngineBrowserContext::CreatePersistent(
- base::FilePath(base::kPersistedDataDirectoryPath),
- network_quality_tracker_.get());
- }
- auto inspect_node_name =
- base::StringPrintf("context-%lu", *base::GetKoid(request.channel()));
- auto context_impl = std::make_unique<ContextImpl>(
- std::move(browser_context),
- component_inspector_->root().CreateChild(inspect_node_name),
- devtools_controller_.get());
- // If this web instance should allow CastStreaming then enable it in this
- // ContextImpl. CastStreaming will not be available in FrameHost contexts.
- if (IsCastStreamingEnabled())
- context_impl->SetCastStreamingEnabled();
- // Create the fuchsia.web.Context implementation using the BrowserContext and
- // configure it to terminate the process when the client goes away.
- context_bindings_.AddBinding(
- std::move(context_impl), std::move(request), /* dispatcher */ nullptr,
- // Quit the browser main loop when the Context connection is
- // dropped.
- [this](zx_status_t status) {
- ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
- << " Context disconnected.";
- BeginGracefulShutdown();
- });
- }
- void WebEngineBrowserMainParts::HandleFrameHostRequest(
- fidl::InterfaceRequest<fuchsia::web::FrameHost> request) {
- auto inspect_node_name =
- base::StringPrintf("framehost-%lu", *base::GetKoid(request.channel()));
- frame_host_bindings_.AddBinding(
- std::make_unique<FrameHostImpl>(
- component_inspector_->root().CreateChild(inspect_node_name),
- devtools_controller_.get(), network_quality_tracker_.get()),
- std::move(request));
- }
- void WebEngineBrowserMainParts::OnIntlProfileChanged(
- const fuchsia::intl::Profile& profile) {
- // Configure the ICU library in this process with the new primary locale.
- std::string primary_locale =
- base::FuchsiaIntlProfileWatcher::GetPrimaryLocaleIdFromProfile(profile);
- base::i18n::SetICUDefaultLocale(primary_locale);
- {
- // Reloading locale-specific resources requires synchronous blocking.
- // Locale changes should not be frequent enough for this to cause jank.
- base::ScopedAllowBlocking allow_blocking;
- std::string loaded_locale =
- ui::ResourceBundle::GetSharedInstance().ReloadLocaleResources(
- base::i18n::GetConfiguredLocale());
- VLOG(1) << "Reloaded locale resources: " << loaded_locale;
- }
- // Reconfigure each web.Context's NetworkContext with the new setting.
- for (auto& binding : context_bindings_.bindings()) {
- content::BrowserContext* const browser_context =
- binding->impl()->browser_context();
- std::string accept_language = net::HttpUtil::GenerateAcceptLanguageHeader(
- browser_client_->GetAcceptLangs(browser_context));
- browser_context->GetDefaultStoragePartition()
- ->GetNetworkContext()
- ->SetAcceptLanguage(accept_language);
- }
- }
- void WebEngineBrowserMainParts::BeginGracefulShutdown() {
- if (quit_closure_)
- std::move(quit_closure_).Run();
- }
|