123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- // Copyright 2019 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 "weblayer/app/content_main_delegate_impl.h"
- #include <iostream>
- #include <tuple>
- #include "base/base_switches.h"
- #include "base/command_line.h"
- #include "base/containers/contains.h"
- #include "base/containers/flat_set.h"
- #include "base/cpu.h"
- #include "base/files/file_util.h"
- #include "base/lazy_instance.h"
- #include "base/logging.h"
- #include "base/path_service.h"
- #include "base/strings/string_split.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.h"
- #include "components/content_capture/common/content_capture_features.h"
- #include "components/startup_metric_utils/browser/startup_metric_utils.h"
- #include "components/translate/core/common/translate_util.h"
- #include "components/variations/variations_ids_provider.h"
- #include "content/public/browser/browser_main_runner.h"
- #include "content/public/common/content_features.h"
- #include "content/public/common/content_switches.h"
- #include "content/public/common/main_function_params.h"
- #include "content/public/common/url_constants.h"
- #include "media/base/media_switches.h"
- #include "services/network/public/cpp/features.h"
- #include "third_party/abseil-cpp/absl/types/variant.h"
- #include "third_party/blink/public/common/features.h"
- #include "third_party/blink/public/platform/web_runtime_features.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/base/ui_base_paths.h"
- #include "weblayer/browser/background_fetch/background_fetch_delegate_factory.h"
- #include "weblayer/browser/content_browser_client_impl.h"
- #include "weblayer/common/content_client_impl.h"
- #include "weblayer/common/weblayer_paths.h"
- #include "weblayer/public/common/switches.h"
- #include "weblayer/renderer/content_renderer_client_impl.h"
- #include "weblayer/utility/content_utility_client_impl.h"
- #if BUILDFLAG(IS_ANDROID)
- #include "base/android/apk_assets.h"
- #include "base/android/build_info.h"
- #include "base/android/bundle_utils.h"
- #include "base/android/java_exception_reporter.h"
- #include "base/android/locale_utils.h"
- #include "base/i18n/rtl.h"
- #include "base/posix/global_descriptors.h"
- #include "components/autofill/core/common/autofill_features.h"
- #include "components/viz/common/features.h"
- #include "content/public/browser/android/compositor.h"
- #include "ui/base/resource/resource_bundle_android.h"
- #include "ui/base/ui_base_switches.h"
- #include "weblayer/browser/android/application_info_helper.h"
- #include "weblayer/browser/android/exception_filter.h"
- #include "weblayer/browser/android_descriptors.h"
- #include "weblayer/common/crash_reporter/crash_keys.h"
- #include "weblayer/common/crash_reporter/crash_reporter_client.h"
- #endif
- #if BUILDFLAG(IS_WIN)
- #include <windows.h>
- #include <initguid.h>
- #include "base/logging_win.h"
- #endif
- namespace weblayer {
- namespace {
- void InitLogging(MainParams* params) {
- if (params->log_filename.empty())
- return;
- logging::LoggingSettings settings;
- settings.logging_dest = logging::LOG_TO_ALL;
- settings.log_file_path = params->log_filename.value().c_str();
- settings.delete_old = logging::DELETE_OLD_LOG_FILE;
- logging::InitLogging(settings);
- logging::SetLogItems(true /* Process ID */, true /* Thread ID */,
- true /* Timestamp */, false /* Tick count */);
- }
- // Enables each feature in |features_to_enable| unless it is already set in the
- // command line, and similarly disables each feature in |features_to_disable|
- // unless it is already set in the command line.
- void ConfigureFeaturesIfNotSet(
- const std::vector<base::Feature>& features_to_enable,
- const std::vector<base::Feature>& features_to_disable) {
- auto* cl = base::CommandLine::ForCurrentProcess();
- std::vector<std::string> enabled_features;
- base::flat_set<std::string> feature_names_enabled_via_command_line;
- std::string enabled_features_str =
- cl->GetSwitchValueASCII(::switches::kEnableFeatures);
- for (const auto& f :
- base::FeatureList::SplitFeatureListString(enabled_features_str)) {
- enabled_features.emplace_back(f);
- // "<" is used as separator for field trial/groups.
- std::vector<base::StringPiece> parts = base::SplitStringPiece(
- f, "<", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
- // Split with supplied params should always return at least one entry.
- DCHECK(!parts.empty());
- if (parts[0].length() > 0)
- feature_names_enabled_via_command_line.insert(std::string(parts[0]));
- }
- std::vector<std::string> disabled_features;
- std::string disabled_features_str =
- cl->GetSwitchValueASCII(::switches::kDisableFeatures);
- for (const auto& f :
- base::FeatureList::SplitFeatureListString(disabled_features_str)) {
- disabled_features.emplace_back(f);
- }
- for (const auto& feature : features_to_enable) {
- if (!base::Contains(disabled_features, feature.name) &&
- !base::Contains(feature_names_enabled_via_command_line, feature.name)) {
- enabled_features.push_back(feature.name);
- }
- }
- cl->AppendSwitchASCII(::switches::kEnableFeatures,
- base::JoinString(enabled_features, ","));
- for (const auto& feature : features_to_disable) {
- if (!base::Contains(disabled_features, feature.name) &&
- !base::Contains(feature_names_enabled_via_command_line, feature.name)) {
- disabled_features.push_back(feature.name);
- }
- }
- cl->AppendSwitchASCII(::switches::kDisableFeatures,
- base::JoinString(disabled_features, ","));
- }
- } // namespace
- ContentMainDelegateImpl::ContentMainDelegateImpl(MainParams params)
- : params_(std::move(params)) {
- #if !BUILDFLAG(IS_ANDROID)
- // On non-Android, the application start time is recorded in this constructor,
- // which runs early during application lifetime. On Android, the application
- // start time is sampled when the Java code is entered, and it is retrieved
- // from C++ after initializing the JNI (see
- // BrowserMainPartsImpl::PreMainMessageLoopRun()).
- startup_metric_utils::RecordApplicationStartTime(base::TimeTicks::Now());
- #endif
- }
- ContentMainDelegateImpl::~ContentMainDelegateImpl() = default;
- absl::optional<int> ContentMainDelegateImpl::BasicStartupComplete() {
- // Disable features which are not currently supported in WebLayer. This allows
- // sites to do feature detection, and prevents crashes in some not fully
- // implemented features.
- base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
- // TODO(crbug.com/1025610): make notifications work with WebLayer.
- // This also turns off Push messaging.
- cl->AppendSwitch(::switches::kDisableNotifications);
- std::vector<base::Feature> enabled_features = {
- #if BUILDFLAG(IS_ANDROID)
- // Overlay promotion requires some guarantees we don't have on WebLayer
- // (e.g. ensuring fullscreen, no movement of the parent view). Given that
- // we're unsure about the benefits when used embedded in a parent app, we
- // will only promote to overlays if needed for secure videos.
- media::kUseAndroidOverlayForSecureOnly,
- #endif
- };
- std::vector<base::Feature> disabled_features = {
- // TODO(crbug.com/1313771): Support Digital Goods API.
- ::features::kDigitalGoodsApi,
- // TODO(crbug.com/1091212): make Notification triggers work with
- // WebLayer.
- ::features::kNotificationTriggers,
- // TODO(crbug.com/1091211): Support PeriodicBackgroundSync on WebLayer.
- ::features::kPeriodicBackgroundSync,
- // TODO(crbug.com/1174856): Support Portals.
- blink::features::kPortals,
- // TODO(crbug.com/1144912): Support BackForwardCache on WebLayer.
- ::features::kBackForwardCache,
- // TODO(crbug.com/1247836): Enable TFLite/Optimization Guide on WebLayer.
- translate::kTFLiteLanguageDetectionEnabled,
- #if BUILDFLAG(IS_ANDROID)
- // TODO(crbug.com/1131016): Support Picture in Picture API on WebLayer.
- media::kPictureInPictureAPI,
- ::features::kDisableDeJelly,
- ::features::kDynamicColorGamut,
- #else
- // WebOTP is supported only on Android in WebLayer.
- ::features::kWebOTP,
- #endif
- };
- #if BUILDFLAG(IS_ANDROID)
- if (base::android::BuildInfo::GetInstance()->sdk_int() >=
- base::android::SDK_VERSION_OREO) {
- enabled_features.push_back(
- autofill::features::kAutofillExtractAllDatalists);
- enabled_features.push_back(
- autofill::features::kAutofillSkipComparingInferredLabels);
- }
- if (GetApplicationMetadataAsBoolean(
- "org.chromium.weblayer.ENABLE_LOGGING_OF_JS_CONSOLE_MESSAGES",
- /*default_value=*/false)) {
- enabled_features.push_back(features::kLogJsConsoleMessages);
- }
- #endif
- ConfigureFeaturesIfNotSet(enabled_features, disabled_features);
- // TODO(crbug.com/1097105): Support Web GPU on WebLayer.
- blink::WebRuntimeFeatures::EnableWebGPU(false);
- #if BUILDFLAG(IS_ANDROID)
- content::Compositor::Initialize();
- #endif
- InitLogging(¶ms_);
- RegisterPathProvider();
- return absl::nullopt;
- }
- bool ContentMainDelegateImpl::ShouldCreateFeatureList(InvokedIn invoked_in) {
- #if BUILDFLAG(IS_ANDROID)
- // On android WebLayer is in charge of creating its own FeatureList in the
- // browser process.
- return absl::holds_alternative<InvokedInChildProcess>(invoked_in);
- #else
- // TODO(weblayer-dev): Support feature lists on desktop.
- return true;
- #endif
- }
- variations::VariationsIdsProvider*
- ContentMainDelegateImpl::CreateVariationsIdsProvider() {
- // As the embedder supplies the set of ids, the signed-in state does not make
- // sense and is ignored.
- return variations::VariationsIdsProvider::Create(
- variations::VariationsIdsProvider::Mode::kIgnoreSignedInState);
- }
- void ContentMainDelegateImpl::PreSandboxStartup() {
- // TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
- // complete.
- #if defined(ARCH_CPU_ARM_FAMILY) && \
- (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || \
- BUILDFLAG(IS_CHROMEOS_LACROS))
- // Create an instance of the CPU class to parse /proc/cpuinfo and cache
- // cpu_brand info.
- base::CPU cpu_info;
- #endif
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- const bool is_browser_process =
- command_line.GetSwitchValueASCII(::switches::kProcessType).empty();
- if (is_browser_process &&
- command_line.HasSwitch(switches::kWebLayerUserDataDir)) {
- base::FilePath path =
- command_line.GetSwitchValuePath(switches::kWebLayerUserDataDir);
- if (base::DirectoryExists(path) || base::CreateDirectory(path)) {
- // Profile needs an absolute path, which we would normally get via
- // PathService. In this case, manually ensure the path is absolute.
- if (!path.IsAbsolute())
- path = base::MakeAbsoluteFilePath(path);
- } else {
- LOG(ERROR) << "Unable to create data-path directory: " << path.value();
- }
- CHECK(base::PathService::OverrideAndCreateIfNeeded(
- DIR_USER_DATA, path, true /* is_absolute */, false /* create */));
- }
- InitializeResourceBundle();
- #if BUILDFLAG(IS_ANDROID)
- EnableCrashReporter(
- command_line.GetSwitchValueASCII(::switches::kProcessType));
- if (is_browser_process) {
- base::android::SetJavaExceptionFilter(
- base::BindRepeating(&WebLayerJavaExceptionFilter));
- }
- SetWebLayerCrashKeys();
- #endif
- }
- absl::optional<int> ContentMainDelegateImpl::PostEarlyInitialization(
- InvokedIn invoked_in) {
- if (absl::holds_alternative<InvokedInBrowserProcess>(invoked_in))
- browser_client_->CreateFeatureListAndFieldTrials();
- return absl::nullopt;
- }
- absl::variant<int, content::MainFunctionParams>
- ContentMainDelegateImpl::RunProcess(
- const std::string& process_type,
- content::MainFunctionParams main_function_params) {
- // For non-browser process, return and have the caller run the main loop.
- if (!process_type.empty())
- return std::move(main_function_params);
- #if !BUILDFLAG(IS_ANDROID)
- // On non-Android, we can return |main_function_params| back and have the
- // caller run BrowserMain() normally.
- return std::move(main_function_params);
- #else
- // On Android, we defer to the system message loop when the stack unwinds.
- // So here we only create (and leak) a BrowserMainRunner. The shutdown
- // of BrowserMainRunner doesn't happen in Chrome Android and doesn't work
- // properly on Android at all.
- auto main_runner = content::BrowserMainRunner::Create();
- // In browser tests, the |main_function_params| contains a |ui_task| which
- // will execute the testing. The task will be executed synchronously inside
- // Initialize() so we don't depend on the BrowserMainRunner being Run().
- int initialize_exit_code =
- main_runner->Initialize(std::move(main_function_params));
- DCHECK_LT(initialize_exit_code, 0)
- << "BrowserMainRunner::Initialize failed in MainDelegate";
- std::ignore = main_runner.release();
- // Return 0 as BrowserMain() should not be called after this, bounce up to
- // the system message loop for ContentShell, and we're already done thanks
- // to the |ui_task| for browser tests.
- return 0;
- #endif
- }
- void ContentMainDelegateImpl::InitializeResourceBundle() {
- #if BUILDFLAG(IS_ANDROID)
- const base::CommandLine& command_line =
- *base::CommandLine::ForCurrentProcess();
- bool is_browser_process =
- command_line.GetSwitchValueASCII(::switches::kProcessType).empty();
- if (is_browser_process) {
- // If we're not being loaded from a bundle, locales will be loaded from the
- // webview stored-locales directory. Otherwise, we are in Monochrome, and
- // we load both chrome and webview's locale assets.
- if (base::android::BundleUtils::IsBundle())
- ui::SetLoadSecondaryLocalePaks(true);
- else
- ui::SetLocalePaksStoredInApk(true);
- // Passing an empty |pref_locale| yields the system default locale.
- std::string locale = ui::ResourceBundle::InitSharedInstanceWithLocale(
- {} /*pref_locale*/, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
- if (locale.empty()) {
- LOG(WARNING) << "Failed to load locale .pak from apk.";
- }
- // Try to directly mmap the resources.pak from the apk. Fall back to load
- // from file, using PATH_SERVICE, otherwise.
- base::FilePath pak_file_path;
- base::PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &pak_file_path);
- pak_file_path = pak_file_path.AppendASCII("resources.pak");
- ui::LoadMainAndroidPackFile("assets/resources.pak", pak_file_path);
- // The English-only workaround is not needed for bundles, since bundles will
- // contain assets for all locales.
- if (!base::android::BundleUtils::IsBundle()) {
- constexpr char kWebLayerLocalePath[] =
- "assets/stored-locales/weblayer/en-US.pak";
- base::MemoryMappedFile::Region region;
- int fd = base::android::OpenApkAsset(kWebLayerLocalePath, ®ion);
- CHECK_GE(fd, 0) << "Could not find " << kWebLayerLocalePath << " in APK.";
- ui::ResourceBundle::GetSharedInstance()
- .LoadSecondaryLocaleDataWithPakFileRegion(base::File(fd), region);
- base::GlobalDescriptors::GetInstance()->Set(
- kWebLayerSecondaryLocalePakDescriptor, fd, region);
- }
- } else {
- base::i18n::SetICUDefaultLocale(
- command_line.GetSwitchValueASCII(::switches::kLang));
- auto* global_descriptors = base::GlobalDescriptors::GetInstance();
- int pak_fd = global_descriptors->Get(kWebLayerLocalePakDescriptor);
- base::MemoryMappedFile::Region pak_region =
- global_descriptors->GetRegion(kWebLayerLocalePakDescriptor);
- ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(base::File(pak_fd),
- pak_region);
- pak_fd = global_descriptors->Get(kWebLayerSecondaryLocalePakDescriptor);
- pak_region =
- global_descriptors->GetRegion(kWebLayerSecondaryLocalePakDescriptor);
- ui::ResourceBundle::GetSharedInstance()
- .LoadSecondaryLocaleDataWithPakFileRegion(base::File(pak_fd),
- pak_region);
- std::vector<std::pair<int, ui::ResourceScaleFactor>> extra_paks = {
- {kWebLayerMainPakDescriptor, ui::kScaleFactorNone},
- {kWebLayer100PercentPakDescriptor, ui::k100Percent}};
- for (const auto& pak_info : extra_paks) {
- pak_fd = global_descriptors->Get(pak_info.first);
- pak_region = global_descriptors->GetRegion(pak_info.first);
- ui::ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion(
- base::File(pak_fd), pak_region, pak_info.second);
- }
- }
- #else
- base::FilePath pak_file;
- bool r = base::PathService::Get(base::DIR_ASSETS, &pak_file);
- DCHECK(r);
- pak_file = pak_file.AppendASCII(params_.pak_name);
- ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file);
- #endif
- }
- content::ContentClient* ContentMainDelegateImpl::CreateContentClient() {
- content_client_ = std::make_unique<ContentClientImpl>();
- return content_client_.get();
- }
- content::ContentBrowserClient*
- ContentMainDelegateImpl::CreateContentBrowserClient() {
- browser_client_ = std::make_unique<ContentBrowserClientImpl>(¶ms_);
- return browser_client_.get();
- }
- content::ContentRendererClient*
- ContentMainDelegateImpl::CreateContentRendererClient() {
- renderer_client_ = std::make_unique<ContentRendererClientImpl>();
- return renderer_client_.get();
- }
- content::ContentUtilityClient*
- ContentMainDelegateImpl::CreateContentUtilityClient() {
- utility_client_ = std::make_unique<ContentUtilityClientImpl>();
- return utility_client_.get();
- }
- } // namespace weblayer
|