#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/app/initialize_mojo_core.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"
#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 , true ,
true , false );
}
void ConfigureFeaturesIfNotSet(
const std::vector<const base::Feature*>& features_to_enable,
const std::vector<const 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);
std::vector<base::StringPiece> parts = base::SplitStringPiece(
f, "<", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
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, ","));
}
}
ContentMainDelegateImpl::ContentMainDelegateImpl(MainParams params)
: params_(std::move(params)) {
#if !BUILDFLAG(IS_ANDROID)
startup_metric_utils::RecordApplicationStartTime(base::TimeTicks::Now());
#endif
}
ContentMainDelegateImpl::~ContentMainDelegateImpl() = default;
absl::optional<int> ContentMainDelegateImpl::BasicStartupComplete() {
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
cl->AppendSwitch(::switches::kDisableNotifications);
std::vector<const base::Feature*> enabled_features = {
#if BUILDFLAG(IS_ANDROID)
&media::kUseAndroidOverlayForSecureOnly,
#endif
};
std::vector<const base::Feature*> disabled_features = {
&::features::kDigitalGoodsApi,
&::features::kNotificationTriggers,
&::features::kPeriodicBackgroundSync,
&blink::features::kPortals,
&::features::kBackForwardCache,
&translate::kTFLiteLanguageDetectionEnabled,
&blink::features::kAnonymousIframeOriginTrial,
#if BUILDFLAG(IS_ANDROID)
&::features::kDynamicColorGamut,
#else
&::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",
false)) {
enabled_features.push_back(&features::kLogJsConsoleMessages);
}
#endif
ConfigureFeaturesIfNotSet(enabled_features, disabled_features);
blink::WebRuntimeFeatures::EnableAnonymousIframe(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)
return absl::holds_alternative<InvokedInChildProcess>(invoked_in);
#else
return true;
#endif
}
bool ContentMainDelegateImpl::ShouldInitializeMojo(InvokedIn invoked_in) {
return ShouldCreateFeatureList(invoked_in);
}
variations::VariationsIdsProvider*
ContentMainDelegateImpl::CreateVariationsIdsProvider() {
return variations::VariationsIdsProvider::Create(
variations::VariationsIdsProvider::Mode::kIgnoreSignedInState);
}
void ContentMainDelegateImpl::PreSandboxStartup() {
#if defined(ARCH_CPU_ARM_FAMILY) && \
(BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS_LACROS))
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::kWebEngineUserDataDir)) {
base::FilePath path =
command_line.GetSwitchValuePath(switches::kWebEngineUserDataDir);
if (base::DirectoryExists(path) || base::CreateDirectory(path)) {
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 , false ));
}
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();
}
if (!ShouldInitializeMojo(invoked_in)) {
content::InitializeMojoCore();
}
return absl::nullopt;
}
absl::variant<int, content::MainFunctionParams>
ContentMainDelegateImpl::RunProcess(
const std::string& process_type,
content::MainFunctionParams main_function_params) {
if (!process_type.empty())
return std::move(main_function_params);
#if !BUILDFLAG(IS_ANDROID)
return std::move(main_function_params);
#else
auto main_runner = content::BrowserMainRunner::Create();
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;
#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 (base::android::BundleUtils::IsBundle())
ui::SetLoadSecondaryLocalePaks(true);
else
ui::SetLocalePaksStoredInApk(true);
std::string locale = ui::ResourceBundle::InitSharedInstanceWithLocale(
{} , nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES);
if (locale.empty()) {
LOG(WARNING) << "Failed to load locale .pak from apk.";
}
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);
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();
}
}