From 5e438474c67e30493a792f1a0b91fb201c68e79a Mon Sep 17 00:00:00 2001 From: uazo Date: Wed, 24 Apr 2024 02:34:52 +0000 Subject: [PATCH] AUTOMATED - new files under control 124.0.6367.82 --- tools/under-control/src/RELEASE | 2 +- .../browser/aw_content_browser_client.cc | 1294 +++ .../browser/chrome_content_browser_client.cc | 8225 +++++++++++++++++ ...hrome_content_browser_client_webui_part.cc | 120 + .../webpreferences/web_preferences.mojom | 479 + .../core/css/media_feature_names.json5 | 81 - .../renderer/core/exported/web_view_impl.cc | 4108 ++++++++ 7 files changed, 14227 insertions(+), 82 deletions(-) create mode 100755 tools/under-control/src/android_webview/browser/aw_content_browser_client.cc create mode 100755 tools/under-control/src/chrome/browser/chrome_content_browser_client.cc create mode 100755 tools/under-control/src/chrome/browser/ui/webui/chrome_content_browser_client_webui_part.cc create mode 100755 tools/under-control/src/third_party/blink/public/mojom/webpreferences/web_preferences.mojom delete mode 100755 tools/under-control/src/third_party/blink/renderer/core/css/media_feature_names.json5 create mode 100755 tools/under-control/src/third_party/blink/renderer/core/exported/web_view_impl.cc diff --git a/tools/under-control/src/RELEASE b/tools/under-control/src/RELEASE index 0538c9c9..d397f8b4 100644 --- a/tools/under-control/src/RELEASE +++ b/tools/under-control/src/RELEASE @@ -1 +1 @@ -124.0.6367.60 +124.0.6367.82 diff --git a/tools/under-control/src/android_webview/browser/aw_content_browser_client.cc b/tools/under-control/src/android_webview/browser/aw_content_browser_client.cc new file mode 100755 index 00000000..71b3dd63 --- /dev/null +++ b/tools/under-control/src/android_webview/browser/aw_content_browser_client.cc @@ -0,0 +1,1294 @@ +// Copyright 2012 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/browser/aw_content_browser_client.h" + +#include +#include +#include +#include +#include +#include + +#include "android_webview/browser/aw_browser_context.h" +#include "android_webview/browser/aw_browser_context_store.h" +#include "android_webview/browser/aw_browser_main_parts.h" +#include "android_webview/browser/aw_browser_process.h" +#include "android_webview/browser/aw_client_hints_controller_delegate.h" +#include "android_webview/browser/aw_contents.h" +#include "android_webview/browser/aw_contents_client_bridge.h" +#include "android_webview/browser/aw_contents_io_thread_client.h" +#include "android_webview/browser/aw_cookie_access_policy.h" +#include "android_webview/browser/aw_devtools_manager_delegate.h" +#include "android_webview/browser/aw_feature_list_creator.h" +#include "android_webview/browser/aw_http_auth_handler.h" +#include "android_webview/browser/aw_settings.h" +#include "android_webview/browser/aw_speech_recognition_manager_delegate.h" +#include "android_webview/browser/aw_web_contents_view_delegate.h" +#include "android_webview/browser/cookie_manager.h" +#include "android_webview/browser/network_service/aw_browser_context_io_thread_handle.h" +#include "android_webview/browser/network_service/aw_proxy_config_monitor.h" +#include "android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.h" +#include "android_webview/browser/network_service/aw_proxying_url_loader_factory.h" +#include "android_webview/browser/network_service/aw_url_loader_throttle.h" +#include "android_webview/browser/safe_browsing/aw_safe_browsing_navigation_throttle.h" +#include "android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.h" +#include "android_webview/browser/supervised_user/aw_supervised_user_throttle.h" +#include "android_webview/browser/supervised_user/aw_supervised_user_url_classifier.h" +#include "android_webview/browser/tracing/aw_tracing_delegate.h" +#include "android_webview/common/aw_content_client.h" +#include "android_webview/common/aw_descriptors.h" +#include "android_webview/common/aw_features.h" +#include "android_webview/common/aw_paths.h" +#include "android_webview/common/aw_switches.h" +#include "android_webview/common/url_constants.h" +#include "base/android/build_info.h" +#include "base/android/locale_utils.h" +#include "base/base_paths_android.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/containers/contains.h" +#include "base/feature_list.h" +#include "base/files/scoped_file.h" +#include "base/functional/bind.h" +#include "base/functional/callback.h" +#include "base/functional/callback_helpers.h" +#include "base/memory/ptr_util.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_macros.h" +#include "base/notreached.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/thread_pool/thread_pool_instance.h" +#include "build/build_config.h" +#include "components/crash/content/browser/crash_handler_host_linux.h" +#include "components/embedder_support/origin_trials/origin_trials_settings_storage.h" +#include "components/embedder_support/switches.h" +#include "components/embedder_support/user_agent_utils.h" +#include "components/navigation_interception/intercept_navigation_delegate.h" +#include "components/page_load_metrics/browser/metrics_navigation_throttle.h" +#include "components/page_load_metrics/browser/metrics_web_contents_observer.h" +#include "components/policy/content/policy_blocklist_navigation_throttle.h" +#include "components/policy/core/browser/browser_policy_connector_base.h" +#include "components/prefs/pref_service.h" +#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h" +#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h" +#include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_utils.h" +#include "components/safe_browsing/core/common/features.h" +#include "components/url_matcher/url_matcher.h" +#include "components/url_matcher/url_util.h" +#include "components/version_info/version_info.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/file_url_loader.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/navigation_throttle.h" +#include "content/public/browser/network_service_instance.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/shared_cors_origin_access_list.h" +#include "content/public/browser/site_isolation_policy.h" +#include "content/public/browser/storage_partition.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_descriptors.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/public/common/user_agent.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "net/android/network_library.h" +#include "net/cookies/site_for_cookies.h" +#include "net/http/http_util.h" +#include "net/net_buildflags.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_info.h" +#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" +#include "services/metrics/public/cpp/ukm_source_id.h" +#include "services/network/network_service.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/url_loader_factory_builder.h" +#include "services/network/public/mojom/cookie_manager.mojom-forward.h" +#include "services/network/public/mojom/fetch_api.mojom.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "services/service_manager/public/cpp/interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" +#include "third_party/blink/public/common/web_preferences/web_preferences.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/resource/resource_bundle_android.h" +#include "ui/display/util/display_util.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/resources/grit/ui_resources.h" + +using content::BrowserThread; +using content::WebContents; +using safe_browsing::hash_realtime_utils::HashRealTimeSelection; +using AttributionReportType = + content::ContentBrowserClient::AttributionReportingOsReportType; + +namespace android_webview { +namespace { +static bool g_should_create_thread_pool = true; +#if DCHECK_IS_ON() +// A boolean value to determine if the NetworkContext has been created yet. This +// exists only to check correctness: g_check_cleartext_permitted may only be set +// before the NetworkContext has been created (otherwise, +// g_check_cleartext_permitted won't have any effect). +bool g_created_network_context_params = false; +#endif + +// On apps targeting API level O or later, check cleartext is enforced. +bool g_check_cleartext_permitted = false; + +} // anonymous namespace + +std::string GetProduct() { + return embedder_support::GetProductAndVersion(); +} + +std::string GetUserAgent() { + // "Version/4.0" had been hardcoded in the legacy WebView. + std::string product = "Version/4.0 " + GetProduct(); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUseMobileUserAgent)) { + product += " Mobile"; + } + + if (base::FeatureList::IsEnabled( + features::kWebViewReduceUAAndroidVersionDeviceModel)) { + return content::BuildUnifiedPlatformUAFromProductAndExtraOs(product, + "; wv"); + } + + return content::BuildUserAgentFromProductAndExtraOSInfo( + product, "; wv", content::IncludeAndroidBuildNumber::Include); +} + +// TODO(yirui): can use similar logic as in PrependToAcceptLanguagesIfNecessary +// in chrome/browser/android/preferences/pref_service_bridge.cc +// static +std::string AwContentBrowserClient::GetAcceptLangsImpl() { + // Start with the current locale(s) in BCP47 format. + std::string locales_string = AwContents::GetLocaleList(); + + // If accept languages do not contain en-US, add in en-US which will be + // used with a lower q-value. + if (!base::Contains(locales_string, "en-US")) { + locales_string += ",en-US"; + } + return locales_string; +} + +// static +void AwContentBrowserClient::set_check_cleartext_permitted(bool permitted) { +#if DCHECK_IS_ON() + DCHECK(!g_created_network_context_params); +#endif + g_check_cleartext_permitted = permitted; +} + +// static +bool AwContentBrowserClient::get_check_cleartext_permitted() { + return g_check_cleartext_permitted; +} + +AwContentBrowserClient::AwContentBrowserClient( + AwFeatureListCreator* aw_feature_list_creator) + : sniff_file_urls_(AwSettings::GetAllowSniffingFileUrls()), + aw_feature_list_creator_(aw_feature_list_creator) { + // |aw_feature_list_creator| should not be null. The AwBrowserContext will + // take the PrefService owned by the creator as the Local State instead + // of loading the JSON file from disk. + DCHECK(aw_feature_list_creator_); +} + +AwContentBrowserClient::~AwContentBrowserClient() {} + +void AwContentBrowserClient::OnNetworkServiceCreated( + network::mojom::NetworkService* network_service) { + // TODO(https://crbug.com/1085233): If CertVerifierServiceFactory is moved to + // a separate process, this will likely need to be set somewhere else instead + // of here. + content::GetCertVerifierServiceFactory()->SetUseChromeRootStore( + false, base::DoNothing()); + + content::GetNetworkService()->SetUpHttpAuth( + network::mojom::HttpAuthStaticParams::New()); + content::GetNetworkService()->ConfigureHttpAuthPrefs( + AwBrowserProcess::GetInstance()->CreateHttpAuthDynamicParams()); +} + +void AwContentBrowserClient::ConfigureNetworkContextParams( + content::BrowserContext* context, + bool in_memory, + const base::FilePath& relative_partition_path, + network::mojom::NetworkContextParams* network_context_params, + cert_verifier::mojom::CertVerifierCreationParams* + cert_verifier_creation_params) { + DCHECK(context); + + content::GetNetworkService()->ConfigureHttpAuthPrefs( + AwBrowserProcess::GetInstance()->CreateHttpAuthDynamicParams()); + + AwBrowserContext* aw_context = static_cast(context); + aw_context->ConfigureNetworkContextParams(in_memory, relative_partition_path, + network_context_params, + cert_verifier_creation_params); + + mojo::PendingRemote cookie_manager_remote; + network_context_params->cookie_manager = + cookie_manager_remote.InitWithNewPipeAndPassReceiver(); + +#if DCHECK_IS_ON() + g_created_network_context_params = true; +#endif + + // Pass the mojo::PendingRemote to + // android_webview::CookieManager, so it can implement its APIs with this mojo + // CookieManager. + aw_context->GetCookieManager()->SetMojoCookieManager( + std::move(cookie_manager_remote)); +} + +AwBrowserContext* AwContentBrowserClient::InitBrowserContext() { + return AwBrowserContextStore::GetOrCreateInstance()->GetDefault(); +} + +std::unique_ptr +AwContentBrowserClient::CreateBrowserMainParts(bool /* is_integration_test */) { + return std::make_unique(this); +} + +std::unique_ptr +AwContentBrowserClient::GetWebContentsViewDelegate( + content::WebContents* web_contents) { + return std::make_unique(web_contents); +} + +void AwContentBrowserClient::RenderProcessWillLaunch( + content::RenderProcessHost* host) { + // Grant content: scheme access to the whole renderer process, since we impose + // per-view access checks, and access is granted by default (see + // AwSettings.mAllowContentUrlAccess). + content::ChildProcessSecurityPolicy::GetInstance()->GrantRequestScheme( + host->GetID(), url::kContentScheme); +} + +bool AwContentBrowserClient::IsExplicitNavigation( + ui::PageTransition transition) { + return ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED); +} + +bool AwContentBrowserClient::IsHandledURL(const GURL& url) { + if (!url.is_valid()) { + // We handle error cases. + return true; + } + + const std::string scheme = url.scheme(); + DCHECK_EQ(scheme, base::ToLowerASCII(scheme)); + static const char* const kProtocolList[] = { + url::kHttpScheme, + url::kHttpsScheme, +#if BUILDFLAG(ENABLE_WEBSOCKETS) + url::kWsScheme, + url::kWssScheme, +#endif // BUILDFLAG(ENABLE_WEBSOCKETS) + url::kDataScheme, + url::kBlobScheme, + url::kFileSystemScheme, + content::kChromeUIScheme, + url::kContentScheme, + }; + if (scheme == url::kFileScheme) { + // Return false for the "special" file URLs, so they can be loaded + // even if access to file: scheme is not granted to the child process. + return !IsAndroidSpecialFileUrl(url); + } + for (const char* supported_protocol : kProtocolList) { + if (scheme == supported_protocol) { + return true; + } + } + return false; +} + +bool AwContentBrowserClient::ForceSniffingFileUrlsForHtml() { + return sniff_file_urls_; +} + +void AwContentBrowserClient::AppendExtraCommandLineSwitches( + base::CommandLine* command_line, + int child_process_id) { + if (!command_line->HasSwitch(switches::kSingleProcess)) { + // The only kind of a child process WebView can have is renderer or utility. + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + DCHECK(process_type == switches::kRendererProcess || + process_type == switches::kUtilityProcess) + << process_type; + + static const char* const kSwitchNames[] = { + ::switches::kEnableCrashReporter, + ::switches::kEnableCrashReporterForTesting, + embedder_support::kOriginTrialDisabledFeatures, + embedder_support::kOriginTrialPublicKey, + }; + + command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), + kSwitchNames); + } +} + +std::string AwContentBrowserClient::GetApplicationLocale() { + return base::android::GetDefaultLocaleString(); +} + +std::string AwContentBrowserClient::GetAcceptLangs( + content::BrowserContext* context) { + return GetAcceptLangsImpl(); +} + +gfx::ImageSkia AwContentBrowserClient::GetDefaultFavicon() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + // TODO(boliu): Bundle our own default favicon? + return rb.GetImageNamed(IDR_DEFAULT_FAVICON).AsImageSkia(); +} + +content::GeneratedCodeCacheSettings +AwContentBrowserClient::GetGeneratedCodeCacheSettings( + content::BrowserContext* context) { + // WebView limits the main HTTP cache to 20MB; we need to set a comparable + // limit for the code cache since the source file needs to be in the HTTP + // cache for the code cache entry to be used. There are two code caches that + // both use this value, so we pass 10MB to keep the total disk usage to + // roughly 2x what it was before the code cache was implemented. + // TODO(crbug/893318): webview should have smarter cache sizing logic. + AwBrowserContext* browser_context = static_cast(context); + return content::GeneratedCodeCacheSettings( + true, 10 * 1024 * 1024, browser_context->GetHttpCachePath()); +} + +void AwContentBrowserClient::AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_primary_main_frame_request, + bool strict_enforcement, + base::OnceCallback callback) { + AwContentsClientBridge* client = + AwContentsClientBridge::FromWebContents(web_contents); + bool cancel_request = true; + // We only call the callback once but we must pass ownership to a function + // that conditionally calls it. + auto split_callback = base::SplitOnceCallback(std::move(callback)); + if (client) { + client->AllowCertificateError(cert_error, ssl_info.cert.get(), request_url, + std::move(split_callback.first), + &cancel_request); + } + if (cancel_request) { + std::move(split_callback.second) + .Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + } +} + +base::OnceClosure AwContentBrowserClient::SelectClientCertificate( + content::BrowserContext* browser_context, + content::WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + net::ClientCertIdentityList client_certs, + std::unique_ptr delegate) { + AwContentsClientBridge* client = + web_contents ? AwContentsClientBridge::FromWebContents(web_contents) + : nullptr; + if (client) { + client->SelectClientCertificate(cert_request_info, std::move(delegate)); + } + return base::OnceClosure(); +} + +bool AwContentBrowserClient::CanCreateWindow( + content::RenderFrameHost* opener, + const GURL& opener_url, + const GURL& opener_top_level_frame_url, + const url::Origin& source_origin, + content::mojom::WindowContainerType container_type, + const GURL& target_url, + const content::Referrer& referrer, + const std::string& frame_name, + WindowOpenDisposition disposition, + const blink::mojom::WindowFeatures& features, + bool user_gesture, + bool opener_suppressed, + bool* no_javascript_access) { + // We unconditionally allow popup windows at this stage and will give + // the embedder the opporunity to handle displaying of the popup in + // WebContentsDelegate::AddContents (via the + // AwContentsClient.onCreateWindow callback). + // Note that if the embedder has blocked support for creating popup + // windows through AwSettings, then we won't get to this point as + // the popup creation will have been blocked at the WebKit level. + if (no_javascript_access) { + *no_javascript_access = false; + } + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(opener); + AwSettings* settings = AwSettings::FromWebContents(web_contents); + + return (settings && settings->GetJavaScriptCanOpenWindowsAutomatically()) || + user_gesture; +} + +base::FilePath AwContentBrowserClient::GetDefaultDownloadDirectory() { + // Android WebView does not currently use the Chromium downloads system. + // Download requests are cancelled immediately when recognized. However the + // download system still tries to start up and calls this before recognizing + // the request has been cancelled. + return base::FilePath(); +} + +std::string AwContentBrowserClient::GetDefaultDownloadName() { + NOTREACHED() << "Android WebView does not use chromium downloads"; + return std::string(); +} + +std::optional +AwContentBrowserClient::GetLocalTracesDirectory() { + base::FilePath user_data_dir; + if (!base::PathService::Get(android_webview::DIR_LOCAL_TRACES, + &user_data_dir)) { + return std::nullopt; + } + DCHECK(!user_data_dir.empty()); + return user_data_dir; +} + +void AwContentBrowserClient::DidCreatePpapiPlugin( + content::BrowserPpapiHost* browser_host) { + NOTREACHED() << "Android WebView does not support plugins"; +} + +bool AwContentBrowserClient::AllowPepperSocketAPI( + content::BrowserContext* browser_context, + const GURL& url, + bool private_api, + const content::SocketPermissionRequest* params) { + NOTREACHED() << "Android WebView does not support plugins"; + return false; +} + +bool AwContentBrowserClient::IsPepperVpnProviderAPIAllowed( + content::BrowserContext* browser_context, + const GURL& url) { + NOTREACHED() << "Android WebView does not support plugins"; + return false; +} + +content::TracingDelegate* AwContentBrowserClient::GetTracingDelegate() { + return new AwTracingDelegate(); +} + +void AwContentBrowserClient::GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + content::PosixFileDescriptorInfo* mappings) { + base::MemoryMappedFile::Region region; + int fd = ui::GetMainAndroidPackFd(®ion); + if (base::FeatureList::IsEnabled(features::kWebViewCheckPakFileDescriptors)) { + CHECK_GE(fd, 0); + } + mappings->ShareWithRegion(kAndroidWebViewMainPakDescriptor, fd, region); + + fd = ui::GetCommonResourcesPackFd(®ion); + if (base::FeatureList::IsEnabled(features::kWebViewCheckPakFileDescriptors)) { + CHECK_GE(fd, 0); + } + mappings->ShareWithRegion(kAndroidWebView100PercentPakDescriptor, fd, region); + + fd = ui::GetLocalePackFd(®ion); + if (base::FeatureList::IsEnabled(features::kWebViewCheckPakFileDescriptors)) { + CHECK_GE(fd, 0); + } + mappings->ShareWithRegion(kAndroidWebViewLocalePakDescriptor, fd, region); + + int crash_signal_fd = + crashpad::CrashHandlerHost::Get()->GetDeathSignalSocket(); + if (crash_signal_fd >= 0) { + mappings->Share(kCrashDumpSignal, crash_signal_fd); + } +} + +void AwContentBrowserClient::OverrideWebkitPrefs( + content::WebContents* web_contents, + blink::web_pref::WebPreferences* web_prefs) { + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + if (aw_settings) { + aw_settings->PopulateWebPreferences(web_prefs); + } + web_prefs->modal_context_menu = + !base::FeatureList::IsEnabled(features::kWebViewImageDrag); +} + +std::vector> +AwContentBrowserClient::CreateThrottlesForNavigation( + content::NavigationHandle* navigation_handle) { + std::vector> throttles; + // We allow intercepting only navigations within main frames. This + // is used to post onPageStarted. We handle shouldOverrideUrlLoading + // via a sync IPC. + if (navigation_handle->IsInMainFrame()) { + // MetricsNavigationThrottle requires that it runs before + // NavigationThrottles that may delay or cancel navigations, so only + // NavigationThrottles that don't delay or cancel navigations (e.g. + // throttles that are only observing callbacks without affecting navigation + // behavior) should be added before MetricsNavigationThrottle. + throttles.push_back(page_load_metrics::MetricsNavigationThrottle::Create( + navigation_handle)); + } + // Use Synchronous mode for the navigation interceptor, since this class + // doesn't actually call into an arbitrary client, it just posts a task to + // call onPageStarted. shouldOverrideUrlLoading happens earlier (see + // ContentBrowserClient::ShouldOverrideUrlLoading). + std::unique_ptr intercept_navigation_throttle = + navigation_interception::InterceptNavigationDelegate:: + MaybeCreateThrottleFor(navigation_handle, + navigation_interception::SynchronyMode::kSync); + if (intercept_navigation_throttle) { + throttles.push_back(std::move(intercept_navigation_throttle)); + } + + throttles.push_back(std::make_unique( + navigation_handle, + AwBrowserContext::FromWebContents(navigation_handle->GetWebContents()))); + + std::unique_ptr safe_browsing_throttle = + AwSafeBrowsingNavigationThrottle::MaybeCreateThrottleFor( + navigation_handle); + if (safe_browsing_throttle) { + throttles.push_back(std::move(safe_browsing_throttle)); + } + return throttles; +} + +std::unique_ptr +AwContentBrowserClient::CreateDevToolsManagerDelegate() { + return std::make_unique(); +} + +std::vector> +AwContentBrowserClient::CreateURLLoaderThrottles( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + content::NavigationUIData* navigation_ui_data, + int frame_tree_node_id, + std::optional navigation_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // Set lookup mechanism based on feature flag + HashRealTimeSelection hash_real_time_selection = + (base::FeatureList::IsEnabled(safe_browsing::kHashPrefixRealTimeLookups)) + ? HashRealTimeSelection::kDatabaseManager + : HashRealTimeSelection::kNone; + std::vector> result; + result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create( + base::BindRepeating( + [](AwContentBrowserClient* client) { + return client->GetSafeBrowsingUrlCheckerDelegate(); + }, + base::Unretained(this)), + wc_getter, frame_tree_node_id, navigation_id, + // TODO(crbug.com/1033760): rt_lookup_service is + // used to perform real time URL check, which is gated by UKM opted-in. + // Since AW currently doesn't support UKM, this feature is not enabled. + /* rt_lookup_service */ nullptr, + /* hash_realtime_service */ nullptr, + /* hash_realtime_selection */ + hash_real_time_selection, + // TODO(crbug.com/1501194): pass in async_check_tracker to support async + // check on WV. + /* async_check_tracker */ nullptr)); + + if (request.destination == network::mojom::RequestDestination::kDocument) { + const bool is_load_url = + request.transition_type & ui::PAGE_TRANSITION_FROM_API; + const bool is_go_back_forward = + request.transition_type & ui::PAGE_TRANSITION_FORWARD_BACK; + const bool is_reload = ui::PageTransitionCoreTypeIs( + static_cast(request.transition_type), + ui::PAGE_TRANSITION_RELOAD); + if (is_load_url || is_go_back_forward || is_reload) { + result.push_back( + std::make_unique(static_cast( + browser_context))); + } + } + + if ((request.destination == network::mojom::RequestDestination::kDocument || + request.destination == network::mojom::RequestDestination::kIframe) && + request.url.SchemeIsHTTPOrHTTPS()) { + AwSupervisedUserUrlClassifier* urlClassifier = + AwSupervisedUserUrlClassifier::GetInstance(); + if (urlClassifier->ShouldCreateThrottle()) { + result.push_back( + std::make_unique(urlClassifier)); + } + } + + return result; +} + +std::vector> +AwContentBrowserClient::CreateURLLoaderThrottlesForKeepAlive( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + int frame_tree_node_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // Set lookup mechanism based on feature flag + HashRealTimeSelection hash_real_time_selection = + (base::FeatureList::IsEnabled(safe_browsing::kHashPrefixRealTimeLookups)) + ? HashRealTimeSelection::kDatabaseManager + : HashRealTimeSelection::kNone; + + std::vector> result; + + result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create( + base::BindRepeating( + [](AwContentBrowserClient* client) { + return client->GetSafeBrowsingUrlCheckerDelegate(); + }, + base::Unretained(this)), + wc_getter, frame_tree_node_id, /*navigation_id=*/std::nullopt, + // TODO(crbug.com/1033760): rt_lookup_service is + // used to perform real time URL check, which is gated by UKM opted-in. + // Since AW currently doesn't support UKM, this feature is not enabled. + /* rt_lookup_service */ nullptr, + /* hash_realtime_service */ nullptr, + /* hash_realtime_selection */ + hash_real_time_selection, + /* async_check_tracker */ nullptr)); + + return result; +} + +scoped_refptr +AwContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (!safe_browsing_url_checker_delegate_) { + safe_browsing_url_checker_delegate_ = new AwUrlCheckerDelegateImpl( + AwBrowserProcess::GetInstance()->GetSafeBrowsingDBManager(), + AwBrowserProcess::GetInstance()->GetSafeBrowsingUIManager(), + AwBrowserProcess::GetInstance()->GetSafeBrowsingAllowlistManager()); + } + + return safe_browsing_url_checker_delegate_; +} + +bool AwContentBrowserClient::ShouldOverrideUrlLoading( + int frame_tree_node_id, + bool browser_initiated, + const GURL& gurl, + const std::string& request_method, + bool has_user_gesture, + bool is_redirect, + bool is_outermost_main_frame, + bool is_prerendering, + ui::PageTransition transition, + bool* ignore_navigation) { + *ignore_navigation = false; + + // Only GETs can be overridden. + if (request_method != "GET") { + return true; + } + + bool application_initiated = + browser_initiated || transition & ui::PAGE_TRANSITION_FORWARD_BACK; + + // Don't offer application-initiated navigations unless it's a redirect. + if (application_initiated && !is_redirect) { + return true; + } + + // For HTTP schemes, only top-level navigations can be overridden. Similarly, + // WebView Classic lets app override only top level about:blank navigations. + // So we filter out non-top about:blank navigations here. + // + // Note: about:blank navigations are not received in this path at the moment, + // they use the old SYNC IPC path as they are not handled by network stack. + // However, the old path should be removed in future. + if (!is_outermost_main_frame && + (gurl.SchemeIs(url::kHttpScheme) || gurl.SchemeIs(url::kHttpsScheme) || + gurl.SchemeIs(url::kAboutScheme))) { + return true; + } + + WebContents* web_contents = + WebContents::FromFrameTreeNodeId(frame_tree_node_id); + if (web_contents == nullptr) { + return true; + } + AwContentsClientBridge* client_bridge = + AwContentsClientBridge::FromWebContents(web_contents); + if (client_bridge == nullptr) { + return true; + } + + std::u16string url = base::UTF8ToUTF16(gurl.possibly_invalid_spec()); + + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + if ((gurl.SchemeIs(url::kHttpScheme) || gurl.SchemeIs(url::kHttpsScheme)) && + aw_settings->enterprise_authentication_app_link_policy_enabled() && + android_webview::AwBrowserProcess::GetInstance() + ->GetEnterpriseAuthenticationAppLinkManager() + ->IsEnterpriseAuthenticationUrl(gurl)) { + bool success = client_bridge->SendBrowseIntent(url); + if (success) { + *ignore_navigation = true; + return true; + } + } + + net::HttpRequestHeaders request_headers; + if (is_prerendering) { + // We pass the `Sec-Purpose` header to tell the embedder that the navigation + // is for prerendering, within the existing API surface. + request_headers.SetHeader("Sec-Purpose", "prefetch;prerender"); + } + + return client_bridge->ShouldOverrideUrlLoading( + url, has_user_gesture, is_redirect, is_outermost_main_frame, + request_headers, ignore_navigation); +} + +bool AwContentBrowserClient::CreateThreadPool(std::string_view name) { + if (g_should_create_thread_pool) { + base::ThreadPoolInstance::Create(name); + return true; + } + return false; +} + +std::unique_ptr +AwContentBrowserClient::CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + content::BrowserContext* browser_context, + const content::GlobalRequestID& request_id, + bool is_request_for_primary_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) { + return std::make_unique(auth_info, web_contents, + first_auth_attempt, + std::move(auth_required_callback)); +} + +bool AwContentBrowserClient::HandleExternalProtocol( + const GURL& url, + content::WebContents::Getter wc_getter, + int frame_tree_node_id, + content::NavigationUIData* navigation_data, + bool is_primary_main_frame, + bool /* is_in_fenced_frame_tree */, + network::mojom::WebSandboxFlags /*sandbox_flags*/, + ui::PageTransition page_transition, + bool has_user_gesture, + const std::optional& initiating_origin, + content::RenderFrameHost* initiator_document, + mojo::PendingRemote* out_factory) { + // Sandbox flags + // ============= + // + // Contrary to the chrome/ implementation, sandbox flags are ignored. Webview + // by itself to not invoke external apps. However it let the embedding + // app to intercept the request and decide what to do. We need to be careful + // here not breaking applications, so the sandbox flags are ignored. + + mojo::PendingReceiver receiver = + out_factory->InitWithNewPipeAndPassReceiver(); + + content::WebContents* web_contents = wc_getter.Run(); + scoped_refptr browser_context_handle = + web_contents == nullptr + ? nullptr + : base::MakeRefCounted( + static_cast( + web_contents->GetBrowserContext())); + + // We don't need to care for |security_options| as the factories constructed + // below are used only for navigation. + if (content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { + // Manages its own lifetime. + new android_webview::AwProxyingURLLoaderFactory( + frame_tree_node_id, std::move(receiver), mojo::NullRemote(), + true /* intercept_only */, std::nullopt /* security_options */, + nullptr /* xrw_allowlist_matcher */, std::move(browser_context_handle)); + } else { + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce( + [](mojo::PendingReceiver receiver, + int frame_tree_node_id, + scoped_refptr + browser_context_handle) { + // Manages its own lifetime. + new android_webview::AwProxyingURLLoaderFactory( + frame_tree_node_id, std::move(receiver), mojo::NullRemote(), + true /* intercept_only */, + std::nullopt /* security_options */, + nullptr /* xrw_allowlist_matcher */, + std::move(browser_context_handle)); + }, + std::move(receiver), frame_tree_node_id, + std::move(browser_context_handle))); + } + return false; +} + +void AwContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( + int render_process_id, + int render_frame_id, + const std::optional& request_initiator_origin, + NonNetworkURLLoaderFactoryMap* factories) { + WebContents* web_contents = content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(render_process_id, render_frame_id)); + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + + if (aw_settings && aw_settings->GetAllowFileAccess()) { + AwBrowserContext* aw_browser_context = + AwBrowserContext::FromWebContents(web_contents); + factories->emplace( + url::kFileScheme, + content::CreateFileURLLoaderFactory( + aw_browser_context->GetPath(), + aw_browser_context->GetSharedCorsOriginAccessList())); + } +} + +bool AwContentBrowserClient::ShouldAllowNoLongerUsedProcessToExit() { + // TODO(crbug.com/1268454): Add Android WebView support for allowing a + // renderer process to exit when only non-live RenderFrameHosts remain, + // without consulting the app's OnRenderProcessGone crash handlers. + return false; +} + +bool AwContentBrowserClient::ShouldIsolateErrorPage(bool in_main_frame) { + return false; +} + +bool AwContentBrowserClient::ShouldEnableStrictSiteIsolation() { + // TODO(lukasza): When/if we eventually add OOPIF support for AW we should + // consider running AW tests with and without site-per-process (and this might + // require returning true below). Adding OOPIF support for AW is tracked by + // https://crbug.com/806404. + return false; +} + +size_t AwContentBrowserClient::GetMaxRendererProcessCountOverride() { + // TODO(crbug.com/806404): These options can currently can only be turned by + // by manually overriding command line switches because + // `ShouldDisableSiteIsolation` returns true. Should coordinate if/when + // enabling this in production. + if (content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites() || + content::SiteIsolationPolicy::AreIsolatedOriginsEnabled() || + content::SiteIsolationPolicy::IsStrictOriginIsolationEnabled()) { + // Do not restrict the max renderer process count for these site isolation + // modes. This allows OOPIFs to happen on android webview. + return 0u; // Use default. + } + return 1u; +} + +bool AwContentBrowserClient::ShouldDisableSiteIsolation( + content::SiteIsolationMode site_isolation_mode) { + // Since AW does not yet support OOPIFs, we must return true here to disable + // features that may trigger OOPIFs, such as origin isolation. + // + // Adding OOPIF support for AW is tracked by https://crbug.com/806404. + return true; +} + +bool AwContentBrowserClient::ShouldLockProcessToSite( + content::BrowserContext* browser_context, + const GURL& effective_url) { + // TODO(lukasza): https://crbug.com/806404: Once Android WebView supports + // OOPIFs, we should remove this ShouldLockProcess overload. Till then, + // returning false helps avoid accidentally applying citadel-style Site + // Isolation enforcement to Android WebView (and causing incorrect renderer + // kills). + return false; +} + +bool AwContentBrowserClient::ShouldEnforceNewCanCommitUrlChecks() { + // TODO(https://crbug.com/326250356): Diagnose and fix Android WebView crashes + // from these new checks and then remove this function. + return false; +} + +void AwContentBrowserClient::WillCreateURLLoaderFactory( + content::BrowserContext* browser_context, + content::RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + std::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + network::URLLoaderFactoryBuilder& factory_builder, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override, + scoped_refptr navigation_response_task_runner) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + mojo::PendingReceiver proxied_receiver; + mojo::PendingRemote target_factory_remote; + + if (factory_override) { + // We are interested in factories "inside" of CORS, so use + // |factory_override|. + *factory_override = network::mojom::URLLoaderFactoryOverride::New(); + proxied_receiver = + (*factory_override) + ->overriding_factory.InitWithNewPipeAndPassReceiver(); + (*factory_override)->overridden_factory_receiver = + target_factory_remote.InitWithNewPipeAndPassReceiver(); + (*factory_override)->skip_cors_enabled_scheme_check = true; + } else { + // In this case, |factory_override| is not given. But all callers of + // ContentBrowserClient::WillCreateURLLoaderFactory guarantee that + // |factory_override| is null only when the security features on the network + // service is no-op for requests coming to the URLLoaderFactory. Hence we + // can use |factory_builder| here. + std::tie(proxied_receiver, target_factory_remote) = + factory_builder.Append(); + } + scoped_refptr browser_context_handle = + base::MakeRefCounted( + static_cast(browser_context)); + if (frame) { + auto security_options = + std::make_optional(); + security_options->disable_web_security = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableWebSecurity); + WebContents* web_contents = WebContents::FromRenderFrameHost(frame); + const auto& preferences = web_contents->GetOrCreateWebPreferences(); + // See also //android_webview/docs/cors-and-webview-api.md to understand how + // each settings affect CORS behaviors on file:// and content://. + if (request_initiator.scheme() == url::kFileScheme) { + security_options->disable_web_security |= + preferences.allow_universal_access_from_file_urls; + // Usual file:// to file:// requests are mapped to kNoCors if the setting + // is set to true. Howover, file:///android_{asset|res}/ still uses kCors + // and needs to permit it in the |security_options|. + security_options->allow_cors_to_same_scheme = + preferences.allow_file_access_from_file_urls; + } else if (request_initiator.scheme() == url::kContentScheme) { + security_options->allow_cors_to_same_scheme = + preferences.allow_file_access_from_file_urls || + preferences.allow_universal_access_from_file_urls; + } + + auto xrw_allowlist_matcher = + AwSettings::FromWebContents(web_contents)->xrw_allowlist_matcher(); + + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&AwProxyingURLLoaderFactory::CreateProxy, + frame->GetFrameTreeNodeId(), std::move(proxied_receiver), + std::move(target_factory_remote), security_options, + std::move(xrw_allowlist_matcher), + std::move(browser_context_handle))); + } else { + // A service worker and worker subresources set nullptr to |frame|, and + // work without seeing the AllowUniversalAccessFromFileURLs setting. So, + // we don't pass a valid |security_options| here. + AwBrowserContext* aw_browser_context = + static_cast(browser_context); + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce( + &AwProxyingURLLoaderFactory::CreateProxy, + content::RenderFrameHost::kNoFrameTreeNodeId, + std::move(proxied_receiver), std::move(target_factory_remote), + std::nullopt /* security_options */, + aw_browser_context->service_worker_xrw_allowlist_matcher(), + std::move(browser_context_handle))); + } +} + +uint32_t AwContentBrowserClient::GetWebSocketOptions( + content::RenderFrameHost* frame) { + uint32_t options = network::mojom::kWebSocketOptionNone; + if (!frame) { + return options; + } + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(frame); + AwContents* aw_contents = AwContents::FromWebContents(web_contents); + + bool global_cookie_policy = + AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies(); + bool third_party_cookie_policy = aw_contents->AllowThirdPartyCookies(); + if (!global_cookie_policy) { + options |= network::mojom::kWebSocketOptionBlockAllCookies; + } else if (!third_party_cookie_policy) { + options |= network::mojom::kWebSocketOptionBlockThirdPartyCookies; + } + return options; +} + +bool AwContentBrowserClient::WillCreateRestrictedCookieManager( + network::mojom::RestrictedCookieManagerRole role, + content::BrowserContext* browser_context, + const url::Origin& origin, + const net::IsolationInfo& isolation_info, + bool is_service_worker, + int process_id, + int routing_id, + mojo::PendingReceiver* receiver) { + mojo::PendingReceiver orig_receiver = + std::move(*receiver); + + mojo::PendingRemote + target_rcm_remote; + *receiver = target_rcm_remote.InitWithNewPipeAndPassReceiver(); + + AwProxyingRestrictedCookieManager::CreateAndBind( + std::move(target_rcm_remote), is_service_worker, process_id, routing_id, + std::move(orig_receiver)); + + return false; // only made a proxy, still need the actual impl to be made. +} + +std::string AwContentBrowserClient::GetProduct() { + // Return the unreduced product version regardless of the user agent reduction + // policy. The call sites do not require user agent reduction and having the + // unreduced version is necessary for performance tracing. + if (base::FeatureList::IsEnabled(features::kWebViewUnreducedProductVersion)) { + return std::string(version_info::GetProductNameAndVersionForUserAgent()); + } + return android_webview::GetProduct(); +} + +std::string AwContentBrowserClient::GetUserAgent() { + return android_webview::GetUserAgent(); +} + +blink::UserAgentMetadata AwContentBrowserClient::GetUserAgentMetadata() { + return AwClientHintsControllerDelegate::GetUserAgentMetadataOverrideBrand(); +} + +content::ContentBrowserClient::WideColorGamutHeuristic +AwContentBrowserClient::GetWideColorGamutHeuristic() { + if (base::FeatureList::IsEnabled(features::kWebViewWideColorGamutSupport)) { + return WideColorGamutHeuristic::kUseWindow; + } + + if (display::HasForceDisplayColorProfile() && + display::GetForcedDisplayColorProfile() == + gfx::ColorSpace::CreateDisplayP3D65()) { + return WideColorGamutHeuristic::kUseWindow; + } + + return WideColorGamutHeuristic::kNone; +} + +void AwContentBrowserClient::LogWebFeatureForCurrentPage( + content::RenderFrameHost* render_frame_host, + blink::mojom::WebFeature feature) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage( + render_frame_host, feature); +} + +content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride +AwContentBrowserClient::ShouldOverridePrivateNetworkRequestPolicy( + content::BrowserContext* browser_context, + const url::Origin& origin) { + // Webview does not implement support for deprecation trials, so webview apps + // broken by Private Network Access restrictions cannot help themselves by + // registering for the trial. + // See crbug.com/1255675. + return content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride:: + kForceAllow; +} + +content::SpeechRecognitionManagerDelegate* +AwContentBrowserClient::CreateSpeechRecognitionManagerDelegate() { + return new AwSpeechRecognitionManagerDelegate(); +} + +bool AwContentBrowserClient::HasErrorPage(int http_status_code) { + return http_status_code >= 400; +} + +bool AwContentBrowserClient::SuppressDifferentOriginSubframeJSDialogs( + content::BrowserContext* browser_context) { + return base::FeatureList::IsEnabled( + features::kWebViewSuppressDifferentOriginSubframeJSDialogs); +} + +bool AwContentBrowserClient::ShouldPreconnectNavigation( + content::RenderFrameHost* render_frame_host) { + // This didn't make a performance improvement in WebView. + return false; +} + +void AwContentBrowserClient::OnDisplayInsecureContent( + content::WebContents* web_contents) { + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + if (aw_settings) { + UMA_HISTOGRAM_ENUMERATION( + "Android.WebView.OptionallyBlockableMixedContentLoaded.Mode", + aw_settings->GetMixedContentMode(), + AwSettings::MixedContentMode::COUNT); + } +} + +// static +void AwContentBrowserClient::DisableCreatingThreadPool() { + g_should_create_thread_pool = false; +} + +blink::mojom::OriginTrialsSettingsPtr +AwContentBrowserClient::GetOriginTrialsSettings() { + return AwBrowserProcess::GetInstance() + ->GetOriginTrialsSettingsStorage() + ->GetSettings(); +} + +network::mojom::AttributionSupport +AwContentBrowserClient::GetAttributionSupport( + AttributionReportingOsApiState state, + bool client_os_disabled) { + // WebView only supports OS-level attribution and not web-attribution. + switch (state) { + case AttributionReportingOsApiState::kDisabled: + return network::mojom::AttributionSupport::kNone; + case AttributionReportingOsApiState::kEnabled: + return client_os_disabled ? network::mojom::AttributionSupport::kNone + : network::mojom::AttributionSupport::kOs; + } +} + +bool AwContentBrowserClient::IsAttributionReportingOperationAllowed( + content::BrowserContext* browser_context, + AttributionReportingOperation operation, + content::RenderFrameHost* rfh, + const url::Origin* source_origin, + const url::Origin* destination_origin, + const url::Origin* reporting_origin, + bool* can_bypass) { + // WebView only supports OS-level attribution and not web-attribution. + // Note: We do not check here if attribution reporting has been disabled + // for the associated WebView as this is checked at the start of processing + // an attribution event. + switch (operation) { + case AttributionReportingOperation::kAny: + case AttributionReportingOperation::kOsSource: + case AttributionReportingOperation::kOsTrigger: + case AttributionReportingOperation::kOsSourceVerboseDebugReport: + case AttributionReportingOperation::kOsTriggerVerboseDebugReport: + return true; + case AttributionReportingOperation::kSource: + case AttributionReportingOperation::kTrigger: + case AttributionReportingOperation::kSourceVerboseDebugReport: + case AttributionReportingOperation::kTriggerVerboseDebugReport: + case AttributionReportingOperation::kReport: + case AttributionReportingOperation::kSourceTransitionalDebugReporting: + case AttributionReportingOperation::kTriggerTransitionalDebugReporting: + return false; + case AttributionReportingOperation::kOsSourceTransitionalDebugReporting: + case AttributionReportingOperation::kOsTriggerTransitionalDebugReporting: { + if (!AwCookieAccessPolicy::GetInstance()->GetShouldAcceptCookies()) { + return false; + } + + WebContents* web_contents = + content::WebContents::FromRenderFrameHost(rfh); + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + if (!aw_settings) { + return false; + } + + return aw_settings->GetAllowThirdPartyCookies(); + } + } + + NOTREACHED_NORETURN(); +} + +content::ContentBrowserClient::AttributionReportingOsReportTypes +AwContentBrowserClient::GetAttributionReportingOsReportTypes( + content::WebContents* web_contents) { + // Attribution reporting can register a source to either the top level origin + // or the app. For WebView the default is to register sources against the app + // as: + // 1. WebViews are often used in cases where for sources the top level origin + // is not as relevant as the app context. + // 2. Web registration APIs currently require a special registration from the + // app in Android for registering sources and the more common case is that the + // app does not have this registration. Note: This behaviour can be switched + // to registering against the top level origin via an AndroidX API + + // Attribution reporting can register a trigger to either the top level origin + // or the app. For WebView the default is to register triggers against the top + // level origin as: + // 1. WebViews are mostly used in cases where for triggers the app context is + // not as relevant as the top level origin. Note: This behaviour can be + // switched to registering against the app via an AndroidX API + + AwSettings* aw_settings = AwSettings::FromWebContents(web_contents); + + if (!aw_settings) { + return {AttributionReportType::kDisabled, AttributionReportType::kDisabled}; + } + + AwSettings::AttributionBehavior attribution_behavior = + aw_settings->GetAttributionBehavior(); + + switch (attribution_behavior) { + case AwSettings::AttributionBehavior::WEB_SOURCE_AND_WEB_TRIGGER: + return {AttributionReportType::kWeb, AttributionReportType::kWeb}; + case AwSettings::AttributionBehavior::APP_SOURCE_AND_WEB_TRIGGER: + return {AttributionReportType::kOs, AttributionReportType::kWeb}; + case AwSettings::AttributionBehavior::APP_SOURCE_AND_APP_TRIGGER: + return {AttributionReportType::kOs, AttributionReportType::kOs}; + case AwSettings::AttributionBehavior::DISABLED: + return {AttributionReportType::kDisabled, + AttributionReportType::kDisabled}; + } + + NOTREACHED_NORETURN(); +} + +network::mojom::IpProtectionProxyBypassPolicy +AwContentBrowserClient::GetIpProtectionProxyBypassPolicy() { + // The exact WebView-specific exclusion policy that is used will depend + // on android_webview::features::kWebViewIpProtectionExclusionCriteria + return network::mojom::IpProtectionProxyBypassPolicy::kExclusionList; +} + +bool AwContentBrowserClient::WillProvidePublicFirstPartySets() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kWebViewFpsComponent); +} + +} // namespace android_webview diff --git a/tools/under-control/src/chrome/browser/chrome_content_browser_client.cc b/tools/under-control/src/chrome/browser/chrome_content_browser_client.cc new file mode 100755 index 00000000..ac42bd8b --- /dev/null +++ b/tools/under-control/src/chrome/browser/chrome_content_browser_client.cc @@ -0,0 +1,8225 @@ +// Copyright 2012 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chrome_content_browser_client.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/base_switches.h" +#include "base/check_deref.h" +#include "base/command_line.h" +#include "base/containers/contains.h" +#include "base/containers/fixed_flat_set.h" +#include "base/dcheck_is_on.h" +#include "base/feature_list.h" +#include "base/functional/bind.h" +#include "base/functional/callback.h" +#include "base/i18n/base_i18n_switches.h" +#include "base/i18n/character_encoding.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/field_trial_params.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" +#include "base/path_service.h" +#include "base/ranges/algorithm.h" +#include "base/stl_util.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/task/sequenced_task_runner.h" +#include "base/types/expected.h" +#include "base/types/expected_macros.h" +#include "base/values.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "build/config/chromebox_for_meetings/buildflags.h" // PLATFORM_CFM +#include "chrome/browser/after_startup_task_utils.h" +#include "chrome/browser/app_mode/app_mode_utils.h" +#include "chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.h" +#include "chrome/browser/browser_about_handler.h" +#include "chrome/browser/browser_features.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browsing_data/chrome_browsing_data_model_delegate.h" +#include "chrome/browser/browsing_topics/browsing_topics_service_factory.h" +#include "chrome/browser/captive_portal/captive_portal_service_factory.h" +#include "chrome/browser/child_process_host_flags.h" +#include "chrome/browser/chrome_browser_main_extra_parts_nacl_deprecation.h" +#include "chrome/browser/chrome_content_browser_client_binder_policies.h" +#include "chrome/browser/chrome_content_browser_client_parts.h" +#include "chrome/browser/content_settings/cookie_settings_factory.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" +#include "chrome/browser/data_saver/data_saver.h" +#include "chrome/browser/defaults.h" +#include "chrome/browser/device_api/device_service_impl.h" +#include "chrome/browser/device_api/managed_configuration_service.h" +#include "chrome/browser/dips/chrome_dips_delegate.h" +#include "chrome/browser/download/chrome_download_manager_delegate.h" +#include "chrome/browser/download/download_prefs.h" +#include "chrome/browser/enterprise/browser_management/management_service_factory.h" +#include "chrome/browser/enterprise/connectors/connectors_service.h" +#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h" +#include "chrome/browser/enterprise/reporting/prefs.h" +#include "chrome/browser/enterprise/util/managed_browser_utils.h" +#include "chrome/browser/extensions/chrome_extension_cookies.h" +#include "chrome/browser/external_protocol/external_protocol_handler.h" +#include "chrome/browser/favicon/favicon_utils.h" +#include "chrome/browser/first_party_sets/first_party_sets_navigation_throttle.h" +#include "chrome/browser/font_family_cache.h" +#include "chrome/browser/gpu/chrome_browser_main_extra_parts_gpu.h" +#include "chrome/browser/hid/chrome_hid_delegate.h" +#include "chrome/browser/interstitials/enterprise_util.h" +#include "chrome/browser/lifetime/browser_shutdown.h" +#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h" +#include "chrome/browser/media/audio_service_util.h" +#include "chrome/browser/media/prefs/capture_device_ranking.h" +#include "chrome/browser/media/router/media_router_feature.h" +#include "chrome/browser/media/webrtc/audio_debug_recordings_handler.h" +#include "chrome/browser/media/webrtc/capture_policy_utils.h" +#include "chrome/browser/media/webrtc/chrome_screen_enumerator.h" +#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" +#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h" +#include "chrome/browser/media/webrtc/webrtc_logging_controller.h" +#include "chrome/browser/memory/chrome_browser_main_extra_parts_memory.h" +#include "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h" +#include "chrome/browser/metrics/chrome_feature_list_creator.h" +#include "chrome/browser/navigation_predictor/anchor_element_preloader.h" +#include "chrome/browser/net/chrome_network_delegate.h" +#include "chrome/browser/net/profile_network_context_service.h" +#include "chrome/browser/net/profile_network_context_service_factory.h" +#include "chrome/browser/net/system_network_context_manager.h" +#include "chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h" +#include "chrome/browser/payments/payment_request_display_manager_factory.h" +#include "chrome/browser/performance_manager/public/chrome_browser_main_extra_parts_performance_manager.h" +#include "chrome/browser/performance_manager/public/chrome_content_browser_client_performance_manager_part.h" +#include "chrome/browser/performance_monitor/chrome_browser_main_extra_parts_performance_monitor.h" +#include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h" +#include "chrome/browser/plugins/plugin_utils.h" +#include "chrome/browser/policy/policy_util.h" +#include "chrome/browser/policy/profile_policy_connector.h" +#include "chrome/browser/predictors/loading_predictor.h" +#include "chrome/browser/predictors/loading_predictor_factory.h" +#include "chrome/browser/preloading/navigation_ablation_throttle.h" +#include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h" +#include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_speculation_host_delegate.h" +#include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h" +#include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h" +#include "chrome/browser/preloading/prefetch/prefetch_service/chrome_prefetch_service_delegate.h" +#include "chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h" +#include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader.h" +#include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.h" +#include "chrome/browser/preloading/preloading_features.h" +#include "chrome/browser/preloading/preloading_prefs.h" +#include "chrome/browser/preloading/prerender/prerender_web_contents_delegate.h" +#include "chrome/browser/privacy_budget/identifiability_study_state.h" +#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h" +#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h" +#include "chrome/browser/private_network_access/chrome_private_network_device_delegate.h" +#include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_io_data.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profile_selections.h" +#include "chrome/browser/profiles/renderer_updater.h" +#include "chrome/browser/profiles/renderer_updater_factory.h" +#include "chrome/browser/profiling_host/chrome_browser_main_extra_parts_profiling.h" +#include "chrome/browser/renderer_host/chrome_navigation_ui_data.h" +#include "chrome/browser/renderer_preferences_util.h" +#include "chrome/browser/safe_browsing/chrome_ping_manager_factory.h" +#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" +#include "chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/safe_browsing/url_checker_delegate_impl.h" +#include "chrome/browser/safe_browsing/url_lookup_service_factory.h" +#include "chrome/browser/search/search.h" +#include "chrome/browser/segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.h" +#include "chrome/browser/sharing/sms/sms_remote_fetcher.h" +#include "chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h" +#include "chrome/browser/signin/chrome_signin_url_loader_throttle.h" +#include "chrome/browser/signin/header_modification_delegate_impl.h" +#include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h" +#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h" +#include "chrome/browser/ssl/https_defaulted_callbacks.h" +#include "chrome/browser/ssl/https_upgrades_interceptor.h" +#include "chrome/browser/ssl/https_upgrades_navigation_throttle.h" +#include "chrome/browser/ssl/sct_reporting_service.h" +#include "chrome/browser/ssl/security_state_tab_helper.h" +#include "chrome/browser/ssl/ssl_client_certificate_selector.h" +#include "chrome/browser/ssl/typed_navigation_upgrade_throttle.h" +#include "chrome/browser/task_manager/sampling/task_manager_impl.h" +#include "chrome/browser/tracing/chrome_tracing_delegate.h" +#include "chrome/browser/translate/translate_service.h" +#include "chrome/browser/ui/blocked_content/blocked_window_params.h" +#include "chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h" +#include "chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_navigator_params.h" +#include "chrome/browser/ui/chrome_select_file_policy.h" +#include "chrome/browser/ui/login/http_auth_coordinator.h" +#include "chrome/browser/ui/login/login_navigation_throttle.h" +#include "chrome/browser/ui/passwords/password_manager_navigation_throttle.h" +#include "chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h" +#include "chrome/browser/ui/prefs/pref_watcher.h" +#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h" +#include "chrome/browser/ui/ui_features.h" +#include "chrome/browser/ui/webid/identity_dialog_controller.h" +#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h" +#include "chrome/browser/ui/webui/log_web_ui_url.h" +#include "chrome/browser/ui/webui/top_chrome/webui_url_utils.h" +#include "chrome/browser/universal_web_contents_observers.h" +#include "chrome/browser/usb/chrome_usb_delegate.h" +#include "chrome/browser/vr/vr_tab_helper.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" +#include "chrome/browser/web_applications/isolated_web_apps/pending_install_info.h" +#include "chrome/browser/webapps/web_app_offline.h" +#include "chrome/browser/webauthn/webauthn_pref_names.h" +#include "chrome/common/buildflags.h" +#include "chrome/common/channel_info.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_content_client.h" +#include "chrome/common/chrome_features.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_paths_internal.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" +#include "chrome/common/google_url_loader_throttle.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/pdf_util.h" +#include "chrome/common/ppapi_utils.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/profiler/thread_profiler_configuration.h" +#include "chrome/common/renderer_configuration.mojom.h" +#include "chrome/common/secure_origin_allowlist.h" +#include "chrome/common/url_constants.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/installer/util/google_update_settings.h" +#include "components/autofill/core/common/autofill_switches.h" +#include "components/blocked_content/popup_blocker.h" +#include "components/browsing_topics/browsing_topics_service.h" +#include "components/captive_portal/core/buildflags.h" +#include "components/content_settings/browser/page_specific_content_settings.h" +#include "components/content_settings/core/browser/content_settings_utils.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/browser/private_network_settings.h" +#include "components/content_settings/core/common/content_settings.h" +#include "components/content_settings/core/common/content_settings_types.h" +#include "components/custom_handlers/protocol_handler_registry.h" +#include "components/custom_handlers/protocol_handler_throttle.h" +#include "components/dom_distiller/core/dom_distiller_switches.h" +#include "components/dom_distiller/core/url_constants.h" +#include "components/embedder_support/content_settings_utils.h" +#include "components/embedder_support/origin_trials/origin_trials_settings_storage.h" +#include "components/embedder_support/switches.h" +#include "components/embedder_support/user_agent_utils.h" +#include "components/enterprise/buildflags/buildflags.h" +#include "components/enterprise/common/proto/connectors.pb.h" +#include "components/enterprise/content/clipboard_restriction_service.h" +#include "components/enterprise/content/pref_names.h" +#include "components/error_page/common/error.h" +#include "components/error_page/common/error_page_switches.h" +#include "components/error_page/common/localized_error.h" +#include "components/error_page/content/browser/net_error_auto_reloader.h" +#include "components/google/core/common/google_switches.h" +#include "components/keep_alive_registry/keep_alive_types.h" +#include "components/keep_alive_registry/scoped_keep_alive.h" +#include "components/language/core/browser/pref_names.h" +#include "components/lens/buildflags.h" +#include "components/live_caption/caption_util.h" +#include "components/media_device_salt/media_device_salt_service.h" +#include "components/media_router/browser/presentation/presentation_service_delegate_impl.h" +#include "components/media_router/browser/presentation/receiver_presentation_service_delegate_impl.h" +#include "components/media_router/browser/presentation/web_contents_presentation_manager.h" +#include "components/metrics/client_info.h" +#include "components/metrics_services_manager/metrics_services_manager.h" +#include "components/net_log/chrome_net_log.h" +#include "components/network_session_configurator/common/network_switches.h" +#include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" +#include "components/no_state_prefetch/common/no_state_prefetch_final_status.h" +#include "components/no_state_prefetch/common/no_state_prefetch_url_loader_throttle.h" +#include "components/omnibox/common/omnibox_features.h" +#include "components/page_load_metrics/browser/metrics_navigation_throttle.h" +#include "components/page_load_metrics/browser/metrics_web_contents_observer.h" +#include "components/payments/content/payment_credential_factory.h" +#include "components/payments/content/payment_handler_navigation_throttle.h" +#include "components/payments/content/payment_request_display_manager.h" +#include "components/performance_manager/embedder/performance_manager_registry.h" +#include "components/permissions/bluetooth_delegate_impl.h" +#include "components/permissions/permission_context_base.h" +#include "components/policy/content/policy_blocklist_navigation_throttle.h" +#include "components/policy/content/policy_blocklist_service.h" +#include "components/policy/core/common/management/management_service.h" +#include "components/policy/core/common/policy_pref_names.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h" +#include "components/privacy_sandbox/privacy_sandbox_features.h" +#include "components/privacy_sandbox/privacy_sandbox_prefs.h" +#include "components/privacy_sandbox/privacy_sandbox_settings.h" +#include "components/privacy_sandbox/tracking_protection_settings.h" +#include "components/safe_browsing/content/browser/async_check_tracker.h" +#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h" +#include "components/safe_browsing/content/browser/password_protection/password_protection_commit_deferring_condition.h" +#include "components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h" +#include "components/safe_browsing/content/browser/ui_manager.h" +#include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h" +#include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_utils.h" +#include "components/safe_browsing/core/browser/realtime/policy_engine.h" +#include "components/safe_browsing/core/browser/realtime/url_lookup_service.h" +#include "components/safe_browsing/core/browser/url_checker_delegate.h" +#include "components/safe_browsing/core/common/features.h" +#include "components/safe_browsing/core/common/safe_browsing_prefs.h" +#include "components/security_interstitials/content/insecure_form_navigation_throttle.h" +#include "components/security_interstitials/content/ssl_error_handler.h" +#include "components/security_interstitials/content/ssl_error_navigation_throttle.h" +#include "components/security_state/core/security_state.h" +#include "components/services/storage/public/cpp/storage_prefs.h" +#include "components/site_isolation/pref_names.h" +#include "components/site_isolation/preloaded_isolated_origins.h" +#include "components/site_isolation/site_isolation_policy.h" +#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h" +#include "components/supervised_user/core/common/buildflags.h" +#include "components/translate/core/common/translate_switches.h" +#include "components/user_prefs/user_prefs.h" +#include "components/variations/variations_associated_data.h" +#include "components/variations/variations_switches.h" +#include "components/version_info/version_info.h" +#include "components/webapps/common/web_app_id.h" +#include "content/public/browser/attribution_data_model.h" +#include "content/public/browser/browser_accessibility_state.h" +#include "content/public/browser/browser_child_process_host.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_main_parts.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/browser_url_handler.h" +#include "content/public/browser/certificate_request_result_type.h" +#include "content/public/browser/child_process_data.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/file_url_loader.h" +#include "content/public/browser/isolated_web_apps_policy.h" +#include "content/public/browser/legacy_tech_cookie_issue_details.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/navigation_throttle.h" +#include "content/public/browser/overlay_window.h" +#include "content/public/browser/permission_controller.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/site_isolation_mode.h" +#include "content/public/browser/sms_fetcher.h" +#include "content/public/browser/tts_controller.h" +#include "content/public/browser/tts_platform.h" +#include "content/public/browser/url_loader_request_interceptor.h" +#include "content/public/browser/vpn_service_proxy.h" +#include "content/public/browser/weak_document_ptr.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_view_delegate.h" +#include "content/public/browser/web_ui_url_loader_factory.h" +#include "content/public/browser/webui_config_map.h" +#include "content/public/common/content_descriptors.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/window_container_type.mojom-shared.h" +#include "device/vr/buildflags/buildflags.h" +#include "extensions/browser/browser_frame_context_data.h" +#include "extensions/buildflags/buildflags.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/google_api_keys.h" +#include "gpu/config/gpu_switches.h" +#include "media/base/media_switches.h" +#include "media/media_buildflags.h" +#include "media/mojo/buildflags.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/base/features.h" +#include "net/cookies/site_for_cookies.h" +#include "net/ssl/client_cert_store.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_private_key.h" +#include "pdf/buildflags.h" +#include "ppapi/buildflags/buildflags.h" +#include "printing/buildflags/buildflags.h" +#include "sandbox/policy/features.h" +#include "sandbox/policy/mojom/sandbox.mojom.h" +#include "sandbox/policy/switches.h" +#include "services/device/public/cpp/geolocation/geolocation_system_permission_manager.h" +#include "services/metrics/public/cpp/ukm_source_id.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/network_switches.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/self_deleting_url_loader_factory.h" +#include "services/network/public/cpp/web_sandbox_flags.h" +#include "services/network/public/mojom/network_service.mojom.h" +#include "services/network/public/mojom/web_transport.mojom.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" +#include "third_party/blink/public/common/navigation/navigation_policy.h" +#include "third_party/blink/public/common/permissions/permission_utils.h" +#include "third_party/blink/public/common/permissions_policy/permissions_policy.h" +#include "third_party/blink/public/common/switches.h" +#include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" +#include "third_party/blink/public/public_buildflags.h" +#include "third_party/widevine/cdm/buildflags.h" +#include "ui/base/clipboard/clipboard_format_type.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/page_transition_types.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/color/color_provider_key.h" +#include "ui/gfx/switches.h" +#include "ui/native_theme/native_theme.h" +#include "url/gurl.h" +#include "url/origin.h" +#include "url/third_party/mozilla/url_parse.h" +#include "url/url_constants.h" + +#if BUILDFLAG(IS_WIN) +#include "base/files/file_util.h" +#include "base/strings/string_tokenizer.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" +#include "chrome/browser/chrome_browser_main_win.h" +#include "chrome/browser/enterprise/platform_auth/platform_auth_navigation_throttle.h" +#include "chrome/browser/lifetime/application_lifetime_desktop.h" +#include "chrome/install_static/install_util.h" +#include "chrome/services/util_win/public/mojom/util_win.mojom.h" +#include "sandbox/win/src/sandbox_policy.h" +#elif BUILDFLAG(IS_MAC) +#include "chrome/browser/browser_process_platform_part_mac.h" +#include "chrome/browser/chrome_browser_main_mac.h" +#include "chrome/browser/mac/auth_session_request.h" +#include "chrome/browser/mac/chrome_browser_main_extra_parts_mac.h" +#include "components/soda/constants.h" +#include "sandbox/mac/sandbox_compiler.h" +#include "sandbox/policy/mac/params.h" +#include "sandbox/policy/mac/sandbox_mac.h" +#elif BUILDFLAG(IS_CHROMEOS_ASH) +#include "ash/constants/ash_features.h" +#include "ash/constants/ash_pref_names.h" +#include "ash/constants/ash_switches.h" +#include "ash/public/cpp/tablet_mode.h" +#include "ash/webui/camera_app_ui/url_constants.h" +#include "ash/webui/help_app_ui/url_constants.h" +#include "ash/webui/media_app_ui/url_constants.h" +#include "ash/webui/scanning/url_constants.h" +#include "chrome/app/chrome_crash_reporter_client.h" +#include "chrome/browser/ash/arc/fileapi/arc_content_file_system_backend_delegate.h" +#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_backend_delegate.h" +#include "chrome/browser/ash/chrome_browser_main_parts_ash.h" +#include "chrome/browser/ash/crosapi/browser_util.h" +#include "chrome/browser/ash/drive/fileapi/drivefs_file_system_backend_delegate.h" +#include "chrome/browser/ash/file_manager/app_id.h" +#include "chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h" +#include "chrome/browser/ash/fileapi/external_file_url_loader_factory.h" +#include "chrome/browser/ash/fileapi/file_system_backend.h" +#include "chrome/browser/ash/fileapi/mtp_file_system_backend_delegate.h" +#include "chrome/browser/ash/http_auth_dialog.h" +#include "chrome/browser/ash/login/signin/merge_session_navigation_throttle.h" +#include "chrome/browser/ash/login/signin/merge_session_throttling_utils.h" +#include "chrome/browser/ash/login/signin_partition_manager.h" +#include "chrome/browser/ash/login/startup_utils.h" +#include "chrome/browser/ash/net/network_health/network_health_manager.h" +#include "chrome/browser/ash/net/system_proxy_manager.h" +#include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/ash/settings/cros_settings.h" +#include "chrome/browser/ash/smb_client/fileapi/smbfs_file_system_backend_delegate.h" +#include "chrome/browser/ash/system/input_device_settings.h" +#include "chrome/browser/ash/url_handler.h" +#include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h" +#include "chrome/browser/speech/tts_chromeos.h" +#include "chrome/browser/speech/tts_controller_delegate_impl.h" +#include "chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h" +#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" +#include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h" +#include "chrome/common/webui_url_constants.h" +#include "chromeos/ash/components/browser_context_helper/browser_context_types.h" +#include "chromeos/ash/services/network_health/public/cpp/network_health_helper.h" +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#include "services/service_manager/public/mojom/interface_provider_spec.mojom.h" +#include "storage/browser/file_system/external_mount_points.h" +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chrome/browser/chrome_browser_main_linux.h" +#include "chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.h" +#elif BUILDFLAG(IS_ANDROID) +#include "base/android/application_status_listener.h" +#include "base/android/build_info.h" +#include "base/feature_list.h" +#include "chrome/android/features/dev_ui/buildflags.h" +#include "chrome/browser/android/customtabs/client_data_header_web_contents_observer.h" +#include "chrome/browser/android/devtools_manager_delegate_android.h" +#include "chrome/browser/android/ntp/new_tab_page_url_handler.h" +#include "chrome/browser/android/service_tab_launcher.h" +#include "chrome/browser/android/tab_android.h" +#include "chrome/browser/android/tab_web_contents_delegate_android.h" +#include "chrome/browser/chrome_browser_main_android.h" +#include "chrome/browser/download/android/available_offline_content_provider.h" +#include "chrome/browser/download/android/intercept_oma_download_navigation_throttle.h" +#include "chrome/browser/flags/android/chrome_feature_list.h" +#include "chrome/browser/ui/android/tab_model/tab_model_list.h" +#include "chrome/common/chrome_descriptors.h" +#include "components/browser_ui/accessibility/android/font_size_prefs_android.h" +#include "components/crash/content/browser/child_exit_observer_android.h" +#include "components/crash/content/browser/crash_memory_metrics_collector_android.h" +#include "components/navigation_interception/intercept_navigation_delegate.h" +#include "components/viz/common/features.h" +#include "components/viz/common/viz_utils.h" +#include "content/public/browser/android/java_interfaces.h" +#include "services/service_manager/public/cpp/interface_provider.h" +#include "ui/base/resource/resource_bundle_android.h" +#include "ui/base/ui_base_paths.h" +#include "ui/display/util/display_util.h" +#if BUILDFLAG(DFMIFY_DEV_UI) +#include "chrome/browser/dev_ui/android/dev_ui_loader_throttle.h" +#endif // BUILDFLAG(DFMIFY_DEV_UI) +#elif BUILDFLAG(IS_POSIX) +#include "chrome/browser/chrome_browser_main_posix.h" +#elif BUILDFLAG(IS_FUCHSIA) +#include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" +#endif + +#if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/preloading/preview/preview_navigation_throttle.h" +#include "chrome/browser/web_applications/isolated_web_apps/chrome_content_browser_client_isolated_web_apps_part.h" +#endif // !BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_CHROMEOS) +#include "base/debug/leak_annotations.h" +#include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" +#include "chrome/browser/apps/intent_helper/chromeos_disabled_apps_throttle.h" +#include "chrome/browser/apps/link_capturing/chromeos_link_capturing_delegate.h" +#include "chrome/browser/chromeos/enterprise/incognito_navigation_throttle.h" +#include "chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h" +#include "chrome/browser/chromeos/quickoffice/quickoffice_prefs.h" +#include "chrome/browser/chromeos/tablet_mode/chrome_content_browser_client_tablet_mode_part.h" +#include "chrome/browser/file_system_access/cloud_identifier/cloud_identifier_util_cros.h" +#include "chrome/browser/policy/networking/policy_cert_service.h" +#include "chrome/browser/policy/networking/policy_cert_service_factory.h" +#include "chrome/browser/policy/system_features_disable_list_policy_handler.h" +#include "chrome/browser/smart_card/chromeos_smart_card_delegate.h" +#include "chrome/browser/web_applications/web_app_tab_helper.h" +#include "chrome/common/chromeos/extensions/chromeos_system_extension_info.h" +#include "chromeos/components/kiosk/kiosk_utils.h" +#include "chromeos/constants/chromeos_features.h" +#include "content/public/browser/chromeos/multi_capture_service.h" +#include "third_party/cros_system_api/switches/chrome_switches.h" +#endif + +#if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/apps/link_capturing/link_capturing_navigation_throttle.h" +#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h" +#include "chrome/browser/devtools/devtools_window.h" +#include "chrome/browser/direct_sockets/chrome_direct_sockets_delegate.h" +#include "chrome/browser/headless/chrome_browser_main_extra_parts_headless.h" +#include "chrome/browser/media/unified_autoplay_config.h" +#include "chrome/browser/metrics/usage_scenario/chrome_responsiveness_calculator_delegate.h" +#include "chrome/browser/new_tab_page/new_tab_page_util.h" +#include "chrome/browser/page_info/about_this_site_side_panel_throttle.h" +#include "chrome/browser/search/instant_service.h" +#include "chrome/browser/search/instant_service_factory.h" +#include "chrome/browser/serial/chrome_serial_delegate.h" +#include "chrome/browser/task_manager/task_manager_interface.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/chrome_pages.h" +#include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h" +#include "chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h" +#include "chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h" +#include "chrome/browser/ui/web_applications/webui_web_app_navigation_throttle.h" +#include "chrome/browser/ui/webui/chrome_content_browser_client_webui_part.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_error_page.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory.h" +#include "chrome/browser/web_applications/policy/web_app_policy_manager.h" +#include "chrome/browser/web_applications/web_app_helpers.h" +#include "chrome/browser/web_applications/web_app_provider.h" +#include "chrome/browser/web_applications/web_app_registrar.h" +#include "chrome/browser/web_applications/web_app_utils.h" +#include "chrome/browser/webauthn/authenticator_request_scheduler.h" +#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h" +#include "chrome/grit/chrome_unscaled_resources.h" // nogncheck crbug.com/1125897 +#include "components/commerce/core/commerce_feature_list.h" +#include "components/media_effects/media_effects_manager_binder.h" +#include "components/password_manager/content/common/web_ui_constants.h" +#include "components/password_manager/core/common/password_manager_features.h" +#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" +#endif // !BUILDFLAG(IS_ANDROID) + +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \ + (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) +#include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h" +#endif + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include "components/crash/core/app/crash_switches.h" +#include "components/crash/core/app/crashpad.h" +#endif + +#if BUILDFLAG(IS_ANDROID) +#include "components/crash/content/browser/crash_handler_host_linux.h" +#else +#include "chrome/browser/apps/link_capturing/web_app_link_capturing_delegate.h" +#endif + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +#include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h" +#include "chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.h" +#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h" +#include "chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h" +#endif + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ + BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h" +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || + // BUILDFLAG(IS_CHROMEOS_ASH) + +#if defined(TOOLKIT_VIEWS) +#include "chrome/browser/ui/side_search/side_search_side_contents_helper.h" +#include "chrome/browser/ui/side_search/side_search_utils.h" +#include "chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h" +#endif + +#if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) +#include "chrome/browser/ui/lens/lens_side_panel_navigation_helper.h" +#include "components/lens/lens_features.h" +#endif + +#if BUILDFLAG(IS_LINUX) +#include "chrome/browser/chrome_browser_main_extra_parts_linux.h" +#elif BUILDFLAG(IS_OZONE) +#include "chrome/browser/chrome_browser_main_extra_parts_ozone.h" +#endif + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) +#include "components/captive_portal/content/captive_portal_tab_helper.h" +#include "components/captive_portal/content/captive_portal_url_loader_throttle.h" +#endif + +#if BUILDFLAG(ENABLE_NACL) +#include "components/nacl/browser/nacl_host_message_filter.h" +#include "components/nacl/browser/nacl_process_host.h" +#include "components/nacl/common/nacl_process_type.h" +#include "components/nacl/common/nacl_switches.h" +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "chrome/browser/accessibility/animation_policy_prefs.h" +#include "chrome/browser/apps/platform_apps/platform_app_navigation_redirector.h" +#include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h" +#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h" +#include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/extensions/user_script_listener.h" +#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h" +#include "chrome/browser/ui/web_applications/app_browser_controller.h" +#include "chrome/browser/web_applications/web_app_utils.h" +#include "content/public/browser/site_isolation_policy.h" +#include "extensions/browser/api/web_request/web_request_api.h" +#include "extensions/browser/api/web_request/web_request_proxying_webtransport.h" +#include "extensions/browser/extension_navigation_throttle.h" +#include "extensions/browser/extension_protocols.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_util.h" +#include "extensions/browser/guest_view/web_view/web_view_guest.h" +#include "extensions/browser/guest_view/web_view/web_view_permission_helper.h" +#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" +#include "extensions/browser/process_map.h" +#include "extensions/browser/script_injection_tracker.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" +#include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/switches.h" +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + +#if BUILDFLAG(ENABLE_PLUGINS) +#include "chrome/browser/plugins/chrome_content_browser_client_plugins_part.h" +#include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h" +#endif + +#if BUILDFLAG(ENABLE_PDF) +#include "chrome/browser/pdf/chrome_pdf_stream_delegate.h" +#include "components/pdf/browser/pdf_navigation_throttle.h" +#include "components/pdf/browser/pdf_url_loader_request_interceptor.h" +#include "components/pdf/common/constants.h" +#endif // BUILDFLAG(ENABLE_PDF) + +#if BUILDFLAG(ENABLE_SUPERVISED_USERS) +#include "chrome/browser/supervised_user/supervised_user_google_auth_navigation_throttle.h" +#endif + +#if BUILDFLAG(ENABLE_MEDIA_REMOTING) +#include "chrome/browser/media/cast_remoting_connector.h" +#endif + +#if BUILDFLAG(ENABLE_SUPERVISED_USERS) +#include "chrome/browser/supervised_user/supervised_user_navigation_throttle.h" +#endif + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) +#include "chrome/browser/safe_browsing/chrome_password_protection_service.h" +#endif + +#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL) +#include "chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.h" // nogncheck crbug.com/1125897 +#include "chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_factory.h" // nogncheck crbug.com/1125897 +#endif + +#if BUILDFLAG(ENABLE_OFFLINE_PAGES) +#include "chrome/browser/offline_pages/offline_page_navigation_throttle.h" +#include "chrome/browser/offline_pages/offline_page_tab_helper.h" +#include "chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h" +#endif + +#if BUILDFLAG(FULL_SAFE_BROWSING) +#include "chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h" +#endif + +#if BUILDFLAG(ENABLE_VR) +#include "chrome/browser/vr/chrome_xr_integration_client.h" +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chrome/browser/chrome_browser_main_parts_lacros.h" +#include "chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h" +#include "chrome/browser/speech/tts_lacros.h" +#include "chrome/browser/ui/views/chrome_browser_main_extra_parts_views_lacros.h" +#include "chrome/common/chrome_descriptors.h" +#include "chromeos/crosapi/mojom/kerberos_in_browser.mojom.h" +#include "chromeos/lacros/lacros_service.h" +#include "chromeos/startup/browser_init_params.h" +#include "chromeos/startup/browser_postlogin_params.h" +#include "chromeos/startup/startup.h" // nogncheck +#include "chromeos/startup/startup_switches.h" // nogncheck +#include "mojo/core/embedder/embedder.h" +#include "ui/base/ui_base_switches.h" +#endif + +#if BUILDFLAG(USE_MINIKIN_HYPHENATION) && !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/component_updater/hyphenation_component_installer.h" +#endif + +#if BUILDFLAG(FULL_SAFE_BROWSING) +#include "components/enterprise/common/files_scan_data.h" +#endif + +// This should be after all other #includes. +#if defined(_WINDOWS_) // Detect whether windows.h was included. +#include "base/win/windows_h_disallowed.h" +#endif // defined(_WINDOWS_) + +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) +#include "chrome/browser/screen_ai/screen_ai_install_state.h" +#endif + +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) || !BUILDFLAG(IS_CHROMEOS_ASH) +#include "ui/accessibility/accessibility_features.h" +#endif + +#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) +#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h" +#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h" +#include "chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_handler_browser_impl.h" +#include "chrome/common/bound_session_request_throttled_handler.h" +#endif // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) + +#if BUILDFLAG(IS_CHROMEOS) +#include "chromeos/components/kiosk/kiosk_utils.h" +#endif // BUILDFLAG(IS_CHROMEOS) + +#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS) +#include "chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.h" +#endif // BUILDFLAG(ENTERPRISE_DATA_CONTROLS) + +using blink::mojom::EffectiveConnectionType; +using blink::web_pref::WebPreferences; +using content::BrowserThread; +using content::BrowserURLHandler; +using content::ChildProcessSecurityPolicy; +using content::RenderFrameHost; +using content::SiteInstance; +using content::WebContents; + +#if BUILDFLAG(IS_POSIX) +using content::PosixFileDescriptorInfo; +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +using extensions::APIPermission; +using extensions::ChromeContentBrowserClientExtensionsPart; +using extensions::Extension; +using extensions::Manifest; +using extensions::mojom::APIPermissionID; +#endif + +#if BUILDFLAG(ENABLE_PLUGINS) +using plugins::ChromeContentBrowserClientPluginsPart; +#endif + +#if !BUILDFLAG(IS_ANDROID) +using web_apps::ChromeContentBrowserClientIsolatedWebAppsPart; +#endif + +namespace { + +#if BUILDFLAG(IS_ANDROID) +// Kill switch that allows falling back to the legacy behavior on Android when +// it comes to site isolation for Gaia's origin (|GaiaUrls::gaia_origin()|). +BASE_FEATURE(kAllowGaiaOriginIsolationOnAndroid, + "AllowGaiaOriginIsolationOnAndroid", + base::FEATURE_ENABLED_BY_DEFAULT); + +BASE_FEATURE(kPrivateNetworkAccessRestrictionsForAutomotive, + "PrivateNetworkAccessRestrictionsForAutomotive", + base::FEATURE_ENABLED_BY_DEFAULT); +#endif // BUILDFLAG(IS_ANDROID) + +// A small ChromeBrowserMainExtraParts that invokes a callback when threads are +// ready. Used to initialize ChromeContentBrowserClient data that needs the UI +// thread. +class ChromeBrowserMainExtraPartsThreadNotifier final + : public ChromeBrowserMainExtraParts { + public: + explicit ChromeBrowserMainExtraPartsThreadNotifier( + base::OnceClosure threads_ready_closure) + : threads_ready_closure_(std::move(threads_ready_closure)) {} + + // ChromeBrowserMainExtraParts: + void PostCreateThreads() final { std::move(threads_ready_closure_).Run(); } + + private: + base::OnceClosure threads_ready_closure_; +}; + +// Wrapper for SSLErrorHandler::HandleSSLError() that supplies //chrome-level +// parameters. +void HandleSSLErrorWrapper( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + SSLErrorHandler::BlockingPageReadyCallback blocking_page_ready_callback) { + DCHECK(request_url.SchemeIsCryptographic()); + + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + // Profile should always outlive a WebContents + DCHECK(profile); + + captive_portal::CaptivePortalService* captive_portal_service = nullptr; + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) + captive_portal_service = CaptivePortalServiceFactory::GetForProfile(profile); +#endif + + const bool is_ssl_error_override_allowed_for_origin = + policy::IsOriginInAllowlist(request_url, profile->GetPrefs(), + prefs::kSSLErrorOverrideAllowedForOrigins, + prefs::kSSLErrorOverrideAllowed); + + SSLErrorHandler::HandleSSLError( + web_contents, cert_error, ssl_info, request_url, + std::move(blocking_page_ready_callback), + g_browser_process->network_time_tracker(), captive_portal_service, + std::make_unique(), + is_ssl_error_override_allowed_for_origin); +} + +// Cached version of the locale so we can return the locale on the I/O +// thread. +std::string& GetIOThreadApplicationLocale() { + static base::NoDestructor s; + return *s; +} + +// Returns a copy of the given url with its host set to given host and path set +// to given path. Other parts of the url will be the same. +GURL ReplaceURLHostAndPath(const GURL& url, + const std::string& host, + const std::string& path) { + GURL::Replacements replacements; + replacements.SetHostStr(host); + replacements.SetPathStr(path); + return url.ReplaceComponents(replacements); +} + +// Handles the rewriting of the new tab page URL based on group policy. +bool HandleNewTabPageLocationOverride( + GURL* url, + content::BrowserContext* browser_context) { + if (!url->SchemeIs(content::kChromeUIScheme) || + url->host() != chrome::kChromeUINewTabHost) { + return false; + } + + Profile* profile = Profile::FromBrowserContext(browser_context); + + // Don't change the URL when incognito mode. + if (profile->IsOffTheRecord()) + return false; + + std::string ntp_location = + profile->GetPrefs()->GetString(prefs::kNewTabPageLocationOverride); + if (ntp_location.empty()) + return false; + url::Component scheme; + if (!url::ExtractScheme(ntp_location.data(), + static_cast(ntp_location.length()), &scheme)) { + ntp_location = base::StrCat( + {url::kHttpsScheme, url::kStandardSchemeSeparator, ntp_location}); + } + + *url = GURL(ntp_location); + return true; +} + +#if !BUILDFLAG(IS_ANDROID) +bool IsFileOrDirectoryPickerWithoutGestureAllowed( + content::WebContents* contents) { + if (!contents) { + return true; + } + + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + if (!profile) { + return true; + } + + PrefService* prefs = profile->GetPrefs(); + if (!prefs) { + return true; + } + + return !policy::IsOriginInAllowlist( + contents->GetURL(), prefs, + prefs::kFileOrDirectoryPickerWithoutGestureAllowedForOrigins); +} + +// Check if autoplay is allowed by policy configuration. +bool IsAutoplayAllowedByPolicy(content::WebContents* contents, + PrefService* prefs) { + if (!contents) { + return false; + } + + return policy::IsOriginInAllowlist(contents->GetURL(), prefs, + prefs::kAutoplayAllowlist, + prefs::kAutoplayAllowed); +} +#endif // !BUILDFLAG(IS_ANDROID) + +blink::mojom::AutoplayPolicy GetAutoplayPolicyForWebContents( + WebContents* web_contents) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + std::string autoplay_policy = media::GetEffectiveAutoplayPolicy(command_line); + auto result = blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired; + + if (autoplay_policy == switches::autoplay::kNoUserGestureRequiredPolicy) { + result = blink::mojom::AutoplayPolicy::kNoUserGestureRequired; + } else if (autoplay_policy == + switches::autoplay::kUserGestureRequiredPolicy) { + result = blink::mojom::AutoplayPolicy::kUserGestureRequired; + } else if (autoplay_policy == + switches::autoplay::kDocumentUserActivationRequiredPolicy) { + result = blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired; + } else { + NOTREACHED(); + } + +#if !BUILDFLAG(IS_ANDROID) + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + PrefService* prefs = profile->GetPrefs(); + + // Override autoplay policy used in internal switch in case of enabling + // features such as policy, allowlisting or disabling from settings. + if (IsAutoplayAllowedByPolicy(web_contents, prefs)) { + result = blink::mojom::AutoplayPolicy::kNoUserGestureRequired; + } else if (base::FeatureList::IsEnabled(media::kAutoplayDisableSettings) && + result == blink::mojom::AutoplayPolicy:: + kDocumentUserActivationRequired) { + result = UnifiedAutoplayConfig::ShouldBlockAutoplay(profile) + ? blink::mojom::AutoplayPolicy::kDocumentUserActivationRequired + : blink::mojom::AutoplayPolicy::kNoUserGestureRequired; + } else if (web_contents->GetPrimaryMainFrame()->IsFeatureEnabled( + blink::mojom::PermissionsPolicyFeature::kAutoplay) && + IsAutoplayAllowedByPolicy(web_contents->GetOuterWebContents(), + prefs)) { + // If the domain policy allows autoplay and has delegated that to an iframe, + // allow autoplay within the iframe. Only allow a nesting of single depth. + result = blink::mojom::AutoplayPolicy::kNoUserGestureRequired; + } +#endif // !BUILDFLAG(IS_ANDROID) + return result; +} + +#if BUILDFLAG(IS_ANDROID) +int GetCrashSignalFD(const base::CommandLine& command_line) { + return crashpad::CrashHandlerHost::Get()->GetDeathSignalSocket(); +} +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +int GetCrashSignalFD(const base::CommandLine& command_line) { + int fd; + return crash_reporter::GetHandlerSocket(&fd, nullptr) ? fd : -1; +} +#endif // BUILDFLAG(IS_ANDROID) + +void SetApplicationLocaleOnIOThread(const std::string& locale) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + GetIOThreadApplicationLocale() = locale; +} + +#if BUILDFLAG(ENABLE_EXTENSIONS) + +// Returns true if there is is an extension matching `url` in +// `render_process_id` with `permission`. +// +// GetExtensionOrAppByURL requires a full URL in order to match with a hosted +// app, even though normal extensions just use the host. +bool URLHasExtensionPermission(extensions::ProcessMap* process_map, + extensions::ExtensionRegistry* registry, + const GURL& url, + int render_process_id, + APIPermissionID permission) { + // Includes web URLs that are part of an extension's web extent. + const Extension* extension = + registry->enabled_extensions().GetExtensionOrAppByURL(url); + return extension && + extension->permissions_data()->HasAPIPermission(permission) && + process_map->Contains(extension->id(), render_process_id); +} + +// Returns true if |extension_id| is allowed to run as an Isolated Context, +// giving it access to additional APIs. +bool IsExtensionIdAllowedToUseIsolatedContext(base::StringPiece extension_id) { + static constexpr auto kAllowedIsolatedContextExtensionIds = + base::MakeFixedFlatSet({ + "algkcnfjnajfhgimadimbjhmpaeohhln", // Secure Shell Extension (dev) + "iodihamcpbpeioajjeobimgagajmlibd", // Secure Shell Extension + // (stable) + // Extension IDs used in tests. + "bbobefdodiifgmhhdijgpelmkdaebfpn", // Controlled Frame Service + // Worker Test + }); + return base::Contains(kAllowedIsolatedContextExtensionIds, extension_id); +} + +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + +mojo::PendingRemote GetPrerenderCanceler( + base::OnceCallback wc_getter) { + mojo::PendingRemote canceler; + prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + std::move(wc_getter).Run()) + ->AddPrerenderCancelerReceiver(canceler.InitWithNewPipeAndPassReceiver()); + return canceler; +} + +bool ShouldHonorPolicies() { +#if BUILDFLAG(IS_WIN) + return policy::ManagementServiceFactory::GetForPlatform() + ->GetManagementAuthorityTrustworthiness() >= + policy::ManagementAuthorityTrustworthiness::TRUSTED; +#else + return true; +#endif +} + +// Used by Enterprise policy. Disable blocking of navigations toward external +// applications from a sandboxed iframe. +// https://chromestatus.com/feature/5680742077038592 +const char kDisableSandboxExternalProtocolSwitch[] = + "disable-sandbox-external-protocols"; + +void LaunchURL( + base::WeakPtr client, + const GURL& url, + content::WebContents::Getter web_contents_getter, + ui::PageTransition page_transition, + bool is_primary_main_frame, + bool is_in_fenced_frame_tree, + network::mojom::WebSandboxFlags sandbox_flags, + bool has_user_gesture, + const std::optional& initiating_origin, + content::WeakDocumentPtr initiator_document +#if BUILDFLAG(IS_ANDROID) + , + mojo::PendingRemote* out_factory +#endif +) { + // If there is no longer a WebContents, the request may have raced with tab + // closing. Don't fire the external request. (It may have been a prerender.) + content::WebContents* web_contents = web_contents_getter.Run(); + if (!web_contents) + return; + + // Do not launch external requests attached to unswapped no-state prefetchers. + prerender::NoStatePrefetchContents* no_state_prefetch_contents = + prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + web_contents); + if (no_state_prefetch_contents) { + no_state_prefetch_contents->Destroy( + prerender::FINAL_STATUS_UNSUPPORTED_SCHEME); + return; + } + + // Do not launch external requests for schemes that have a handler registered. + custom_handlers::ProtocolHandlerRegistry* protocol_handler_registry = + ProtocolHandlerRegistryFactory::GetForBrowserContext( + web_contents->GetBrowserContext()); + if (protocol_handler_registry && + protocol_handler_registry->IsHandledProtocol(url.scheme())) + return; + + // Sandbox flags + // ============= + // + // Navigations to external protocol in iframe can be seen as "top-level" + // navigations somehow, because they cause the user to switch from Chrome's + // page toward a different application. + // + // Internally in Chrome, they are seen as aborted iframe navigation, so the + // regular sandbox logic do not really apply. + // + // This block adds an extra logic, gating external protocol in iframes to have + // one of: + // - 'allow-top-navigation' + // - 'allow-top-navigation-to-custom-protocols' + // - 'allow-top-navigation-by-user-navigation' + user-activation + // - 'allow-popups' + // + // See https://crbug.com/1148777 + if (!is_primary_main_frame) { + using SandboxFlags = network::mojom::WebSandboxFlags; + auto allow = [&](SandboxFlags flag) { + return (sandbox_flags & flag) == SandboxFlags::kNone; + }; + bool allowed = (allow(SandboxFlags::kTopNavigationToCustomProtocols)) || + (allow(SandboxFlags::kTopNavigationByUserActivation) && + has_user_gesture); + + if (!allowed) { + content::RenderFrameHost* rfh = web_contents->GetPrimaryMainFrame(); + if (client) { + client->LogWebFeatureForCurrentPage( + rfh, blink::mojom::WebFeature::kExternalProtocolBlockedBySandbox); + } + + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + kDisableSandboxExternalProtocolSwitch)) { + if (base::FeatureList::IsEnabled( + features::kSandboxExternalProtocolBlocked)) { + rfh->AddMessageToConsole( + blink::mojom::ConsoleMessageLevel::kError, + "Navigation to external protocol blocked by sandbox, because it " + "doesn't contain any of: " + "'allow-top-navigation-to-custom-protocols', " + "'allow-top-navigation-by-user-activation', " + "'allow-top-navigation', or " + "'allow-popups'. See " + "https://chromestatus.com/feature/5680742077038592 and " + "https://chromeenterprise.google/policies/" + "#SandboxExternalProtocolBlocked"); + return; + } + + if (base::FeatureList::IsEnabled( + features::kSandboxExternalProtocolBlockedWarning)) { + rfh->AddMessageToConsole( + blink::mojom::ConsoleMessageLevel::kError, + "After Chrome M103, navigation toward external protocol " + "will be blocked by sandbox, if it doesn't contain any of:" + "'allow-top-navigation-to-custom-protocols', " + "'allow-top-navigation-by-user-activation', " + "'allow-top-navigation', or " + "'allow-popups'. See " + "https://chromestatus.com/feature/5680742077038592 and " + "https://chromeenterprise.google/policies/" + "#SandboxExternalProtocolBlocked"); + } + } + } + } + + bool is_allowlisted = false; + PolicyBlocklistService* service = + PolicyBlocklistFactory::GetForBrowserContext( + web_contents->GetBrowserContext()); + if (ShouldHonorPolicies() && service) { + const policy::URLBlocklist::URLBlocklistState url_state = + service->GetURLBlocklistState(url); + is_allowlisted = + url_state == policy::URLBlocklist::URLBlocklistState::URL_IN_ALLOWLIST; + } + + // If the URL is in allowlist, we launch it without asking the user and + // without any additional security checks. Since the URL is allowlisted, + // we assume it can be executed. + if (is_allowlisted) { + ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck( + url, web_contents, std::move(initiator_document)); + } else { + ExternalProtocolHandler::LaunchUrl( + url, std::move(web_contents_getter), page_transition, has_user_gesture, + is_in_fenced_frame_tree, initiating_origin, + std::move(initiator_document) +#if BUILDFLAG(IS_ANDROID) + , + out_factory +#endif + ); + } +} + +void MaybeAppendSecureOriginsAllowlistSwitch(base::CommandLine* cmdline) { + // |allowlist| combines pref/policy + cmdline switch in the browser process. + // For renderer and utility (e.g. NetworkService) processes the switch is the + // only available source, so below the combined (pref/policy + cmdline) + // allowlist of secure origins is injected into |cmdline| for these other + // processes. + std::vector allowlist = + network::SecureOriginAllowlist::GetInstance().GetCurrentAllowlist(); + if (!allowlist.empty()) { + cmdline->AppendSwitchASCII( + network::switches::kUnsafelyTreatInsecureOriginAsSecure, + base::JoinString(allowlist, ",")); + } +} + +#if BUILDFLAG(IS_WIN) && !defined(COMPONENT_BUILD) && \ + !defined(ADDRESS_SANITIZER) +// Returns the full path to |module_name|. Both dev builds (where |module_name| +// is in the current executable's directory) and proper installs (where +// |module_name| is in a versioned sub-directory of the current executable's +// directory) are supported. The identified file is not guaranteed to exist. +base::FilePath GetModulePath(std::wstring_view module_name) { + base::FilePath exe_dir; + const bool has_path = base::PathService::Get(base::DIR_EXE, &exe_dir); + DCHECK(has_path); + + // Look for the module in a versioned sub-directory of the current + // executable's directory and return the path if it can be read. This is the + // expected location of modules for proper installs. + const base::FilePath module_path = + exe_dir.AppendASCII(chrome::kChromeVersion).Append(module_name); + if (base::PathExists(module_path)) + return module_path; + + // Otherwise, return the path to the module in the current executable's + // directory. This is the expected location of modules for dev builds. + return exe_dir.Append(module_name); +} +#endif // BUILDFLAG(IS_WIN) && !defined(COMPONENT_BUILD) && + // !defined(ADDRESS_SANITIZER) + +void MaybeAddThrottle( + std::unique_ptr maybe_throttle, + std::vector>* throttles) { + if (maybe_throttle) + throttles->push_back(std::move(maybe_throttle)); +} + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) +void MaybeAddCondition( + std::unique_ptr maybe_condition, + std::vector>* + conditions) { + if (maybe_condition) + conditions->push_back(std::move(maybe_condition)); +} +#endif + +void MaybeAddThrottles( + std::vector> additional, + std::vector>* combined) { + combined->insert(combined->end(), std::make_move_iterator(additional.begin()), + std::make_move_iterator(additional.end())); +} + +// Returns whether |web_contents| is within a hosted app. +bool IsInHostedApp(WebContents* web_contents) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + Browser* browser = chrome::FindBrowserWithTab(web_contents); + return web_app::AppBrowserController::IsWebApp(browser); +#else + return false; +#endif +} + +bool IsErrorPageAutoReloadEnabled() { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kEnableAutomation)) + return false; + if (command_line.HasSwitch(embedder_support::kEnableAutoReload)) + return true; + if (command_line.HasSwitch(embedder_support::kDisableAutoReload)) + return false; + return true; +} + +// Checks whether a render process hosting a top chrome page exists. +bool IsTopChromeRendererPresent(Profile* profile) { + for (auto rph_iterator = content::RenderProcessHost::AllHostsIterator(); + !rph_iterator.IsAtEnd(); rph_iterator.Advance()) { + content::RenderProcessHost* rph = rph_iterator.GetCurrentValue(); + + // Consider only valid RenderProcessHosts that belong to the current + // profile. + if (rph->IsInitializedAndNotDead() && + profile->IsSameOrParent( + Profile::FromBrowserContext(rph->GetBrowserContext()))) { + bool is_top_chrome_renderer_present = false; + rph->ForEachRenderFrameHost( + [&is_top_chrome_renderer_present](content::RenderFrameHost* rfh) { + is_top_chrome_renderer_present |= + IsTopChromeWebUIURL(rfh->GetSiteInstance()->GetSiteURL()); + }); + + // Return true if a rph hosting a top chrome WebUI has been found. + if (is_top_chrome_renderer_present) + return true; + } + } + return false; +} + +// Return false if a top chrome renderer exists. This is done to ensure the +// spare renderer is not taken and the existing top chrome renderer is +// considered instead. +// TODO(crbug.com/1291351, tluk): This is needed since spare renderers are +// considered before existing processes for reuse. This can be simplified by +// migrating to SiteInstanceGroups once the project has landed. +bool ShouldUseSpareRenderProcessHostForTopChromePage(Profile* profile) { + return base::FeatureList::IsEnabled( + features::kTopChromeWebUIUsesSpareRenderer) && + !IsTopChromeRendererPresent(profile); +} + +#if BUILDFLAG(IS_CHROMEOS) +void NotifyMultiCaptureStarted(const std::string& label, + content::WebContents* web_contents, + const webapps::AppId* app_id) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (app_id && + video_capture::mojom::MultiCaptureServiceClient::Version_ >= + video_capture::mojom::MultiCaptureServiceClient::MethodMinVersions:: + kMultiCaptureStartedFromAppMinVersion) { + content::GetMultiCaptureService().NotifyMultiCaptureStartedFromApp( + label, *app_id, + web_app::WebAppProvider::GetForWebContents(web_contents) + ->registrar_unsafe() + .GetAppShortName(*app_id)); + } else { + // TODO(b/319317165): Remove this case once the pivot to web apps is + // complete. + content::GetMultiCaptureService().NotifyMultiCaptureStarted( + label, url::Origin::Create(web_contents->GetLastCommittedURL())); + } +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + chromeos::LacrosService& service = + CHECK_DEREF(chromeos::LacrosService::Get()); + crosapi::mojom::MultiCaptureService& multi_capture_service = CHECK_DEREF( + service.GetRemote().get()); + if (app_id && + service.GetInterfaceVersion() >= + (int)crosapi::mojom::MultiCaptureService::MethodMinVersions:: + kMultiCaptureStartedFromAppMinVersion) { + multi_capture_service.MultiCaptureStartedFromApp( + label, *app_id, + web_app::WebAppProvider::GetForWebContents(web_contents) + ->registrar_unsafe() + .GetAppShortName(*app_id)); + } else { + // TODO(b/319317165): Remove this case once the pivot to web apps is + // complete. + multi_capture_service.MultiCaptureStarted( + label, web_contents->GetLastCommittedURL().host()); + } +#endif +} + +void NotifyMultiCaptureStopped(const std::string& label) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + content::GetMultiCaptureService().NotifyMultiCaptureStopped(label); +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + crosapi::mojom::MultiCaptureService& multi_capture_service = + CHECK_DEREF(chromeos::LacrosService::Get() + ->GetRemote() + .get()); + multi_capture_service.MultiCaptureStopped(label); +#endif +} + +bool IsSubAppsPermissionGrantedByAdmins(content::WebContents* contents) { + if (!contents) { + return false; + } + + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + if (!profile) { + return false; + } + + PrefService* prefs = profile->GetPrefs(); + if (!prefs) { + return false; + } + + return policy::IsOriginInAllowlist( + contents->GetURL(), prefs, + prefs::kSubAppsAPIsAllowedWithoutGestureAndAuthorizationForOrigins); +} + +// Checks if installation and removal of subapps require a user gesture and +// authorization. Both requirements can be overridden via admin policy. +bool SubAppsAPIsRequireUserGestureAndAuthorization( + content::WebContents* web_contents) { + return !IsSubAppsPermissionGrantedByAdmins(web_contents); +} +#endif // BUILDFLAG(IS_CHROMEOS) + +std::unique_ptr +CreatePopupNavigationDelegate(NavigateParams params) { + return std::make_unique(std::move(params)); +} + +ChromeContentBrowserClient::PopupNavigationDelegateFactory + g_popup_navigation_delegate_factory = &CreatePopupNavigationDelegate; + +} // namespace + +// static +ChromeContentBrowserClient::PopupNavigationDelegateFactory& +ChromeContentBrowserClient::GetPopupNavigationDelegateFactoryForTesting() { + return g_popup_navigation_delegate_factory; +} + +ChromeContentBrowserClient::ChromeContentBrowserClient() { +#if BUILDFLAG(ENABLE_PLUGINS) + extra_parts_.push_back( + std::make_unique()); +#endif + +#if BUILDFLAG(IS_CHROMEOS) + extra_parts_.push_back( + std::make_unique()); +#endif // BUILDFLAG(IS_CHROMEOS) + +#if !BUILDFLAG(IS_ANDROID) + extra_parts_.push_back( + std::make_unique()); +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + extra_parts_.push_back( + std::make_unique()); +#endif + +#if !BUILDFLAG(IS_ANDROID) + extra_parts_.push_back( + std::make_unique()); +#endif + + extra_parts_.push_back( + std::make_unique()); +} + +ChromeContentBrowserClient::~ChromeContentBrowserClient() { + // std::vector<> does not guarantee any specific destruction order, so + // explicitly destroy elements in the reverse order per header comment. + while (!extra_parts_.empty()) { + extra_parts_.pop_back(); + } +} + +// static +void ChromeContentBrowserClient::RegisterLocalStatePrefs( + PrefRegistrySimple* registry) { + registry->RegisterFilePathPref(prefs::kDiskCacheDir, base::FilePath()); + registry->RegisterIntegerPref(prefs::kDiskCacheSize, 0); + registry->RegisterStringPref(prefs::kIsolateOrigins, std::string()); + registry->RegisterBooleanPref(prefs::kSitePerProcess, false); + registry->RegisterBooleanPref(prefs::kTabFreezingEnabled, true); + registry->RegisterIntegerPref(prefs::kSCTAuditingHashdanceReportCount, 0); + registry->RegisterBooleanPref(prefs::kNewBaseUrlInheritanceBehaviorAllowed, + true); +#if BUILDFLAG(IS_CHROMEOS) + registry->RegisterBooleanPref(prefs::kNativeClientForceAllowed, false); +#endif // BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) + registry->RegisterBooleanPref(prefs::kOutOfProcessSystemDnsResolutionEnabled, + true); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) +} + +// static +void ChromeContentBrowserClient::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref(prefs::kDisable3DAPIs, false); + registry->RegisterBooleanPref(prefs::kEnableHyperlinkAuditing, true); + // Register user prefs for mapping SitePerProcess and IsolateOrigins in + // user policy in addition to the same named ones in Local State (which are + // used for mapping the command-line flags). + registry->RegisterStringPref(prefs::kIsolateOrigins, std::string()); + registry->RegisterBooleanPref(prefs::kSitePerProcess, false); + registry->RegisterListPref( + site_isolation::prefs::kUserTriggeredIsolatedOrigins); + registry->RegisterDictionaryPref( + site_isolation::prefs::kWebTriggeredIsolatedOrigins); + registry->RegisterDictionaryPref( + prefs::kDevToolsBackgroundServicesExpirationDict); + registry->RegisterBooleanPref(prefs::kSignedHTTPExchangeEnabled, true); +#if !BUILDFLAG(IS_ANDROID) + registry->RegisterBooleanPref(prefs::kAutoplayAllowed, false); + registry->RegisterListPref(prefs::kAutoplayAllowlist); + registry->RegisterListPref( + prefs::kScreenCaptureWithoutGestureAllowedForOrigins); + registry->RegisterListPref( + prefs::kFileOrDirectoryPickerWithoutGestureAllowedForOrigins); + registry->RegisterIntegerPref(prefs::kFetchKeepaliveDurationOnShutdown, 0); + registry->RegisterBooleanPref( + prefs::kSharedArrayBufferUnrestrictedAccessAllowed, false); +#endif + registry->RegisterBooleanPref(prefs::kSandboxExternalProtocolBlocked, true); + registry->RegisterBooleanPref(prefs::kSSLErrorOverrideAllowed, true); + registry->RegisterListPref(prefs::kSSLErrorOverrideAllowedForOrigins); + registry->RegisterBooleanPref(prefs::kCompressionDictionaryTransportEnabled, + true); + registry->RegisterBooleanPref( + prefs::kSuppressDifferentOriginSubframeJSDialogs, true); +#if BUILDFLAG(IS_ANDROID) + registry->RegisterBooleanPref(prefs::kWebXRImmersiveArEnabled, true); +#endif + registry->RegisterBooleanPref(prefs::kPromptOnMultipleMatchingCertificates, + false); + registry->RegisterBooleanPref(prefs::kCorsNonWildcardRequestHeadersSupport, + true); + registry->RegisterBooleanPref(prefs::kIPv6ReachabilityOverrideEnabled, false); + registry->RegisterDictionaryPref( + enterprise::content::kCopyPreventionSettings); + registry->RegisterIntegerPref( + prefs::kUserAgentReduction, + static_cast( + embedder_support::UserAgentReductionEnterprisePolicyState::kDefault)); + registry->RegisterBooleanPref(prefs::kOriginAgentClusterDefaultEnabled, true); + registry->RegisterBooleanPref( + policy::policy_prefs::kIsolatedAppsDeveloperModeAllowed, true); + + registry->RegisterBooleanPref( + prefs::kStrictMimetypeCheckForWorkerScriptsEnabled, true); + registry->RegisterBooleanPref(policy::policy_prefs::kFeedbackSurveysEnabled, + true); + registry->RegisterBooleanPref( + prefs::kAccessControlAllowMethodsInCORSPreflightSpecConformant, true); + registry->RegisterBooleanPref(prefs::kDataUrlInSvgUseEnabled, false); + + registry->RegisterBooleanPref(policy::policy_prefs::kMutationEventsEnabled, + false); + + registry->RegisterBooleanPref( + policy::policy_prefs::kBeforeunloadEventCancelByPreventDefaultEnabled, + true); + + registry->RegisterBooleanPref( + policy::policy_prefs:: + kAllowBackForwardCacheForCacheControlNoStorePageEnabled, + true); + + registry->RegisterBooleanPref( + policy::policy_prefs::kForcePermissionPolicyUnloadDefaultEnabled, false); + +#if BUILDFLAG(IS_CHROMEOS) + registry->RegisterListPref(prefs::kMandatoryExtensionsForIncognitoNavigation); + registry->RegisterListPref( + prefs::kSubAppsAPIsAllowedWithoutGestureAndAuthorizationForOrigins); +#endif +} + +// static +void ChromeContentBrowserClient::SetApplicationLocale( + const std::string& locale) { + // The common case is that this function is called early in Chrome startup + // before any threads are created or registered. When there are no threads, + // we can just set the string without worrying about threadsafety. + if (!BrowserThread::IsThreadInitialized(BrowserThread::IO)) { + GetIOThreadApplicationLocale() = locale; + return; + } + + // Otherwise we're being called to change the locale. In this case set it on + // the IO thread. + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&SetApplicationLocaleOnIOThread, locale)); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { + std::unique_ptr main_parts; + // Construct the Main browser parts based on the OS type. +#if BUILDFLAG(IS_WIN) + main_parts = std::make_unique(is_integration_test, + &startup_data_); +#elif BUILDFLAG(IS_MAC) + main_parts = std::make_unique(is_integration_test, + &startup_data_); +#elif BUILDFLAG(IS_CHROMEOS_ASH) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#elif BUILDFLAG(IS_ANDROID) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#elif BUILDFLAG(IS_POSIX) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#elif BUILDFLAG(IS_FUCHSIA) + main_parts = std::make_unique( + is_integration_test, &startup_data_); +#else +#error "Unimplemented platform" +#endif + + main_parts->AddParts( + std::make_unique( + base::BindOnce(&ChromeContentBrowserClient::InitOnUIThread, + weak_factory_.GetWeakPtr()))); + + bool add_profiles_extra_parts = true; +#if BUILDFLAG(IS_ANDROID) + if (startup_data_.HasBuiltProfilePrefService()) + add_profiles_extra_parts = false; +#endif + if (add_profiles_extra_parts) + chrome::AddProfilesExtraParts(main_parts.get()); + + // Construct additional browser parts. Stages are called in the order in + // which they are added. +#if defined(TOOLKIT_VIEWS) +#if BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts->AddParts( + std::make_unique()); +// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch +// of lacros-chrome is complete. +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts->AddParts( + std::make_unique()); +#else + main_parts->AddParts(std::make_unique()); +#endif +#endif + +#if BUILDFLAG(IS_MAC) + main_parts->AddParts(std::make_unique()); +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // TODO(jamescook): Combine with `ChromeBrowserMainPartsAsh`. + main_parts->AddParts(std::make_unique()); +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts->AddParts(std::make_unique()); +#endif + +#if BUILDFLAG(IS_LINUX) + main_parts->AddParts(std::make_unique()); +#elif BUILDFLAG(IS_OZONE) + main_parts->AddParts(std::make_unique()); +#endif + + main_parts->AddParts( + std::make_unique()); + + main_parts->AddParts( + std::make_unique()); + + main_parts->AddParts( + std::make_unique()); + + main_parts->AddParts(std::make_unique()); + + chrome::AddMetricsExtraParts(main_parts.get()); + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + main_parts->AddParts( + std::make_unique< + chrome::enterprise_util::ChromeBrowserMainExtraPartsEnterprise>()); +#endif + +#if !BUILDFLAG(IS_ANDROID) + main_parts->AddParts( + std::make_unique()); +#endif + + // Always add ChromeBrowserMainExtraPartsGpu last to make sure + // GpuDataManager initialization could pick up about:flags settings. + main_parts->AddParts(std::make_unique()); + + main_parts->AddParts( + std::make_unique()); + + main_parts->AddParts( + std::make_unique()); + + main_parts->AddParts( + std::make_unique()); + + return main_parts; +} + +void ChromeContentBrowserClient::PostAfterStartupTask( + const base::Location& from_here, + const scoped_refptr& task_runner, + base::OnceClosure task) { + AfterStartupTaskUtils::PostTask(from_here, task_runner, std::move(task)); +} + +bool ChromeContentBrowserClient::IsBrowserStartupComplete() { + return AfterStartupTaskUtils::IsBrowserStartupComplete(); +} + +void ChromeContentBrowserClient::SetBrowserStartupIsCompleteForTesting() { + AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting(); +} + +bool ChromeContentBrowserClient::IsShuttingDown() { + return browser_shutdown::HasShutdownStarted(); +} + +content::StoragePartitionConfig +ChromeContentBrowserClient::GetStoragePartitionConfigForSite( + content::BrowserContext* browser_context, + const GURL& site) { + // Default to the browser-wide storage partition and override based on |site| + // below. + content::StoragePartitionConfig default_storage_partition_config = + content::StoragePartitionConfig::CreateDefault(browser_context); + + // A non-default storage partition is used in the following situations: + // - To enforce process isolation between a more-trusted content (Chrome Apps, + // Extensions, and Isolated Web Apps) and regular web content. + // - For the tag, which Chrome Apps, Isolated Web Apps and WebUI use + // to create temporary storage buckets for loading various kinds of web + // content. + // + // In general, those use cases aren't considered part of the user's normal + // browsing activity. +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (site.SchemeIs(extensions::kExtensionScheme)) { + // The host in an extension site URL is the extension_id. + CHECK(site.has_host()); + return extensions::util::GetStoragePartitionConfigForExtensionId( + site.host(), browser_context); + } + + if (content::SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel( + browser_context, site)) { + CHECK(site.SchemeIs(chrome::kIsolatedAppScheme)); + ASSIGN_OR_RETURN(const auto iwa_url_info, + web_app::IsolatedWebAppUrlInfo::Create(site), [&](auto) { + LOG(ERROR) << "Invalid isolated-app URL: " << site; + return default_storage_partition_config; + }); + return iwa_url_info.storage_partition_config(browser_context); + } +#endif + + return default_storage_partition_config; +} + +std::unique_ptr +ChromeContentBrowserClient::GetWebContentsViewDelegate( + content::WebContents* web_contents) { + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + // Do not track web contents performance for profiles that have Keyed Services + // disabled. + if (!AreKeyedServicesDisabledForProfileByDefault(profile)) { + if (auto* registry = + performance_manager::PerformanceManagerRegistry::GetInstance()) { + registry->MaybeCreatePageNodeForWebContents(web_contents); + } + } + return CreateWebContentsViewDelegate(web_contents); +} + +bool ChromeContentBrowserClient::AllowGpuLaunchRetryOnIOThread() { +#if BUILDFLAG(IS_ANDROID) + const base::android::ApplicationState app_state = + base::android::ApplicationStatusListener::GetState(); + return base::android::APPLICATION_STATE_UNKNOWN == app_state || + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES == app_state || + base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES == app_state; +#else + return true; +#endif +} + +void ChromeContentBrowserClient::RenderProcessWillLaunch( + content::RenderProcessHost* host) { + Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); + + WebRtcLoggingController::AttachToRenderProcessHost(host); + + // The audio manager outlives the host, so it's safe to hand a raw pointer to + // it to the AudioDebugRecordingsHandler, which is owned by the host. + AudioDebugRecordingsHandler* audio_debug_recordings_handler = + new AudioDebugRecordingsHandler(profile); + host->SetUserData( + AudioDebugRecordingsHandler::kAudioDebugRecordingsHandlerKey, + std::make_unique>( + audio_debug_recordings_handler)); + +#if BUILDFLAG(ENABLE_NACL) + if (IsNaclAllowed() && !profile->IsSystemProfile()) { + host->AddFilter(new nacl::NaClHostMessageFilter( + host->GetID(), profile->IsOffTheRecord(), profile->GetPath())); + } +#endif + +#if BUILDFLAG(IS_ANDROID) + // Register CrashMemoryMetricsCollector to report oom related metrics. + host->SetUserData( + CrashMemoryMetricsCollector::kCrashMemoryMetricsCollectorKey, + std::make_unique(host)); +#endif + + IdentifiabilityStudyState* identifiability_study_state = + g_browser_process->GetMetricsServicesManager() + ->GetIdentifiabilityStudyState(); + if (identifiability_study_state) { + identifiability_study_state->InitializeRenderer(host); + } + + // The RendereUpdater might be null for some irregular profiles, e.g. the + // System Profile. + if (RendererUpdater* service = RendererUpdaterFactory::GetForProfile(profile)) + service->InitializeRenderer(host); + + for (auto& part : extra_parts_) { + part->RenderProcessWillLaunch(host); + } +} + +GURL ChromeContentBrowserClient::GetEffectiveURL( + content::BrowserContext* browser_context, + const GURL& url) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (!profile) + return url; + +#if !BUILDFLAG(IS_ANDROID) + // If the input |url| should be assigned to the Instant renderer, make its + // effective URL distinct from other URLs on the search provider's domain. + // This needs to happen even if |url| corresponds to an isolated origin; see + // https://crbug.com/755595. + if (search::ShouldAssignURLToInstantRenderer(url, profile)) + return search::GetEffectiveURLForInstant(url, profile); +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + profile)) + return url; + + return ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(profile, + url); +#else + return url; +#endif +} + +bool ChromeContentBrowserClient:: + ShouldCompareEffectiveURLsForSiteInstanceSelection( + content::BrowserContext* browser_context, + content::SiteInstance* candidate_site_instance, + bool is_outermost_main_frame, + const GURL& candidate_url, + const GURL& destination_url) { + DCHECK(browser_context); + DCHECK(candidate_site_instance); +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + browser_context)) { + return true; + } + + return ChromeContentBrowserClientExtensionsPart:: + ShouldCompareEffectiveURLsForSiteInstanceSelection( + browser_context, candidate_site_instance, is_outermost_main_frame, + candidate_url, destination_url); +#else + return true; +#endif +} + +bool ChromeContentBrowserClient::ShouldUseProcessPerSite( + content::BrowserContext* browser_context, + const GURL& site_url) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (!profile) + return false; + + // NTP should use process-per-site. This is a performance optimization to + // reduce process count associated with NTP tabs. + if (site_url == GURL(chrome::kChromeUINewTabURL) || + site_url == GURL(chrome::kChromeUINewTabPageURL)) { + return true; + } + +#if !BUILDFLAG(IS_ANDROID) + if (search::ShouldUseProcessPerSiteForInstantSiteURL(site_url, profile)) + return true; +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite( + profile, site_url)) + return true; +#endif + + // Non-extension, non-NTP URLs should generally use process-per-site-instance + // (rather than process-per-site). + return false; +} + +bool ChromeContentBrowserClient::ShouldUseSpareRenderProcessHost( + content::BrowserContext* browser_context, + const GURL& site_url) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (!profile) + return false; + + // Returning false here will ensure existing Top Chrome WebUI renderers are + // considered for process reuse over the spare renderer. + if (IsTopChromeWebUIURL(site_url) && + !ShouldUseSpareRenderProcessHostForTopChromePage(profile)) { + return false; + } + +#if !BUILDFLAG(IS_ANDROID) + // Instant renderers should not use a spare process, because they require + // passing switches::kInstantProcess to the renderer process when it + // launches. A spare process is launched earlier, before it is known which + // navigation will use it, so it lacks this flag. + if (search::ShouldAssignURLToInstantRenderer(site_url, profile)) + return false; +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart:: + ShouldUseSpareRenderProcessHost(profile, site_url); +#else + return true; +#endif +} + +bool ChromeContentBrowserClient::DoesSiteRequireDedicatedProcess( + content::BrowserContext* browser_context, + const GURL& effective_site_url) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::DoesSiteRequireDedicatedProcess( + browser_context, effective_site_url)) { + return true; + } +#endif + return false; +} + +bool ChromeContentBrowserClient::DoesWebUIUrlRequireProcessLock( + const GURL& url) { + // Note: This method can be called from multiple threads. It is not safe to + // assume it runs only on the UI thread. + + // We only allow the most visited tiles on third-party NTPs to not require a + // process lock. Everything else, including the actual third-party NTP which + // embeds those tiles, should be locked. This allows most visited tiles to + // stay in their parent (i.e., third-party NTP's) process. + if (url.SchemeIs(chrome::kChromeSearchScheme) && + url.host() == chrome::kChromeSearchMostVisitedHost) { + return false; + } + + // All other WebUIs must be locked to origin. + return true; +} + +bool ChromeContentBrowserClient::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel( + base::StringPiece scheme, + bool is_embedded_origin_secure) { + // This is needed to bypass the normal SameSite rules for any chrome:// page + // embedding a secure origin, regardless of the registrable domains of any + // intervening frames. For example, this is needed for browser UI to interact + // with SameSite cookies on accounts.google.com, which is used for displaying + // a list of available accounts on the NTP (chrome://new-tab-page), etc. + if (is_embedded_origin_secure && scheme == content::kChromeUIScheme) + return true; +#if BUILDFLAG(ENABLE_EXTENSIONS) + return scheme == extensions::kExtensionScheme; +#else + return false; +#endif +} + +bool ChromeContentBrowserClient:: + ShouldIgnoreSameSiteCookieRestrictionsWhenTopLevel( + base::StringPiece scheme, + bool is_embedded_origin_secure) { + return is_embedded_origin_secure && scheme == content::kChromeUIScheme; +} + +// TODO(crbug.com/1087559): This is based on SubframeTask::GetTitle() +// implementation. Find a general solution to avoid code duplication. +std::string ChromeContentBrowserClient::GetSiteDisplayNameForCdmProcess( + content::BrowserContext* browser_context, + const GURL& site_url) { + // By default, use the |site_url| spec as the display name. + std::string name = site_url.spec(); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + // If |site_url| wraps a chrome extension ID, we can display the extension + // name instead, which is more human-readable. + if (site_url.SchemeIs(extensions::kExtensionScheme)) { + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetExtensionOrAppByURL(site_url); + if (extension) + name = extension->name(); + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + + return name; +} + +void ChromeContentBrowserClient::OverrideURLLoaderFactoryParams( + content::BrowserContext* browser_context, + const url::Origin& origin, + bool is_for_isolated_world, + network::mojom::URLLoaderFactoryParams* factory_params) { +#if BUILDFLAG(IS_ANDROID) + // Loading state text isn't used on Android, only in desktop UI. + factory_params->provide_loading_state_updates = false; +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + browser_context)) { + return; + } + + ChromeContentBrowserClientExtensionsPart::OverrideURLLoaderFactoryParams( + browser_context, origin, is_for_isolated_world, factory_params); +#endif +} + +// These are treated as WebUI schemes but do not get WebUI bindings. Also, +// view-source is allowed for these schemes. +void ChromeContentBrowserClient::GetAdditionalWebUISchemes( + std::vector* additional_schemes) { + additional_schemes->emplace_back(chrome::kChromeSearchScheme); + additional_schemes->emplace_back(dom_distiller::kDomDistillerScheme); + additional_schemes->emplace_back(content::kChromeDevToolsScheme); +} + +void ChromeContentBrowserClient::GetAdditionalViewSourceSchemes( + std::vector* additional_schemes) { + GetAdditionalWebUISchemes(additional_schemes); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + additional_schemes->push_back(extensions::kExtensionScheme); +#endif +} + +network::mojom::IPAddressSpace +ChromeContentBrowserClient::DetermineAddressSpaceFromURL(const GURL& url) { + if (url.SchemeIs(chrome::kChromeSearchScheme)) + return network::mojom::IPAddressSpace::kLocal; + if (url.SchemeIs(dom_distiller::kDomDistillerScheme)) + return network::mojom::IPAddressSpace::kPublic; +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (url.SchemeIs(extensions::kExtensionScheme)) + return network::mojom::IPAddressSpace::kLocal; +#endif + + return network::mojom::IPAddressSpace::kUnknown; +} + +bool ChromeContentBrowserClient::LogWebUIUrl(const GURL& web_ui_url) { + return webui::LogWebUIUrl(web_ui_url); +} + +bool ChromeContentBrowserClient::IsWebUIAllowedToMakeNetworkRequests( + const url::Origin& origin) { + return ChromeWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests( + origin); +} + +bool ChromeContentBrowserClient::IsHandledURL(const GURL& url) { + return ProfileIOData::IsHandledURL(url); +} + +bool ChromeContentBrowserClient::HasCustomSchemeHandler( + content::BrowserContext* browser_context, + const std::string& scheme) { + if (custom_handlers::ProtocolHandlerRegistry* protocol_handler_registry = + ProtocolHandlerRegistryFactory::GetForBrowserContext( + browser_context)) { + return protocol_handler_registry->IsHandledProtocol(scheme); + } + + return false; +} + +bool ChromeContentBrowserClient::CanCommitURL( + content::RenderProcessHost* process_host, + const GURL& url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart::CanCommitURL(process_host, + url); +#else + return true; +#endif +} + +void ChromeContentBrowserClient::OverrideNavigationParams( + std::optional source_process_site_url, + ui::PageTransition* transition, + bool* is_renderer_initiated, + content::Referrer* referrer, + std::optional* initiator_origin) { + DCHECK(transition); + DCHECK(is_renderer_initiated); + DCHECK(referrer); + // IsNTPURL only looks at the origin of the parameter, so it is safe to use + // the effective site URL for the source process. + if (source_process_site_url && + search::IsNTPURL(source_process_site_url.value()) && + ui::PageTransitionCoreTypeIs(*transition, ui::PAGE_TRANSITION_LINK)) { + // Clicks on tiles of the new tab page should be treated as if a user + // clicked on a bookmark. This is consistent with native implementations + // like Android's. This also helps ensure that security features (like + // Sec-Fetch-Site and SameSite-cookies) will treat the navigation as + // browser-initiated. + *transition = ui::PAGE_TRANSITION_AUTO_BOOKMARK; + *is_renderer_initiated = false; + *referrer = content::Referrer(); + *initiator_origin = std::nullopt; + } +} + +bool ChromeContentBrowserClient::ShouldStayInParentProcessForNTP( + const GURL& url, + const GURL& parent_site_url) { + // Allow most visited iframes to stay in the parent process but only if that + // process is for NTP. + // + // TODO(alexmos): Consider further tightening this exception to just the + // third-party remote NTP in the parent, rather than any NTP. + // + // TODO(crbug.com/566091): place those iframes into OOPIFs and remove this + // exception. Relaxing site isolation like this is a bad idea and should be + // avoided. + // + // TODO(crbug.com/624410): clean up the logic for detecting NTP. + return url.SchemeIs(chrome::kChromeSearchScheme) && + url.host() == chrome::kChromeSearchMostVisitedHost && + search::IsNTPURL(parent_site_url); +} + +bool ChromeContentBrowserClient::IsSuitableHost( + content::RenderProcessHost* process_host, + const GURL& site_url) { + Profile* profile = + Profile::FromBrowserContext(process_host->GetBrowserContext()); + // This may be nullptr during tests. In that case, just assume any site can + // share any host. + if (!profile) + return true; + +#if !BUILDFLAG(IS_ANDROID) + // Instant URLs should only be in the instant process and instant process + // should only have Instant URLs. + InstantService* instant_service = + InstantServiceFactory::GetForProfile(profile); + if (instant_service) { + bool is_instant_process = + instant_service->IsInstantProcess(process_host->GetID()); + bool should_be_in_instant_process = + search::ShouldAssignURLToInstantRenderer(site_url, profile); + if (is_instant_process || should_be_in_instant_process) + return is_instant_process && should_be_in_instant_process; + } +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart::IsSuitableHost( + profile, process_host, site_url); +#else + return true; +#endif +} + +bool ChromeContentBrowserClient::MayReuseHost( + content::RenderProcessHost* process_host) { + // If there is currently a no-state prefetcher in progress for the host + // provided, it may not be shared. We require prefetchers to be by themselves + // in a separate process so that we can monitor their resource usage. + prerender::NoStatePrefetchManager* no_state_prefetch_manager = + prerender::NoStatePrefetchManagerFactory::GetForBrowserContext( + process_host->GetBrowserContext()); + if (no_state_prefetch_manager && + !no_state_prefetch_manager->MayReuseProcessHost(process_host)) { + return false; + } + + return true; +} + +size_t ChromeContentBrowserClient::GetProcessCountToIgnoreForLimit() { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart:: + GetProcessCountToIgnoreForLimit(); +#else + return 0; +#endif +} + +std::optional +ChromeContentBrowserClient::GetPermissionsPolicyForIsolatedWebApp( + content::WebContents* web_contents, + const url::Origin& app_origin) { +#if !BUILDFLAG(IS_ANDROID) + // Extensions are exempt from manifest policy enforcement and retain the + // default frame permissions policy. + if (app_origin.scheme() == extensions::kExtensionScheme) { + return std::nullopt; + } + CHECK(web_contents); + if (web_app::IsolatedWebAppPendingInstallInfo::HasPendingInstallSource( + *web_contents)) { + // We allow sync-xhr for apps that are being installed or updated to + // allow synchronous requests from the generated install page, which is + // used to implement manifest fallback logic while we migrate the + // expected manifest path from /manifest.webmanifest to + // /.well-known/manifest.webmanifest. + // + // TODO(crbug.com/325132780): Remove when manifest fallback logic is gone. + return blink::ParsedPermissionsPolicy( + {blink::ParsedPermissionsPolicyDeclaration( + blink::mojom::PermissionsPolicyFeature::kSyncXHR, + /*allowed_origins=*/{}, /*self_if_matches=*/app_origin, + /*matches_all_origins=*/false, /*matches_opaque_src=*/false)}); + } + + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + auto& registrar = + web_app::WebAppProvider::GetForWebApps(profile)->registrar_unsafe(); + std::vector app_ids_for_origin = + registrar.FindAppsInScope(app_origin.GetURL()); + if (app_ids_for_origin.empty()) { + return blink::ParsedPermissionsPolicy(); + } + + return registrar.GetPermissionsPolicy(app_ids_for_origin[0]); +#else + NOTIMPLEMENTED(); + return blink::ParsedPermissionsPolicy(); +#endif +} + +bool ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost( + content::BrowserContext* browser_context, + const GURL& url) { + // Top Chrome WebUI should try to share a RenderProcessHost with other + // existing Top Chrome WebUI. + if (IsTopChromeWebUIURL(url)) { + return true; + } + + return false; +} + +bool ChromeContentBrowserClient::ShouldEmbeddedFramesTryToReuseExistingProcess( + content::RenderFrameHost* outermost_main_frame) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart:: + ShouldEmbeddedFramesTryToReuseExistingProcess(outermost_main_frame); +#else + return true; +#endif +} + +void ChromeContentBrowserClient::SiteInstanceGotProcessAndSite( + SiteInstance* site_instance) { + CHECK(site_instance->HasProcess()); + + Profile* profile = + Profile::FromBrowserContext(site_instance->GetBrowserContext()); + if (!profile) + return; + +#if !BUILDFLAG(IS_ANDROID) + // Remember the ID of the Instant process to signal the renderer process + // on startup in |AppendExtraCommandLineSwitches| below. + if (search::ShouldAssignURLToInstantRenderer(site_instance->GetSiteURL(), + profile)) { + InstantService* instant_service = + InstantServiceFactory::GetForProfile(profile); + if (instant_service) + instant_service->AddInstantProcess(site_instance->GetProcess()); + } +#endif + + for (auto& part : extra_parts_) { + part->SiteInstanceGotProcessAndSite(site_instance); + } +} + +bool ChromeContentBrowserClient::ShouldSwapBrowsingInstancesForNavigation( + SiteInstance* site_instance, + const GURL& current_effective_url, + const GURL& destination_effective_url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart:: + ShouldSwapBrowsingInstancesForNavigation( + site_instance, current_effective_url, destination_effective_url); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::ShouldIsolateErrorPage(bool in_main_frame) { + // TODO(nasko): Consider supporting error page isolation in subframes if + // Site Isolation is enabled. + return in_main_frame; +} + +std::vector +ChromeContentBrowserClient::GetOriginsRequiringDedicatedProcess() { + std::vector isolated_origin_list; + + if (DoesGaiaOriginRequireDedicatedProcess()) { + isolated_origin_list.push_back(GaiaUrls::GetInstance()->gaia_origin()); + } + +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto origins_from_extensions = ChromeContentBrowserClientExtensionsPart:: + GetOriginsRequiringDedicatedProcess(); + std::move(std::begin(origins_from_extensions), + std::end(origins_from_extensions), + std::back_inserter(isolated_origin_list)); +#endif + + // Include additional origins preloaded with specific browser configurations, + // if any. For example, this is used on Google Chrome for Android to preload + // a list of important sites to isolate. + auto built_in_origins = + site_isolation::GetBrowserSpecificBuiltInIsolatedOrigins(); + std::move(std::begin(built_in_origins), std::end(built_in_origins), + std::back_inserter(isolated_origin_list)); + + return isolated_origin_list; +} + +bool ChromeContentBrowserClient::ShouldEnableStrictSiteIsolation() { + return base::FeatureList::IsEnabled(features::kSitePerProcess); +} + +bool ChromeContentBrowserClient::ShouldDisableSiteIsolation( + content::SiteIsolationMode site_isolation_mode) { + return site_isolation::SiteIsolationPolicy:: + ShouldDisableSiteIsolationDueToMemoryThreshold(site_isolation_mode); +} + +std::vector +ChromeContentBrowserClient::GetAdditionalSiteIsolationModes() { + std::vector modes; + if (site_isolation::SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled()) + modes.push_back("Password Sites"); + if (site_isolation::SiteIsolationPolicy::IsIsolationForOAuthSitesEnabled()) + modes.push_back("Logged-in Sites"); + return modes; +} + +void ChromeContentBrowserClient::PersistIsolatedOrigin( + content::BrowserContext* context, + const url::Origin& origin, + content::ChildProcessSecurityPolicy::IsolatedOriginSource source) { + site_isolation::SiteIsolationPolicy::PersistIsolatedOrigin(context, origin, + source); +} + +bool ChromeContentBrowserClient::ShouldUrlUseApplicationIsolationLevel( + content::BrowserContext* browser_context, + const GURL& url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + + if (!content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context)) { + return false; + } + + // Convert |url| to an origin to resolve blob: URLs. + auto origin = url::Origin::Create(url); + if (origin.scheme() == chrome::kIsolatedAppScheme) { + return true; + } +#endif + return false; +} + +bool ChromeContentBrowserClient::IsIsolatedContextAllowedForUrl( + content::BrowserContext* browser_context, + const GURL& lock_url) { +#if BUILDFLAG(IS_CHROMEOS) + if (base::FeatureList::IsEnabled(features::kWebKioskEnableIwaApis) && + chromeos::IsWebKioskSession()) { + return true; + } +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + browser_context)) { + return false; + } + + // Allow restricted context APIs in Chrome Apps. + auto* extension = extensions::ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetExtensionOrAppByURL(lock_url); + return extension && + (extension->is_platform_app() || + IsExtensionIdAllowedToUseIsolatedContext(extension->id())); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::IsGetAllScreensMediaAllowed( + content::BrowserContext* context, + const url::Origin& origin) { + return capture_policy::IsGetAllScreensMediaAllowed(context, origin.GetURL()); +} + +bool ChromeContentBrowserClient::IsFileAccessAllowed( + const base::FilePath& path, + const base::FilePath& absolute_path, + const base::FilePath& profile_path) { + return ChromeNetworkDelegate::IsAccessAllowed(path, absolute_path, + profile_path); +} + +namespace { + +void MaybeAppendBlinkSettingsSwitchForFieldTrial( + const base::CommandLine& browser_command_line, + base::CommandLine* command_line) { + // List of field trials that modify the blink-settings command line flag. No + // two field trials in the list should specify the same keys, otherwise one + // field trial may overwrite another. See Source/core/frame/Settings.in in + // Blink for the list of valid keys. + static const char* const kBlinkSettingsFieldTrials[] = { + // Keys: disallowFetchForDocWrittenScriptsInMainFrame + // disallowFetchForDocWrittenScriptsInMainFrameOnSlowConnections + // disallowFetchForDocWrittenScriptsInMainFrameIfEffectively2G + "DisallowFetchForDocWrittenScriptsInMainFrame", + }; + + std::vector blink_settings; + for (const char* field_trial_name : kBlinkSettingsFieldTrials) { + // Each blink-settings field trial should include a forcing_flag group, + // to make sure that clients that specify the blink-settings flag on the + // command line are excluded from the experiment groups. To make + // sure we assign clients that specify this flag to the forcing_flag + // group, we must call GetFieldTrialParams for each field trial first + // (for example, before checking HasSwitch() and returning), since + // GetFieldTrialParams has the side-effect of assigning the client to + // a field trial group. + std::map params; + if (base::GetFieldTrialParams(field_trial_name, ¶ms)) { + for (const auto& param : params) { + blink_settings.push_back(base::StringPrintf( + "%s=%s", param.first.c_str(), param.second.c_str())); + } + } + } + + if (blink_settings.empty()) { + return; + } + + if (browser_command_line.HasSwitch(blink::switches::kBlinkSettings) || + command_line->HasSwitch(blink::switches::kBlinkSettings)) { + // The field trials should be configured to force users that specify the + // blink-settings flag into a group with no params, and we return + // above if no params were specified, so it's an error if we reach + // this point. + LOG(WARNING) << "Received field trial params, " + "but blink-settings switch already specified."; + return; + } + + command_line->AppendSwitchASCII(blink::switches::kBlinkSettings, + base::JoinString(blink_settings, ",")); +} + +} // namespace + +void ChromeContentBrowserClient::AppendExtraCommandLineSwitches( + base::CommandLine* command_line, + int child_process_id) { +#if BUILDFLAG(IS_MAC) + std::unique_ptr client_info = + GoogleUpdateSettings::LoadMetricsClientInfo(); + if (client_info) { + command_line->AppendSwitchASCII(switches::kMetricsClientID, + client_info->client_id); + } +#elif BUILDFLAG(IS_POSIX) +#if !BUILDFLAG(IS_ANDROID) + pid_t pid; + if (crash_reporter::GetHandlerSocket(nullptr, &pid)) { + command_line->AppendSwitchASCII( + crash_reporter::switches::kCrashpadHandlerPid, + base::NumberToString(pid)); + } +#endif + std::string switch_value; + std::unique_ptr client_info = + GoogleUpdateSettings::LoadMetricsClientInfo(); + if (client_info) { + switch_value = client_info->client_id; + } + switch_value.push_back(','); + switch_value.append(chrome::GetChannelName(chrome::WithExtendedStable(true))); + command_line->AppendSwitchASCII(switches::kEnableCrashReporter, switch_value); +#endif + + if (logging::DialogsAreSuppressed()) + command_line->AppendSwitch(switches::kNoErrorDialogs); + + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // Pass startup and post-login parameter FDs to child processes in Lacros. + if (process_type != switches::kZygoteProcess) { + constexpr int kStartupDataFD = + kCrosStartupDataDescriptor + base::GlobalDescriptors::kBaseDescriptor; + command_line->AppendSwitchASCII(chromeos::switches::kCrosStartupDataFD, + base::NumberToString(kStartupDataFD)); + + if (chromeos::IsLaunchedWithPostLoginParams()) { + constexpr int kPostLoginDataFD = kCrosPostLoginDataDescriptor + + base::GlobalDescriptors::kBaseDescriptor; + command_line->AppendSwitchASCII(chromeos::switches::kCrosPostLoginDataFD, + base::NumberToString(kPostLoginDataFD)); + } + } +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) + + static const char* const kCommonSwitchNames[] = { + embedder_support::kUserAgent, + switches::kUserDataDir, // Make logs go to the right file. + }; + command_line->CopySwitchesFrom(browser_command_line, kCommonSwitchNames); + + static const char* const kDinosaurEasterEggSwitches[] = { + error_page::switches::kDisableDinosaurEasterEgg, + error_page::switches::kEnableDinosaurEasterEggAltGameImages, + }; + command_line->CopySwitchesFrom(browser_command_line, + kDinosaurEasterEggSwitches); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // On Chrome OS need to pass primary user homedir (in multi-profiles session). + base::FilePath homedir; + base::PathService::Get(base::DIR_HOME, &homedir); + command_line->AppendSwitchASCII(ash::switches::kHomedir, homedir.value()); +#endif + + if (process_type == switches::kRendererProcess) { + content::RenderProcessHost* process = + content::RenderProcessHost::FromID(child_process_id); + if (process) { + for (auto& part : extra_parts_) { + part->AppendExtraRendererCommandLineSwitches(command_line, *process); + } + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + const std::string& login_profile = + browser_command_line.GetSwitchValueASCII(ash::switches::kLoginProfile); + if (!login_profile.empty()) { + command_line->AppendSwitchASCII(ash::switches::kLoginProfile, + login_profile); + } +#endif + + MaybeCopyDisableWebRtcEncryptionSwitch(command_line, browser_command_line, + chrome::GetChannel()); + if (process) { + Profile* profile = + Profile::FromBrowserContext(process->GetBrowserContext()); + PrefService* prefs = profile->GetPrefs(); + // Currently this pref is only registered if applied via a policy. + if (prefs->HasPrefPath(prefs::kDisable3DAPIs) && + prefs->GetBoolean(prefs::kDisable3DAPIs)) { + // Turn this policy into a command line switch. + command_line->AppendSwitch(switches::kDisable3DAPIs); + } + + if (prefs->GetBoolean(prefs::kPrintPreviewDisabled)) + command_line->AppendSwitch(switches::kDisablePrintPreview); + + // This passes the preference set by an enterprise policy on to a blink + // switch so that we know whether to force WebSQL to be enabled. + if (prefs->GetBoolean(storage::kWebSQLAccess)) { + command_line->AppendSwitch(blink::switches::kWebSQLAccess); + } + + if (prefs->GetBoolean(prefs::kDataUrlInSvgUseEnabled)) { + command_line->AppendSwitch(blink::switches::kDataUrlInSvgUseEnabled); + } + + if (prefs->GetBoolean(policy::policy_prefs::kMutationEventsEnabled)) { + command_line->AppendSwitch(blink::switches::kMutationEventsEnabled); + } + + if (prefs->GetBoolean(policy::policy_prefs:: + kForcePermissionPolicyUnloadDefaultEnabled)) { + command_line->AppendSwitch( + blink::switches::kForcePermissionPolicyUnloadDefaultEnabled); + } + +#if !BUILDFLAG(IS_ANDROID) + InstantService* instant_service = + InstantServiceFactory::GetForProfile(profile); + if (instant_service && + instant_service->IsInstantProcess(process->GetID())) { + command_line->AppendSwitch(switches::kInstantProcess); + } + + // Enable SharedArrayBuffer on desktop if allowed by Enterprise Policy. + // TODO(crbug.com/1144104) Remove when migration to COOP+COEP is complete. + if (prefs->GetBoolean( + prefs::kSharedArrayBufferUnrestrictedAccessAllowed)) { + command_line->AppendSwitch( + switches::kSharedArrayBufferUnrestrictedAccessAllowed); + } +#endif + if (!prefs->GetBoolean(prefs::kSandboxExternalProtocolBlocked)) + command_line->AppendSwitch(kDisableSandboxExternalProtocolSwitch); + + if (prefs->HasPrefPath(prefs::kAllowDinosaurEasterEgg) && + !prefs->GetBoolean(prefs::kAllowDinosaurEasterEgg)) { + command_line->AppendSwitch( + error_page::switches::kDisableDinosaurEasterEgg); + } + + auto* management_service_factory = + policy::ManagementServiceFactory::GetInstance(); + auto* browser_managment_service = + management_service_factory->GetForProfile(profile); + if ((browser_managment_service && + browser_managment_service->IsManaged()) || + management_service_factory->GetForPlatform()->IsManaged()) { + command_line->AppendSwitch( + error_page::switches::kEnableDinosaurEasterEggAltGameImages); + } + + MaybeAppendSecureOriginsAllowlistSwitch(command_line); + + if (prefs->HasPrefPath(prefs::kScrollToTextFragmentEnabled) && + !prefs->GetBoolean(prefs::kScrollToTextFragmentEnabled)) { + command_line->AppendSwitch(switches::kDisableScrollToTextFragment); + } + + if (!prefs->GetList(enterprise_reporting::kCloudLegacyTechReportAllowlist) + .empty()) { + command_line->AppendSwitch( + blink::switches::kLegacyTechReportPolicyEnabled); + } + + // The IntensiveWakeUpThrottling feature is typically managed via a + // base::Feature, but it has a managed policy override. The override is + // communicated to blink via a custom command-line flag. See + // PageSchedulerImpl for the other half of related logic. + PrefService* local_state = g_browser_process->local_state(); + const PrefService::Preference* pref = local_state->FindPreference( + policy::policy_prefs::kIntensiveWakeUpThrottlingEnabled); + if (pref && pref->IsManaged()) { + command_line->AppendSwitchASCII( + blink::switches::kIntensiveWakeUpThrottlingPolicy, + pref->GetValue()->GetBool() + ? blink::switches::kIntensiveWakeUpThrottlingPolicy_ForceEnable + : blink::switches:: + kIntensiveWakeUpThrottlingPolicy_ForceDisable); + } + +#if BUILDFLAG(IS_ANDROID) + // Communicating to content/ for BackForwardCache. + if (prefs->HasPrefPath(policy::policy_prefs::kBackForwardCacheEnabled) && + !prefs->GetBoolean(policy::policy_prefs::kBackForwardCacheEnabled)) { + command_line->AppendSwitch(switches::kDisableBackForwardCache); + } +#endif // BUILDFLAG(IS_ANDROID) + +#if !BUILDFLAG(IS_ANDROID) + // Make the WebAuthenticationRemoteProxiedRequestsAllowed policy enable + // the experimental WebAuthenticationRemoteDesktopSupport Blink runtime + // feature. + if (prefs->GetBoolean( + webauthn::pref_names::kRemoteProxiedRequestsAllowed)) { + command_line->AppendSwitch(switches::kWebAuthRemoteDesktopSupport); + } + + if (IsCartModuleEnabled()) { + command_line->AppendSwitch(commerce::switches::kEnableChromeCart); + } +#endif + } + + MaybeAppendBlinkSettingsSwitchForFieldTrial(browser_command_line, + command_line); + +#if BUILDFLAG(IS_ANDROID) + // If the platform is Android, force the distillability service on. + command_line->AppendSwitch(switches::kEnableDistillabilityService); +#endif + +#if BUILDFLAG(ENABLE_NACL) + AppendDisableNaclSwitchIfNecessary(command_line); +#endif + + // Please keep this in alphabetical order. + static const char* const kSwitchNames[] = { + autofill::switches::kIgnoreAutocompleteOffForAutofill, + autofill::switches::kShowAutofillSignatures, +#if BUILDFLAG(IS_CHROMEOS_ASH) + switches::kShortMergeSessionTimeoutForTest, // For tests only. +#endif +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::switches::kAllowHTTPBackgroundPage, + extensions::switches::kAllowLegacyExtensionManifests, + extensions::switches::kDisableExtensionsHttpThrottling, + extensions::switches::kEnableExperimentalExtensionApis, + extensions::switches::kExtensionsOnChromeURLs, + extensions::switches::kSetExtensionThrottleTestParams, // For tests only. + extensions::switches::kAllowlistedExtensionID, +#endif + switches::kAllowInsecureLocalhost, + switches::kAppsGalleryURL, + switches::kDisableJavaScriptHarmonyShipping, + variations::switches::kEnableBenchmarking, + switches::kEnableDistillabilityService, + switches::kEnableNaCl, +#if BUILDFLAG(ENABLE_NACL) + switches::kEnableNaClDebug, +#endif + switches::kEnableNetBenchmarking, +#if BUILDFLAG(IS_CHROMEOS) + chromeos::switches:: + kTelemetryExtensionPwaOriginOverrideForTesting, // For tests only. + switches::kForceAppMode, +#endif +#if BUILDFLAG(ENABLE_NACL) + switches::kForcePNaClSubzero, +#endif + switches::kForceUIDirection, + switches::kIgnoreGooglePortNumbers, + switches::kJavaScriptHarmony, + switches::kEnableExperimentalWebAssemblyFeatures, + embedder_support::kOriginTrialDisabledFeatures, + embedder_support::kOriginTrialPublicKey, + switches::kReaderModeHeuristics, + translate::switches::kTranslateSecurityOrigin, + }; + + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames); + } else if (process_type == switches::kUtilityProcess) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + static const char* const kSwitchNames[] = { + extensions::switches::kAllowHTTPBackgroundPage, + extensions::switches::kEnableExperimentalExtensionApis, + extensions::switches::kExtensionsOnChromeURLs, + extensions::switches::kAllowlistedExtensionID, + }; + + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames); +#endif + MaybeAppendSecureOriginsAllowlistSwitch(command_line); + } else if (process_type == switches::kZygoteProcess) { + // It would be preferable to call AppendDisableNaclSwitchIfNecessary to + // disable NaCl for the zygote process. Unfortunately that method depends on + // state (including policy) that is determined after the zygote is forked. + // Instead we rely on renderers overriding the zygote state. + + // Load (in-process) Pepper plugins in-process in the zygote pre-sandbox. +#if BUILDFLAG(ENABLE_NACL) + static const char* const kSwitchNames[] = { + switches::kEnableNaClDebug, + switches::kForcePNaClSubzero, + switches::kVerboseLoggingInNacl, + }; + + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames); +#endif +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // Ensure zygote loads the resource bundle for the right locale. + static const char* const kMoreSwitchNames[] = {switches::kLang}; + command_line->CopySwitchesFrom(browser_command_line, kMoreSwitchNames); +#endif +#if BUILDFLAG(IS_CHROMEOS) + // This is called before feature flags are parsed, so pass them in their raw + // form. + static const char* const kMoreCrOSSwitchNames[] = { + chromeos::switches::kFeatureFlags}; + command_line->CopySwitchesFrom(browser_command_line, kMoreCrOSSwitchNames); +#endif + } else if (process_type == switches::kGpuProcess) { + // If --ignore-gpu-blocklist is passed in, don't send in crash reports + // because GPU is expected to be unreliable. + if (browser_command_line.HasSwitch(switches::kIgnoreGpuBlocklist) && + !command_line->HasSwitch(switches::kDisableBreakpad)) + command_line->AppendSwitch(switches::kDisableBreakpad); + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (ChromeCrashReporterClient::ShouldPassCrashLoopBefore(process_type)) { + static const char* const kSwitchNames[] = { + crash_reporter::switches::kCrashLoopBefore, + }; + command_line->CopySwitchesFrom(browser_command_line, kSwitchNames); + } +#endif + +#if BUILDFLAG(IS_WIN) + if (base::FeatureList::IsEnabled(features::kNoPreReadMainDll)) { + command_line->AppendSwitch(switches::kNoPreReadMainDll); + } + if (base::FeatureList::IsEnabled(features::kNoAppCompatClearInChildren)) { + command_line->AppendSwitch(switches::kNoAppCompatClear); + } +#endif + + ThreadProfilerConfiguration::Get()->AppendCommandLineSwitchForChildProcess( + command_line); + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH) + // Opt into a hardened stack canary mitigation if it hasn't already been + // force-disabled. + if (!browser_command_line.HasSwitch(switches::kChangeStackGuardOnFork)) { + command_line->AppendSwitchASCII(switches::kChangeStackGuardOnFork, + switches::kChangeStackGuardOnForkEnabled); + } +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +} + +std::string +ChromeContentBrowserClient::GetApplicationClientGUIDForQuarantineCheck() { + return std::string(chrome::kApplicationClientIDStringForAVScanning); +} + +download::QuarantineConnectionCallback +ChromeContentBrowserClient::GetQuarantineConnectionCallback() { + return base::BindRepeating( + &ChromeDownloadManagerDelegate::ConnectToQuarantineService); +} + +std::string ChromeContentBrowserClient::GetApplicationLocale() { + if (BrowserThread::CurrentlyOn(BrowserThread::IO)) + return GetIOThreadApplicationLocale(); + return g_browser_process->GetApplicationLocale(); +} + +std::string ChromeContentBrowserClient::GetAcceptLangs( + content::BrowserContext* context) { + Profile* profile = Profile::FromBrowserContext(context); + return profile->GetPrefs()->GetString(language::prefs::kAcceptLanguages); +} + +gfx::ImageSkia ChromeContentBrowserClient::GetDefaultFavicon() { + return favicon::GetDefaultFavicon().AsImageSkia(); +} + +bool ChromeContentBrowserClient::IsDataSaverEnabled( + content::BrowserContext* browser_context) { + if (!browser_context || browser_context->IsOffTheRecord()) + return false; + + return data_saver::IsDataSaverEnabled(); +} + +void ChromeContentBrowserClient::UpdateRendererPreferencesForWorker( + content::BrowserContext* browser_context, + blink::RendererPreferences* out_prefs) { + DCHECK(browser_context); + DCHECK(out_prefs); + renderer_preferences_util::UpdateFromSystemSettings( + out_prefs, Profile::FromBrowserContext(browser_context)); +} + +content::AllowServiceWorkerResult +ChromeContentBrowserClient::AllowServiceWorker( + const GURL& scope, + const net::SiteForCookies& site_for_cookies, + const std::optional& top_frame_origin, + const GURL& script_url, + content::BrowserContext* context) { + DCHECK(context); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + GURL first_party_url = top_frame_origin ? top_frame_origin->GetURL() : GURL(); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + // Check if this is an extension-related service worker, and, if so, if it's + // allowed (this can return false if, e.g., the extension is disabled). + // If it's not allowed, return immediately. We deliberately do *not* report + // to the PageSpecificContentSettings, since the service worker is blocked + // because of the extension, rather than because of the user's content + // settings. + if (!ChromeContentBrowserClientExtensionsPart::AllowServiceWorker( + scope, first_party_url, script_url, context)) { + return content::AllowServiceWorkerResult::No(); + } +#endif + + Profile* profile = Profile::FromBrowserContext(context); + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile(profile); + return embedder_support::AllowServiceWorker( + scope, site_for_cookies, top_frame_origin, cookie_settings.get(), + HostContentSettingsMapFactory::GetForProfile(profile)); +} + +bool ChromeContentBrowserClient::MayDeleteServiceWorkerRegistration( + const GURL& scope, + content::BrowserContext* browser_context) { + DCHECK(browser_context); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!ChromeContentBrowserClientExtensionsPart:: + MayDeleteServiceWorkerRegistration(scope, browser_context)) { + return false; + } +#endif + + return true; +} + +bool ChromeContentBrowserClient::ShouldTryToUpdateServiceWorkerRegistration( + const GURL& scope, + content::BrowserContext* browser_context) { + DCHECK(browser_context); + DCHECK_CURRENTLY_ON(BrowserThread::UI); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!ChromeContentBrowserClientExtensionsPart:: + ShouldTryToUpdateServiceWorkerRegistration(scope, browser_context)) { + return false; + } +#endif + + return true; +} + +bool ChromeContentBrowserClient::AllowSharedWorker( + const GURL& worker_url, + const net::SiteForCookies& site_for_cookies, + const std::optional& top_frame_origin, + const std::string& name, + const blink::StorageKey& storage_key, + const blink::mojom::SharedWorkerSameSiteCookies same_site_cookies, + content::BrowserContext* context, + int render_process_id, + int render_frame_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // Check if cookies are allowed. + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(context)); + return embedder_support::AllowSharedWorker( + worker_url, site_for_cookies, top_frame_origin, name, storage_key, + same_site_cookies, render_process_id, render_frame_id, + cookie_settings.get()); +} + +bool ChromeContentBrowserClient::DoesSchemeAllowCrossOriginSharedWorker( + const std::string& scheme) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // Extensions are allowed to start cross-origin shared workers. + if (scheme == extensions::kExtensionScheme) + return true; +#endif + + return false; +} + +bool ChromeContentBrowserClient::AllowSignedExchange( + content::BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + Profile* profile = Profile::FromBrowserContext(browser_context); + return profile->GetPrefs()->GetBoolean(prefs::kSignedHTTPExchangeEnabled); +} + +bool ChromeContentBrowserClient::AllowCompressionDictionaryTransport( + content::BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + Profile* profile = Profile::FromBrowserContext(browser_context); + return profile->GetPrefs()->GetBoolean( + prefs::kCompressionDictionaryTransportEnabled); +} + +void ChromeContentBrowserClient::RequestFilesAccess( + const std::vector& files, + const GURL& destination_url, + base::OnceCallback + continuation_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +#if BUILDFLAG(IS_CHROMEOS) + auto* delegate = policy::DlpScopedFileAccessDelegate::Get(); + if (delegate) { + delegate->RequestFilesAccess(files, destination_url, + std::move(continuation_callback)); + } else { + std::move(continuation_callback) + .Run(file_access::ScopedFileAccess::Allowed()); + } +#else + std::move(continuation_callback) + .Run(file_access::ScopedFileAccess::Allowed()); +#endif +} + +void ChromeContentBrowserClient::AllowWorkerFileSystem( + const GURL& url, + content::BrowserContext* browser_context, + const std::vector& render_frames, + base::OnceCallback callback) { + // An empty list is passed for render_frames here since we manually notify + // PageSpecificContentSettings that the file system was accessed below. + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + bool allow = + embedder_support::AllowWorkerFileSystem(url, {}, cookie_settings.get()); +#if BUILDFLAG(ENABLE_EXTENSIONS) + GuestPermissionRequestHelper(url, render_frames, std::move(callback), allow); +#else + FileSystemAccessed(url, render_frames, std::move(callback), allow); +#endif +} + +#if BUILDFLAG(ENABLE_EXTENSIONS) +void ChromeContentBrowserClient::GuestPermissionRequestHelper( + const GURL& url, + const std::vector& render_frames, + base::OnceCallback callback, + bool allow) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + std::map process_map; + bool has_web_view_guest = false; + // Record access to file system for potential display in UI. + for (const auto& it : render_frames) { + if (process_map.find(it.child_id) != process_map.end()) + continue; + + process_map.insert(std::pair(it.child_id, it.frame_routing_id)); + + if (extensions::WebViewRendererState::GetInstance()->IsGuest(it.child_id)) + has_web_view_guest = true; + } + if (!has_web_view_guest) { + FileSystemAccessed(url, render_frames, std::move(callback), allow); + return; + } + DCHECK_EQ(1U, process_map.size()); + std::map::const_iterator it = process_map.begin(); + + extensions::WebViewPermissionHelper* web_view_permission_helper = + extensions::WebViewPermissionHelper::FromRenderFrameHostId( + content::GlobalRenderFrameHostId(it->first, it->second)); + web_view_permission_helper->RequestFileSystemPermission( + url, allow, + base::BindOnce(&ChromeContentBrowserClient::FileSystemAccessed, + weak_factory_.GetWeakPtr(), url, render_frames, + std::move(callback))); +} +#endif + +void ChromeContentBrowserClient::FileSystemAccessed( + const GURL& url, + const std::vector& render_frames, + base::OnceCallback callback, + bool allow) { + // Record access to file system for potential display in UI. + for (const auto& it : render_frames) { + auto* rfh = content::RenderFrameHost::FromID(it); + if (!rfh) { + continue; + } + content_settings::PageSpecificContentSettings::StorageAccessed( + content_settings::mojom::ContentSettingsManager::StorageType:: + FILE_SYSTEM, + it, rfh->GetStorageKey(), !allow); + } + std::move(callback).Run(allow); +} + +bool ChromeContentBrowserClient::AllowWorkerIndexedDB( + const GURL& url, + content::BrowserContext* browser_context, + const std::vector& render_frames) { + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + return embedder_support::AllowWorkerIndexedDB(url, render_frames, + cookie_settings.get()); +} + +bool ChromeContentBrowserClient::AllowWorkerCacheStorage( + const GURL& url, + content::BrowserContext* browser_context, + const std::vector& render_frames) { + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + return embedder_support::AllowWorkerCacheStorage(url, render_frames, + cookie_settings.get()); +} + +bool ChromeContentBrowserClient::AllowWorkerWebLocks( + const GURL& url, + content::BrowserContext* browser_context, + const std::vector& render_frames) { + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + return embedder_support::AllowWorkerWebLocks(url, cookie_settings.get()); +} + +ChromeContentBrowserClient::AllowWebBluetoothResult +ChromeContentBrowserClient::AllowWebBluetooth( + content::BrowserContext* browser_context, + const url::Origin& requesting_origin, + const url::Origin& embedding_origin) { + // TODO(crbug.com/598890): Don't disable if + // base::CommandLine::ForCurrentProcess()-> + // HasSwitch(switches::kEnableWebBluetooth) is true. + if (base::GetFieldTrialParamValue( + permissions::PermissionContextBase::kPermissionsKillSwitchFieldStudy, + "Bluetooth") == + permissions::PermissionContextBase::kPermissionsKillSwitchBlockedValue) { + // The kill switch is enabled for this permission. Block requests. + return AllowWebBluetoothResult::BLOCK_GLOBALLY_DISABLED; + } + + const HostContentSettingsMap* const content_settings = + HostContentSettingsMapFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + + if (content_settings->GetContentSetting( + requesting_origin.GetURL(), embedding_origin.GetURL(), + ContentSettingsType::BLUETOOTH_GUARD) == CONTENT_SETTING_BLOCK) { + return AllowWebBluetoothResult::BLOCK_POLICY; + } + return AllowWebBluetoothResult::ALLOW; +} + +std::string ChromeContentBrowserClient::GetWebBluetoothBlocklist() { + return base::GetFieldTrialParamValue("WebBluetoothBlocklist", + "blocklist_additions"); +} + +bool ChromeContentBrowserClient::IsInterestGroupAPIAllowed( + content::RenderFrameHost* render_frame_host, + InterestGroupApiOperation operation, + const url::Origin& top_frame_origin, + const url::Origin& api_origin) { + Profile* profile = + Profile::FromBrowserContext(render_frame_host->GetBrowserContext()); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + + bool allowed = privacy_sandbox_settings->IsFledgeAllowed( + top_frame_origin, api_origin, operation, render_frame_host); + + if (operation == InterestGroupApiOperation::kJoin) { + content_settings::PageSpecificContentSettings::InterestGroupJoined( + render_frame_host, api_origin, !allowed); + content_settings::PageSpecificContentSettings::BrowsingDataAccessed( + render_frame_host, + content::InterestGroupManager::InterestGroupDataKey{api_origin, + top_frame_origin}, + BrowsingDataModel::StorageType::kInterestGroup, !allowed); + } + + return allowed; +} + +bool ChromeContentBrowserClient::IsPrivacySandboxReportingDestinationAttested( + content::BrowserContext* browser_context, + const url::Origin& destination_origin, + content::PrivacySandboxInvokingAPI invoking_api, + bool post_impression_reporting) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + + if (invoking_api == content::PrivacySandboxInvokingAPI::kProtectedAudience) { + if (base::FeatureList::IsEnabled( + blink::features::kFencedFramesReportingAttestationsChanges) && + post_impression_reporting) { + // M120 and afterwards: For beacons sent by `reportEvent()` and automatic + // beacons, the destination is required to be attested for either + // Protected Audience or Attribution Reporting. + return privacy_sandbox_settings->IsEventReportingDestinationAttested( + destination_origin, + privacy_sandbox::PrivacySandboxAttestationsGatedAPI:: + kProtectedAudience) || + privacy_sandbox_settings->IsEventReportingDestinationAttested( + destination_origin, + privacy_sandbox::PrivacySandboxAttestationsGatedAPI:: + kAttributionReporting); + } else { + // Before M120: The reporting destination is required to be attested for + // its invoking API only. + // M120 and afterwards: For beacons sent by `reportResult()` and + // `reportWin()`, the destination is required to be attested for Protected + // Audience only. + return privacy_sandbox_settings->IsEventReportingDestinationAttested( + destination_origin, + privacy_sandbox::PrivacySandboxAttestationsGatedAPI:: + kProtectedAudience); + } + } else if (invoking_api == + content::PrivacySandboxInvokingAPI::kSharedStorage) { + return privacy_sandbox_settings->IsEventReportingDestinationAttested( + destination_origin, + privacy_sandbox::PrivacySandboxAttestationsGatedAPI::kSharedStorage); + } + + return false; +} + +void ChromeContentBrowserClient::OnAuctionComplete( + content::RenderFrameHost* render_frame_host, + content::InterestGroupManager::InterestGroupDataKey winner_data_key) { + content_settings::PageSpecificContentSettings::BrowsingDataAccessed( + render_frame_host, winner_data_key, + BrowsingDataModel::StorageType::kInterestGroup, + /*blocked=*/false); +} + +bool ChromeContentBrowserClient::IsAttributionReportingOperationAllowed( + content::BrowserContext* browser_context, + AttributionReportingOperation operation, + content::RenderFrameHost* rfh, + const url::Origin* source_origin, + const url::Origin* destination_origin, + const url::Origin* reporting_origin, + bool* can_bypass) { + Profile* profile = Profile::FromBrowserContext(browser_context); + + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + if (!privacy_sandbox_settings) + return false; + + switch (operation) { + case AttributionReportingOperation::kSource: + case AttributionReportingOperation::kOsSource: { + DCHECK(source_origin); + DCHECK(reporting_origin); + bool allowed = privacy_sandbox_settings->IsAttributionReportingAllowed( + *source_origin, *reporting_origin, rfh); + if (rfh) { + content_settings::PageSpecificContentSettings::BrowsingDataAccessed( + rfh, content::AttributionDataModel::DataKey(*reporting_origin), + BrowsingDataModel::StorageType::kAttributionReporting, + /*blocked=*/!allowed); + } + return allowed; + } + case AttributionReportingOperation::kSourceVerboseDebugReport: + case AttributionReportingOperation::kOsSourceVerboseDebugReport: + DCHECK(source_origin); + DCHECK(reporting_origin); + return privacy_sandbox_settings->IsAttributionReportingAllowed( + *source_origin, *reporting_origin, rfh); + case AttributionReportingOperation::kTrigger: + case AttributionReportingOperation::kOsTrigger: { + DCHECK(destination_origin); + DCHECK(reporting_origin); + bool allowed = privacy_sandbox_settings->IsAttributionReportingAllowed( + *destination_origin, *reporting_origin, rfh); + if (rfh) { + content_settings::PageSpecificContentSettings::BrowsingDataAccessed( + rfh, content::AttributionDataModel::DataKey(*reporting_origin), + BrowsingDataModel::StorageType::kAttributionReporting, + /*blocked=*/!allowed); + } + return allowed; + } + case AttributionReportingOperation::kTriggerVerboseDebugReport: + case AttributionReportingOperation::kOsTriggerVerboseDebugReport: + DCHECK(destination_origin); + DCHECK(reporting_origin); + return privacy_sandbox_settings->IsAttributionReportingAllowed( + *destination_origin, *reporting_origin, rfh); + case AttributionReportingOperation::kReport: + DCHECK(source_origin); + DCHECK(destination_origin); + DCHECK(reporting_origin); + return privacy_sandbox_settings->MaySendAttributionReport( + *source_origin, *destination_origin, *reporting_origin, rfh); + case AttributionReportingOperation::kSourceTransitionalDebugReporting: + case AttributionReportingOperation::kOsSourceTransitionalDebugReporting: + DCHECK(source_origin); + DCHECK(reporting_origin); + DCHECK(can_bypass); + return privacy_sandbox_settings + ->IsAttributionReportingTransitionalDebuggingAllowed( + *source_origin, *reporting_origin, *can_bypass); + case AttributionReportingOperation::kTriggerTransitionalDebugReporting: + case AttributionReportingOperation::kOsTriggerTransitionalDebugReporting: + DCHECK(destination_origin); + DCHECK(reporting_origin); + DCHECK(can_bypass); + return privacy_sandbox_settings + ->IsAttributionReportingTransitionalDebuggingAllowed( + *destination_origin, *reporting_origin, *can_bypass); + case AttributionReportingOperation::kAny: + return privacy_sandbox_settings->IsAttributionReportingEverAllowed(); + } +} + +bool ChromeContentBrowserClient::IsAttributionReportingAllowedForContext( + content::BrowserContext* browser_context, + content::RenderFrameHost* rfh, + const url::Origin& context_origin, + const url::Origin& reporting_origin) { + Profile* profile = Profile::FromBrowserContext(browser_context); + + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + if (!privacy_sandbox_settings) { + return false; + } + + return privacy_sandbox_settings->IsAttributionReportingAllowed( + context_origin, reporting_origin, rfh); +} + +bool ChromeContentBrowserClient::IsSharedStorageAllowed( + content::BrowserContext* browser_context, + content::RenderFrameHost* rfh, + const url::Origin& top_frame_origin, + const url::Origin& accessing_origin, + std::string* out_debug_message) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + bool allowed = privacy_sandbox_settings->IsSharedStorageAllowed( + top_frame_origin, accessing_origin, out_debug_message, rfh); + if (rfh) { + content_settings::PageSpecificContentSettings::BrowsingDataAccessed( + rfh, blink::StorageKey::CreateFirstParty(accessing_origin), + BrowsingDataModel::StorageType::kSharedStorage, !allowed); + } + return allowed; +} + +bool ChromeContentBrowserClient::IsSharedStorageSelectURLAllowed( + content::BrowserContext* browser_context, + const url::Origin& top_frame_origin, + const url::Origin& accessing_origin, + std::string* out_debug_message) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + return privacy_sandbox_settings->IsSharedStorageSelectURLAllowed( + top_frame_origin, accessing_origin, out_debug_message); +} + +bool ChromeContentBrowserClient::IsPrivateAggregationAllowed( + content::BrowserContext* browser_context, + const url::Origin& top_frame_origin, + const url::Origin& reporting_origin) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + + return privacy_sandbox_settings->IsPrivateAggregationAllowed( + top_frame_origin, reporting_origin); +} + +bool ChromeContentBrowserClient::IsPrivateAggregationDebugModeAllowed( + content::BrowserContext* browser_context, + const url::Origin& top_frame_origin, + const url::Origin& reporting_origin) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + + return privacy_sandbox_settings->IsPrivateAggregationDebugModeAllowed( + top_frame_origin, reporting_origin); +} + +bool ChromeContentBrowserClient::IsCookieDeprecationLabelAllowed( + content::BrowserContext* browser_context) { + Profile* profile = Profile::FromBrowserContext(browser_context); + + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + if (!privacy_sandbox_settings) { + return false; + } + return privacy_sandbox_settings->IsCookieDeprecationLabelAllowed(); +} + +bool ChromeContentBrowserClient::IsCookieDeprecationLabelAllowedForContext( + content::BrowserContext* browser_context, + const url::Origin& top_frame_origin, + const url::Origin& context_origin) { + Profile* profile = Profile::FromBrowserContext(browser_context); + + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + DCHECK(privacy_sandbox_settings); + return privacy_sandbox_settings->IsCookieDeprecationLabelAllowedForContext( + top_frame_origin, context_origin); +} + +bool ChromeContentBrowserClient::IsFullCookieAccessAllowed( + content::BrowserContext* browser_context, + const GURL& url, + const blink::StorageKey& storage_key) { + Profile* profile = Profile::FromBrowserContext(browser_context); + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile(profile); + if (!cookie_settings) { + return true; + } + return cookie_settings->IsFullCookieAccessAllowed( + url, storage_key.ToNetSiteForCookies(), + url::Origin::Create(storage_key.top_level_site().GetURL()), + cookie_settings->SettingOverridesForStorage()); +} + +#if BUILDFLAG(IS_CHROMEOS) +void ChromeContentBrowserClient::OnTrustAnchorUsed( + content::BrowserContext* browser_context) { + policy::PolicyCertService* service = + policy::PolicyCertServiceFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + if (!service) { + NOTREACHED(); + return; + } + service->SetUsedPolicyCertificates(); +} +#endif + +bool ChromeContentBrowserClient::CanSendSCTAuditingReport( + content::BrowserContext* browser_context) { + return SCTReportingService::CanSendSCTAuditingReport(); +} + +void ChromeContentBrowserClient::OnNewSCTAuditingReportSent( + content::BrowserContext* browser_context) { + SCTReportingService::OnNewSCTAuditingReportSent(); +} + +scoped_refptr +ChromeContentBrowserClient::GetSystemSharedURLLoaderFactory() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || + !BrowserThread::IsThreadInitialized(BrowserThread::UI)); + + if (!SystemNetworkContextManager::GetInstance()) + return nullptr; + + return SystemNetworkContextManager::GetInstance() + ->GetSharedURLLoaderFactory(); +} + +network::mojom::NetworkContext* +ChromeContentBrowserClient::GetSystemNetworkContext() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(g_browser_process->system_network_context_manager()); + return g_browser_process->system_network_context_manager()->GetContext(); +} + +std::string ChromeContentBrowserClient::GetGeolocationApiKey() { + return google_apis::GetAPIKey(); +} + +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) +device::GeolocationSystemPermissionManager* +ChromeContentBrowserClient::GetGeolocationSystemPermissionManager() { + return device::GeolocationSystemPermissionManager::GetInstance(); +} +#endif + +#if BUILDFLAG(IS_ANDROID) +bool ChromeContentBrowserClient::ShouldUseGmsCoreGeolocationProvider() { + // Indicate that Chrome uses the GMS core location provider. + return true; +} +#endif + +content::GeneratedCodeCacheSettings +ChromeContentBrowserClient::GetGeneratedCodeCacheSettings( + content::BrowserContext* context) { + base::FilePath cache_path; + chrome::GetUserCacheDirectory(context->GetPath(), &cache_path); + // If we pass 0 for size, disk_cache will pick a default size using the + // heuristics based on available disk size. These are implemented in + // disk_cache::PreferredCacheSize in net/disk_cache/cache_util.cc. + int64_t size_in_bytes = 0; + DCHECK(g_browser_process); + PrefService* local_state = g_browser_process->local_state(); + if (local_state) { + size_in_bytes = local_state->GetInteger(prefs::kDiskCacheSize); + base::FilePath disk_cache_dir = + local_state->GetFilePath(prefs::kDiskCacheDir); + if (!disk_cache_dir.empty()) + cache_path = disk_cache_dir.Append(cache_path.BaseName()); + } + return content::GeneratedCodeCacheSettings(true, size_in_bytes, cache_path); +} + +void ChromeContentBrowserClient::AllowCertificateError( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + bool is_primary_main_frame_request, + bool strict_enforcement, + base::OnceCallback callback) { + DCHECK(web_contents); + if (!is_primary_main_frame_request) { + // A sub-resource has a certificate error. The user doesn't really + // have a context for making the right decision, so block the + // request hard, without an info bar to allow showing the insecure + // content. + if (!callback.is_null()) + std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + return; + } + + // If the tab is being no-state prefetched, cancel the prefetcher and the + // request. + prerender::NoStatePrefetchContents* no_state_prefetch_contents = + prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + web_contents); + if (no_state_prefetch_contents) { + no_state_prefetch_contents->Destroy(prerender::FINAL_STATUS_SSL_ERROR); + if (!callback.is_null()) { + std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL); + } + return; + } + + std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + return; +} + +#if !BUILDFLAG(IS_ANDROID) +bool ChromeContentBrowserClient::ShouldDenyRequestOnCertificateError( + const GURL main_page_url) { + // Desktop Reader Mode pages should never load resources with certificate + // errors. Desktop Reader Mode is more strict about security than Reader Mode + // on Android: the desktop version has its own security indicator and + // is not downgraded to a WARNING, whereas Android will show "Not secure" + // in the omnibox (for low-end devices which show the omnibox on Reader Mode + // pages). + return main_page_url.SchemeIs(dom_distiller::kDomDistillerScheme); +} +#endif + +namespace { + +#if !BUILDFLAG(IS_ANDROID) +blink::mojom::PreferredColorScheme ToBlinkPreferredColorScheme( + ui::NativeTheme::PreferredColorScheme native_theme_scheme) { + switch (native_theme_scheme) { + case ui::NativeTheme::PreferredColorScheme::kDark: + return blink::mojom::PreferredColorScheme::kDark; + case ui::NativeTheme::PreferredColorScheme::kLight: + return blink::mojom::PreferredColorScheme::kLight; + } +} +#endif // !BUILDFLAG(IS_ANDROID) + +// Returns true if preferred color scheme is modified based on at least one of +// the following - +// |url| - Last committed url. +// |web_contents| - For Android based on IsNightModeEnabled(). +// |native_theme| - For other platforms based on native theme scheme. +bool UpdatePreferredColorScheme(WebPreferences* web_prefs, + const GURL& url, + WebContents* web_contents, + const ui::NativeTheme* native_theme) { + auto old_preferred_color_scheme = web_prefs->preferred_color_scheme; + +#if BUILDFLAG(IS_ANDROID) + auto* delegate = TabAndroid::FromWebContents(web_contents) + ? static_cast( + web_contents->GetDelegate()) + : nullptr; + if (delegate) { + web_prefs->preferred_color_scheme = + delegate->IsNightModeEnabled() + ? blink::mojom::PreferredColorScheme::kDark + : blink::mojom::PreferredColorScheme::kLight; + } +#else + // Update based on native theme scheme. + web_prefs->preferred_color_scheme = + ToBlinkPreferredColorScheme(native_theme->GetPreferredColorScheme()); +#endif // BUILDFLAG(IS_ANDROID) + + // Reauth WebUI doesn't support dark mode yet because it shares the dialog + // with GAIA web contents that is not correctly themed. + const bool force_light = + url.SchemeIs(content::kChromeUIScheme) && + url.host_piece() == chrome::kChromeUISigninReauthHost; + + if (force_light) { + web_prefs->preferred_color_scheme = + blink::mojom::PreferredColorScheme::kLight; + } else if (url.SchemeIs(content::kChromeUIScheme)) { + // If color scheme is not forced, WebUI should track the color mode of the + // ColorProvider associated with `web_contents`. + web_prefs->preferred_color_scheme = + web_contents->GetColorMode() == ui::ColorProviderKey::ColorMode::kLight + ? blink::mojom::PreferredColorScheme::kLight + : blink::mojom::PreferredColorScheme::kDark; + } + + return old_preferred_color_scheme != web_prefs->preferred_color_scheme; +} + +// Returns whether the user can be prompted to select a client certificate after +// no certificate got auto-selected. +bool CanPromptWithNonmatchingCertificates(const Profile* profile) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (ash::ProfileHelper::IsSigninProfile(profile) || + ash::ProfileHelper::IsLockScreenProfile(profile) || + ash::ProfileHelper::IsLockScreenAppProfile(profile)) { + // On non-regular profiles (e.g. sign-in profile or lock-screen profile), + // never show certificate selection to the user. A client certificate is an + // identifier that can be stable for a long time, so only the administrator + // is allowed to decide which endpoints should see it. + // This also returns false for the lock screen app profile which can + // not use client certificates anyway - to be on the safe side in case + // support for client certificates is added later. + return false; + } +#endif + return true; +} + +// Returns whether the user should be prompted to select a client certificate +// when multiple certificates got auto-selected. +bool ShouldPromptOnMultipleMatchingCertificates(const Profile* profile) { + const PrefService* const prefs = profile->GetPrefs(); + DCHECK(prefs); + const PrefService::Preference* pref = + prefs->FindPreference(prefs::kPromptOnMultipleMatchingCertificates); + if (pref && pref->IsManaged() && pref->GetValue()->is_bool()) + return pref->GetValue()->GetBool(); + return false; +} + +} // namespace + +base::OnceClosure ChromeContentBrowserClient::SelectClientCertificate( + content::BrowserContext* browser_context, + content::WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + net::ClientCertIdentityList client_certs, + std::unique_ptr delegate) { + prerender::NoStatePrefetchContents* no_state_prefetch_contents = + web_contents + ? prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + web_contents) + : nullptr; + if (no_state_prefetch_contents) { + no_state_prefetch_contents->Destroy( + prerender::FINAL_STATUS_SSL_CLIENT_CERTIFICATE_REQUESTED); + return base::OnceClosure(); + } + + Profile* profile = Profile::FromBrowserContext(browser_context); +#if BUILDFLAG(IS_CHROMEOS_ASH) + // On the sign-in or lock screen profile, only allow client certs in the + // context of the sign-in frame. + // Note that this is explicitly not happening for the lock screen app profile + // which does not support a gaia / SAML IdP sign-in frame. + if (ash::ProfileHelper::IsSigninProfile(profile) || + ash::ProfileHelper::IsLockScreenProfile(profile)) { + const char* profile_name = ash::ProfileHelper::IsSigninProfile(profile) + ? "sign-in" + : "lock screen"; + + // TODO(b/290262513): See also comment below -- if the continuation should + // be a cancelation, this check is unnecessary and we can just fall-through + // without treating signin profiles differently for service workers. + if (!web_contents) { + LOG(WARNING) << "Client cert requested in " << profile_name + << " profile from service worker. This is not supported."; + // Return without calling anything on `delegate`. This results in the + // `delegate` being deleted, which implicitly calls to cancel the request. + return base::OnceClosure(); + } + + content::StoragePartition* storage_partition = + profile->GetStoragePartition(web_contents->GetSiteInstance()); + auto* signin_partition_manager = + ash::login::SigninPartitionManager::Factory::GetForBrowserContext( + profile); + if (!signin_partition_manager->IsCurrentSigninStoragePartition( + storage_partition)) { + LOG(WARNING) << "Client cert requested in " << profile_name + << " profile in wrong context."; + // Continue without client certificate. We do this to mimic the case of no + // client certificate being present in the profile's certificate store. + // TODO(b/290262513): Should this be a cancel? Selecting "no certificate" + // is a sticky decision. + delegate->ContinueWithCertificate(nullptr, nullptr); + return base::OnceClosure(); + } + VLOG(1) << "Client cert requested in " << profile_name << " profile."; + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + + GURL requesting_url = chrome::enterprise_util::GetRequestingUrl( + cert_request_info->host_and_port); + DCHECK(requesting_url.is_valid()) << "Invalid URL string: " << requesting_url; + + net::ClientCertIdentityList matching_certificates, nonmatching_certificates; + chrome::enterprise_util::AutoSelectCertificates( + profile, requesting_url, std::move(client_certs), &matching_certificates, + &nonmatching_certificates); + + if (matching_certificates.size() == 1 || + (matching_certificates.size() > 1 && + !ShouldPromptOnMultipleMatchingCertificates(profile))) { + // Always take the first certificate, even if multiple ones matched - + // there's no other criteria available for tie-breaking, and user prompts + // aren't enabled. + std::unique_ptr auto_selected_identity = + std::move(matching_certificates[0]); + // The callback will own |auto_selected_identity| and |delegate|, keeping + // them alive until after ContinueWithCertificate is called. + scoped_refptr cert = + auto_selected_identity->certificate(); + net::ClientCertIdentity::SelfOwningAcquirePrivateKey( + std::move(auto_selected_identity), + base::BindOnce( + &content::ClientCertificateDelegate::ContinueWithCertificate, + std::move(delegate), std::move(cert))); + return base::OnceClosure(); + } + + // At this point, we're going to either a) continue without a valid + // certificate (if we're not allowed to prompt) or b) show the picker for the + // user to select a valid cert. Only do this if the requestor has a valid + // WebContents. In the case of a), we want to preserve consistency (so that + // requests always fail or succeed across different platforms and contexts), + // and for b), we don't want to pop up UI for background requests like + // service workers (where there's no visual context to the user). + if (!web_contents) { + // Return without calling anything on `delegate`. This results in the + // `delegate` being deleted, which implicitly calls to cancel the request. + return base::OnceClosure(); + } + + if (matching_certificates.empty() && + !CanPromptWithNonmatchingCertificates(profile)) { + LOG(WARNING) << "No client cert matched by policy and user selection is " + "not allowed."; + // Continue without client certificate. We do this to mimic the case of no + // client certificate being present in the profile's certificate store. + delegate->ContinueWithCertificate(nullptr, nullptr); + return base::OnceClosure(); + } + + // Note: It can happen that both lists are empty, still the selector needs to + // be shown - see the comment in SSLClientAuthHandler::DidGetClientCerts() + // about platforms not having a client cert store. + net::ClientCertIdentityList client_cert_choices = + !matching_certificates.empty() ? std::move(matching_certificates) + : std::move(nonmatching_certificates); + + return chrome::ShowSSLClientCertificateSelector( + web_contents, cert_request_info, std::move(client_cert_choices), + std::move(delegate)); +} + +content::MediaObserver* ChromeContentBrowserClient::GetMediaObserver() { + return MediaCaptureDevicesDispatcher::GetInstance(); +} + +content::FeatureObserverClient* +ChromeContentBrowserClient::GetFeatureObserverClient() { + return ChromeBrowserMainExtraPartsPerformanceManager::GetInstance() + ->GetFeatureObserverClient(); +} + +bool ChromeContentBrowserClient::CanCreateWindow( + RenderFrameHost* opener, + const GURL& opener_url, + const GURL& opener_top_level_frame_url, + const url::Origin& source_origin, + content::mojom::WindowContainerType container_type, + const GURL& target_url, + const content::Referrer& referrer, + const std::string& frame_name, + WindowOpenDisposition disposition, + const blink::mojom::WindowFeatures& features, + bool user_gesture, + bool opener_suppressed, + bool* no_javascript_access) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(opener); + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(opener); + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + DCHECK(profile); + *no_javascript_access = false; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Try to intercept the request and open the URL with Lacros. + if (ash::TryOpenUrl(target_url, disposition)) { + return false; + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + + // If the opener is trying to create a background window but doesn't have + // the appropriate permission, fail the attempt. + if (container_type == content::mojom::WindowContainerType::BACKGROUND) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto* process_map = extensions::ProcessMap::Get(profile); + auto* registry = extensions::ExtensionRegistry::Get(profile); + if (!URLHasExtensionPermission(process_map, registry, opener_url, + opener->GetProcess()->GetID(), + APIPermissionID::kBackground)) { + return false; + } + + // Note: this use of GetExtensionOrAppByURL is safe but imperfect. It may + // return a recently installed Extension even if this CanCreateWindow call + // was made by an old copy of the page in a normal web process. That's ok, + // because the permission check above would have caused an early return + // already. We must use the full URL to find hosted apps, though, and not + // just the origin. + const Extension* extension = + registry->enabled_extensions().GetExtensionOrAppByURL(opener_url); + if (extension && !extensions::BackgroundInfo::AllowJSAccess(extension)) + *no_javascript_access = true; +#endif + + return true; + } + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (extensions::WebViewRendererState::GetInstance()->IsGuest( + opener->GetProcess()->GetID())) { + return true; + } + + if (target_url.SchemeIs(extensions::kExtensionScheme)) { + // Intentionally duplicating |registry| code from above because we want to + // reduce calls to retrieve them as this function is a SYNC IPC handler. + auto* registry = extensions::ExtensionRegistry::Get(profile); + const Extension* extension = + registry->enabled_extensions().GetExtensionOrAppByURL(target_url); + if (extension && extension->is_platform_app()) { + // window.open() may not be used to load v2 apps in a regular tab. + return false; + } + } +#endif + + DCHECK(!prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + web_contents)); + + BlockedWindowParams blocked_params( + target_url, source_origin, opener->GetSiteInstance(), referrer, + frame_name, disposition, features, user_gesture, opener_suppressed); + NavigateParams nav_params = + blocked_params.CreateNavigateParams(opener->GetProcess(), web_contents); + return !blocked_content::ConsiderForPopupBlocking(disposition) || + blocked_content::MaybeBlockPopup( + web_contents, &opener_top_level_frame_url, + (*g_popup_navigation_delegate_factory)(std::move(nav_params)), + nullptr /*=open_url_params*/, blocked_params.features(), + HostContentSettingsMapFactory::GetForProfile(profile)) != nullptr; +} + +content::SpeechRecognitionManagerDelegate* +ChromeContentBrowserClient::CreateSpeechRecognitionManagerDelegate() { + return new speech::ChromeSpeechRecognitionManagerDelegate(); +} + +#if BUILDFLAG(IS_CHROMEOS_ASH) +content::TtsControllerDelegate* +ChromeContentBrowserClient::GetTtsControllerDelegate() { + return TtsControllerDelegateImpl::GetInstance(); +} +#endif + +void ChromeContentBrowserClient::MaybeOverrideManifest( + content::RenderFrameHost* render_frame_host, + blink::mojom::ManifestPtr& manifest) { +#if !BUILDFLAG(IS_ANDROID) + Profile* profile = + Profile::FromBrowserContext(render_frame_host->GetBrowserContext()); + auto* provider = web_app::WebAppProvider::GetForWebApps(profile); + if (provider) + provider->policy_manager().MaybeOverrideManifest(render_frame_host, + manifest); +#endif +} + +content::TtsPlatform* ChromeContentBrowserClient::GetTtsPlatform() { +#if !BUILDFLAG(IS_ANDROID) + content::TtsController::GetInstance()->SetTtsEngineDelegate( + TtsExtensionEngine::GetInstance()); +#endif +#if BUILDFLAG(IS_CHROMEOS_ASH) + return TtsPlatformImplChromeOs::GetInstance(); +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + return TtsPlatformImplLacros::GetInstance(); +#else + return nullptr; +#endif +} + +void ChromeContentBrowserClient::OverrideWebkitPrefs( + WebContents* web_contents, + WebPreferences* web_prefs) { + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + PrefService* prefs = profile->GetPrefs(); + +// Fill font preferences. These are not registered on Android +// - http://crbug.com/308033, http://crbug.com/696364. +#if !BUILDFLAG(IS_ANDROID) + // Enabling the FontFamilyCache needs some KeyedService that might not be + // available for some irregular profiles, like the System Profile. + if (!AreKeyedServicesDisabledForProfileByDefault(profile)) { + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitStandardFontFamilyMap, + &web_prefs->standard_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitFixedFontFamilyMap, + &web_prefs->fixed_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitSerifFontFamilyMap, + &web_prefs->serif_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitSansSerifFontFamilyMap, + &web_prefs->sans_serif_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitCursiveFontFamilyMap, + &web_prefs->cursive_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, + prefs::kWebKitFantasyFontFamilyMap, + &web_prefs->fantasy_font_family_map); + FontFamilyCache::FillFontFamilyMap(profile, prefs::kWebKitMathFontFamilyMap, + &web_prefs->math_font_family_map); + } + + web_prefs->default_font_size = + prefs->GetInteger(prefs::kWebKitDefaultFontSize); + web_prefs->default_fixed_font_size = + prefs->GetInteger(prefs::kWebKitDefaultFixedFontSize); + web_prefs->minimum_font_size = + prefs->GetInteger(prefs::kWebKitMinimumFontSize); + web_prefs->minimum_logical_font_size = + prefs->GetInteger(prefs::kWebKitMinimumLogicalFontSize); +#endif + + web_prefs->default_encoding = prefs->GetString(prefs::kDefaultCharset); + + web_prefs->dom_paste_enabled = + prefs->GetBoolean(prefs::kWebKitDomPasteEnabled); + web_prefs->javascript_can_access_clipboard = + prefs->GetBoolean(prefs::kWebKitJavascriptCanAccessClipboard); + web_prefs->tabs_to_links = prefs->GetBoolean(prefs::kWebkitTabsToLinks); + + if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled)) + web_prefs->javascript_enabled = false; + + if (!prefs->GetBoolean(prefs::kWebKitWebSecurityEnabled)) + web_prefs->web_security_enabled = false; + + if (!prefs->GetBoolean(prefs::kWebKitPluginsEnabled)) + web_prefs->plugins_enabled = false; + web_prefs->loads_images_automatically = + prefs->GetBoolean(prefs::kWebKitLoadsImagesAutomatically); + + if (prefs->GetBoolean(prefs::kDisable3DAPIs)) { + web_prefs->webgl1_enabled = false; + web_prefs->webgl2_enabled = false; + } + + web_prefs->allow_running_insecure_content = + prefs->GetBoolean(prefs::kWebKitAllowRunningInsecureContent); +#if BUILDFLAG(IS_ANDROID) + web_prefs->font_scale_factor = static_cast( + prefs->GetDouble(browser_ui::prefs::kWebKitFontScaleFactor)); + web_prefs->text_size_contrast_factor = + prefs->GetInteger(prefs::kAccessibilityTextSizeContrastFactor); + web_prefs->force_enable_zoom = + prefs->GetBoolean(browser_ui::prefs::kWebKitForceEnableZoom); + web_prefs->font_weight_adjustment = + prefs->GetInteger(prefs::kAccessibilityFontWeightAdjustment); +#endif + web_prefs->force_dark_mode_enabled = + prefs->GetBoolean(prefs::kWebKitForceDarkModeEnabled); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + web_prefs->always_show_focus = + prefs->GetBoolean(ash::prefs::kAccessibilityFocusHighlightEnabled); +#else + if (features::IsAccessibilityFocusHighlightEnabled()) { + web_prefs->always_show_focus = + prefs->GetBoolean(prefs::kAccessibilityFocusHighlightEnabled); + } +#endif + +#if BUILDFLAG(IS_ANDROID) + web_prefs->password_echo_enabled = + prefs->GetBoolean(prefs::kWebKitPasswordEchoEnabled); +#else + web_prefs->password_echo_enabled = false; +#endif + + web_prefs->text_areas_are_resizable = + prefs->GetBoolean(prefs::kWebKitTextAreasAreResizable); + web_prefs->hyperlink_auditing_enabled = + prefs->GetBoolean(prefs::kEnableHyperlinkAuditing); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + std::string image_animation_policy = + prefs->GetString(prefs::kAnimationPolicy); + if (image_animation_policy == kAnimationPolicyOnce) { + web_prefs->animation_policy = + blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAnimateOnce; + } else if (image_animation_policy == kAnimationPolicyNone) { + web_prefs->animation_policy = + blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyNoAnimation; + } else { + web_prefs->animation_policy = + blink::mojom::ImageAnimationPolicy::kImageAnimationPolicyAllowed; + } +#endif + + // Make sure we will set the default_encoding with canonical encoding name. + web_prefs->default_encoding = + base::GetCanonicalEncodingNameByAliasName(web_prefs->default_encoding); + if (web_prefs->default_encoding.empty()) { + prefs->ClearPref(prefs::kDefaultCharset); + web_prefs->default_encoding = prefs->GetString(prefs::kDefaultCharset); + } + DCHECK(!web_prefs->default_encoding.empty()); + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnablePotentiallyAnnoyingSecurityFeatures)) { + web_prefs->disable_reading_from_canvas = true; + web_prefs->strict_mixed_content_checking = true; + web_prefs->strict_powerful_feature_restrictions = true; + } + + // See crbug.com/1238157: the Native Client flag (chrome://flags/#enable-nacl) + // can be manually re-enabled. In that case, we also need to return the full + // plugins list, for compat. + web_prefs->allow_non_empty_navigator_plugins |= + base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl); + + web_prefs->data_saver_enabled = IsDataSaverEnabled(profile); + + if (web_contents) { +#if BUILDFLAG(IS_ANDROID) + auto* delegate = TabAndroid::FromWebContents(web_contents) + ? static_cast( + web_contents->GetDelegate()) + : nullptr; + if (delegate) { + web_prefs->embedded_media_experience_enabled = + delegate->ShouldEnableEmbeddedMediaExperience(); + + web_prefs->picture_in_picture_enabled = + delegate->IsPictureInPictureEnabled(); + + web_prefs->force_dark_mode_enabled = + delegate->IsForceDarkWebContentEnabled(); + + web_prefs->modal_context_menu = delegate->IsModalContextMenu(); + } +#endif // BUILDFLAG(IS_ANDROID) + + // web_app_scope value is platform specific. +#if BUILDFLAG(IS_ANDROID) + if (delegate) + web_prefs->web_app_scope = delegate->GetManifestScope(); +#elif BUILDFLAG(ENABLE_EXTENSIONS) + { + web_prefs->web_app_scope = GURL(); + // Set |web_app_scope| based on the app associated with the app window if + // any. Note that the app associated with the window never changes, even + // if the app navigates off scope. This is not a problem because we still + // want to use the scope of the app associated with the window, not the + // WebContents. + Browser* browser = chrome::FindBrowserWithTab(web_contents); + if (browser && browser->app_controller()) { + web_app::WebAppProvider* const web_app_provider = + web_app::WebAppProvider::GetForLocalAppsUnchecked(profile); + const webapps::AppId& app_id = browser->app_controller()->app_id(); + const web_app::WebAppRegistrar& registrar = + web_app_provider->registrar_unsafe(); + if (registrar.IsLocallyInstalled(app_id)) + web_prefs->web_app_scope = registrar.GetAppScope(app_id); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + auto* system_app = browser->app_controller()->system_app(); + if (system_app) { + web_prefs->allow_scripts_to_close_windows = + system_app->ShouldAllowScriptsToCloseWindows(); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + } + } +#endif + + web_prefs->immersive_mode_enabled = vr::VrTabHelper::IsInVr(web_contents); + } + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableLazyLoading)) { + web_prefs->lazy_load_enabled = false; + } else { + web_prefs->lazy_load_enabled = + !web_contents || !web_contents->GetDelegate() || + web_contents->GetDelegate()->ShouldAllowLazyLoad(); + } + + if (base::FeatureList::IsEnabled( + features::kNetworkQualityEstimatorWebHoldback)) { + std::string effective_connection_type_param = + base::GetFieldTrialParamValueByFeature( + features::kNetworkQualityEstimatorWebHoldback, + "web_effective_connection_type_override"); + + std::optional effective_connection_type = + net::GetEffectiveConnectionTypeForName(effective_connection_type_param); + DCHECK(effective_connection_type_param.empty() || + effective_connection_type); + if (effective_connection_type) { + DCHECK_NE(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, + effective_connection_type.value()); + web_prefs->network_quality_estimator_web_holdback = + static_cast( + effective_connection_type.value()); + } + } + + web_prefs->autoplay_policy = GetAutoplayPolicyForWebContents(web_contents); +#if !BUILDFLAG(IS_ANDROID) + web_prefs->require_transient_activation_for_get_display_media = + capture_policy::IsTransientActivationRequiredForGetDisplayMedia( + web_contents); + web_prefs->require_transient_activation_for_show_file_or_directory_picker = + IsFileOrDirectoryPickerWithoutGestureAllowed(web_contents); +#endif // !BUILDFLAG(IS_ANDROID) + web_prefs->require_transient_activation_for_html_fullscreen = + IsTransientActivationRequiredForHtmlFullscreen( + web_contents->GetPrimaryMainFrame()); + +#if BUILDFLAG(IS_CHROMEOS) + web_prefs->subapps_apis_require_user_gesture_and_authorization = + SubAppsAPIsRequireUserGestureAndAuthorization(web_contents); +#endif // BUILDFLAG(IS_CHROMEOS) + + switch (GetWebTheme()->GetPreferredContrast()) { + case ui::NativeTheme::PreferredContrast::kNoPreference: + web_prefs->preferred_contrast = + blink::mojom::PreferredContrast::kNoPreference; + break; + case ui::NativeTheme::PreferredContrast::kMore: + web_prefs->preferred_contrast = blink::mojom::PreferredContrast::kMore; + break; + case ui::NativeTheme::PreferredContrast::kLess: + web_prefs->preferred_contrast = blink::mojom::PreferredContrast::kLess; + break; + case ui::NativeTheme::PreferredContrast::kCustom: + web_prefs->preferred_contrast = blink::mojom::PreferredContrast::kCustom; + break; + } + + web_prefs->in_forced_colors = GetWebTheme()->InForcedColorsMode(); + + UpdatePreferredColorScheme( + web_prefs, + web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(), + web_contents, GetWebTheme()); + + web_prefs->translate_service_available = TranslateService::IsAvailable(prefs); + + std::optional style = + captions::GetCaptionStyleFromUserSettings(prefs, + true /* record_metrics */); + if (style) { + web_prefs->text_track_background_color = style->background_color; + web_prefs->text_track_text_color = style->text_color; + web_prefs->text_track_text_size = style->text_size; + web_prefs->text_track_text_shadow = style->text_shadow; + web_prefs->text_track_font_family = style->font_family; + web_prefs->text_track_font_variant = style->font_variant; + web_prefs->text_track_window_color = style->window_color; + web_prefs->text_track_window_radius = style->window_radius; + } + +#if BUILDFLAG(IS_ANDROID) + // If the pref is not set, the default value (true) will be used: + web_prefs->webxr_immersive_ar_allowed = + prefs->GetBoolean(prefs::kWebXRImmersiveArEnabled); +#endif + + // Only set `databases_enabled` if disabled. Otherwise check blink::feature + // settings for Origin Trial and Chrome flag settings, or prefs setting + // for Enterprise Policy. + web_prefs->databases_enabled = + !web_prefs->databases_enabled + ? false + : (base::FeatureList::IsEnabled(blink::features::kWebSQLAccess) || + prefs->GetBoolean(storage::kWebSQLAccess)); + +#if BUILDFLAG(IS_FUCHSIA) + // Disable WebSQL support since it is being removed from the web platform + // and does not work. See crbug.com/1317431. + web_prefs->databases_enabled = false; + + // TODO(crbug.com/1311019): Implement WebAuthn integration and remove. + web_prefs->disable_webauthn = true; +#endif + + for (auto& parts : extra_parts_) { + parts->OverrideWebkitPrefs(web_contents, web_prefs); + } + + web_prefs->prefers_default_scrollbar_styles = + prefs->GetBoolean(prefs::kPrefersDefaultScrollbarStyles); +} + +bool ChromeContentBrowserClientParts::OverrideWebPreferencesAfterNavigation( + WebContents* web_contents, + WebPreferences* web_prefs) { + return false; +} + +bool ChromeContentBrowserClient::OverrideWebPreferencesAfterNavigation( + WebContents* web_contents, + WebPreferences* web_prefs) { + bool prefs_changed = false; + + const auto autoplay_policy = GetAutoplayPolicyForWebContents(web_contents); + prefs_changed |= (web_prefs->autoplay_policy != autoplay_policy); + web_prefs->autoplay_policy = autoplay_policy; + +#if !BUILDFLAG(IS_ANDROID) + const bool require_transient_activation_for_get_display_media = + capture_policy::IsTransientActivationRequiredForGetDisplayMedia( + web_contents); + prefs_changed |= + (web_prefs->require_transient_activation_for_get_display_media != + require_transient_activation_for_get_display_media); + web_prefs->require_transient_activation_for_get_display_media = + require_transient_activation_for_get_display_media; + + const bool require_transient_activation_for_show_file_or_directory_picker = + IsFileOrDirectoryPickerWithoutGestureAllowed(web_contents); + prefs_changed |= + (web_prefs + ->require_transient_activation_for_show_file_or_directory_picker != + require_transient_activation_for_show_file_or_directory_picker); + web_prefs->require_transient_activation_for_show_file_or_directory_picker = + require_transient_activation_for_show_file_or_directory_picker; +#endif // !BUILDFLAG(IS_ANDROID) + const bool require_transient_activation_for_html_fullscreen = + IsTransientActivationRequiredForHtmlFullscreen( + web_contents->GetPrimaryMainFrame()); + prefs_changed |= + (web_prefs->require_transient_activation_for_html_fullscreen != + require_transient_activation_for_html_fullscreen); + web_prefs->require_transient_activation_for_html_fullscreen = + require_transient_activation_for_html_fullscreen; + + for (auto& parts : extra_parts_) { + prefs_changed |= + parts->OverrideWebPreferencesAfterNavigation(web_contents, web_prefs); + } + + prefs_changed |= GetWebTheme()->InForcedColorsMode(); + + prefs_changed |= + UpdatePreferredColorScheme(web_prefs, web_contents->GetLastCommittedURL(), + web_contents, GetWebTheme()); + +#if BUILDFLAG(IS_ANDROID) + auto* delegate = TabAndroid::FromWebContents(web_contents) + ? static_cast( + web_contents->GetDelegate()) + : nullptr; + if (delegate) { + bool force_dark_mode_new_state = delegate->IsForceDarkWebContentEnabled(); + prefs_changed |= + (web_prefs->force_dark_mode_enabled != force_dark_mode_new_state); + web_prefs->force_dark_mode_enabled = force_dark_mode_new_state; + } +#endif + +#if BUILDFLAG(IS_CHROMEOS) + const bool subapps_apis_require_user_gesture_and_authorization = + SubAppsAPIsRequireUserGestureAndAuthorization(web_contents); + prefs_changed |= + (web_prefs->subapps_apis_require_user_gesture_and_authorization != + subapps_apis_require_user_gesture_and_authorization); +#endif // BUILDFLAG(IS_CHROMEOS) + + return prefs_changed; +} + +void ChromeContentBrowserClient::BrowserURLHandlerCreated( + BrowserURLHandler* handler) { + // The group policy NTP URL handler must be registered before the other NTP + // URL handlers below. Also register it before the "parts" handlers, so the + // NTP policy takes precedence over extensions that override the NTP. + handler->AddHandlerPair(&HandleNewTabPageLocationOverride, + BrowserURLHandler::null_handler()); + + for (auto& part : extra_parts_) { + part->BrowserURLHandlerCreated(handler); + } + + // Handler to rewrite chrome://about and chrome://sync URLs. + handler->AddHandlerPair(&HandleChromeAboutAndChromeSyncRewrite, + BrowserURLHandler::null_handler()); + +#if BUILDFLAG(IS_ANDROID) + // Handler to rewrite chrome://newtab on Android. + handler->AddHandlerPair(&chrome::android::HandleAndroidNativePageURL, + BrowserURLHandler::null_handler()); +#else // BUILDFLAG(IS_ANDROID) + // Handler to rewrite chrome://newtab for InstantExtended. + handler->AddHandlerPair(&search::HandleNewTabURLRewrite, + &search::HandleNewTabURLReverseRewrite); +#endif // BUILDFLAG(IS_ANDROID) + + // chrome: & friends. + handler->AddHandlerPair(&ChromeContentBrowserClient::HandleWebUI, + &ChromeContentBrowserClient::HandleWebUIReverse); +} + +base::FilePath ChromeContentBrowserClient::GetDefaultDownloadDirectory() { + return DownloadPrefs::GetDefaultDownloadDirectory(); +} + +std::string ChromeContentBrowserClient::GetDefaultDownloadName() { + return l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME); +} + +base::FilePath ChromeContentBrowserClient::GetShaderDiskCacheDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir.Append(FILE_PATH_LITERAL("ShaderCache")); +} + +base::FilePath ChromeContentBrowserClient::GetGrShaderDiskCacheDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir.Append(FILE_PATH_LITERAL("GrShaderCache")); +} + +base::FilePath ChromeContentBrowserClient::GetGraphiteDawnDiskCacheDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + return user_data_dir.Append(FILE_PATH_LITERAL("GraphiteDawnCache")); +} + +base::FilePath ChromeContentBrowserClient::GetNetLogDefaultDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir; +} + +base::FilePath ChromeContentBrowserClient::GetFirstPartySetsDirectory() { + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + return user_data_dir; +} + +std::optional +ChromeContentBrowserClient::GetLocalTracesDirectory() { + base::FilePath user_data_dir; + if (!base::PathService::Get(chrome::DIR_LOCAL_TRACES, &user_data_dir)) { + return std::nullopt; + } + DCHECK(!user_data_dir.empty()); + return user_data_dir; +} + +void ChromeContentBrowserClient::DidCreatePpapiPlugin( + content::BrowserPpapiHost* browser_host) { +#if BUILDFLAG(ENABLE_PLUGINS) + ChromeContentBrowserClientPluginsPart::DidCreatePpapiPlugin(browser_host); +#endif +} + +content::BrowserPpapiHost* +ChromeContentBrowserClient::GetExternalBrowserPpapiHost(int plugin_process_id) { +#if BUILDFLAG(ENABLE_NACL) + content::BrowserChildProcessHostIterator iter(PROCESS_TYPE_NACL_LOADER); + while (!iter.Done()) { + nacl::NaClProcessHost* host = + static_cast(iter.GetDelegate()); + if (host->process() && host->process()->GetData().id == plugin_process_id) { + // Found the plugin. + return host->browser_ppapi_host(); + } + ++iter; + } +#endif + return nullptr; +} + +bool ChromeContentBrowserClient::AllowPepperSocketAPI( + content::BrowserContext* browser_context, + const GURL& url, + bool private_api, + const content::SocketPermissionRequest* params) { +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientPluginsPart::AllowPepperSocketAPI( + browser_context, url, private_api, params); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::IsPepperVpnProviderAPIAllowed( + content::BrowserContext* browser_context, + const GURL& url) { +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientPluginsPart::IsPepperVpnProviderAPIAllowed( + browser_context, url); +#else + return false; +#endif +} + +std::unique_ptr +ChromeContentBrowserClient::GetVpnServiceProxy( + content::BrowserContext* browser_context) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart::GetVpnServiceProxy( + browser_context); +#else + return nullptr; +#endif +} + +std::unique_ptr +ChromeContentBrowserClient::CreateSelectFilePolicy(WebContents* web_contents) { + return std::make_unique(web_contents); +} + +void ChromeContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem( + std::vector* additional_allowed_schemes) { + ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem( + additional_allowed_schemes); + additional_allowed_schemes->push_back(content::kChromeDevToolsScheme); + additional_allowed_schemes->push_back(content::kChromeUIScheme); + additional_allowed_schemes->push_back(content::kChromeUIUntrustedScheme); + for (auto& extra_part : extra_parts_) { + extra_part->GetAdditionalAllowedSchemesForFileSystem( + additional_allowed_schemes); + } +} + +void ChromeContentBrowserClient::GetSchemesBypassingSecureContextCheckAllowlist( + std::set* schemes) { + *schemes = secure_origin_allowlist::GetSchemesBypassingSecureContextCheck(); +} + +void ChromeContentBrowserClient::GetURLRequestAutoMountHandlers( + std::vector* handlers) { + for (auto& part : extra_parts_) { + part->GetURLRequestAutoMountHandlers(handlers); + } +} + +void ChromeContentBrowserClient::GetAdditionalFileSystemBackends( + content::BrowserContext* browser_context, + const base::FilePath& storage_partition_path, + std::vector>* + additional_backends) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + storage::ExternalMountPoints* external_mount_points = + browser_context->GetMountPoints(); + DCHECK(external_mount_points); + auto backend = std::make_unique( + Profile::FromBrowserContext(browser_context), + ash::file_system_provider::BackendDelegate::MakeUnique(), + std::make_unique( + storage_partition_path), + std::make_unique(), + std::make_unique(), + std::make_unique( + Profile::FromBrowserContext(browser_context)), + std::make_unique( + Profile::FromBrowserContext(browser_context)), + external_mount_points, storage::ExternalMountPoints::GetSystemInstance()); + backend->AddSystemMountPoints(); + DCHECK(backend->CanHandleType(storage::kFileSystemTypeExternal)); + additional_backends->push_back(std::move(backend)); +#endif + + for (auto& part : extra_parts_) { + part->GetAdditionalFileSystemBackends( + browser_context, storage_partition_path, + GetQuarantineConnectionCallback(), additional_backends); + } +} + +#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) +void ChromeContentBrowserClient::GetAdditionalMappedFilesForChildProcess( + const base::CommandLine& command_line, + int child_process_id, + PosixFileDescriptorInfo* mappings) { +#if BUILDFLAG(IS_ANDROID) + base::MemoryMappedFile::Region region; + int fd = ui::GetMainAndroidPackFd(®ion); + mappings->ShareWithRegion(kAndroidUIResourcesPakDescriptor, fd, region); + + // For Android: Native resources for DFMs should only be used by the browser + // process. Their file descriptors and memory mapped file regions are not + // passed to child processes. + + fd = ui::GetCommonResourcesPackFd(®ion); + mappings->ShareWithRegion(kAndroidChrome100PercentPakDescriptor, fd, region); + + fd = ui::GetLocalePackFd(®ion); + mappings->ShareWithRegion(kAndroidLocalePakDescriptor, fd, region); + + // Optional secondary locale .pak file. + fd = ui::GetSecondaryLocalePackFd(®ion); + if (fd != -1) { + mappings->ShareWithRegion(kAndroidSecondaryLocalePakDescriptor, fd, region); + } + + base::FilePath app_data_path; + base::PathService::Get(base::DIR_ANDROID_APP_DATA, &app_data_path); + DCHECK(!app_data_path.empty()); +#endif // BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + int crash_signal_fd = GetCrashSignalFD(command_line); + if (crash_signal_fd >= 0) { + mappings->Share(kCrashDumpSignal, crash_signal_fd); + } +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // Map startup and post-login parameter files to child processes in Lacros. + // The FD numbers are passed via command line switches in + // |AppendExtraCommandLineSwitches|. + // + // NOTE: the Zygote process requires special handling. + // It doesn't need the post-login parameters, so it can be fully launched at + // login screen. Also, serializing startup data early in the initialization + // process requires temporarily initializing Mojo. That's handled in the + // |LaunchZygoteHelper| function in |content_main_runner_impl.cc|. Here, we + // deal with all other type of processes. + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + if (process_type != switches::kZygoteProcess) { + base::ScopedFD cros_startup_fd = + chromeos::BrowserInitParams::CreateStartupData(); + if (cros_startup_fd.is_valid()) { + constexpr int kStartupDataFD = + kCrosStartupDataDescriptor + base::GlobalDescriptors::kBaseDescriptor; + mappings->Transfer(kStartupDataFD, std::move(cros_startup_fd)); + } + + if (chromeos::IsLaunchedWithPostLoginParams()) { + base::ScopedFD cros_postlogin_fd = + chromeos::BrowserPostLoginParams::CreatePostLoginData(); + if (cros_postlogin_fd.is_valid()) { + constexpr int kPostLoginDataFD = + kCrosPostLoginDataDescriptor + + base::GlobalDescriptors::kBaseDescriptor; + mappings->Transfer(kPostLoginDataFD, std::move(cros_postlogin_fd)); + } + } + } +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) +} +#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +void ChromeContentBrowserClient::GetAdditionalMappedFilesForZygote( + base::CommandLine* command_line, + PosixFileDescriptorInfo* mappings) { + // Create the file descriptor for Cros startup data and pass it. + // This FD will be used to obtain BrowserInitParams in Zygote process. + // Note that this requires Mojo, but Mojo cannot be fully initialized this + // due to dependencies on base::FeatureList. So we also temporarily initialize + // Mojo and then shut it down immediately after preparing the FD. This is + // inexpensive, an the features which control Mojo behavior aren't relevant + // for this operation. + // + // TODO(https://crbug.com/1299283): This will need to be changed before + // MojoIpcz experimentation can happen on Lacros, as it results in + // inconsistent MojoIpcz feature status across Mojo initializations. + mojo::core::Init(); + base::ScopedFD cros_startup_fd = + chromeos::BrowserInitParams::CreateStartupData(); + mojo::core::ShutDown(); + + if (cros_startup_fd.is_valid()) { + constexpr int kStartupDataFD = + kCrosStartupDataDescriptor + base::GlobalDescriptors::kBaseDescriptor; + command_line->AppendSwitchASCII(chromeos::switches::kCrosStartupDataFD, + base::NumberToString(kStartupDataFD)); + mappings->Transfer(kStartupDataFD, std::move(cros_startup_fd)); + } +} +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) + +#if BUILDFLAG(IS_WIN) +std::wstring ChromeContentBrowserClient::GetAppContainerSidForSandboxType( + sandbox::mojom::Sandbox sandbox_type, + AppContainerFlags flags) { + // TODO(wfh): Add support for more process types here. crbug.com/499523 + switch (sandbox_type) { + case sandbox::mojom::Sandbox::kRenderer: + if (flags & AppContainerFlags::kAppContainerFlagDisableAppContainer) + return std::wstring(); + return std::wstring(install_static::GetSandboxSidPrefix()) + L"129201922"; + case sandbox::mojom::Sandbox::kUtility: + return std::wstring(); + case sandbox::mojom::Sandbox::kGpu: + return std::wstring(); + case sandbox::mojom::Sandbox::kOnDeviceModelExecution: + return std::wstring(); +#if BUILDFLAG(ENABLE_PPAPI) + case sandbox::mojom::Sandbox::kPpapi: +#endif + case sandbox::mojom::Sandbox::kNoSandbox: + case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges: + case sandbox::mojom::Sandbox::kXrCompositing: + case sandbox::mojom::Sandbox::kNetwork: + case sandbox::mojom::Sandbox::kCdm: +#if BUILDFLAG(ENABLE_OOP_PRINTING) + case sandbox::mojom::Sandbox::kPrintBackend: +#endif + case sandbox::mojom::Sandbox::kPrintCompositor: + case sandbox::mojom::Sandbox::kAudio: +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + case sandbox::mojom::Sandbox::kScreenAI: +#endif + case sandbox::mojom::Sandbox::kSpeechRecognition: + case sandbox::mojom::Sandbox::kPdfConversion: + case sandbox::mojom::Sandbox::kService: + case sandbox::mojom::Sandbox::kServiceWithJit: + case sandbox::mojom::Sandbox::kIconReader: + case sandbox::mojom::Sandbox::kMediaFoundationCdm: + case sandbox::mojom::Sandbox::kWindowsSystemProxyResolver: + // Should never reach here. + CHECK(0); + return std::wstring(); + } +} + +bool ChromeContentBrowserClient::IsRendererAppContainerDisabled() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + PrefService* local_state = g_browser_process->local_state(); + const PrefService::Preference* pref = + local_state->FindPreference(prefs::kRendererAppContainerEnabled); + // App Container is disabled if managed pref is set to false. + if (pref && pref->IsManaged() && !pref->GetValue()->GetBool()) + return true; + + return false; +} + +std::wstring +ChromeContentBrowserClient::GetLPACCapabilityNameForNetworkService() { + // Use a different LPAC capability name for each Chrome channel so network + // service data between hannels is isolated. + version_info::Channel channel = chrome::GetChannel(); + switch (channel) { + case version_info::Channel::CANARY: + return std::wstring(L"lpacChromeCanaryNetworkSandbox"); + case version_info::Channel::BETA: + return std::wstring(L"lpacChromeBetaNetworkSandbox"); + case version_info::Channel::DEV: + return std::wstring(L"lpacChromeDevNetworkSandbox"); + case version_info::Channel::STABLE: + return std::wstring(L"lpacChromeStableNetworkSandbox"); + case version_info::Channel::UNKNOWN: + return std::wstring(L"lpacChromeNetworkSandbox"); + } +} + +// Note: Only use sparingly to add Chrome specific sandbox functionality here. +// Other code should reside in the content layer. Changes to this function +// should be reviewed by the security team. +bool ChromeContentBrowserClient::PreSpawnChild( + sandbox::TargetConfig* config, + sandbox::mojom::Sandbox sandbox_type, + ChildSpawnFlags flags) { + DCHECK(!config->IsConfigured()); +// Does not work under component build because all the component DLLs would need +// to be manually added and maintained. Does not work under ASAN build because +// ASAN has not yet fully initialized its instrumentation by the time the CIG +// intercepts run. +#if !defined(COMPONENT_BUILD) && !defined(ADDRESS_SANITIZER) + bool enforce_code_integrity = false; + + switch (sandbox_type) { + case sandbox::mojom::Sandbox::kRenderer: + enforce_code_integrity = + (flags & ChildSpawnFlags::kChildSpawnFlagRendererCodeIntegrity); + break; + case sandbox::mojom::Sandbox::kNetwork: + enforce_code_integrity = base::FeatureList::IsEnabled( + sandbox::policy::features::kNetworkServiceCodeIntegrity); + break; + case sandbox::mojom::Sandbox::kServiceWithJit: + enforce_code_integrity = true; + break; + case sandbox::mojom::Sandbox::kUtility: + case sandbox::mojom::Sandbox::kGpu: +#if BUILDFLAG(ENABLE_PPAPI) + case sandbox::mojom::Sandbox::kPpapi: +#endif + case sandbox::mojom::Sandbox::kNoSandbox: + case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges: + case sandbox::mojom::Sandbox::kXrCompositing: + case sandbox::mojom::Sandbox::kCdm: +#if BUILDFLAG(ENABLE_PRINTING) + case sandbox::mojom::Sandbox::kPrintBackend: +#endif + case sandbox::mojom::Sandbox::kPrintCompositor: +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + case sandbox::mojom::Sandbox::kScreenAI: +#endif + case sandbox::mojom::Sandbox::kAudio: + case sandbox::mojom::Sandbox::kOnDeviceModelExecution: + case sandbox::mojom::Sandbox::kSpeechRecognition: + case sandbox::mojom::Sandbox::kPdfConversion: + case sandbox::mojom::Sandbox::kService: + case sandbox::mojom::Sandbox::kIconReader: + case sandbox::mojom::Sandbox::kMediaFoundationCdm: + case sandbox::mojom::Sandbox::kWindowsSystemProxyResolver: + break; + } + + if (!enforce_code_integrity) + return true; + + // Only enable signing mitigation if launching from chrome.exe. + base::FilePath exe_path; + if (!base::PathService::Get(base::FILE_EXE, &exe_path)) + return true; + if (chrome::kBrowserProcessExecutableName != exe_path.BaseName().value()) + return true; + + sandbox::MitigationFlags mitigations = config->GetProcessMitigations(); + mitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS; + sandbox::ResultCode result = config->SetProcessMitigations(mitigations); + if (result != sandbox::SBOX_ALL_OK) + return false; + + // Allow loading Chrome's DLLs. + for (const auto* dll : {chrome::kBrowserResourcesDll, chrome::kElfDll}) { + result = config->AllowExtraDlls(GetModulePath(dll).value().c_str()); + if (result != sandbox::SBOX_ALL_OK) + return false; + } +#endif // !defined(COMPONENT_BUILD) && !defined(ADDRESS_SANITIZER) + return true; +} + +bool ChromeContentBrowserClient::IsRendererCodeIntegrityEnabled() { + // Emergency 'on switch' to re-enable the policy if force-disabling it causes + // issues. + if (base::FeatureList::IsEnabled( + sandbox::policy::features::kWinSboxForceRendererCodeIntegrity)) { + return true; + } + + PrefService* local_state = g_browser_process->local_state(); + + // If kWinSboxForceRendererCodeIntegrity is set to disabled, then code + // integrity defaults to enabled, unless specifically overridden by a policy + // controlled pref being set to false. + return !local_state->HasPrefPath(prefs::kRendererCodeIntegrityEnabled) || + local_state->GetBoolean(prefs::kRendererCodeIntegrityEnabled); +} + +// Note: Only use sparingly to add Chrome specific sandbox functionality here. +// Other code should reside in the content layer. Changes to this function +// should be reviewed by the security team. +bool ChromeContentBrowserClient::IsUtilityCetCompatible( + const std::string& utility_sub_type) { + if (utility_sub_type == chrome::mojom::UtilWin::Name_) + return false; + return true; +} + +void ChromeContentBrowserClient::SessionEnding( + std::optional control_type) { + chrome::SessionEnding(); +} + +bool ChromeContentBrowserClient::ShouldEnableAudioProcessHighPriority() { + return IsAudioProcessHighPriorityEnabled(); +} + +#endif // BUILDFLAG(IS_WIN) + +void ChromeContentBrowserClient:: + RegisterMojoBinderPoliciesForSameOriginPrerendering( + content::MojoBinderPolicyMap& policy_map) { + // Changes to `policy_map` should be made in + // RegisterChromeMojoBinderPoliciesForSameOriginPrerendering() which requires + // security review. + RegisterChromeMojoBinderPoliciesForSameOriginPrerendering(policy_map); +} + +void ChromeContentBrowserClient::RegisterMojoBinderPoliciesForPreview( + content::MojoBinderPolicyMap& policy_map) { + // Changes to `policy_map` should be made in + // RegisterChromeMojoBinderPoliciesForPreview() which requires security + // review. + RegisterChromeMojoBinderPoliciesForPreview(policy_map); +} + +void ChromeContentBrowserClient::OpenURL( + content::SiteInstance* site_instance, + const content::OpenURLParams& params, + base::OnceCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + content::BrowserContext* browser_context = site_instance->GetBrowserContext(); + +#if BUILDFLAG(IS_ANDROID) + ServiceTabLauncher::GetInstance()->LaunchTab(browser_context, params, + std::move(callback)); +#else + NavigateParams nav_params(Profile::FromBrowserContext(browser_context), + params.url, params.transition); + nav_params.FillNavigateParamsFromOpenURLParams(params); + + Navigate(&nav_params); + std::move(callback).Run(nav_params.navigated_or_inserted_contents); +#endif +} + +content::ControllerPresentationServiceDelegate* +ChromeContentBrowserClient::GetControllerPresentationServiceDelegate( + content::WebContents* web_contents) { + if (media_router::MediaRouterEnabled(web_contents->GetBrowserContext())) { + return media_router::PresentationServiceDelegateImpl:: + GetOrCreateForWebContents(web_contents); + } + return nullptr; +} + +content::ReceiverPresentationServiceDelegate* +ChromeContentBrowserClient::GetReceiverPresentationServiceDelegate( + content::WebContents* web_contents) { + if (media_router::MediaRouterEnabled(web_contents->GetBrowserContext())) { + // ReceiverPresentationServiceDelegateImpl exists only for WebContents + // created for offscreen presentations. The WebContents must belong to + // an incognito profile. + if (auto* impl = media_router::ReceiverPresentationServiceDelegateImpl:: + FromWebContents(web_contents)) { + DCHECK(web_contents->GetBrowserContext()->IsOffTheRecord()); + return impl; + } + } + return nullptr; +} + +void ChromeContentBrowserClient::AddPresentationObserver( + content::PresentationObserver* observer, + content::WebContents* web_contents) { + if (media_router::MediaRouterEnabled(web_contents->GetBrowserContext())) { + media_router::WebContentsPresentationManager::Get(web_contents) + ->AddObserver(observer); + } +} + +void ChromeContentBrowserClient::RemovePresentationObserver( + content::PresentationObserver* observer, + content::WebContents* web_contents) { + if (media_router::MediaRouterEnabled(web_contents->GetBrowserContext())) { + media_router::WebContentsPresentationManager::Get(web_contents) + ->RemoveObserver(observer); + } +} + +bool ChromeContentBrowserClient::AddPrivacySandboxAttestationsObserver( + content::PrivacySandboxAttestationsObserver* observer) { + return privacy_sandbox::PrivacySandboxAttestations::GetInstance() + ->AddObserver(observer); +} + +void ChromeContentBrowserClient::RemovePrivacySandboxAttestationsObserver( + content::PrivacySandboxAttestationsObserver* observer) { + privacy_sandbox::PrivacySandboxAttestations::GetInstance()->RemoveObserver( + observer); +} + +std::vector> +ChromeContentBrowserClient::CreateThrottlesForNavigation( + content::NavigationHandle* handle) { + std::vector> throttles; + + // MetricsNavigationThrottle requires that it runs before NavigationThrottles + // that may delay or cancel navigations, so only NavigationThrottles that + // don't delay or cancel navigations (e.g. throttles that are only observing + // callbacks without affecting navigation behavior) should be added before + // MetricsNavigationThrottle. + if (handle->IsInMainFrame()) { + throttles.push_back( + page_load_metrics::MetricsNavigationThrottle::Create(handle)); + } + +#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + MaybeAddThrottle( + SupervisedUserNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#endif + +#if BUILDFLAG(IS_ANDROID) + // TODO(davidben): This is insufficient to integrate with prerender properly. + // https://crbug.com/370595 + prerender::NoStatePrefetchContents* no_state_prefetch_contents = + prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + handle->GetWebContents()); + if (!no_state_prefetch_contents) { + MaybeAddThrottle( + navigation_interception::InterceptNavigationDelegate:: + MaybeCreateThrottleFor( + handle, navigation_interception::SynchronyMode::kAsync), + &throttles); + } + throttles.push_back(InterceptOMADownloadNavigationThrottle::Create(handle)); + +#if BUILDFLAG(DFMIFY_DEV_UI) + // If the DevUI DFM is already installed, then this is a no-op, except for the + // side effect of ensuring that the DevUI DFM is loaded. + MaybeAddThrottle(dev_ui::DevUiLoaderThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#endif // BUILDFLAG(DFMIFY_DEV_UI) + +#elif BUILDFLAG(ENABLE_EXTENSIONS) + // Redirect some navigations to apps that have registered matching URL + // handlers ('url_handlers' in the manifest). + MaybeAddThrottle( + PlatformAppNavigationRedirector::MaybeCreateThrottleFor(handle), + &throttles); +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Check if we need to add merge session throttle. This throttle will postpone + // loading of main frames. + if (handle->IsInMainFrame()) { + // Add interstitial page while merge session process (cookie reconstruction + // from OAuth2 refresh token in ChromeOS login) is still in progress while + // we are attempting to load a google property. + if (ash::merge_session_throttling_utils::ShouldAttachNavigationThrottle() && + !ash::merge_session_throttling_utils::AreAllSessionMergedAlready() && + handle->GetURL().SchemeIsHTTPOrHTTPS()) { + throttles.push_back(ash::MergeSessionNavigationThrottle::Create(handle)); + } + } +#endif + +#if BUILDFLAG(IS_CHROMEOS) + auto disabled_app_throttle = + apps::ChromeOsDisabledAppsThrottle::MaybeCreate(handle); + if (disabled_app_throttle) { + throttles.push_back(std::move(disabled_app_throttle)); + } +#endif // BUILDFLAG(IS_CHROMEOS) + +#if !BUILDFLAG(IS_ANDROID) + std::unique_ptr + link_capturing_delegate; + +#if BUILDFLAG(IS_CHROMEOS) + link_capturing_delegate = + std::make_unique(); +#else + link_capturing_delegate = + std::make_unique(); +#endif + std::unique_ptr url_to_apps_throttle = + apps::LinkCapturingNavigationThrottle::MaybeCreate( + handle, std::move(link_capturing_delegate)); + if (url_to_apps_throttle) { + throttles.push_back(std::move(url_to_apps_throttle)); + } +#endif // !BUILDFLAG(IS_ANDROID) + + Profile* profile = Profile::FromBrowserContext( + handle->GetWebContents()->GetBrowserContext()); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!ChromeContentBrowserClientExtensionsPart:: + AreExtensionsDisabledForProfile(profile)) { + throttles.push_back( + std::make_unique(handle)); + + MaybeAddThrottle(extensions::ExtensionsBrowserClient::Get() + ->GetUserScriptListener() + ->CreateNavigationThrottle(handle), + &throttles); + } +#endif + +#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + MaybeAddThrottle( + SupervisedUserGoogleAuthNavigationThrottle::MaybeCreate(handle), + &throttles); +#endif + + if (auto* throttle_manager = + subresource_filter::ContentSubresourceFilterThrottleManager:: + FromNavigationHandle(*handle)) { + throttle_manager->MaybeAppendNavigationThrottles(handle, &throttles); + } + + MaybeAddThrottle( + LookalikeUrlNavigationThrottle::MaybeCreateNavigationThrottle(handle), + &throttles); + + MaybeAddThrottle(PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#if BUILDFLAG(ENABLE_PDF) + throttles.push_back(std::make_unique( + handle, std::make_unique())); +#endif // BUILDFLAG(ENABLE_PDF) + + MaybeAddThrottle(TabUnderNavigationThrottle::MaybeCreate(handle), &throttles); + + MaybeAddThrottle( + WellKnownChangePasswordNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + + MaybeAddThrottle( + PasswordManagerNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + + throttles.push_back(std::make_unique( + handle, handle->GetWebContents()->GetBrowserContext())); + + // Before setting up SSL error detection, configure SSLErrorHandler to invoke + // the relevant extension API whenever an SSL interstitial is shown. + SSLErrorHandler::SetClientCallbackOnInterstitialsShown( + base::BindRepeating(&MaybeTriggerSecurityInterstitialShownEvent)); + throttles.push_back(std::make_unique( + handle, + base::BindOnce(&HandleSSLErrorWrapper), base::BindOnce(&IsInHostedApp), + base::BindOnce( + &ShouldIgnoreSslInterstitialBecauseNavigationDefaultedToHttps))); + + throttles.push_back(std::make_unique(handle)); + + if (base::FeatureList::IsEnabled(omnibox::kDefaultTypedNavigationsToHttps)) { + MaybeAddThrottle( + TypedNavigationUpgradeThrottle::MaybeCreateThrottleFor(handle), + &throttles); + } + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + MaybeAddThrottle( + WebAppSettingsNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + MaybeAddThrottle(profile_management::ProfileManagementNavigationThrottle:: + MaybeCreateThrottleFor(handle), + &throttles); + MaybeAddThrottle( + profile_management::OidcAuthResponseCaptureNavigationThrottle:: + MaybeCreateThrottleFor(handle), + &throttles); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ + BUILDFLAG(IS_CHROMEOS_ASH) + MaybeAddThrottle(enterprise_connectors::DeviceTrustNavigationThrottle:: + MaybeCreateThrottleFor(handle), + &throttles); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || + // BUILDFLAG(IS_CHROMEOS_ASH) + +#if !BUILDFLAG(IS_ANDROID) + MaybeAddThrottle(DevToolsWindow::MaybeCreateNavigationThrottle(handle), + &throttles); + + MaybeAddThrottle(NewTabPageNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + + MaybeAddThrottle( + web_app::TabbedWebAppNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + + MaybeAddThrottle( + web_app::WebUIWebAppNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#endif + + // g_browser_process->safe_browsing_service() may be null in unittests. + safe_browsing::SafeBrowsingUIManager* ui_manager = + g_browser_process->safe_browsing_service() + ? g_browser_process->safe_browsing_service()->ui_manager().get() + : nullptr; + MaybeAddThrottle( + safe_browsing::SafeBrowsingNavigationThrottle::MaybeCreateThrottleFor( + handle, ui_manager), + &throttles); + + if (base::FeatureList::IsEnabled(safe_browsing::kDelayedWarnings)) { + throttles.push_back( + std::make_unique( + handle)); + } + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) + MaybeAddThrottle(browser_switcher::BrowserSwitcherNavigationThrottle:: + MaybeCreateThrottleFor(handle), + &throttles); +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + MaybeAddThrottle( + chromeos::KioskSettingsNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#endif + +#if BUILDFLAG(IS_MAC) + MaybeAddThrottle(MaybeCreateAuthSessionThrottleFor(handle), &throttles); +#endif + + auto* performance_manager_registry = + performance_manager::PerformanceManagerRegistry::GetInstance(); + if (performance_manager_registry) { + MaybeAddThrottles( + performance_manager_registry->CreateThrottlesForNavigation(handle), + &throttles); + } + + if (profile && profile->GetPrefs()) { + MaybeAddThrottle( + security_interstitials::InsecureFormNavigationThrottle:: + MaybeCreateNavigationThrottle( + handle, std::make_unique(), + profile->GetPrefs()), + &throttles); + } + + if (IsErrorPageAutoReloadEnabled()) { + MaybeAddThrottle( + error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(handle), + &throttles); + } + + MaybeAddThrottle( + payments::PaymentHandlerNavigationThrottle::MaybeCreateThrottleFor( + handle), + &throttles); + + MaybeAddThrottle( + prerender::NoStatePrefetchNavigationThrottle::MaybeCreateThrottleFor( + handle), + &throttles); + +#if defined(TOOLKIT_VIEWS) + if (profile && IsSideSearchEnabled(profile)) { + MaybeAddThrottle( + SideSearchSideContentsHelper::MaybeCreateThrottleFor(handle), + &throttles); + } +#endif + +#if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) + if (lens::features::IsLensSidePanelEnabled()) { + MaybeAddThrottle( + lens::LensSidePanelNavigationHelper::MaybeCreateThrottleFor(handle), + &throttles); + } +#endif + +#if !BUILDFLAG(IS_ANDROID) + if (features::IsReadAnythingEnabled()) { + MaybeAddThrottle(ReadAnythingSidePanelNavigationThrottle::CreateFor(handle), + &throttles); + } +#endif + +#if BUILDFLAG(ENABLE_OFFLINE_PAGES) + MaybeAddThrottle( + offline_pages::OfflinePageNavigationThrottle::MaybeCreateThrottleFor( + handle), + &throttles); +#endif + + if (profile) { + MaybeAddThrottle( + HttpsUpgradesNavigationThrottle::MaybeCreateThrottleFor( + handle, std::make_unique(), + profile), + &throttles); + } + + MaybeAddThrottle(MaybeCreateNavigationAblationThrottle(handle), &throttles); + +#if !BUILDFLAG(IS_ANDROID) + MaybeAddThrottle(MaybeCreateAboutThisSiteThrottleFor(handle), &throttles); +#endif + + auto* privacy_sandbox_settings = + PrivacySandboxSettingsFactory::GetForProfile(profile); + if (privacy_sandbox_settings && + privacy_sandbox_settings->AreRelatedWebsiteSetsEnabled()) { + MaybeAddThrottle(first_party_sets::FirstPartySetsNavigationThrottle:: + MaybeCreateNavigationThrottle(handle), + &throttles); + } + +#if BUILDFLAG(IS_WIN) + // Don't perform platform authentication in incognito and guest profiles. + if (profile && !profile->IsOffTheRecord()) { + MaybeAddThrottle( + enterprise_auth::PlatformAuthNavigationThrottle::MaybeCreateThrottleFor( + handle), + &throttles); + } +#endif // BUILDFLAG(IS_WIN) + +#if BUILDFLAG(IS_CHROMEOS) + // TODO(b:296844164) Handle captive portal signin properly. + if (profile && profile->IsIncognitoProfile() && profile->IsOffTheRecord() && + !profile->GetOTRProfileID().IsCaptivePortal()) { + MaybeAddThrottle( + chromeos::IncognitoNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); + } + + MaybeAddThrottle(apps::AppInstallNavigationThrottle::MaybeCreate(handle), + &throttles); +#endif // BUILDFLAG(IS_CHROMEOS) + +#if !BUILDFLAG(IS_ANDROID) + MaybeAddThrottle(PreviewNavigationThrottle::MaybeCreateThrottleFor(handle), + &throttles); +#endif // !BUILDFLAG(IS_ANDROID) + + return throttles; +} + +std::vector> +ChromeContentBrowserClient::CreateCommitDeferringConditionsForNavigation( + content::NavigationHandle* navigation_handle, + content::CommitDeferringCondition::NavigationType navigation_type) { + auto conditions = + std::vector>(); + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) + MaybeAddCondition( + safe_browsing::MaybeCreateCommitDeferringCondition(*navigation_handle), + &conditions); +#endif + + return conditions; +} + +std::unique_ptr +ChromeContentBrowserClient::GetNavigationUIData( + content::NavigationHandle* navigation_handle) { + return std::make_unique(navigation_handle); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateScreenEnumerator() const { + return std::make_unique(); +} + +bool ChromeContentBrowserClient::EnforceSystemAudioEchoCancellation() { + // TODO(b/270042522): This is a short term solution to enforce the system + // audio cancellation and will be removed before Lacros is released. The + // short term solution will not work on Lacros. +#if BUILDFLAG(IS_CHROMEOS_ASH) && defined(USE_CRAS) + bool system_aec_enabled = false; + ash::CrosSettings::Get()->GetBoolean(ash::kDeviceSystemAecEnabled, + &system_aec_enabled); + return system_aec_enabled; +#else + return false; +#endif +} + +std::unique_ptr +ChromeContentBrowserClient::CreateDevToolsManagerDelegate() { +#if BUILDFLAG(IS_ANDROID) + return std::make_unique(); +#else + return std::make_unique(); +#endif +} + +void ChromeContentBrowserClient::UpdateDevToolsBackgroundServiceExpiration( + content::BrowserContext* browser_context, + int service, + base::Time expiration_time) { + Profile* profile = Profile::FromBrowserContext(browser_context); + DCHECK(profile); + + auto* pref_service = profile->GetPrefs(); + DCHECK(pref_service); + + ScopedDictPrefUpdate pref_update( + pref_service, prefs::kDevToolsBackgroundServicesExpirationDict); + base::Value::Dict& exp_dict = pref_update.Get(); + + // Convert |expiration_time| to minutes since that is the most granular + // option that returns an int. base::Value does not accept int64. + int expiration_time_minutes = + expiration_time.ToDeltaSinceWindowsEpoch().InMinutes(); + exp_dict.Set(base::NumberToString(service), expiration_time_minutes); +} + +base::flat_map +ChromeContentBrowserClient::GetDevToolsBackgroundServiceExpirations( + content::BrowserContext* browser_context) { + Profile* profile = Profile::FromBrowserContext(browser_context); + DCHECK(profile); + + auto* pref_service = profile->GetPrefs(); + DCHECK(pref_service); + + const auto& expiration_dict = + pref_service->GetDict(prefs::kDevToolsBackgroundServicesExpirationDict); + + base::flat_map expiration_times; + for (auto it : expiration_dict) { + // key. + int service = 0; + bool did_convert = base::StringToInt(it.first, &service); + DCHECK(did_convert); + + // value. + DCHECK(it.second.is_int()); + base::TimeDelta delta = base::Minutes(it.second.GetInt()); + base::Time expiration_time = base::Time::FromDeltaSinceWindowsEpoch(delta); + + expiration_times[service] = expiration_time; + } + + return expiration_times; +} + +std::optional +ChromeContentBrowserClient::GetSpareRendererDelayForSiteURL( + const GURL& site_url) { + if (!base::FeatureList::IsEnabled( + features::kDeferredSpareRendererForTopChromeWebUI)) { + return std::nullopt; + } + + if (!IsTopChromeWebUIURL(site_url)) { + return std::nullopt; + } + + // Prevent new spare renderer creation until page loading completes, signaled + // by WebContentsImpl::DidStopLoading. When enabled, delay spare renderer + // creation indefinitely by returning the maximum TimeDelta value. + if (features::kSpareRendererWarmupDelayUntilPageStopsLoading.Get()) { + return base::TimeDelta::Max(); + } + + // Otherwise, schedule new spare renderer creation after a predefined delay. + return features::kSpareRendererWarmupDelay.Get(); +} + +content::TracingDelegate* ChromeContentBrowserClient::GetTracingDelegate() { + return new ChromeTracingDelegate(); +} + +bool ChromeContentBrowserClient::IsPluginAllowedToCallRequestOSFileHandle( + content::BrowserContext* browser_context, + const GURL& url) { +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientPluginsPart:: + IsPluginAllowedToCallRequestOSFileHandle(browser_context, url); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::IsPluginAllowedToUseDevChannelAPIs( + content::BrowserContext* browser_context, + const GURL& url) { +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientPluginsPart:: + IsPluginAllowedToUseDevChannelAPIs(browser_context, url); +#else + return false; +#endif +} + +void ChromeContentBrowserClient::InitOnUIThread() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + safe_browsing_service_ = g_browser_process->safe_browsing_service(); + + // Initialize `network_contexts_parent_directory_`. + base::FilePath user_data_dir; + base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + DCHECK(!user_data_dir.empty()); + network_contexts_parent_directory_.push_back(user_data_dir); + + base::FilePath cache_dir; + chrome::GetUserCacheDirectory(user_data_dir, &cache_dir); + DCHECK(!cache_dir.empty()); + // On some platforms, the cache is a child of the user_data_dir so only + // return the one path. + if (!user_data_dir.IsParent(cache_dir)) + network_contexts_parent_directory_.push_back(cache_dir); + + // If the cache location has been overridden by a switch or preference, + // include that as well. + if (auto* local_state = g_browser_process->local_state()) { + base::FilePath pref_cache_dir = + local_state->GetFilePath(prefs::kDiskCacheDir); + if (!pref_cache_dir.empty() && !user_data_dir.IsParent(cache_dir)) + network_contexts_parent_directory_.push_back(pref_cache_dir); + } +} + +void ChromeContentBrowserClient::MaybeCopyDisableWebRtcEncryptionSwitch( + base::CommandLine* to_command_line, + const base::CommandLine& from_command_line, + version_info::Channel channel) { +#if BUILDFLAG(IS_ANDROID) + const version_info::Channel kMaxDisableEncryptionChannel = + version_info::Channel::BETA; +#else + const version_info::Channel kMaxDisableEncryptionChannel = + version_info::Channel::DEV; +#endif + if (channel <= kMaxDisableEncryptionChannel) { + static const char* const kWebRtcDevSwitchNames[] = { + switches::kDisableWebRtcEncryption, + }; + to_command_line->CopySwitchesFrom(from_command_line, kWebRtcDevSwitchNames); + } +} + +#if BUILDFLAG(ENABLE_MEDIA_REMOTING) +void ChromeContentBrowserClient::CreateMediaRemoter( + content::RenderFrameHost* render_frame_host, + mojo::PendingRemote source, + mojo::PendingReceiver receiver) { + CastRemotingConnector::CreateMediaRemoter( + render_frame_host, std::move(source), std::move(receiver)); +} +#endif // BUILDFLAG(ENABLE_MEDIA_REMOTING) + +base::FilePath ChromeContentBrowserClient::GetLoggingFileName( + const base::CommandLine& command_line) { + return logging::GetLogFileName(command_line); +} + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) +std::unique_ptr +ChromeContentBrowserClient::MaybeCreateSafeBrowsingURLLoaderThrottle( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + int frame_tree_node_id, + std::optional navigation_id, + Profile* profile) { + bool matches_enterprise_allowlist = safe_browsing::IsURLAllowlistedByPolicy( + request.url, *profile->GetPrefs()); + if (!matches_enterprise_allowlist) { +#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL) + auto* connectors_service = + enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext( + browser_context); + bool has_valid_dm_token = + connectors_service && + connectors_service->GetDMTokenForRealTimeUrlCheck().has_value(); + bool is_enterprise_lookup_enabled = + safe_browsing::RealTimePolicyEngine::CanPerformEnterpriseFullURLLookup( + profile->GetPrefs(), has_valid_dm_token, profile->IsOffTheRecord()); +#else + bool is_enterprise_lookup_enabled = false; +#endif + bool is_consumer_lookup_enabled = + safe_browsing::RealTimePolicyEngine::CanPerformFullURLLookup( + profile->GetPrefs(), profile->IsOffTheRecord(), + g_browser_process->variations_service()); + + // |url_lookup_service| is used when real time url check is enabled. + safe_browsing::RealTimeUrlLookupServiceBase* url_lookup_service = + GetUrlLookupService(browser_context, is_enterprise_lookup_enabled, + is_consumer_lookup_enabled); + safe_browsing::HashRealTimeService* hash_realtime_service = + safe_browsing_service_ + ? safe_browsing_service_->GetHashRealTimeService(profile) + : nullptr; + safe_browsing::hash_realtime_utils::HashRealTimeSelection + hash_realtime_selection = + safe_browsing::hash_realtime_utils::DetermineHashRealTimeSelection( + profile->IsOffTheRecord(), profile->GetPrefs(), + safe_browsing::hash_realtime_utils::GetCountryCode( + g_browser_process->variations_service()), + /*log_usage_histograms=*/true); + safe_browsing::AsyncCheckTracker* async_check_tracker = + GetAsyncCheckTracker(wc_getter, is_enterprise_lookup_enabled, + is_consumer_lookup_enabled, + hash_realtime_selection, frame_tree_node_id); + + return safe_browsing::BrowserURLLoaderThrottle::Create( + base::BindRepeating( + &ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate, + base::Unretained(this), + safe_browsing::IsSafeBrowsingEnabled(*profile->GetPrefs()), + // Should check for enterprise when safe browsing is disabled. + /*should_check_on_sb_disabled=*/is_enterprise_lookup_enabled, + safe_browsing::GetURLAllowlistByPolicy(profile->GetPrefs())), + wc_getter, frame_tree_node_id, navigation_id, + url_lookup_service ? url_lookup_service->GetWeakPtr() : nullptr, + hash_realtime_service ? hash_realtime_service->GetWeakPtr() : nullptr, + hash_realtime_selection, + async_check_tracker ? async_check_tracker->GetWeakPtr() : nullptr); + } + return nullptr; +} +#endif + +#if BUILDFLAG(IS_ANDROID) +std::tuple +GetClientDataHeader(int frame_tree_node_id) { + std::string client_data_header; + bool is_custom_tab = false; + if (frame_tree_node_id != content::RenderFrameHost::kNoFrameTreeNodeId) { + auto* web_contents = WebContents::FromFrameTreeNodeId(frame_tree_node_id); + // Could be null if the FrameTreeNode's RenderFrameHost is shutting down. + if (web_contents) { + auto* client_data_header_observer = + customtabs::ClientDataHeaderWebContentsObserver::FromWebContents( + web_contents); + if (client_data_header_observer) { + client_data_header = client_data_header_observer->header(); + } + + auto* delegate = + TabAndroid::FromWebContents(web_contents) + ? static_cast( + web_contents->GetDelegate()) + : nullptr; + if (delegate) { + is_custom_tab = delegate->IsCustomTab(); + } + } + } + return {client_data_header, is_custom_tab}; +} +#endif + +std::unique_ptr CreateGoogleURLLoaderThrottle( +#if BUILDFLAG(IS_ANDROID) + const std::string& client_data_header, +#endif + Profile* profile) { +#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) + BoundSessionCookieRefreshService* bound_session_cookie_refresh_service = + BoundSessionCookieRefreshServiceFactory::GetForProfile(profile); + std::unique_ptr + bound_session_request_throttled_handler; + chrome::mojom::BoundSessionThrottlerParamsPtr bound_session_throttler_params; + + if (bound_session_cookie_refresh_service) { + bound_session_request_throttled_handler = + std::make_unique( + *bound_session_cookie_refresh_service); + bound_session_throttler_params = + bound_session_cookie_refresh_service->GetBoundSessionThrottlerParams(); + } +#endif + + chrome::mojom::DynamicParamsPtr dynamic_params = + chrome::mojom::DynamicParams::New( +#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) + std::move(bound_session_throttler_params), +#endif + profile->GetPrefs()->GetBoolean( + policy::policy_prefs::kForceGoogleSafeSearch), + profile->GetPrefs()->GetInteger( + policy::policy_prefs::kForceYouTubeRestrict), + profile->GetPrefs()->GetString(prefs::kAllowedDomainsForApps)); + return std::make_unique( +#if BUILDFLAG(IS_ANDROID) + client_data_header, +#endif +#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS) + std::move(bound_session_request_throttled_handler), +#endif + std::move(dynamic_params)); +} + +std::vector> +ChromeContentBrowserClient::CreateURLLoaderThrottles( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + content::NavigationUIData* navigation_ui_data, + int frame_tree_node_id, + std::optional navigation_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + std::vector> result; + + DCHECK(browser_context); + Profile* profile = Profile::FromBrowserContext(browser_context); + DCHECK(profile); + + ChromeNavigationUIData* chrome_navigation_ui_data = + static_cast(navigation_ui_data); + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) + if (auto safe_browsing_throttle = MaybeCreateSafeBrowsingURLLoaderThrottle( + request, browser_context, wc_getter, frame_tree_node_id, + navigation_id, profile); + safe_browsing_throttle) { + result.push_back(std::move(safe_browsing_throttle)); + } +#endif + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) + result.push_back( + std::make_unique( + wc_getter.Run())); +#endif + + if (chrome_navigation_ui_data && + chrome_navigation_ui_data->is_no_state_prefetching()) { + result.push_back( + std::make_unique( + GetPrerenderCanceler(wc_getter))); + } + +#if BUILDFLAG(IS_ANDROID) + auto [client_data_header, is_custom_tab] = + GetClientDataHeader(frame_tree_node_id); +#endif + + if (auto google_throttle = CreateGoogleURLLoaderThrottle( +#if BUILDFLAG(IS_ANDROID) + client_data_header, +#endif + profile); + google_throttle) { + result.push_back(std::move(google_throttle)); + } + + { + auto* factory = + ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context); + // null in unit tests. + if (factory) { + result.push_back( + std::make_unique(*factory)); + } + } + +#if BUILDFLAG(ENABLE_PLUGINS) + result.push_back(std::make_unique( + request.destination, frame_tree_node_id)); +#endif + +#if BUILDFLAG(IS_ANDROID) + auto delegate = std::make_unique( + profile, /*incognito_enabled=*/!is_custom_tab); +#else + auto delegate = + std::make_unique(profile); +#endif + + auto signin_throttle = + signin::URLLoaderThrottle::MaybeCreate(std::move(delegate), wc_getter); + if (signin_throttle) + result.push_back(std::move(signin_throttle)); + + return result; +} + +std::vector> +ChromeContentBrowserClient::CreateURLLoaderThrottlesForKeepAlive( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + int frame_tree_node_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + std::vector> result; + + DCHECK(browser_context); + Profile* profile = Profile::FromBrowserContext(browser_context); + DCHECK(profile); + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) + if (auto safe_browsing_throttle = MaybeCreateSafeBrowsingURLLoaderThrottle( + request, browser_context, wc_getter, frame_tree_node_id, + /*navigation_id=*/std::nullopt, profile); + safe_browsing_throttle) { + result.push_back(std::move(safe_browsing_throttle)); + } +#endif + +#if BUILDFLAG(IS_ANDROID) + auto [client_data_header, unused_is_custom_tab] = + GetClientDataHeader(frame_tree_node_id); +#endif + + if (auto google_throttle = CreateGoogleURLLoaderThrottle( +#if BUILDFLAG(IS_ANDROID) + client_data_header, +#endif + profile); + google_throttle) { + result.push_back(std::move(google_throttle)); + } + + return result; +} + +mojo::PendingRemote +ChromeContentBrowserClient::CreateNonNetworkNavigationURLLoaderFactory( + const std::string& scheme, + int frame_tree_node_id) { +#if BUILDFLAG(ENABLE_EXTENSIONS) || BUILDFLAG(IS_CHROMEOS_ASH) || \ + !BUILDFLAG(IS_ANDROID) + content::WebContents* web_contents = + content::WebContents::FromFrameTreeNodeId(frame_tree_node_id); + content::BrowserContext* browser_context = web_contents->GetBrowserContext(); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (scheme == extensions::kExtensionScheme) { + if (!ChromeContentBrowserClientExtensionsPart:: + AreExtensionsDisabledForProfile(browser_context)) { + return extensions::CreateExtensionNavigationURLLoaderFactory( + browser_context, + !!extensions::WebViewGuest::FromWebContents(web_contents)); + } + + return {}; + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + + Profile* profile = Profile::FromBrowserContext(browser_context); + // KeyedServices could be disabled based on the profile type, e.g. System + // Profile doesn't construct services by default. + if (AreKeyedServicesDisabledForProfileByDefault(profile)) + return {}; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (scheme == content::kExternalFileScheme) { + return ash::ExternalFileURLLoaderFactory::Create( + profile, content::ChildProcessHost::kInvalidUniqueID); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if !BUILDFLAG(IS_ANDROID) + if (scheme == chrome::kIsolatedAppScheme) { + if (content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context) && + !browser_context->ShutdownStarted()) { + // TODO(crbug.com/1365848): Only register the factory if we are already in + // an isolated storage partition. + return web_app::IsolatedWebAppURLLoaderFactory::Create(frame_tree_node_id, + browser_context); + } + + return {}; + } +#endif // !BUILDFLAG(IS_ANDROID) +#endif // BUILDFLAG(ENABLE_EXTENSIONS) || BUILDFLAG(IS_CHROMEOS_ASH) || + // !BUILDFLAG(IS_ANDROID) + + return {}; +} + +void ChromeContentBrowserClient:: + RegisterNonNetworkWorkerMainResourceURLLoaderFactories( + content::BrowserContext* browser_context, + NonNetworkURLLoaderFactoryMap* factories) { + DCHECK(browser_context); + DCHECK(factories); + +#if !BUILDFLAG(IS_ANDROID) + if (content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context) && + !browser_context->ShutdownStarted()) { + factories->emplace( + chrome::kIsolatedAppScheme, + web_app::IsolatedWebAppURLLoaderFactory::CreateForServiceWorker( + browser_context)); + } +#endif // !BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(ENABLE_EXTENSIONS) + DCHECK(!ChromeContentBrowserClientExtensionsPart:: + AreExtensionsDisabledForProfile(browser_context)); + + factories->emplace( + extensions::kExtensionScheme, + extensions::CreateExtensionWorkerMainResourceURLLoaderFactory( + browser_context)); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +} + +void ChromeContentBrowserClient:: + RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories( + content::BrowserContext* browser_context, + NonNetworkURLLoaderFactoryMap* factories) { + DCHECK(browser_context); + DCHECK(factories); + +#if !BUILDFLAG(IS_ANDROID) + if (content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context) && + !browser_context->ShutdownStarted()) { + factories->emplace( + chrome::kIsolatedAppScheme, + web_app::IsolatedWebAppURLLoaderFactory::CreateForServiceWorker( + browser_context)); + } +#endif // !BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + browser_context)) { + return; + } + + factories->emplace( + extensions::kExtensionScheme, + extensions::CreateExtensionServiceWorkerScriptURLLoaderFactory( + browser_context)); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +} + +namespace { + +// The SpecialAccessFileURLLoaderFactory provided to the extension background +// pages. Checks with the ChildProcessSecurityPolicy to validate the file +// access. +class SpecialAccessFileURLLoaderFactory + : public network::SelfDeletingURLLoaderFactory { + public: + // Returns mojo::PendingRemote to a newly constructed + // SpecialAccessFileURLLoaderFactory. The factory is self-owned - it will + // delete itself once there are no more receivers (including the receiver + // associated with the returned mojo::PendingRemote and the receivers bound by + // the Clone method). + static mojo::PendingRemote Create( + int child_id) { + mojo::PendingRemote pending_remote; + + // The SpecialAccessFileURLLoaderFactory will delete itself when there are + // no more receivers - see the + // network::SelfDeletingURLLoaderFactory::OnDisconnect method. + new SpecialAccessFileURLLoaderFactory( + child_id, pending_remote.InitWithNewPipeAndPassReceiver()); + + return pending_remote; + } + + SpecialAccessFileURLLoaderFactory(const SpecialAccessFileURLLoaderFactory&) = + delete; + SpecialAccessFileURLLoaderFactory& operator=( + const SpecialAccessFileURLLoaderFactory&) = delete; + + private: + explicit SpecialAccessFileURLLoaderFactory( + int child_id, + mojo::PendingReceiver factory_receiver) + : network::SelfDeletingURLLoaderFactory(std::move(factory_receiver)), + child_id_(child_id) {} + + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart( + mojo::PendingReceiver loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL( + child_id_, request.url)) { + mojo::Remote(std::move(client)) + ->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED)); + return; + } + content::CreateFileURLLoaderBypassingSecurityChecks( + request, std::move(loader), std::move(client), + /*observer=*/nullptr, + /* allow_directory_listing */ true); + } + + int child_id_; +}; + +#if BUILDFLAG(IS_CHROMEOS) +bool IsSystemFeatureDisabled(policy::SystemFeature system_feature) { + return policy::SystemFeaturesDisableListPolicyHandler:: + IsSystemFeatureDisabled(system_feature, g_browser_process->local_state()); +} + +bool IsSystemFeatureURLDisabled(const GURL& url) { + if (!url.SchemeIs(content::kChromeUIScheme) && + !url.SchemeIs(content::kChromeUIUntrustedScheme)) { + return false; + } + + // chrome://os-settings/pwa.html shouldn't be replaced to let the settings app + // installation complete successfully. + if (url.DomainIs(chrome::kChromeUIOSSettingsHost) && + url.path() != "/pwa.html") { + return IsSystemFeatureDisabled(policy::SystemFeature::kOsSettings); + } + + if (url.DomainIs(chrome::kChromeUISettingsHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kBrowserSettings); + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (url.DomainIs(chrome::kChromeUIUntrustedCroshHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kCrosh); + } + + if (url.DomainIs(ash::kChromeUIScanningAppHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kScanning); + } + + if (url.DomainIs(ash::kChromeUICameraAppHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kCamera); + } + + if (url.DomainIs(ash::kChromeUIHelpAppHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kExplore); + } + + if (url.DomainIs(ash::kChromeUIMediaAppHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kGallery); + } + + if (url.DomainIs(chrome::kChromeUIUntrustedTerminalHost)) { + return IsSystemFeatureDisabled(policy::SystemFeature::kTerminal); + } + +#endif + + return false; +} +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +void InitializeFileURLLoaderFactoryForExtension( + int render_process_id, + content::BrowserContext* browser_context, + const extensions::Extension* extension, + ChromeContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) { + // Extensions with the necessary permissions get access to file:// URLs that + // gets approval from ChildProcessSecurityPolicy. Keep this logic in sync with + // ExtensionWebContentsObserver::RenderFrameCreated. + Manifest::Type type = extension->GetType(); + if ((type == Manifest::TYPE_EXTENSION || + type == Manifest::TYPE_LEGACY_PACKAGED_APP) && + extensions::util::AllowFileAccess(extension->id(), browser_context)) { + factories->emplace( + url::kFileScheme, + SpecialAccessFileURLLoaderFactory::Create(render_process_id)); + } +} + +void AddChromeSchemeFactories( + int render_process_id, + content::RenderFrameHost* frame_host, + content::WebContents* web_contents, + const extensions::Extension* extension, + ChromeContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) { + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + InstantService* instant_service = + InstantServiceFactory::GetForProfile(profile); + // The test below matches when a remote 3P NTP is loaded. The effective + // URL is chrome-search://remote-ntp. This is to allow the use of the NTP + // public api and to embed most-visited tiles + // (chrome-search://most-visited/title.html). + // + // InstantService might be null for some irregular profiles, e.g. the System + // Profile. + if (instant_service && instant_service->IsInstantProcess(render_process_id)) { + factories->emplace( + chrome::kChromeSearchScheme, + content::CreateWebUIURLLoaderFactory( + frame_host, chrome::kChromeSearchScheme, + /*allowed_webui_hosts=*/base::flat_set())); + } + + extensions::ChromeExtensionWebContentsObserver* web_observer = + extensions::ChromeExtensionWebContentsObserver::FromWebContents( + web_contents); + + // There is nothing to do if no ChromeExtensionWebContentsObserver is attached + // to the |web_contents| or no enabled extension exists. + if (!web_observer || !extension) + return; + + std::vector allowed_webui_hosts; + // Support for chrome:// scheme if appropriate. + if ((extension->is_extension() || extension->is_platform_app()) && + Manifest::IsComponentLocation(extension->location())) { + // Components of chrome that are implemented as extensions or platform apps + // are allowed to use chrome://resources/ and chrome://theme/ URLs. + allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost); + allowed_webui_hosts.emplace_back(chrome::kChromeUIThemeHost); + // For testing purposes chrome://webui-test/ is also allowed. + allowed_webui_hosts.emplace_back(chrome::kChromeUIWebUITestHost); + } + if (extension->is_extension() || extension->is_legacy_packaged_app() || + (extension->is_platform_app() && + Manifest::IsComponentLocation(extension->location()))) { + // Extensions, legacy packaged apps, and component platform apps are allowed + // to use chrome://favicon/, chrome://extension-icon/ and chrome://app-icon + // URLs. Hosted apps are not allowed because they are served via web servers + // (and are generally never given access to Chrome APIs). + allowed_webui_hosts.emplace_back(chrome::kChromeUIExtensionIconHost); + allowed_webui_hosts.emplace_back(chrome::kChromeUIFaviconHost); + allowed_webui_hosts.emplace_back(chrome::kChromeUIAppIconHost); + } + if (!allowed_webui_hosts.empty()) { + factories->emplace(content::kChromeUIScheme, + content::CreateWebUIURLLoaderFactory( + frame_host, content::kChromeUIScheme, + std::move(allowed_webui_hosts))); + } +} +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +} // namespace + +void ChromeContentBrowserClient:: + RegisterNonNetworkSubresourceURLLoaderFactories( + int render_process_id, + int render_frame_id, + const std::optional& request_initiator_origin, + NonNetworkURLLoaderFactoryMap* factories) { +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ + !BUILDFLAG(IS_ANDROID) + content::RenderFrameHost* frame_host = + RenderFrameHost::FromID(render_process_id, render_frame_id); + WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ + // !BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (web_contents) { + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + factories->emplace( + content::kExternalFileScheme, + ash::ExternalFileURLLoaderFactory::Create(profile, render_process_id)); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + +#if !BUILDFLAG(IS_ANDROID) + { + content::BrowserContext* browser_context = + content::RenderProcessHost::FromID(render_process_id) + ->GetBrowserContext(); + DCHECK(browser_context); + if (content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context) && + !browser_context->ShutdownStarted()) { + // TODO(crbug.com/1365848): Only register the factory if we are already + // in an isolated storage partition. + + if (frame_host != nullptr) { + factories->emplace( + chrome::kIsolatedAppScheme, + web_app::IsolatedWebAppURLLoaderFactory::Create( + frame_host->GetFrameTreeNodeId(), browser_context)); + } else { + factories->emplace( + chrome::kIsolatedAppScheme, + web_app::IsolatedWebAppURLLoaderFactory::CreateForServiceWorker( + browser_context)); + } + } + } +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) + content::BrowserContext* browser_context = + content::RenderProcessHost::FromID(render_process_id) + ->GetBrowserContext(); + if (ChromeContentBrowserClientExtensionsPart::AreExtensionsDisabledForProfile( + browser_context)) + return; + + factories->emplace(extensions::kExtensionScheme, + extensions::CreateExtensionURLLoaderFactory( + render_process_id, render_frame_id)); + + const extensions::Extension* extension = nullptr; + if (request_initiator_origin != std::nullopt) { + extensions::ExtensionRegistry* registry = + extensions::ExtensionRegistry::Get( + Profile::FromBrowserContext(browser_context)); + DCHECK(registry); + extension = registry->enabled_extensions().GetExtensionOrAppByURL( + request_initiator_origin->GetURL()); + } + + // For service worker contexts, we only allow file access. The remainder of + // this code is used to allow extensions to access chrome:-scheme + // resources, which we are moving away from. + // TODO(crbug.com/1280411) Factories should not be created for unloaded + // extensions. + if (extension) { + InitializeFileURLLoaderFactoryForExtension( + render_process_id, browser_context, extension, factories); + } + + // This logic should match + // ChromeExtensionWebContentsObserver::RenderFrameCreated. + if (web_contents) { + AddChromeSchemeFactories(render_process_id, frame_host, web_contents, + extension, factories); + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +} + +void ChromeContentBrowserClient::WillCreateURLLoaderFactory( + content::BrowserContext* browser_context, + content::RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + std::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + network::URLLoaderFactoryBuilder& factory_builder, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override, + scoped_refptr navigation_response_task_runner) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (web_request_api) { + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame, render_process_id, type, + std::move(navigation_id), ukm_source_id, factory_builder, + header_client, navigation_response_task_runner, request_initiator); + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + } +#endif + + signin::ProxyingURLLoaderFactory::MaybeProxyRequest( + frame, type == URLLoaderFactoryType::kNavigation, request_initiator, + factory_builder); + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) + if (disable_secure_dns) { + WebContents* web_contents = WebContents::FromRenderFrameHost(frame); + *disable_secure_dns = + web_contents && + captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents) && + captive_portal::CaptivePortalTabHelper::FromWebContents(web_contents) + ->is_captive_portal_window(); + } +#endif +} + +std::vector> +ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( + content::NavigationUIData* navigation_ui_data, + int frame_tree_node_id, + int64_t navigation_id, + scoped_refptr navigation_response_task_runner) { + std::vector> + interceptors; +#if BUILDFLAG(ENABLE_OFFLINE_PAGES) + interceptors.push_back( + std::make_unique( + navigation_ui_data, frame_tree_node_id)); +#endif + +#if BUILDFLAG(ENABLE_PDF) + { + std::unique_ptr pdf_interceptor = + pdf::PdfURLLoaderRequestInterceptor::MaybeCreateInterceptor( + frame_tree_node_id, std::make_unique()); + if (pdf_interceptor) + interceptors.push_back(std::move(pdf_interceptor)); + } +#endif + + interceptors.push_back(std::make_unique( + frame_tree_node_id, navigation_id, navigation_response_task_runner)); + + auto https_upgrades_interceptor = + HttpsUpgradesInterceptor::MaybeCreateInterceptor(frame_tree_node_id, + navigation_ui_data); + if (https_upgrades_interceptor) { + interceptors.push_back(std::move(https_upgrades_interceptor)); + } + + return interceptors; +} + +content::ContentBrowserClient::URLLoaderRequestHandler +ChromeContentBrowserClient:: + CreateURLLoaderHandlerForServiceWorkerNavigationPreload( + int frame_tree_node_id, + const network::ResourceRequest& resource_request) { + SearchPrefetchURLLoader::RequestHandler prefetch_handler = + SearchPrefetchURLLoaderInterceptor::MaybeCreateLoaderForRequest( + resource_request, frame_tree_node_id); + return prefetch_handler; +} + +bool ChromeContentBrowserClient::WillInterceptWebSocket( + content::RenderFrameHost* frame) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!frame) { + return false; + } + const auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + frame->GetBrowserContext()); + + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (!web_request_api) + return false; + + return (web_request_api->MayHaveProxies() || + web_request_api->MayHaveWebsocketProxiesForExtensionTelemetry() || + web_request_api->IsAvailableToWebViewEmbedderFrame(frame)); +#else + return false; +#endif +} + +void ChromeContentBrowserClient::CreateWebSocket( + content::RenderFrameHost* frame, + WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const std::optional& user_agent, + mojo::PendingRemote + handshake_client) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // TODO(crbug.com/1243518): Request w/o a frame also should be proxied. + if (!frame) { + return; + } + auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + frame->GetBrowserContext()); + + DCHECK(web_request_api); + web_request_api->ProxyWebSocket(frame, std::move(factory), url, + site_for_cookies, user_agent, + std::move(handshake_client)); +#endif +} + +void ChromeContentBrowserClient::WillCreateWebTransport( + int process_id, + int frame_routing_id, + const GURL& url, + const url::Origin& initiator_origin, + mojo::PendingRemote + handshake_client, + WillCreateWebTransportCallback callback) { +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) + content::RenderFrameHost* frame = + content::RenderFrameHost::FromID(process_id, frame_routing_id); + if (frame) { + int frame_tree_node_id = frame->GetFrameTreeNodeId(); + content::WebContents* web_contents = + content::WebContents::FromFrameTreeNodeId(frame_tree_node_id); + DCHECK(web_contents); + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + DCHECK(profile); + auto checker = std::make_unique( + base::BindOnce( + &ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate, + base::Unretained(this), + safe_browsing::IsSafeBrowsingEnabled(*profile->GetPrefs()), + /*should_check_on_sb_disabled=*/false, + safe_browsing::GetURLAllowlistByPolicy(profile->GetPrefs())), + base::BindRepeating(&content::WebContents::FromFrameTreeNodeId, + frame_tree_node_id), + frame_tree_node_id); + auto* raw_checker = checker.get(); + raw_checker->Check( + url, + base::BindOnce( + &ChromeContentBrowserClient::SafeBrowsingWebApiHandshakeChecked, + weak_factory_.GetWeakPtr(), std::move(checker), process_id, + frame_routing_id, url, initiator_origin, + std::move(handshake_client), std::move(callback))); + return; + } +#endif + MaybeInterceptWebTransport(process_id, frame_routing_id, url, + initiator_origin, std::move(handshake_client), + std::move(callback)); +} + +void ChromeContentBrowserClient::SafeBrowsingWebApiHandshakeChecked( + std::unique_ptr checker, + int process_id, + int frame_routing_id, + const GURL& url, + const url::Origin& initiator_origin, + mojo::PendingRemote + handshake_client, + WillCreateWebTransportCallback callback, + safe_browsing::WebApiHandshakeChecker::CheckResult result) { + if (result == safe_browsing::WebApiHandshakeChecker::CheckResult::kProceed) { + MaybeInterceptWebTransport(process_id, frame_routing_id, url, + initiator_origin, std::move(handshake_client), + std::move(callback)); + } else { + std::move(callback).Run(std::move(handshake_client), + network::mojom::WebTransportError::New( + net::ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, + "SafeBrowsing check failed", false)); + } +} + +void ChromeContentBrowserClient::MaybeInterceptWebTransport( + int process_id, + int frame_routing_id, + const GURL& url, + const url::Origin& initiator_origin, + mojo::PendingRemote + handshake_client, + WillCreateWebTransportCallback callback) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // TODO(1243518): Add a unit test which calls + // ChromeContentBrowserClient::WillCreateWebTransport() with invalid process + // id and routing id. + auto* render_process_host = content::RenderProcessHost::FromID(process_id); + if (!render_process_host) { + std::move(callback).Run(std::move(handshake_client), std::nullopt); + return; + } + content::BrowserContext* browser_context = + render_process_host->GetBrowserContext(); + auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + // NOTE: Some unit test environments do not initialize BrowserContextKeyedAPI + // factories like WebRequestAPI. + if (!web_request_api) { + std::move(callback).Run(std::move(handshake_client), std::nullopt); + return; + } + web_request_api->ProxyWebTransport( + *render_process_host, frame_routing_id, url, initiator_origin, + std::move(handshake_client), std::move(callback)); +#else + std::move(callback).Run(std::move(handshake_client), std::nullopt); +#endif +} + +bool ChromeContentBrowserClient::WillCreateRestrictedCookieManager( + network::mojom::RestrictedCookieManagerRole role, + content::BrowserContext* browser_context, + const url::Origin& origin, + const net::IsolationInfo& isolation_info, + bool is_service_worker, + int process_id, + int routing_id, + mojo::PendingReceiver* receiver) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (origin.scheme() == extensions::kExtensionScheme) { + DCHECK_EQ(network::mojom::RestrictedCookieManagerRole::SCRIPT, role); + extensions::ChromeExtensionCookies::Get(browser_context) + ->CreateRestrictedCookieManager(origin, isolation_info, + std::move(*receiver)); + return true; + } +#endif + return false; +} + +void ChromeContentBrowserClient::OnNetworkServiceCreated( + network::mojom::NetworkService* network_service) { + PrefService* local_state; + if (g_browser_process) { + DCHECK(g_browser_process->local_state()); + local_state = g_browser_process->local_state(); + } else { + DCHECK(startup_data_.chrome_feature_list_creator()->local_state()); + local_state = startup_data_.chrome_feature_list_creator()->local_state(); + } + + // Create SystemNetworkContextManager if it has not been created yet. We need + // to set up global NetworkService state before anything else uses it and this + // is the first opportunity to initialize SystemNetworkContextManager with the + // NetworkService. + if (!SystemNetworkContextManager::HasInstance()) + SystemNetworkContextManager::CreateInstance(local_state); + + SystemNetworkContextManager::GetInstance()->OnNetworkServiceCreated( + network_service); + +#if !BUILDFLAG(IS_ANDROID) + if (task_manager::TaskManagerImpl::IsCreated() && + task_manager::TaskManagerImpl::GetInstance()->is_running()) { + network_service->EnableDataUseUpdates(true); + } +#endif +} + +void ChromeContentBrowserClient::ConfigureNetworkContextParams( + content::BrowserContext* context, + bool in_memory, + const base::FilePath& relative_partition_path, + network::mojom::NetworkContextParams* network_context_params, + cert_verifier::mojom::CertVerifierCreationParams* + cert_verifier_creation_params) { + ProfileNetworkContextService* service = + ProfileNetworkContextServiceFactory::GetForContext(context); + if (service) { + service->ConfigureNetworkContextParams(in_memory, relative_partition_path, + network_context_params, + cert_verifier_creation_params); + } else { + // Set default params. + network_context_params->user_agent = GetUserAgentBasedOnPolicy(context); + network_context_params->accept_language = GetApplicationLocale(); + } +} + +std::vector +ChromeContentBrowserClient::GetNetworkContextsParentDirectory() { + DCHECK(!network_contexts_parent_directory_.empty()); + return network_contexts_parent_directory_; +} + +base::Value::Dict ChromeContentBrowserClient::GetNetLogConstants() { + return net_log::GetPlatformConstantsForNetLog( + base::CommandLine::ForCurrentProcess()->GetCommandLineString(), + chrome::GetChannelName(chrome::WithExtendedStable(true))); +} + +bool ChromeContentBrowserClient::AllowRenderingMhtmlOverHttp( + content::NavigationUIData* navigation_ui_data) { +#if BUILDFLAG(ENABLE_OFFLINE_PAGES) + // It is OK to load the saved offline copy, in MHTML format. + ChromeNavigationUIData* chrome_navigation_ui_data = + static_cast(navigation_ui_data); + if (!chrome_navigation_ui_data) + return false; + offline_pages::OfflinePageNavigationUIData* offline_page_data = + chrome_navigation_ui_data->GetOfflinePageNavigationUIData(); + return offline_page_data && offline_page_data->is_offline_page(); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::ShouldForceDownloadResource( + content::BrowserContext* browser_context, + const GURL& url, + const std::string& mime_type) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // Special-case user scripts to get downloaded instead of viewed. + if (extensions::UserScript::IsURLUserScript(url, mime_type)) { + return true; + } + +#if BUILDFLAG(IS_CHROMEOS) + // QuickOffice file interception is deprecated. If QuickOffice would + // have intercepted this file and this feature is disabled, download + // it instead. + if (browser_context) { + Profile* profile = Profile::FromBrowserContext(browser_context); + bool force_download = profile->GetPrefs()->GetBoolean( + quickoffice::kQuickOfficeForceFileDownloadEnabled); + + if (base::FeatureList::IsEnabled(features::kQuickOfficeForceFileDownload) && + force_download) { + std::string extension_id = + PluginUtils::GetExtensionIdForMimeType(browser_context, mime_type); + + if (extension_misc::IsQuickOfficeExtension(extension_id)) { + return true; + } + } + } +#endif // BUILDFLAG(IS_CHROMEOS) +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + return false; +} + +content::BluetoothDelegate* ChromeContentBrowserClient::GetBluetoothDelegate() { + if (!bluetooth_delegate_) { + bluetooth_delegate_ = std::make_unique( + std::make_unique()); + } + return bluetooth_delegate_.get(); +} + +content::UsbDelegate* ChromeContentBrowserClient::GetUsbDelegate() { + if (!usb_delegate_) + usb_delegate_ = std::make_unique(); + return usb_delegate_.get(); +} + +content::PrivateNetworkDeviceDelegate* +ChromeContentBrowserClient::GetPrivateNetworkDeviceDelegate() { + if (!private_network_device_delegate_) { + private_network_device_delegate_ = + std::make_unique(); + } + return private_network_device_delegate_.get(); +} + +bool ChromeContentBrowserClient::IsSecurityLevelAcceptableForWebAuthn( + content::RenderFrameHost* rfh, + const url::Origin& caller_origin) { + const Profile* profile = + Profile::FromBrowserContext(rfh->GetBrowserContext()); + if (profile->GetPrefs()->GetBoolean( + webauthn::pref_names::kAllowWithBrokenCerts)) { + return true; + } +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (caller_origin.scheme() == extensions::kExtensionScheme) { + return true; + } +#endif + if (net::IsLocalhost(caller_origin.GetURL())) { + return true; + } + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(rfh); + SecurityStateTabHelper::CreateForWebContents(web_contents); + SecurityStateTabHelper* helper = + SecurityStateTabHelper::FromWebContents(web_contents); + security_state::SecurityLevel security_level = helper->GetSecurityLevel(); + return security_level == security_state::SecurityLevel::SECURE || + security_level == + security_state::SecurityLevel::SECURE_WITH_POLICY_INSTALLED_CERT || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kIgnoreCertificateErrors); +} + +#if !BUILDFLAG(IS_ANDROID) +void ChromeContentBrowserClient::CreateDeviceInfoService( + content::RenderFrameHost* render_frame_host, + mojo::PendingReceiver receiver) { + DCHECK(render_frame_host); + DeviceServiceImpl::Create(render_frame_host, std::move(receiver)); +} + +void ChromeContentBrowserClient::CreateManagedConfigurationService( + content::RenderFrameHost* render_frame_host, + mojo::PendingReceiver receiver) { + DCHECK(render_frame_host); + ManagedConfigurationServiceImpl::Create(render_frame_host, + std::move(receiver)); +} + +content::SerialDelegate* ChromeContentBrowserClient::GetSerialDelegate() { + if (!serial_delegate_) + serial_delegate_ = std::make_unique(); + return serial_delegate_.get(); +} + +content::HidDelegate* ChromeContentBrowserClient::GetHidDelegate() { + if (!hid_delegate_) + hid_delegate_ = std::make_unique(); + return hid_delegate_.get(); +} + +content::DirectSocketsDelegate* +ChromeContentBrowserClient::GetDirectSocketsDelegate() { + if (!direct_sockets_delegate_) { + direct_sockets_delegate_ = std::make_unique(); + } + return direct_sockets_delegate_.get(); +} + +content::WebAuthenticationDelegate* +ChromeContentBrowserClient::GetWebAuthenticationDelegate() { + if (!web_authentication_delegate_) { + web_authentication_delegate_ = + std::make_unique(); + } + return web_authentication_delegate_.get(); +} + +std::unique_ptr +ChromeContentBrowserClient::GetWebAuthenticationRequestDelegate( + content::RenderFrameHost* render_frame_host) { + return AuthenticatorRequestScheduler::CreateRequestDelegate( + render_frame_host); +} +#endif + +void ChromeContentBrowserClient::CreatePaymentCredential( + content::RenderFrameHost* render_frame_host, + mojo::PendingReceiver receiver) { + payments::CreatePaymentCredential(render_frame_host, std::move(receiver)); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateClientCertStore( + content::BrowserContext* browser_context) { + return ProfileNetworkContextServiceFactory::GetForContext(browser_context) + ->CreateClientCertStore(); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + content::BrowserContext* browser_context, + const content::GlobalRequestID& request_id, + bool is_request_for_primary_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) { +#if BUILDFLAG(IS_CHROMEOS) + // Negotiate challenge is handled via GSSAPI library, which can not receive + // external credentials. However, on ChromeOS we can suggest the user to + // create a TGT using their credentials. Note that the credentials are NOT + // passed to the browser and everything happens on OS level, hence we return + // nullptr instead of LoginDelegate to fail authentication. (See b/260522530). + if (base::FeatureList::IsEnabled(net::features::kKerberosInBrowserRedirect) && + auth_info.scheme == + net::HttpAuth::SchemeToString(net::HttpAuth::AUTH_SCHEME_NEGOTIATE)) { +#if BUILDFLAG(IS_CHROMEOS_ASH) + ash::KerberosInBrowserDialog::Show(); +#else + // Requests to show Kerberos ui via crosapi mojo call. + chromeos::LacrosService::Get() + ->GetRemote() + ->ShowKerberosInBrowserDialog(); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + return nullptr; + } +#endif // BUILDFLAG(IS_CHROMEOS) + +#if BUILDFLAG(IS_CHROMEOS_ASH) + auto* system_proxy_manager = ash::SystemProxyManager::Get(); + // For Managed Guest Session and Kiosk devices, the credentials configured + // via the policy SystemProxySettings may be used for proxy authentication. + // Note: |system_proxy_manager| may be missing in tests. + if (system_proxy_manager && system_proxy_manager->CanUsePolicyCredentials( + auth_info, first_auth_attempt)) { + return system_proxy_manager->CreateLoginDelegate( + std::move(auth_required_callback)); + } + + if (ash::HttpAuthDialog::IsEnabled()) { + return ash::HttpAuthDialog::Create(auth_info, web_contents, url, + std::move(auth_required_callback)); + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + + if (!http_auth_coordinator_) { + http_auth_coordinator_ = CreateHttpAuthCoordinator(); + } + // Once Lacros ships this logic will no longer need to be included in + // ash-chrome. + return http_auth_coordinator_->CreateLoginDelegate( + web_contents, browser_context, auth_info, request_id, + is_request_for_primary_main_frame, url, response_headers, + std::move(auth_required_callback)); +} + +bool ChromeContentBrowserClient::HandleExternalProtocol( + const GURL& url, + content::WebContents::Getter web_contents_getter, + int frame_tree_node_id, + content::NavigationUIData* navigation_data, + bool is_primary_main_frame, + bool is_in_fenced_frame_tree, + network::mojom::WebSandboxFlags sandbox_flags, + ui::PageTransition page_transition, + bool has_user_gesture, + const std::optional& initiating_origin, + content::RenderFrameHost* initiator_document, + mojo::PendingRemote* out_factory) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // External protocols are disabled for guests. An exception is made for the + // "mailto" protocol, so that pages that utilize it work properly in a + // WebView. + ChromeNavigationUIData* chrome_data = + static_cast(navigation_data); + if ((chrome_data && + chrome_data->GetExtensionNavigationUIData()->is_web_view()) && + !url.SchemeIs(url::kMailToScheme)) { + return false; + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + +#if BUILDFLAG(IS_ANDROID) + // Main frame external protocols are handled by + // InterceptNavigationResourceThrottle. + if (is_primary_main_frame) + return false; +#endif // defined(ANDROID) + + auto weak_initiator_document = initiator_document + ? initiator_document->GetWeakDocumentPtr() + : content::WeakDocumentPtr(); + +#if BUILDFLAG(IS_ANDROID) + // For Android this is always called on the UI thread. + CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + // Called synchronously so we can populate the |out_factory| param. + LaunchURL(weak_factory_.GetWeakPtr(), url, std::move(web_contents_getter), + page_transition, is_primary_main_frame, is_in_fenced_frame_tree, + sandbox_flags, has_user_gesture, initiating_origin, + std::move(weak_initiator_document), out_factory); +#else + // TODO(crbug.com/1394838): Figure out why this was initially made async, and, + // if possible, unify with the sync path above. + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&LaunchURL, weak_factory_.GetWeakPtr(), url, + std::move(web_contents_getter), page_transition, + is_primary_main_frame, is_in_fenced_frame_tree, + sandbox_flags, has_user_gesture, initiating_origin, + std::move(weak_initiator_document))); +#endif + return true; +} + +std::unique_ptr +ChromeContentBrowserClient::CreateWindowForVideoPictureInPicture( + content::VideoPictureInPictureWindowController* controller) { + // Note: content::VideoOverlayWindow::Create() is defined by platform-specific + // implementation in chrome/browser/ui/views. This layering hack, which goes + // through //content and ContentBrowserClient, allows us to work around the + // dependency constraints that disallow directly calling + // chrome/browser/ui/views code either from here or from other code in + // chrome/browser. + return content::VideoOverlayWindow::Create(controller); +} + +void ChromeContentBrowserClient::RegisterRendererPreferenceWatcher( + content::BrowserContext* browser_context, + mojo::PendingRemote watcher) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (PrefWatcher* pref_watcher = PrefWatcher::Get(profile)) + pref_watcher->RegisterRendererPreferenceWatcher(std::move(watcher)); +} + +// Static; handles rewriting Web UI URLs. +bool ChromeContentBrowserClient::HandleWebUI( + GURL* url, + content::BrowserContext* browser_context) { + DCHECK(browser_context); + + // Rewrite chrome://help to chrome://settings/help. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUIHelpHost) { + *url = ReplaceURLHostAndPath(*url, chrome::kChromeUISettingsHost, + chrome::kChromeUIHelpHost); + } + +#if !BUILDFLAG(IS_ANDROID) + // Redirect from the preloading sub-page to the performance page. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUISettingsHost && + url->path() == chrome::kPreloadingSubPagePath) { + GURL::Replacements replacements; + replacements.SetPathStr(chrome::kPerformanceSubPagePath); + *url = url->ReplaceComponents(replacements); + UMA_HISTOGRAM_BOOLEAN("Settings.Preloading.DeprecatedRedirect", true); + } else if (url->path() == chrome::kPerformanceSubPagePath) { + UMA_HISTOGRAM_BOOLEAN("Settings.Preloading.DeprecatedRedirect", false); + } + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* tracking_protection_settings = + TrackingProtectionSettingsFactory::GetForProfile(profile); + if (tracking_protection_settings && + tracking_protection_settings->IsTrackingProtection3pcdEnabled()) { + // Redirect from cookies to trackingProtection in experiment. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUISettingsHost && + url->path() == chrome::kCookiesSubPagePath) { + GURL::Replacements replacements; + replacements.SetPathStr(chrome::kTrackingProtectionSubPagePath); + *url = url->ReplaceComponents(replacements); + UMA_HISTOGRAM_BOOLEAN("Settings.TrackingProtection.Redirect", true); + } else if (url->path() == chrome::kTrackingProtectionSubPagePath) { + UMA_HISTOGRAM_BOOLEAN("Settings.TrackingProtection.Redirect", false); + } + } else { + // Redirect from trackingProtection to cookies outside experiment. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUISettingsHost && + url->path() == chrome::kTrackingProtectionSubPagePath) { + GURL::Replacements replacements; + replacements.SetPathStr(chrome::kCookiesSubPagePath); + *url = url->ReplaceComponents(replacements); + } + } +#endif + +#if BUILDFLAG(IS_WIN) + // TODO(crbug.com/1003960): Remove when issue is resolved. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUIWelcomeWin10Host) { + *url = + ReplaceURLHostAndPath(*url, chrome::kChromeUIWelcomeHost, url->path()); + return true; + } +#endif // BUILDFLAG(IS_WIN) + + if (!ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL( + browser_context, *url) && + !content::WebUIConfigMap::GetInstance().GetConfig(browser_context, + *url)) { + return false; + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Special case : in ChromeOS in Guest mode bookmarks and history are + // disabled for security reasons. New tab page explains the reasons, so + // we redirect user to new tab page. + if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) { + if (url->SchemeIs(content::kChromeUIScheme) && + (url->DomainIs(chrome::kChromeUIBookmarksHost) || + url->DomainIs(chrome::kChromeUIHistoryHost))) { + // Rewrite with new tab URL + *url = GURL(chrome::kChromeUINewTabURL); + } + } +#endif + +#if BUILDFLAG(IS_CHROMEOS) + if (IsSystemFeatureURLDisabled(*url)) { + *url = GURL(chrome::kChromeUIAppDisabledURL); + return true; + } +#endif + + return true; +} + +#if BUILDFLAG(IS_CHROMEOS) +content::SmartCardDelegate* ChromeContentBrowserClient::GetSmartCardDelegate() { + if (!smart_card_delegate_) { + smart_card_delegate_ = std::make_unique(); + } + return smart_card_delegate_.get(); +} +#endif + +bool ChromeContentBrowserClient::ShowPaymentHandlerWindow( + content::BrowserContext* browser_context, + const GURL& url, + base::OnceCallback callback) { +#if BUILDFLAG(IS_ANDROID) + return false; +#else + payments::PaymentRequestDisplayManagerFactory::GetInstance() + ->GetForBrowserContext(browser_context) + ->ShowPaymentHandlerWindow(url, std::move(callback)); + return true; +#endif +} + +// static +bool ChromeContentBrowserClient::HandleWebUIReverse( + GURL* url, + content::BrowserContext* browser_context) { +#if BUILDFLAG(IS_WIN) + // TODO(crbug.com/1003960): Remove when issue is resolved. + // No need to actually reverse-rewrite the URL, but return true to update the + // displayed URL when rewriting chrome://welcome-win10 to chrome://welcome. + if (url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUIWelcomeHost) { + return true; + } +#endif // BUILDFLAG(IS_WIN) + +#if !BUILDFLAG(IS_ANDROID) + // TODO(crbug.com/1420597): Remove this after feature is launched. + // No need to actually reverse-rewrite the URL, but return true to update the + // displayed URL when rewriting chrome://settings/passwords to + // chrome://password-manager. + if (url->SchemeIs(content::kChromeUIScheme) && + url->DomainIs(password_manager::kChromeUIPasswordManagerHost)) { + return true; + } +#endif + + // No need to actually reverse-rewrite the URL, but return true to update the + // displayed URL when rewriting chrome://help to chrome://settings/help. + return url->SchemeIs(content::kChromeUIScheme) && + url->host() == chrome::kChromeUISettingsHost; +} + +const ui::NativeTheme* ChromeContentBrowserClient::GetWebTheme() const { + return ui::NativeTheme::GetInstanceForWeb(); +} + +void ChromeContentBrowserClient::AddExtraPart( + ChromeContentBrowserClientParts* part) { + extra_parts_.push_back(base::WrapUnique(part)); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateHttpAuthCoordinator() { + return std::make_unique(); +} + +scoped_refptr +ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate( + bool safe_browsing_enabled_for_profile, + bool should_check_on_sb_disabled, + const std::vector& allowlist_domains) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // Should not bypass safe browsing check if the check is for enterprise + // lookup. + if (!safe_browsing_enabled_for_profile && !should_check_on_sb_disabled) + return nullptr; + + // |safe_browsing_service_| may be unavailable in tests. + if (safe_browsing_service_ && !safe_browsing_url_checker_delegate_) { + safe_browsing_url_checker_delegate_ = + base::MakeRefCounted( + safe_browsing_service_->database_manager(), + safe_browsing_service_->ui_manager()); + } + + // Update allowlist domains. + if (safe_browsing_url_checker_delegate_) { + safe_browsing_url_checker_delegate_->SetPolicyAllowlistDomains( + allowlist_domains); + } + + return safe_browsing_url_checker_delegate_; +} + +safe_browsing::RealTimeUrlLookupServiceBase* +ChromeContentBrowserClient::GetUrlLookupService( + content::BrowserContext* browser_context, + bool is_enterprise_lookup_enabled, + bool is_consumer_lookup_enabled) { + // |safe_browsing_service_| may be unavailable in tests. + if (!safe_browsing_service_) { + return nullptr; + } + + Profile* profile = Profile::FromBrowserContext(browser_context); + +#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL) + if (is_enterprise_lookup_enabled) { + return safe_browsing::ChromeEnterpriseRealTimeUrlLookupServiceFactory:: + GetForProfile(profile); + } +#endif + + if (is_consumer_lookup_enabled) { + return safe_browsing::RealTimeUrlLookupServiceFactory::GetForProfile( + profile); + } + return nullptr; +} + +safe_browsing::AsyncCheckTracker* +ChromeContentBrowserClient::GetAsyncCheckTracker( + const base::RepeatingCallback& wc_getter, + bool is_enterprise_lookup_enabled, + bool is_consumer_lookup_enabled, + safe_browsing::hash_realtime_utils::HashRealTimeSelection + hash_realtime_selection, + int frame_tree_node_id) { + content::WebContents* contents = wc_getter.Run(); + if (!contents || !safe_browsing_service_ || + !safe_browsing_service_->ui_manager()) { + return nullptr; + } + if (is_enterprise_lookup_enabled) { + // No async checks for enterprise real-time checks. URL filtering rules + // need to be applied before the navigation is completed. + return nullptr; + } + if (!is_consumer_lookup_enabled && + hash_realtime_selection == + safe_browsing::hash_realtime_utils::HashRealTimeSelection::kNone) { + return nullptr; + } + if (prerender::ChromeNoStatePrefetchContentsDelegate::FromWebContents( + contents) || + contents->IsPrerenderedFrame(frame_tree_node_id)) { + return nullptr; + } + if (!base::FeatureList::IsEnabled( + safe_browsing::kSafeBrowsingAsyncRealTimeCheck)) { + return nullptr; + } + return safe_browsing::AsyncCheckTracker::GetOrCreateForWebContents( + contents, safe_browsing_service_->ui_manager().get()); +} + +void ChromeContentBrowserClient::ReportLegacyTechEvent( + content::RenderFrameHost* render_frame_host, + const std::string type, + const GURL& url, + const GURL& frame_url, + const std::string& filename, + uint64_t line, + uint64_t column, + std::optional cookie_issue_details) { + WebContents* web_contents = + WebContents::FromRenderFrameHost(render_frame_host); + DCHECK(web_contents); + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + if (!profile) { + return; + } + enterprise_reporting::LegacyTechService* service = + enterprise_reporting::LegacyTechServiceFactory::GetForProfile(profile); + if (!service) { + return; + } + service->ReportEvent(type, url, frame_url, filename, line, column, + cookie_issue_details); +} + +bool ChromeContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() { + // We require --user-data-dir flag too so that no dangerous changes are made + // in the user's regular profile. + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUserDataDir); +} + +void ChromeContentBrowserClient::OnNetworkServiceDataUseUpdate( + content::GlobalRenderFrameHostId render_frame_host_id, + int32_t network_traffic_annotation_id_hash, + int64_t recv_bytes, + int64_t sent_bytes) { +#if !BUILDFLAG(IS_ANDROID) + task_manager::TaskManagerInterface::UpdateAccumulatedStatsNetworkForRoute( + render_frame_host_id, recv_bytes, sent_bytes); +#endif +} + +base::FilePath +ChromeContentBrowserClient::GetSandboxedStorageServiceDataDirectory() { + if (!g_browser_process || !g_browser_process->profile_manager()) + return base::FilePath(); + return g_browser_process->profile_manager()->user_data_dir(); +} + +bool ChromeContentBrowserClient::ShouldSandboxAudioService() { + return IsAudioServiceSandboxEnabled(); +} + +bool ChromeContentBrowserClient::ShouldSandboxNetworkService() { + return SystemNetworkContextManager::IsNetworkSandboxEnabled(); +} + +bool ChromeContentBrowserClient::ShouldRunOutOfProcessSystemDnsResolution() { +// This enterprise policy is supported on Android, but the feature will not be +// launched there. +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) + // This is possibly called before `g_browser_process` is initialized. + PrefService* local_state; + if (g_browser_process) { + local_state = g_browser_process->local_state(); + } else { + local_state = startup_data_.chrome_feature_list_creator()->local_state(); + } + if (local_state && local_state->HasPrefPath( + prefs::kOutOfProcessSystemDnsResolutionEnabled)) { + return local_state->GetBoolean( + prefs::kOutOfProcessSystemDnsResolutionEnabled); + } +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) + + return ContentBrowserClient::ShouldRunOutOfProcessSystemDnsResolution(); +} + +void ChromeContentBrowserClient::LogWebFeatureForCurrentPage( + content::RenderFrameHost* render_frame_host, + blink::mojom::WebFeature feature) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage( + render_frame_host, feature); +} + +std::string ChromeContentBrowserClient::GetProduct() { + return std::string(version_info::GetProductNameAndVersionForUserAgent()); +} + +std::string ChromeContentBrowserClient::GetUserAgent() { + return embedder_support::GetUserAgent(); +} + +std::string ChromeContentBrowserClient::GetUserAgentBasedOnPolicy( + content::BrowserContext* context) { + const PrefService* prefs = Profile::FromBrowserContext(context)->GetPrefs(); + embedder_support::UserAgentReductionEnterprisePolicyState + user_agent_reduction = + embedder_support::GetUserAgentReductionFromPrefs(prefs); + return embedder_support::GetUserAgent(user_agent_reduction); +} + +blink::UserAgentMetadata ChromeContentBrowserClient::GetUserAgentMetadata() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return embedder_support::GetUserAgentMetadata( + g_browser_process->local_state()); +} + +std::optional ChromeContentBrowserClient::GetProductLogo() { + // This icon is available on Android, but adds 19KiB to the APK. Since it + // isn't used on Android we exclude it to avoid bloat. +#if !BUILDFLAG(IS_ANDROID) + return std::optional( + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_PRODUCT_LOGO_256)); +#else + return std::nullopt; +#endif +} + +bool ChromeContentBrowserClient::IsBuiltinComponent( + content::BrowserContext* browser_context, + const url::Origin& origin) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart::IsBuiltinComponent( + browser_context, origin); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::ShouldBlockRendererDebugURL( + const GURL& url, + content::BrowserContext* context, + content::RenderFrameHost* render_frame_host) { +#if !BUILDFLAG(IS_ANDROID) + // If devtools access is blocked for the page, debug URLs should also be + // blocked for the page. + Profile* profile = Profile::FromBrowserContext(context); + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (!DevToolsWindow::AllowDevToolsFor(profile, web_contents)) { + return true; + } +#endif + + // If the debug URL being visited is listed in the URLBlocklist policy it + // should be blocked. + PolicyBlocklistService* service = + PolicyBlocklistFactory::GetForBrowserContext(context); + using URLBlocklistState = policy::URLBlocklist::URLBlocklistState; + URLBlocklistState blocklist_state = service->GetURLBlocklistState(url); + return blocklist_state == URLBlocklistState::URL_IN_BLOCKLIST; +} + +#if BUILDFLAG(IS_ANDROID) +content::ContentBrowserClient::WideColorGamutHeuristic +ChromeContentBrowserClient::GetWideColorGamutHeuristic() { + if (viz::AlwaysUseWideColorGamut() || + features::IsDynamicColorGamutEnabled()) { + return WideColorGamutHeuristic::kUseDisplay; + } + + if (display::HasForceDisplayColorProfile() && + display::GetForcedDisplayColorProfile() == + gfx::ColorSpace::CreateDisplayP3D65()) { + return WideColorGamutHeuristic::kUseDisplay; + } + + return WideColorGamutHeuristic::kNone; +} +#endif + +base::flat_set +ChromeContentBrowserClient::GetPluginMimeTypesWithExternalHandlers( + content::BrowserContext* browser_context) { + base::flat_set mime_types; +#if BUILDFLAG(ENABLE_PLUGINS) + auto map = PluginUtils::GetMimeTypeToExtensionIdMap(browser_context); + for (const auto& pair : map) + mime_types.insert(pair.first); +#endif +#if BUILDFLAG(ENABLE_PDF) + mime_types.insert(pdf::kInternalPluginMimeType); +#endif + return mime_types; +} + +void ChromeContentBrowserClient::AugmentNavigationDownloadPolicy( + content::RenderFrameHost* frame_host, + bool user_gesture, + blink::NavigationDownloadPolicy* download_policy) { + const auto* throttle_manager = + subresource_filter::ContentSubresourceFilterThrottleManager::FromPage( + frame_host->GetPage()); + if (throttle_manager && + throttle_manager->IsRenderFrameHostTaggedAsAd(frame_host)) { + download_policy->SetAllowed(blink::NavigationDownloadType::kAdFrame); + if (!user_gesture) { + if (base::FeatureList::IsEnabled( + blink::features:: + kBlockingDownloadsInAdFrameWithoutUserActivation)) { + download_policy->SetDisallowed( + blink::NavigationDownloadType::kAdFrameNoGesture); + } else { + download_policy->SetAllowed( + blink::NavigationDownloadType::kAdFrameNoGesture); + } + } + } +} + +bool ChromeContentBrowserClient::HandleTopicsWebApi( + const url::Origin& context_origin, + content::RenderFrameHost* main_frame, + browsing_topics::ApiCallerSource caller_source, + bool get_topics, + bool observe, + std::vector& topics) { + browsing_topics::BrowsingTopicsService* browsing_topics_service = + browsing_topics::BrowsingTopicsServiceFactory::GetForProfile( + Profile::FromBrowserContext( + content::WebContents::FromRenderFrameHost(main_frame) + ->GetBrowserContext())); + + if (!browsing_topics_service) + return {}; + + bool allowed = browsing_topics_service->HandleTopicsWebApi( + context_origin, main_frame, caller_source, get_topics, observe, topics); + + if (main_frame) { + ChromeBrowsingDataModelDelegate::BrowsingDataAccessed( + main_frame, context_origin, + ChromeBrowsingDataModelDelegate::StorageType::kTopics, !allowed); + } + + return allowed; +} + +int ChromeContentBrowserClient::NumVersionsInTopicsEpochs( + content::RenderFrameHost* main_frame) const { + browsing_topics::BrowsingTopicsService* browsing_topics_service = + browsing_topics::BrowsingTopicsServiceFactory::GetForProfile( + Profile::FromBrowserContext( + content::WebContents::FromRenderFrameHost(main_frame) + ->GetBrowserContext())); + + CHECK(browsing_topics_service); + return browsing_topics_service->NumVersionsInEpochs( + main_frame->GetLastCommittedOrigin()); +} + +bool ChromeContentBrowserClient::IsBluetoothScanningBlocked( + content::BrowserContext* browser_context, + const url::Origin& requesting_origin, + const url::Origin& embedding_origin) { + const HostContentSettingsMap* const content_settings = + HostContentSettingsMapFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + + if (content_settings->GetContentSetting( + requesting_origin.GetURL(), embedding_origin.GetURL(), + ContentSettingsType::BLUETOOTH_SCANNING) == CONTENT_SETTING_BLOCK) { + return true; + } + + return false; +} + +void ChromeContentBrowserClient::BlockBluetoothScanning( + content::BrowserContext* browser_context, + const url::Origin& requesting_origin, + const url::Origin& embedding_origin) { + HostContentSettingsMap* const content_settings = + HostContentSettingsMapFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + + content_settings->SetContentSettingDefaultScope( + requesting_origin.GetURL(), embedding_origin.GetURL(), + ContentSettingsType::BLUETOOTH_SCANNING, CONTENT_SETTING_BLOCK); +} + +void ChromeContentBrowserClient::GetMediaDeviceIDSalt( + content::RenderFrameHost* rfh, + const net::SiteForCookies& site_for_cookies, + const blink::StorageKey& storage_key, + base::OnceCallback callback) { + GURL url = rfh->GetLastCommittedURL(); + url::Origin top_frame_origin = rfh->GetMainFrame()->GetLastCommittedOrigin(); + content::BrowserContext* browser_context = rfh->GetBrowserContext(); + + // Persistent MediaDevice IDs are allowed if cookies are allowed or if the + // user is in the default state in 3PCD. + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + privacy_sandbox::TrackingProtectionSettings* tracking_protection = + TrackingProtectionSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + bool allowed = cookie_settings->IsFullCookieAccessAllowed( + url, site_for_cookies, top_frame_origin, + cookie_settings->SettingOverridesForStorage()) || + (tracking_protection->IsTrackingProtection3pcdEnabled() && + !tracking_protection->AreAllThirdPartyCookiesBlocked()); + ChromeBrowsingDataModelDelegate::BrowsingDataAccessed( + rfh, storage_key, + ChromeBrowsingDataModelDelegate::StorageType::kMediaDeviceSalt, !allowed); + media_device_salt::MediaDeviceSaltService* salt_service = + MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext( + browser_context); + if (!allowed || !salt_service) { + // Use ephemeral salt. + std::move(callback).Run(allowed, browser_context->UniqueId()); + return; + } + + salt_service->GetSalt(rfh->GetStorageKey(), + base::BindOnce(std::move(callback), allowed)); +} + +#if !BUILDFLAG(IS_ANDROID) +base::OnceClosure ChromeContentBrowserClient::FetchRemoteSms( + content::WebContents* web_contents, + const std::vector& origin_list, + base::OnceCallback>, + std::optional, + std::optional)> + callback) { + return ::FetchRemoteSms(web_contents, origin_list, std::move(callback)); +} +#endif + +bool ChromeContentBrowserClient::IsClipboardPasteAllowed( + content::RenderFrameHost* render_frame_host) { + DCHECK(render_frame_host); + + // Paste requires either (1) user activation, ... + if (WebContents::FromRenderFrameHost(render_frame_host) + ->HasRecentInteraction()) { + return true; + } + + // (2) granted web permission, ... + content::BrowserContext* browser_context = + render_frame_host->GetBrowserContext(); + content::PermissionController* permission_controller = + browser_context->GetPermissionController(); + blink::mojom::PermissionStatus status = + permission_controller->GetPermissionStatusForCurrentDocument( + blink::PermissionType::CLIPBOARD_READ_WRITE, render_frame_host); + if (status == blink::mojom::PermissionStatus::GRANTED) + return true; + +#if BUILDFLAG(ENABLE_EXTENSIONS) + // (3) origination directly from a Chrome extension, ... + Profile* profile = Profile::FromBrowserContext(browser_context); + DCHECK(profile); + const GURL& url = + render_frame_host->GetMainFrame()->GetLastCommittedOrigin().GetURL(); + auto* registry = extensions::ExtensionRegistry::Get(profile); + if (url.SchemeIs(extensions::kExtensionScheme)) { + return URLHasExtensionPermission(extensions::ProcessMap::Get(profile), + registry, url, + render_frame_host->GetProcess()->GetID(), + APIPermissionID::kClipboardRead); + } + + // or (4) origination from a process that at least might be running a + // content script from an extension with the clipboardRead permission. + // Note that we currently don't allow clipboard operations based just on user + // script injections. + extensions::ExtensionIdSet extension_ids = extensions:: + ScriptInjectionTracker::GetExtensionsThatRanContentScriptsInProcess( + *render_frame_host->GetProcess()); + for (const auto& extension_id : extension_ids) { + const Extension* extension = + registry->enabled_extensions().GetByID(extension_id); + if (extension && extension->permissions_data()->HasAPIPermission( + APIPermissionID::kClipboardRead)) { + return true; + } + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + + return false; +} + +void ChromeContentBrowserClient::IsClipboardPasteAllowedByPolicy( + const content::ClipboardEndpoint& source, + const content::ClipboardEndpoint& destination, + const content::ClipboardMetadata& metadata, + ClipboardPasteData clipboard_paste_data, + IsClipboardPasteAllowedCallback callback) { +#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS) + enterprise_data_protection::PasteIfAllowedByPolicy( + source, destination, metadata, std::move(clipboard_paste_data), + std::move(callback)); +#else + std::move(callback).Run(std::move(clipboard_paste_data)); +#endif // BUILDFLAG(ENTERPRISE_DATA_CONTROLS) +} + +void ChromeContentBrowserClient::IsClipboardCopyAllowedByPolicy( + const content::ClipboardEndpoint& source, + const content::ClipboardMetadata& metadata, + const ClipboardPasteData& data, + IsClipboardCopyAllowedCallback callback) { +#if BUILDFLAG(ENTERPRISE_DATA_CONTROLS) + enterprise_data_protection::IsClipboardCopyAllowedByPolicy( + source, metadata, data, std::move(callback)); +#else + std::u16string replacement_data; + ClipboardRestrictionService* service = + ClipboardRestrictionServiceFactory::GetInstance()->GetForBrowserContext( + source.browser_context()); + if (service->IsUrlAllowedToCopy(*source.data_transfer_endpoint()->GetURL(), + metadata.size.value_or(0), + &replacement_data)) { + std::move(callback).Run(data, std::nullopt); + } else { + std::move(callback).Run(data, std::move(replacement_data)); + } +#endif // BUILDFLAG(ENTERPRISE_DATA_CONTROLS) +} + +#if BUILDFLAG(ENABLE_VR) +content::XrIntegrationClient* +ChromeContentBrowserClient::GetXrIntegrationClient() { + if (!xr_integration_client_) + xr_integration_client_ = std::make_unique( + base::PassKey()); + return xr_integration_client_.get(); +} +#endif // BUILDFLAG(ENABLE_VR) + +void ChromeContentBrowserClient::BindBrowserControlInterface( + mojo::ScopedMessagePipeHandle pipe) { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + chromeos::LacrosService::Get()->BindReceiver( + chrome::GetVersionString(chrome::WithExtendedStable(true))); +#endif +} + +bool ChromeContentBrowserClient:: + ShouldInheritCrossOriginEmbedderPolicyImplicitly(const GURL& url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return url.SchemeIs(extensions::kExtensionScheme); +#else + return false; +#endif +} + +bool ChromeContentBrowserClient:: + ShouldServiceWorkerInheritPolicyContainerFromCreator(const GURL& url) { + if (url.SchemeIsLocal()) { + return true; + } +#if BUILDFLAG(ENABLE_EXTENSIONS) + return url.SchemeIs(extensions::kExtensionScheme); +#else + return false; +#endif +} + +content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride +ChromeContentBrowserClient::ShouldOverridePrivateNetworkRequestPolicy( + content::BrowserContext* browser_context, + const url::Origin& origin) { + // The host content settings map might no be null for some irregular profiles, + // e.g. the System Profile. + if (HostContentSettingsMap* service = + HostContentSettingsMapFactory::GetForProfile(browser_context)) { + if (content_settings::ShouldAllowInsecurePrivateNetworkRequests(service, + origin)) { + return content::ContentBrowserClient:: + PrivateNetworkRequestPolicyOverride::kForceAllow; + } + } + +#if BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( + kPrivateNetworkAccessRestrictionsForAutomotive) && + base::android::BuildInfo::GetInstance()->is_automotive()) { + return content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride:: + kBlockInsteadOfWarn; + } +#endif + + Profile* profile = Profile::FromBrowserContext(browser_context); + if (profile->GetPrefs()->GetBoolean( + prefs::kManagedPrivateNetworkAccessRestrictionsEnabled)) { + return content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride:: + kBlockInsteadOfWarn; + } + + return content::ContentBrowserClient::PrivateNetworkRequestPolicyOverride:: + kDefault; +} + +bool ChromeContentBrowserClient::IsJitDisabledForSite( + content::BrowserContext* browser_context, + const GURL& site_url) { + Profile* profile = Profile::FromBrowserContext(browser_context); + auto* map = HostContentSettingsMapFactory::GetForProfile(profile); + // Special case to determine if any policy is set. + if (map && site_url.is_empty()) { + return map->GetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_JIT, + nullptr) == CONTENT_SETTING_BLOCK; + } + + // Only disable JIT for web schemes. + if (!site_url.SchemeIsHTTPOrHTTPS()) + return false; + + return (map && map->GetContentSetting(site_url, site_url, + ContentSettingsType::JAVASCRIPT_JIT) == + CONTENT_SETTING_BLOCK); +} + +ukm::UkmService* ChromeContentBrowserClient::GetUkmService() { + return g_browser_process->GetMetricsServicesManager()->GetUkmService(); +} + +blink::mojom::OriginTrialsSettingsPtr +ChromeContentBrowserClient::GetOriginTrialsSettings() { + return g_browser_process->GetOriginTrialsSettingsStorage()->GetSettings(); +} + +void ChromeContentBrowserClient::OnKeepaliveRequestStarted( + content::BrowserContext* context) { +#if !BUILDFLAG(IS_ANDROID) + DVLOG(1) << "OnKeepaliveRequestStarted: " << num_keepalive_requests_ + << " ==> " << num_keepalive_requests_ + 1; + ++num_keepalive_requests_; + DCHECK_GT(num_keepalive_requests_, 0u); + + if (!context) { + // We somehow failed to associate the request and the BrowserContext. Bail + // out. + return; + } + + const auto now = base::TimeTicks::Now(); + const auto timeout = GetKeepaliveTimerTimeout(context); + keepalive_deadline_ = std::max(keepalive_deadline_, now + timeout); + if (keepalive_deadline_ > now && !keepalive_timer_.IsRunning()) { + DVLOG(1) << "Starting a keepalive timer(" << timeout.InSecondsF() + << " seconds)"; + keepalive_timer_.Start( + FROM_HERE, keepalive_deadline_ - now, + base::BindOnce( + &ChromeContentBrowserClient::OnKeepaliveTimerFired, + weak_factory_.GetWeakPtr(), + std::make_unique( + KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED))); + } +#endif // !BUILDFLAG(IS_ANDROID) +} + +void ChromeContentBrowserClient::OnKeepaliveRequestFinished() { +#if !BUILDFLAG(IS_ANDROID) + DCHECK_GT(num_keepalive_requests_, 0u); + DVLOG(1) << "OnKeepaliveRequestFinished: " << num_keepalive_requests_ + << " ==> " << num_keepalive_requests_ - 1; + --num_keepalive_requests_; + if (num_keepalive_requests_ == 0) { + DVLOG(1) << "Stopping the keepalive timer"; + keepalive_timer_.Stop(); + // This deletes the keep alive handle attached to the timer function and + // unblock the shutdown sequence. + } +#endif // !BUILDFLAG(IS_ANDROID) +} + +#if BUILDFLAG(IS_MAC) +bool ChromeContentBrowserClient::SetupEmbedderSandboxParameters( + sandbox::mojom::Sandbox sandbox_type, + sandbox::SandboxCompiler* compiler) { + if (sandbox_type == sandbox::mojom::Sandbox::kSpeechRecognition) { + base::FilePath soda_component_path = speech::GetSodaDirectory(); + CHECK(!soda_component_path.empty()); + CHECK(compiler->SetParameter(sandbox::policy::kParamSodaComponentPath, + soda_component_path.value())); + + base::FilePath soda_language_pack_path = + speech::GetSodaLanguagePacksDirectory(); + CHECK(!soda_language_pack_path.empty()); + CHECK(compiler->SetParameter(sandbox::policy::kParamSodaLanguagePackPath, + soda_language_pack_path.value())); + return true; +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + } else if (sandbox_type == sandbox::mojom::Sandbox::kScreenAI) { + // ScreenAI service needs read access to ScreenAI component binary path to + // load it. + base::FilePath screen_ai_binary_path = + screen_ai::ScreenAIInstallState::GetInstance() + ->get_component_binary_path(); + if (screen_ai_binary_path.empty()) { + VLOG(1) << "Screen AI component not found."; + return false; + } + return compiler->SetParameter(sandbox::policy::kParamScreenAiComponentPath, + screen_ai_binary_path.value()); +#endif + } + + return false; +} + +#endif // BUILDFLAG(IS_MAC) + +void ChromeContentBrowserClient::GetHyphenationDictionary( + base::OnceCallback callback) { +#if BUILDFLAG(USE_MINIKIN_HYPHENATION) && !BUILDFLAG(IS_ANDROID) + component_updater::HyphenationComponentInstallerPolicy:: + GetHyphenationDictionary(std::move(callback)); +#endif +} + +bool ChromeContentBrowserClient::HasErrorPage(int http_status_code) { + // Use an internal error page, if we have one for the status code. + return error_page::LocalizedError::HasStrings( + error_page::Error::kHttpErrorDomain, http_status_code); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateIdentityRequestDialogController( + content::WebContents* web_contents) { + return std::make_unique(web_contents); +} + +bool ChromeContentBrowserClient::SuppressDifferentOriginSubframeJSDialogs( + content::BrowserContext* browser_context) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (!profile->GetPrefs()->GetBoolean( + prefs::kSuppressDifferentOriginSubframeJSDialogs)) { + return false; + } + return ContentBrowserClient::SuppressDifferentOriginSubframeJSDialogs( + browser_context); +} + +bool ChromeContentBrowserClient::IsFindInPageDisabledForOrigin( + const url::Origin& origin) { +#if BUILDFLAG(ENABLE_PDF) + // For PDF viewing with the PPAPI-free PDF Viewer, find-in-page should only + // display results from the PDF content, and not from the UI. + return IsPdfExtensionOrigin(origin); +#else + return false; +#endif +} + +std::unique_ptr +ChromeContentBrowserClient::CreateAnchorElementPreconnectDelegate( + content::RenderFrameHost& render_frame_host) { + return std::make_unique(render_frame_host); +} + +std::unique_ptr +ChromeContentBrowserClient::CreateSpeculationHostDelegate( + content::RenderFrameHost& render_frame_host) { + return std::make_unique(render_frame_host); +} + +std::unique_ptr +ChromeContentBrowserClient::CreatePrefetchServiceDelegate( + content::BrowserContext* browser_context) { + return std::make_unique(browser_context); +} + +std::unique_ptr +ChromeContentBrowserClient::CreatePrerenderWebContentsDelegate() { + return std::make_unique(); +} + +void ChromeContentBrowserClient::OnWebContentsCreated( + content::WebContents* web_contents) { + // NOTE: Please don't add additional code to this method - attaching universal + // WebContentsObservers goes through the separate function, to ensure that the + // (rare) additions of universal helpers are code reviewed by separate OWNERS. + AttachUniversalWebContentsObservers(web_contents); +} + +#if !BUILDFLAG(IS_ANDROID) +base::TimeDelta ChromeContentBrowserClient::GetKeepaliveTimerTimeout( + content::BrowserContext* context) { + Profile* profile = Profile::FromBrowserContext(context); + PrefService* prefs = profile->GetPrefs(); + if (!prefs) { + return base::TimeDelta(); + } + + const int seconds = + prefs->GetInteger(prefs::kFetchKeepaliveDurationOnShutdown); + // The preference is set only be the corresponding enterprise policy, and + // we have minimum/maximum values on it. + DCHECK_LE(0, seconds); + DCHECK_LE(seconds, 5); + return base::Seconds(seconds); +} + +void ChromeContentBrowserClient::OnKeepaliveTimerFired( + std::unique_ptr keep_alive_handle) { + const auto now = base::TimeTicks::Now(); + const auto then = keepalive_deadline_; + if (now < then) { + keepalive_timer_.Start( + FROM_HERE, then - now, + base::BindOnce(&ChromeContentBrowserClient::OnKeepaliveTimerFired, + weak_factory_.GetWeakPtr(), + std::move(keep_alive_handle))); + } +} +#endif + +bool ChromeContentBrowserClient::ShouldPreconnectNavigation( + content::RenderFrameHost* render_frame_host) { + content::BrowserContext* browser_context = + render_frame_host->GetBrowserContext(); +#if BUILDFLAG(ENABLE_EXTENSIONS) + // An extension could be blocking connections for privacy reasons, so skip + // optimization if there are any extensions with WebRequest permissions. + const auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + if (!web_request_api || web_request_api->MayHaveProxies() || + web_request_api->IsAvailableToWebViewEmbedderFrame(render_frame_host)) { + return false; + } +#endif + return prefetch::IsSomePreloadingEnabled( + *Profile::FromBrowserContext(browser_context)->GetPrefs()) == + content::PreloadingEligibility::kEligible; +} + +bool ChromeContentBrowserClient::ShouldDisableOriginAgentClusterDefault( + content::BrowserContext* browser_context) { + // The enterprise policy for kOriginAgentClusterDefaultEnabled defaults to + // true to defer to Chromium's decision. If it is set to false, it should + // override Chromium's decision and use site-keyed agent clusters by default + // instead. + return !Profile::FromBrowserContext(browser_context) + ->GetPrefs() + ->GetBoolean(prefs::kOriginAgentClusterDefaultEnabled); +} + +bool ChromeContentBrowserClient::WillProvidePublicFirstPartySets() { +#if BUILDFLAG(ENABLE_COMPONENT_UPDATER) + return !is_minimal_mode_ && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableComponentUpdate); +#else + return false; +#endif // BUILDFLAG(ENABLE_COMPONENT_UPDATER) +} + +content::mojom::AlternativeErrorPageOverrideInfoPtr +ChromeContentBrowserClient::GetAlternativeErrorPageOverrideInfo( + const GURL& url, + content::RenderFrameHost* render_frame_host, + content::BrowserContext* browser_context, + int32_t error_code) { +#if !BUILDFLAG(IS_ANDROID) + if (content::IsolatedWebAppsPolicy::AreIsolatedWebAppsEnabled( + browser_context) && + url.SchemeIs(chrome::kIsolatedAppScheme)) { + content::mojom::AlternativeErrorPageOverrideInfoPtr + alternative_error_page_override_info = + web_app::MaybeGetIsolatedWebAppErrorPageInfo( + url, render_frame_host, browser_context, error_code); + if (alternative_error_page_override_info) { + alternative_error_page_override_info->alternative_error_page_params.Set( + error_page::kOverrideErrorPage, base::Value(true)); + return alternative_error_page_override_info; + } + } +#endif + + if (error_code == net::ERR_INTERNET_DISCONNECTED) { + content::mojom::AlternativeErrorPageOverrideInfoPtr + alternative_error_page_override_info = web_app::GetOfflinePageInfo( + url, render_frame_host, browser_context); + if (alternative_error_page_override_info) { + // Use the alternative error page dictionary to override the error page. + alternative_error_page_override_info->alternative_error_page_params.Set( + error_page::kOverrideErrorPage, base::Value(true)); + web_app::TrackOfflinePageVisibility(render_frame_host); + return alternative_error_page_override_info; + } + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + using PortalState = chromeos::network_config::mojom::PortalState; + auto portal_state = ash::network_health::NetworkHealthManager::GetInstance() + ->helper() + ->WiFiPortalState(); + if (portal_state != PortalState::kUnknown) { + auto alternative_error_page_override_info = + content::mojom::AlternativeErrorPageOverrideInfo::New(); + bool is_portal_state = portal_state == PortalState::kPortal || + portal_state == PortalState::kPortalSuspected || + portal_state == PortalState::kProxyAuthRequired; + // Use the alternative error page dictionary to provide additional + // suggestions in the default error page. + alternative_error_page_override_info->alternative_error_page_params.Set( + error_page::kOverrideErrorPage, base::Value(false)); + alternative_error_page_override_info->alternative_error_page_params.Set( + error_page::kIsPortalStateKey, base::Value(is_portal_state)); + return alternative_error_page_override_info; + } +#endif + + return nullptr; +} + +void ChromeContentBrowserClient::OnSharedStorageWorkletHostCreated( + content::RenderFrameHost* rfh) { + if (auto* observer = + page_load_metrics::MetricsWebContentsObserver::FromWebContents( + WebContents::FromRenderFrameHost(rfh))) { + observer->OnSharedStorageWorkletHostCreated(rfh); + } +} + +bool ChromeContentBrowserClient::ShouldSendOutermostOriginToRenderer( + const url::Origin& outermost_origin) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // We only want to send the outermost origin if it is an extension scheme. + // We do not send the outermost origin to every renderer to avoid leaking + // additional information into the renderer about the embedder. For + // extensions though this is required for the way content injection API + // works. We do not want one extension injecting content into the context + // of another extension. + return outermost_origin.scheme() == extensions::kExtensionScheme; +#else + return false; +#endif +} + +bool ChromeContentBrowserClient::IsFileSystemURLNavigationAllowed( + content::BrowserContext* browser_context, + const GURL& url) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + // filesystem: URLs for Chrome Apps are in the following format: + // `filesystem:chrome-extension:///...` + if (!url.SchemeIsFileSystem()) + return false; + // Once converted into an origin, we expect the following: + // scheme() is chrome-extension: (filesystem: is automatically discarded) + // host() is the extension-id + const url::Origin origin = url::Origin::Create(url); + if (origin.scheme() == extensions::kExtensionScheme) { + const Extension* extension = + extensions::ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetByID(origin.host()); + DCHECK(extension); + return extension->is_platform_app(); + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + return false; +} + +bool ChromeContentBrowserClient::AreIsolatedWebAppsEnabled( + content::BrowserContext* browser_context) { +#if !BUILDFLAG(IS_ANDROID) + return ChromeContentBrowserClientIsolatedWebAppsPart:: + AreIsolatedWebAppsEnabled(browser_context); +#else // BUILDFLAG(IS_ANDROID) + return false; +#endif +} + +bool ChromeContentBrowserClient::IsThirdPartyStoragePartitioningAllowed( + content::BrowserContext* browser_context, + const url::Origin& top_level_origin) { + const HostContentSettingsMap* const content_settings = + HostContentSettingsMapFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + if (!content_settings) { + // We fail permissive as this function is used to check whether partitioning + // should be blocked, but isn't the final word on if it's allowed. + return true; + } + return content_settings->GetContentSetting( + top_level_origin.GetURL(), top_level_origin.GetURL(), + ContentSettingsType::THIRD_PARTY_STORAGE_PARTITIONING) == + CONTENT_SETTING_ALLOW; +} + +bool ChromeContentBrowserClient::AreDeprecatedAutomaticBeaconCredentialsAllowed( + content::BrowserContext* browser_context, + const GURL& destination_url, + const url::Origin& top_frame_origin) { + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + return cookie_settings->IsFullCookieAccessAllowed( + destination_url, net::SiteForCookies(), top_frame_origin, + cookie_settings->SettingOverridesForStorage()); +} + +bool ChromeContentBrowserClient:: + IsTransientActivationRequiredForShowFileOrDirectoryPicker( + content::WebContents* web_contents) { +#if !BUILDFLAG(IS_ANDROID) + return IsFileOrDirectoryPickerWithoutGestureAllowed(web_contents); +#else // !BUILDFLAG(IS_ANDROID) + return true; +#endif // !BUILDFLAG(IS_ANDROID) +} + +bool ChromeContentBrowserClient::IsTransientActivationRequiredForHtmlFullscreen( + content::RenderFrameHost* render_frame_host) { + if (base::FeatureList::IsEnabled( + features::kAutomaticFullscreenContentSetting)) { + const GURL& url = render_frame_host->GetLastCommittedURL(); + const HostContentSettingsMap* const content_settings = + HostContentSettingsMapFactory::GetForProfile( + render_frame_host->GetBrowserContext()); + if (content_settings->GetContentSetting( + url, url, ContentSettingsType::AUTOMATIC_FULLSCREEN) == + CONTENT_SETTING_ALLOW) { + return false; + } + } + + return true; +} + +#if BUILDFLAG(IS_MAC) +std::string ChromeContentBrowserClient::GetChildProcessSuffix(int child_flags) { + if (child_flags == chrome::kChildProcessHelperAlerts) { + return chrome::kMacHelperSuffixAlerts; + } + NOTREACHED() << "Unsupported child process flags!"; + return {}; +} +#endif // BUILDFLAG(IS_MAC) + +bool ChromeContentBrowserClient::ShouldUseFirstPartyStorageKey( + const url::Origin& origin) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return origin.scheme() == extensions::kExtensionScheme; +#else + return false; +#endif // BUILDFLAG(ENABLE_EXTENSIONS) +} + +std::unique_ptr +ChromeContentBrowserClient::CreateResponsivenessCalculatorDelegate() { +#if !BUILDFLAG(IS_ANDROID) + return ChromeResponsivenessCalculatorDelegate::Create(); +#else + return nullptr; +#endif +} + +// static +bool ChromeContentBrowserClient::DoesGaiaOriginRequireDedicatedProcess() { +#if !BUILDFLAG(IS_ANDROID) + return true; +#else + // Sign-in process isolation is not strictly needed on Android, see + // https://crbug.com/739418. On Android, it's more optional but it does + // improve security generally and specifically it allows the exposure of + // certain optional privileged APIs. + + // Kill switch that falls back to the legacy behavior. + if (!base::FeatureList::IsEnabled(kAllowGaiaOriginIsolationOnAndroid)) { + return false; + } + + if (site_isolation::SiteIsolationPolicy:: + ShouldDisableSiteIsolationDueToMemoryThreshold( + content::SiteIsolationMode::kPartialSiteIsolation)) { + // Insufficient memory to isolate Gaia's origin. + return false; + } + + return true; +#endif // !BUILDFLAG(IS_ANDROID) +} + +bool ChromeContentBrowserClient::CanBackForwardCachedPageReceiveCookieChanges( + content::BrowserContext& browser_context, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const std::optional& top_frame_origin, + const net::CookieSettingOverrides overrides) { + scoped_refptr cookie_settings = + CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(&browser_context)); + CHECK(cookie_settings); + return cookie_settings->IsFullCookieAccessAllowed( + url, site_for_cookies, top_frame_origin, overrides); +} + +void ChromeContentBrowserClient::GetCloudIdentifiers( + const storage::FileSystemURL& url, + content::FileSystemAccessPermissionContext::HandleType handle_type, + GetCloudIdentifiersCallback callback) { +#if BUILDFLAG(IS_CHROMEOS) + cloud_identifier::GetCloudIdentifierFromAsh(url, handle_type, + std::move(callback)); +#else // BUILDFLAG(IS_CHROMEOS) + return ContentBrowserClient::GetCloudIdentifiers(url, handle_type, + std::move(callback)); +#endif // BUILDFLAG(IS_CHROMEOS) +} + +bool ChromeContentBrowserClient:: + ShouldAllowBackForwardCacheForCacheControlNoStorePage( + content::BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +#if BUILDFLAG(IS_CHROMEOS) + // Do not store CCNS page into BFCache in the kiosk session. + if (chromeos::IsKioskSession()) { + return false; + } +#endif + + if (chrome::IsRunningInAppMode()) { + return false; + } + + const PrefService::Preference* pref = + Profile::FromBrowserContext(browser_context) + ->GetPrefs() + ->FindPreference( + policy::policy_prefs:: + kAllowBackForwardCacheForCacheControlNoStorePageEnabled); + if (pref && pref->IsManaged() && pref->GetValue()->is_bool()) { + return pref->GetValue()->GetBool(); + } + // If the pref is not found or not managed, BFCaching CCNS page should be + // enabled by default. + return true; +} + +void ChromeContentBrowserClient::SetIsMinimalMode(bool minimal) { + is_minimal_mode_ = minimal; +} + +bool ChromeContentBrowserClient:: + UseOutermostMainFrameOrEmbedderForSubCaptureTargets() const { +#if BUILDFLAG(PLATFORM_CFM) + return true; +#else + return false; +#endif +} + +#if !BUILDFLAG(IS_ANDROID) +void ChromeContentBrowserClient::BindVideoEffectsManager( + const std::string& device_id, + content::BrowserContext* browser_context, + mojo::PendingReceiver + video_effects_manager) { + media_effects::BindVideoEffectsManager(device_id, browser_context, + std::move(video_effects_manager)); +} +#endif // !BUILDFLAG(IS_ANDROID) + +void ChromeContentBrowserClient::PreferenceRankAudioDeviceInfos( + content::BrowserContext* browser_context, + blink::WebMediaDeviceInfoArray& infos) { + if (!user_prefs::UserPrefs::IsInitialized(browser_context)) { + return; + } + auto* prefs = user_prefs::UserPrefs::Get(browser_context); + CHECK(prefs); + media_prefs::PreferenceRankAudioDeviceInfos(*prefs, infos); +} + +void ChromeContentBrowserClient::PreferenceRankVideoDeviceInfos( + content::BrowserContext* browser_context, + blink::WebMediaDeviceInfoArray& infos) { + if (!user_prefs::UserPrefs::IsInitialized(browser_context)) { + return; + } + auto* prefs = user_prefs::UserPrefs::Get(browser_context); + CHECK(prefs); + media_prefs::PreferenceRankVideoDeviceInfos(*prefs, infos); +} + +network::mojom::IpProtectionProxyBypassPolicy +ChromeContentBrowserClient::GetIpProtectionProxyBypassPolicy() { + return network::mojom::IpProtectionProxyBypassPolicy:: + kFirstPartyToTopLevelFrame; +} + +void ChromeContentBrowserClient::MaybePrewarmHttpDiskCache( + content::BrowserContext& browser_context, + const GURL& navigation_url) { + Profile* profile = Profile::FromBrowserContext(&browser_context); + CHECK(profile); + + // `loading_predictor` can be nullptr if the profile `IsOffTheRecord`. + if (predictors::LoadingPredictor* loading_predictor = + predictors::LoadingPredictorFactory::GetForProfile(profile)) { + loading_predictor->MaybePrewarmResources(navigation_url); + } +} + +#if BUILDFLAG(IS_CHROMEOS) +void ChromeContentBrowserClient::NotifyMultiCaptureStateChanged( + content::GlobalRenderFrameHostId capturer_rfh_id, + const std::string& label, + MultiCaptureChanged state) { + content::WebContents* const web_contents = WebContents::FromRenderFrameHost( + RenderFrameHost::FromID(capturer_rfh_id)); + if (!web_contents) { + return; + } + + switch (state) { + case MultiCaptureChanged::kStarted: + NotifyMultiCaptureStarted( + label, web_contents, + web_app::WebAppTabHelper::GetAppId(web_contents)); + break; + case MultiCaptureChanged::kStopped: + NotifyMultiCaptureStopped(label); + break; + } +} +#endif // BUILDFLAG(IS_CHROMEOS) + +std::unique_ptr +ChromeContentBrowserClient::CreateDipsDelegate() { + return std::make_unique(); +} + +bool ChromeContentBrowserClient::ShouldSuppressAXLoadComplete( + RenderFrameHost* rfh) { + CHECK(rfh); + WebContents* web_contents = WebContents::FromRenderFrameHost(rfh); + + const GURL& url = web_contents->GetVisibleURL(); + return url == GURL(chrome::kChromeUINewTabURL) || + url == GURL(chrome::kChromeUINewTabPageURL); +} diff --git a/tools/under-control/src/chrome/browser/ui/webui/chrome_content_browser_client_webui_part.cc b/tools/under-control/src/chrome/browser/ui/webui/chrome_content_browser_client_webui_part.cc new file mode 100755 index 00000000..bb8b5940 --- /dev/null +++ b/tools/under-control/src/chrome/browser/ui/webui/chrome_content_browser_client_webui_part.cc @@ -0,0 +1,120 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/chrome_content_browser_client_webui_part.h" + +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/webui_url_constants.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "extensions/common/constants.h" +#include "third_party/blink/public/common/web_preferences/web_preferences.h" + +namespace { + +// Returns whether any prefs were changed. +bool CopyFontPrefs(const blink::web_pref::WebPreferences& source, + blink::web_pref::WebPreferences* destination) { + bool changed = false; + changed |= destination->default_font_size != source.default_font_size; + changed |= + destination->default_fixed_font_size != source.default_fixed_font_size; + changed |= destination->minimum_font_size != source.minimum_font_size; + changed |= destination->minimum_logical_font_size != + source.minimum_logical_font_size; + + if (!changed) { + return false; + } + + destination->default_font_size = source.default_font_size; + destination->default_fixed_font_size = source.default_fixed_font_size; + destination->minimum_font_size = source.minimum_font_size; + destination->minimum_logical_font_size = source.minimum_logical_font_size; + + return true; +} + +// Returns the visible URL or GURL() if unavailable. +GURL GetVisibleURL(content::WebContents* web_contents) { + if (!web_contents) { + return GURL(); + } + + content::NavigationEntry* entry = + web_contents->GetController().GetVisibleEntry(); + return entry ? entry->GetURL() : GURL(); +} + +} // namespace + +ChromeContentBrowserClientWebUiPart::ChromeContentBrowserClientWebUiPart() = + default; +ChromeContentBrowserClientWebUiPart::~ChromeContentBrowserClientWebUiPart() = + default; + +void ChromeContentBrowserClientWebUiPart::OverrideWebkitPrefs( + content::WebContents* web_contents, + blink::web_pref::WebPreferences* web_prefs) { + // This logic is invoked at startup, and anytime the default prefs change. + GURL url = GetVisibleURL(web_contents); + + if (!url.SchemeIs(content::kChromeUIScheme)) { + return; + } + + // Use default font sizes for WebUi. + blink::web_pref::WebPreferences default_prefs; + CopyFontPrefs(/*source=*/default_prefs, /*destination=*/web_prefs); + +#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) + // Set some non-font prefs for webui tabstrip. The tabstrip renderer is never + // navigated to or from, so we don't need to replicate this logic in + // OverrideWebPreferencesAfterNavigation. + if (url.host_piece() == chrome::kChromeUITabStripHost) { + web_prefs->touch_drag_drop_enabled = true; + web_prefs->touch_dragend_context_menu = true; + } +#endif +} + +bool ChromeContentBrowserClientWebUiPart::OverrideWebPreferencesAfterNavigation( + content::WebContents* web_contents, + blink::web_pref::WebPreferences* web_prefs) { + // This logic is invoked once on each navigation. + + GURL url = GetVisibleURL(web_contents); + if (!url.is_valid()) { + return false; + } + + // Extensions are handled by ChromeContentBrowserClientExtensionsPart. + const GURL& site_url = + web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(); + if (site_url.SchemeIs(extensions::kExtensionScheme)) { + return false; + } + + blink::web_pref::WebPreferences web_prefs_source; + if (url.SchemeIs(content::kChromeUIScheme)) { + // Use default prefs for WebUi. Not further modifications necessary for + // web_prefs_source. + } else { + // Use profile prefs for normal websites. + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + PrefService* prefs = profile->GetPrefs(); + web_prefs_source.default_font_size = + prefs->GetInteger(prefs::kWebKitDefaultFontSize); + web_prefs_source.default_fixed_font_size = + prefs->GetInteger(prefs::kWebKitDefaultFixedFontSize); + web_prefs_source.minimum_font_size = + prefs->GetInteger(prefs::kWebKitMinimumFontSize); + web_prefs_source.minimum_logical_font_size = + prefs->GetInteger(prefs::kWebKitMinimumLogicalFontSize); + } + return CopyFontPrefs(web_prefs_source, web_prefs); +} diff --git a/tools/under-control/src/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/tools/under-control/src/third_party/blink/public/mojom/webpreferences/web_preferences.mojom new file mode 100755 index 00000000..c3ff064d --- /dev/null +++ b/tools/under-control/src/third_party/blink/public/mojom/webpreferences/web_preferences.mojom @@ -0,0 +1,479 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module blink.mojom; + +import "third_party/blink/public/mojom/css/preferred_color_scheme.mojom"; +import "third_party/blink/public/mojom/css/preferred_contrast.mojom"; +import "third_party/blink/public/mojom/v8_cache_options.mojom"; +import "url/mojom/url.mojom"; +import "mojo/public/mojom/base/string16.mojom"; + +enum PointerType { + kPointerNone = 1, // 1 << 0 + kPointerFirstType = kPointerNone, + kPointerCoarseType = 2, // 1 << 1 + kPointerFineType = 4 // 1 << 2 +}; + +enum HoverType { + kHoverNone = 1, // 1 << 0 + kHoverFirstType = kHoverNone, + kHoverHoverType = 2 // 1 << 1 +}; + +// For media feature update, indicating the update ability of the output display, +// once renering is done: +// Slow - e-ink screens, underpowered devices. +// Fast - regular computer screen. +enum OutputDeviceUpdateAbilityType { + kSlowType, + kFastType +}; + +// There are multiple editing details that are different on Windows than +// Macintosh. We use a single switch for all of them. Some examples: +// +// 1) Clicking below the last line of an editable area puts the caret at the +// end of the last line on Mac, but in the middle of the last line on +// Windows. +// 2) Pushing the down arrow key on the last line puts the caret at the end +// of the last line on Mac, but does nothing on Windows. A similar case +// exists on the top line. +// +// This setting is intended to control these sorts of behaviors. There are some +// other behaviors with individual function calls on EditorClient (smart copy +// and paste and selecting the space after a double click) that could be +// combined with this if if possible in the future. +enum EditingBehavior { + kEditingMacBehavior, + kEditingWindowsBehavior, + kEditingUnixBehavior, + kEditingAndroidBehavior, + kEditingChromeOSBehavior +}; + +// ImageAnimationPolicy is used for controlling image animation +// when image frame is rendered for animation. +// See third_party/WebKit/Source/platform/graphics/ImageAnimationPolicy.h +// for information on the options. +enum ImageAnimationPolicy { + kImageAnimationPolicyAllowed, + kImageAnimationPolicyAnimateOnce, + kImageAnimationPolicyNoAnimation +}; + +enum ViewportStyle { kDefault, kMobile, kTelevision, kLast = kTelevision }; + +// Defines the autoplay policy to be used. Should match the class in +// WebSettings.h. +enum AutoplayPolicy { + kNoUserGestureRequired, + kUserGestureRequired, + kDocumentUserActivationRequired, +}; + +enum EffectiveConnectionType { + // Effective connection type reported when the network quality is unknown. + kEffectiveConnectionUnknownType, + + // Effective connection type reported when the Internet is unreachable + // because the device does not have a connection (as reported by underlying + // platform APIs). Note that due to rare but potential bugs in the platform + // APIs, it is possible that effective connection type is reported as + // EFFECTIVE_CONNECTION_TYPE_OFFLINE. Callers must use caution when using + // acting on this. + kEffectiveConnectionOfflineType, + + // Effective connection type reported when the network has the quality of a + // poor 2G connection. + kEffectiveConnectionSlow2GType, + + // Effective connection type reported when the network has the quality of a + // faster 2G connection. + kEffectiveConnection2GType, + + // Effective connection type reported when the network has the quality of a 3G + // connection. + kEffectiveConnection3GType, + + // Effective connection type reported when the network has the quality of a 4G + // connection. + kEffectiveConnection4GType, + + // Last value of the effective connection type. This value is unused. + kEffectiveConnectionTypeLast, +}; + +struct WebPreferences { + map standard_font_family_map; + map fixed_font_family_map; + map serif_font_family_map; + map sans_serif_font_family_map; + map cursive_font_family_map; + map fantasy_font_family_map; + map math_font_family_map; + int32 default_font_size; + int32 default_fixed_font_size; + int32 minimum_font_size; + int32 minimum_logical_font_size; + string default_encoding; + bool context_menu_on_mouse_up; + bool javascript_enabled; + bool web_security_enabled; + bool loads_images_automatically; + bool images_enabled; + bool plugins_enabled; + bool dom_paste_enabled; + bool shrinks_standalone_images_to_fit; + bool text_areas_are_resizable; + bool allow_scripts_to_close_windows; + bool remote_fonts_enabled; + bool javascript_can_access_clipboard; + // We don't use dns_prefetching_enabled to disable DNS prefetching. Instead, + // we disable the feature at a lower layer so that we catch non-WebKit uses + // of DNS prefetch as well. + bool dns_prefetching_enabled; + // Preference to save data. When enabled, requests will contain the header + // 'Save-Data: on'. + bool data_saver_enabled; + bool local_storage_enabled; + bool databases_enabled; + bool tabs_to_links; + bool disable_ipc_flooding_protection; + bool hyperlink_auditing_enabled; + bool allow_universal_access_from_file_urls; + bool allow_file_access_from_file_urls; + bool webgl1_enabled; + bool webgl2_enabled; + bool pepper_3d_enabled; + bool privileged_webgl_extensions_enabled; + bool webgl_errors_to_console_enabled; + bool hide_scrollbars; + bool prefers_default_scrollbar_styles; + bool accelerated_2d_canvas_enabled; + bool canvas_2d_layers_enabled; + bool antialiased_2d_canvas_disabled; + bool antialiased_clips_2d_canvas_enabled; + bool accelerated_filters_enabled; + bool deferred_filters_enabled; + bool container_culling_enabled; + bool allow_running_insecure_content; + // If true, taint32s all elements, regardless of origin. + bool disable_reading_from_canvas; + // Strict mixed content checking disables both displaying and running insecure + // mixed content, and disables embedder notifications that such content was + // requested (thereby preventing user override). + bool strict_mixed_content_checking; + // Strict powerful feature restrictions block insecure usage of powerful + // features (like device orientation) that we haven't yet disabled for the web + // at large. + bool strict_powerful_feature_restrictions; + // TODO(jww): Remove when WebView no longer needs this exception. + bool allow_geolocation_on_insecure_origins; + // Disallow user opt-in for blockable mixed content. + bool strictly_block_blockable_mixed_content; + bool block_mixed_plugin_content; + bool password_echo_enabled; + bool should_clear_document_background; + bool enable_scroll_animator; + bool prefers_reduced_motion; + bool prefers_reduced_transparency; + bool inverted_colors; + bool touch_event_feature_detection_enabled; + int32 pointer_events_max_touch_points; + int32 available_pointer_types; + PointerType primary_pointer_type; + OutputDeviceUpdateAbilityType output_device_update_ability_type; + int32 available_hover_types; + HoverType primary_hover_type; + bool dont_send_key_events_to_javascript; + bool barrel_button_for_drag_enabled; + bool sync_xhr_in_documents_enabled; + bool target_blank_implies_no_opener_enabled_will_be_removed; + bool allow_non_empty_navigator_plugins; + int32 number_of_cpu_cores; + EditingBehavior editing_behavior; + bool supports_multiple_windows; + bool viewport_enabled; + bool viewport_meta_enabled; + bool auto_zoom_focused_editable_to_legible_scale; + + // If true - Blink will clamp the minimum scale factor to the content width, + // preventing zoom beyond the visible content. This is really only needed if + // viewport_enabled is on. + bool shrinks_viewport_contents_to_fit; + + ViewportStyle viewport_style; + bool smooth_scroll_for_find_enabled; + bool main_frame_resizes_are_orientation_changes; + bool initialize_at_minimum_page_scale; + bool smart_insert_delete_enabled; + bool spatial_navigation_enabled; + bool fake_no_alloc_direct_call_for_testing_enabled; + V8CacheOptions v8_cache_options; + bool record_whole_document; + + // If true, stylus handwriting recognition to text input will be available in + // editable input fields which are non-password type. + bool stylus_handwriting_enabled; + + // This flags corresponds to a Page's Settings' setCookieEnabled state. It + // only controls whether or not the "document.cookie" field is properly + // connected to the backing store, for instance if you wanted to be able to + // define custom getters and setters from within a unique security content + // without raising a DOM security exception. + bool cookie_enabled; + + // This flag indicates whether H/W accelerated video decode is enabled. + // Defaults to false. + bool accelerated_video_decode_enabled; + + ImageAnimationPolicy animation_policy; + + bool user_gesture_required_for_presentation; + + bool text_tracks_enabled; + + // These fields specify the foreground and background color for WebVTT text + // tracks. Their values can be any legal CSS color descriptor. + string text_track_background_color; + string text_track_text_color; + + // These fields specify values for CSS properties used to style WebVTT text + // tracks. + // Specifies CSS font-size property in percentage. + string text_track_text_size; + string text_track_text_shadow; + string text_track_font_family; + string text_track_font_style; + // Specifies the value for CSS font-variant property. + string text_track_font_variant; + + // These fields specify values for CSS properties used to style the window + // around WebVTT text tracks. + // Window color can be any legal CSS color descriptor. + string text_track_window_color; + // Window radius is in pixels. + string text_track_window_radius; + + // Specifies the margin for WebVTT text tracks as a percentage of media + // element height/width (for horizontal/vertical text respectively). + // Cues will not be placed in this margin area. + float text_track_margin_percentage; + + bool immersive_mode_enabled; + + bool double_tap_to_zoom_enabled; + + bool fullscreen_supported; + + bool text_autosizing_enabled; + + // Representation of the Web App Manifest scope if any. + url.mojom.Url web_app_scope; + + [EnableIf=is_android] + float font_scale_factor; + + [EnableIf=is_android] + int32 font_weight_adjustment; + + [EnableIf=is_android] + int32 text_size_contrast_factor; + + [EnableIf=is_android] + float device_scale_adjustment; + + [EnableIf=is_android] + bool force_enable_zoom; + + [EnableIf=is_android] + url.mojom.Url default_video_poster_url; + + [EnableIf=is_android] + bool support_deprecated_target_density_dpi; + + [EnableIf=is_android] + bool wide_viewport_quirk; + + [EnableIf=is_android] + bool use_wide_viewport; + + [EnableIf=is_android] + bool force_zero_layout_height; + + [EnableIf=is_android] + bool viewport_meta_merge_content_quirk; + + [EnableIf=is_android] + bool viewport_meta_non_user_scalable_quirk; + + [EnableIf=is_android] + bool viewport_meta_zero_values_quirk; + + [EnableIf=is_android] + bool clobber_user_agent_initial_scale_quirk; + + [EnableIf=is_android] + bool ignore_main_frame_overflow_hidden_quirk; + + [EnableIf=is_android] + bool report_screen_size_in_physical_pixels_quirk; + + // Used by Android_WebView only to support legacy apps that inject script int32o + // a top-level initial empty document and expect it to persist on navigation. + [EnableIf=is_android] + bool reuse_global_for_unowned_main_frame; + + // Specifies default setting for spellcheck when the spellcheck attribute is + // not explicitly specified. + [EnableIf=is_android] + bool spellcheck_enabled_by_default; + + // If enabled, when a video goes fullscreen, the orientation should be locked. + [EnableIf=is_android] + bool video_fullscreen_orientation_lock_enabled; + + // If enabled, fullscreen should be entered/exited when the device is rotated + // to/from the orientation of the video. + [EnableIf=is_android] + bool video_rotate_to_fullscreen_enabled; + + [EnableIf=is_android] + bool embedded_media_experience_enabled; + + // Enable 8 (#RRGGBBAA) and 4 (#RGBA) value hex colors in CSS Android + // WebView quirk (http://crbug.com/618472). + [EnableIf=is_android] + bool css_hex_alpha_color_enabled; + + // Enable support for document.scrollingElement + // WebView sets this to false to retain old documentElement behaviour + // (http://crbug.com/761016). + [EnableIf=is_android] + bool scroll_top_left_interop_enabled; + + [EnableIf=is_android] + // Don't accelerate small canvases to avoid crashes TODO(crbug.com/1004304) + bool disable_accelerated_small_canvases; + + // Disable the Web Authentication (WebAuthn) API. + // TODO(crbug.com/1284805): Remove once WebView supports WebAuthn. + [EnableIf=is_android] + bool disable_webauthn; + // TODO(crbug.com/1382970): Remove once all Content embedders on Fuchsia + // support WebAuthn. + [EnableIf=is_fuchsia] + bool disable_webauthn; + + // Enable forcibly modifying content rendering to result in a light on dark + // color scheme. + bool force_dark_mode_enabled; + + // Default (used if the page or UA doesn't override these) values for page + // scale limits. These are set directly on the WebView so there's no analogue + // in WebSettings. + float default_minimum_page_scale_factor; + float default_maximum_page_scale_factor; + + // Whether download UI should be hidden on this page. + bool hide_download_ui; + + // Whether it is a presentation receiver. + bool presentation_receiver; + + // If disabled, media controls should never be used. + bool media_controls_enabled; + + // Whether we want to disable updating selection on mutating selection range. + // This is to work around Samsung's email app issue. See + // https://crbug.com/699943 for details. + // TODO(changwan): remove this once we no longer support Android N. + bool do_not_update_selection_on_mutating_selection_range; + + // Defines the current autoplay policy. + AutoplayPolicy autoplay_policy; + + // `getDisplayMedia()`'s transient activation requirement can be bypassed via + // `ScreenCaptureWithoutGestureAllowedForOrigins` policy. + bool require_transient_activation_for_get_display_media; + + // `show{OpenFile|SaveFile|Directory}Picker()`'s user activation requirement + // can be bypassed via `FileOrDirectoryPickerWithoutGestureAllowedForOrigins` + // policy. + bool require_transient_activation_for_show_file_or_directory_picker; + + // HTML Fullscreen (e.g. `Element.requestFullscreen()`)'s transient activation + // requirement can be bypassed via the "Automatic Fullscreen" content setting. + bool require_transient_activation_for_html_fullscreen; + + // `navigator.subApps.{add|remove|list}()`'s user gesture and authorization + // can be bypassed via + // `SubAppsAPIsAllowedWithoutGestureAndAuthorizationForOrigins` policy. + bool require_transient_activation_and_user_confirmation_for_subapps_api; + + // The forced colors state for the web content. The forced colors state + // is used to evaluate the forced-colors media query, as well as determining + // when to apply system color overrides to author specified styles. + bool in_forced_colors; + + // The preferred color scheme for the web content. The scheme is used to + // evaluate the prefers-color-scheme media query and resolve UA color scheme + // to be used based on the supported-color-schemes META tag and CSS property. + PreferredColorScheme preferred_color_scheme; + + // The preferred contrast for the web content. Used to evaluate the + // prefers-contrast media query. + PreferredContrast preferred_contrast; + + // Network quality threshold below which resources from iframes are assigned + // either kVeryLow or kVeryLow Blink priority. + EffectiveConnectionType low_priority_iframes_threshold; + + // Whether Picture-in-Picture is enabled. + bool picture_in_picture_enabled; + + // Whether a translate service is available. + // blink's hrefTranslate attribute existence relies on the result. + // See https://github.com/dtapuska/html-translate + bool translate_service_available; + + // A value other than + // mojom::blink::EffectiveConnectionType::kEffectiveConnectionUnknownType + // implies that the network quality estimate related Web APIs are in the + // holdback mode. When the holdback is enabled, the related Web APIs return + // network quality estimate corresponding to + // |network_quality_estimator_web_holdback| regardless of the actual quality. + EffectiveConnectionType network_quality_estimator_web_holdback; + + // Whether lazy loading of frames and images is enabled. + bool lazy_load_enabled; + + // Setting to false disables upgrades to HTTPS for HTTP resources in HTTPS + // sites. + bool allow_mixed_content_upgrades; + + // Whether the focused element should always be indicated (for example, by + // forcing :focus-visible to match regardless of focus method). + bool always_show_focus; + + // Whether touch input can trigger HTML drag-and-drop operations. The + // default value depends on the platform. + bool touch_drag_drop_enabled; + + // Controls whether WebXR's immersive-ar is allowed. + bool webxr_immersive_ar_allowed; + + // Whether lookup of frames in the associated WebView (e.g. lookup via + // window.open or via ) should be renderer-wide (i.e. going + // beyond the usual opener-relationship-based BrowsingInstance boundaries). + bool renderer_wide_named_frame_lookup; + + bool strict_mime_type_check_for_worker_scripts_enabled = true; + + // Whether modal context menu is used. A modal context menu meaning it is + // blocking user's access to the background web content. + bool modal_context_menu = true; +}; diff --git a/tools/under-control/src/third_party/blink/renderer/core/css/media_feature_names.json5 b/tools/under-control/src/third_party/blink/renderer/core/css/media_feature_names.json5 deleted file mode 100755 index 1c4a4ecb..00000000 --- a/tools/under-control/src/third_party/blink/renderer/core/css/media_feature_names.json5 +++ /dev/null @@ -1,81 +0,0 @@ -{ - metadata: { - namespace: "media_feature_names", - export: "CORE_EXPORT", - }, - - data: [ - "any-hover", - "any-pointer", - "block-size", - "color", - "color-index", - "color-gamut", - "forced-colors", - "grid", - "monochrome", - "height", - "hover", - "width", - "orientation", - "aspect-ratio", - "device-aspect-ratio", - "dynamic-range", - "video-dynamic-range", - "-webkit-device-pixel-ratio", - "device-height", - "device-width", - "display-mode", - "display-state", - "inline-size", - "max-color", - "max-color-index", - "max-aspect-ratio", - "max-block-size", - "min-block-size", - "max-device-aspect-ratio", - "-webkit-max-device-pixel-ratio", - "max-device-height", - "max-device-width", - "max-height", - "max-inline-size", - "max-monochrome", - "max-width", - "max-resolution", - "min-color", - "min-color-index", - "min-aspect-ratio", - "min-device-aspect-ratio", - "-webkit-min-device-pixel-ratio", - "min-device-height", - "min-device-width", - "min-height", - "min-inline-size", - "min-monochrome", - "min-width", - "min-resolution", - "navigation-controls", - // This feature only exists to test out origin-trial infrastructure. - "origin-trial-test", - "pointer", - "prefers-color-scheme", - "prefers-contrast", - "prefers-reduced-motion", - "prefers-reduced-data", - "prefers-reduced-transparency", - "resolution", - "-webkit-transform-3d", - "scan", - "overflow-inline", - "overflow-block", - "update", - "device-posture", - "horizontal-viewport-segments", - "vertical-viewport-segments", - "snapped", - "stuck", - "inverted-colors", - "scripting", - "resizable", - ], -} diff --git a/tools/under-control/src/third_party/blink/renderer/core/exported/web_view_impl.cc b/tools/under-control/src/third_party/blink/renderer/core/exported/web_view_impl.cc new file mode 100755 index 00000000..fb042e63 --- /dev/null +++ b/tools/under-control/src/third_party/blink/renderer/core/exported/web_view_impl.cc @@ -0,0 +1,4108 @@ +/* + * Copyright (C) 2011, 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "third_party/blink/renderer/core/exported/web_view_impl.h" + +#include +#include +#include + +#include "base/command_line.h" +#include "base/debug/crash_logging.h" +#include "base/debug/dump_without_crashing.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/histogram_macros.h" +#include "base/observer_list.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "cc/layers/picture_layer.h" +#include "components/viz/common/features.h" +#include "media/base/media_switches.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/history/session_history_constants.h" +#include "third_party/blink/public/common/input/web_input_event.h" +#include "third_party/blink/public/common/input/web_menu_source_type.h" +#include "third_party/blink/public/common/page/color_provider_color_maps.h" +#include "third_party/blink/public/common/page/page_zoom.h" +#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h" +#include "third_party/blink/public/common/switches.h" +#include "third_party/blink/public/common/web_preferences/web_preferences.h" +#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom-blink.h" +#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h" +#include "third_party/blink/public/mojom/window_features/window_features.mojom-blink.h" +#include "third_party/blink/public/platform/interface_registry.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" +#include "third_party/blink/public/platform/web_media_player.h" +#include "third_party/blink/public/platform/web_network_state_notifier.h" +#include "third_party/blink/public/platform/web_runtime_features.h" +#include "third_party/blink/public/platform/web_text_input_info.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/public/platform/web_vector.h" +#include "third_party/blink/public/web/web_autofill_client.h" +#include "third_party/blink/public/web/web_console_message.h" +#include "third_party/blink/public/web/web_element.h" +#include "third_party/blink/public/web/web_frame.h" +#include "third_party/blink/public/web/web_hit_test_result.h" +#include "third_party/blink/public/web/web_input_element.h" +#include "third_party/blink/public/web/web_local_frame_client.h" +#include "third_party/blink/public/web/web_meaningful_layout.h" +#include "third_party/blink/public/web/web_navigation_type.h" +#include "third_party/blink/public/web/web_node.h" +#include "third_party/blink/public/web/web_plugin.h" +#include "third_party/blink/public/web/web_range.h" +#include "third_party/blink/public/web/web_render_theme.h" +#include "third_party/blink/public/web/web_view_client.h" +#include "third_party/blink/public/web/web_window_features.h" +#include "third_party/blink/renderer/core/clipboard/data_object.h" +#include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" +#include "third_party/blink/renderer/core/core_initializer.h" +#include "third_party/blink/renderer/core/css_value_keywords.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/events/native_event_listener.h" +#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h" +#include "third_party/blink/renderer/core/dom/text.h" +#include "third_party/blink/renderer/core/editing/editing_utilities.h" +#include "third_party/blink/renderer/core/editing/editor.h" +#include "third_party/blink/renderer/core/editing/ephemeral_range.h" +#include "third_party/blink/renderer/core/editing/frame_selection.h" +#include "third_party/blink/renderer/core/editing/ime/edit_context.h" +#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h" +#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h" +#include "third_party/blink/renderer/core/editing/selection_template.h" +#include "third_party/blink/renderer/core/editing/serializers/html_interchange.h" +#include "third_party/blink/renderer/core/editing/serializers/serialization.h" +#include "third_party/blink/renderer/core/events/current_input_event.h" +#include "third_party/blink/renderer/core/events/keyboard_event.h" +#include "third_party/blink/renderer/core/events/ui_event_with_key_state.h" +#include "third_party/blink/renderer/core/events/web_input_event_conversion.h" +#include "third_party/blink/renderer/core/events/wheel_event.h" +#include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h" +#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" +#include "third_party/blink/renderer/core/exported/web_settings_impl.h" +#include "third_party/blink/renderer/core/frame/browser_controls.h" +#include "third_party/blink/renderer/core/frame/dom_window.h" +#include "third_party/blink/renderer/core/frame/event_handler_registry.h" +#include "third_party/blink/renderer/core/frame/fullscreen_controller.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/local_frame_client.h" +#include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h" +#include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h" +#include "third_party/blink/renderer/core/frame/remote_frame.h" +#include "third_party/blink/renderer/core/frame/resize_viewport_anchor.h" +#include "third_party/blink/renderer/core/frame/rotation_viewport_anchor.h" +#include "third_party/blink/renderer/core/frame/settings.h" +#include "third_party/blink/renderer/core/frame/viewport_data.h" +#include "third_party/blink/renderer/core/frame/visual_viewport.h" +#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h" +#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" +#include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h" +#include "third_party/blink/renderer/core/fullscreen/fullscreen.h" +#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h" +#include "third_party/blink/renderer/core/html/html_plugin_element.h" +#include "third_party/blink/renderer/core/html/plugin_document.h" +#include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h" +#include "third_party/blink/renderer/core/input/event_handler.h" +#include "third_party/blink/renderer/core/input/touch_action_util.h" +#include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h" +#include "third_party/blink/renderer/core/layout/layout_embedded_content.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/text_autosizer.h" +#include "third_party/blink/renderer/core/loader/document_loader.h" +#include "third_party/blink/renderer/core/loader/frame_load_request.h" +#include "third_party/blink/renderer/core/loader/frame_loader.h" +#include "third_party/blink/renderer/core/loader/interactive_detector.h" +#include "third_party/blink/renderer/core/loader/no_state_prefetch_client.h" +#include "third_party/blink/renderer/core/page/chrome_client_impl.h" +#include "third_party/blink/renderer/core/page/context_menu_controller.h" +#include "third_party/blink/renderer/core/page/context_menu_provider.h" +#include "third_party/blink/renderer/core/page/focus_controller.h" +#include "third_party/blink/renderer/core/page/frame_tree.h" +#include "third_party/blink/renderer/core/page/link_highlight.h" +#include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/page/page_popup_client.h" +#include "third_party/blink/renderer/core/page/pointer_lock_controller.h" +#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" +#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" +#include "third_party/blink/renderer/core/paint/timing/paint_timing.h" +#include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h" +#include "third_party/blink/renderer/core/probe/core_probes.h" +#include "third_party/blink/renderer/core/scroll/scroll_into_view_util.h" +#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h" +#include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h" +#include "third_party/blink/renderer/core/timing/dom_window_performance.h" +#include "third_party/blink/renderer/core/timing/performance.h" +#include "third_party/blink/renderer/core/timing/window_performance.h" +#include "third_party/blink/renderer/core/view_transition/view_transition_supplement.h" +#include "third_party/blink/renderer/platform/fonts/font_cache.h" +#include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h" +#include "third_party/blink/renderer/platform/graphics/image.h" +#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h" +#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" +#include "third_party/blink/renderer/platform/instrumentation/histogram.h" +#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" +#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" +#include "third_party/blink/renderer/platform/keyboard_codes.h" +#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h" +#include "third_party/blink/renderer/platform/theme/web_theme_engine_helper.h" +#include "third_party/blink/renderer/platform/weborigin/known_ports.h" +#include "third_party/blink/renderer/platform/widget/widget_base.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" +#include "third_party/icu/source/common/unicode/uscript.h" +#include "ui/base/ui_base_features.h" +#include "ui/gfx/geometry/skia_conversions.h" + +#if !BUILDFLAG(IS_MAC) +#include "skia/ext/legacy_display_globals.h" +#include "third_party/blink/public/platform/web_font_render_style.h" +#include "ui/gfx/font_render_params.h" +#endif + +#if BUILDFLAG(IS_WIN) +#include "third_party/blink/public/web/win/web_font_rendering.h" +#endif + +// Get rid of WTF's pow define so we can use std::pow. +#undef pow +#include // for std::pow + +#include "build/chromeos_buildflags.h" + +// The following constants control parameters for automated scaling of webpages +// (such as due to a double tap gesture or find in page etc.). These are +// experimentally determined. +static const int touchPointPadding = 32; +static const int nonUserInitiatedPointPadding = 11; +static const float minScaleDifference = 0.01f; +static const float doubleTapZoomContentDefaultMargin = 5; +static const float doubleTapZoomContentMinimumMargin = 2; +static constexpr base::TimeDelta kDoubleTapZoomAnimationDuration = + base::Milliseconds(250); +static const float doubleTapZoomAlreadyLegibleRatio = 1.2f; + +static constexpr base::TimeDelta kFindInPageAnimationDuration; + +// Constants for viewport anchoring on resize. +static const float viewportAnchorCoordX = 0.5f; +static const float viewportAnchorCoordY = 0; + +// Constants for zooming in on a focused text field. +static constexpr base::TimeDelta kScrollAndScaleAnimationDuration = + base::Milliseconds(200); +static const int minReadableCaretHeight = 16; +static const int minReadableCaretHeightForTextArea = 13; +static const float minScaleChangeToTriggerZoom = 1.5f; +static const float leftBoxRatio = 0.3f; +static const int caretPadding = 10; + +namespace blink { + +using mojom::blink::EffectiveConnectionType; + +// Historically, these values came from Webkit in +// WebKitLegacy/mac/WebView/WebView.mm (named MinimumZoomMultiplier and +// MaximumZoomMultiplier there). +const double WebView::kMinTextSizeMultiplier = 0.5; +const double WebView::kMaxTextSizeMultiplier = 3.0; + +// static +HashSet& WebViewImpl::AllInstances() { + DEFINE_STATIC_LOCAL(HashSet, all_instances, ()); + return all_instances; +} + +static bool g_should_use_external_popup_menus = false; + +void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) { + g_should_use_external_popup_menus = use_external_popup_menus; +} + +bool WebViewImpl::UseExternalPopupMenus() { + return g_should_use_external_popup_menus; +} + +namespace { + +class EmptyEventListener final : public NativeEventListener { + public: + void Invoke(ExecutionContext* execution_context, Event*) override {} +}; + +typedef void (*SetFontFamilyWrapper)(blink::WebSettings*, + const std::u16string&, + UScriptCode); + +void SetStandardFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetStandardFontFamily(WebString::FromUTF16(font), script); +} + +void SetFixedFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetFixedFontFamily(WebString::FromUTF16(font), script); +} + +void SetSerifFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetSerifFontFamily(WebString::FromUTF16(font), script); +} + +void SetSansSerifFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetSansSerifFontFamily(WebString::FromUTF16(font), script); +} + +void SetCursiveFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetCursiveFontFamily(WebString::FromUTF16(font), script); +} + +void SetFantasyFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetFantasyFontFamily(WebString::FromUTF16(font), script); +} + +void SetMathFontFamilyWrapper(WebSettings* settings, + const std::u16string& font, + UScriptCode script) { + settings->SetMathFontFamily(WebString::FromUTF16(font), script); +} + +// If |scriptCode| is a member of a family of "similar" script codes, returns +// the script code in that family that is used by WebKit for font selection +// purposes. For example, USCRIPT_KATAKANA_OR_HIRAGANA and USCRIPT_JAPANESE are +// considered equivalent for the purposes of font selection. WebKit uses the +// script code USCRIPT_KATAKANA_OR_HIRAGANA. So, if |scriptCode| is +// USCRIPT_JAPANESE, the function returns USCRIPT_KATAKANA_OR_HIRAGANA. WebKit +// uses different scripts than the ones in Chrome pref names because the version +// of ICU included on certain ports does not have some of the newer scripts. If +// |scriptCode| is not a member of such a family, returns |scriptCode|. +UScriptCode GetScriptForWebSettings(UScriptCode scriptCode) { + switch (scriptCode) { + case USCRIPT_HIRAGANA: + case USCRIPT_KATAKANA: + case USCRIPT_JAPANESE: + return USCRIPT_KATAKANA_OR_HIRAGANA; + case USCRIPT_KOREAN: + return USCRIPT_HANGUL; + default: + return scriptCode; + } +} + +void ApplyFontsFromMap(const web_pref::ScriptFontFamilyMap& map, + SetFontFamilyWrapper setter, + WebSettings* settings) { + for (auto& it : map) { + int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, (it.first).c_str()); + if (script >= 0 && script < USCRIPT_CODE_LIMIT) { + UScriptCode code = static_cast(script); + (*setter)(settings, it.second, GetScriptForWebSettings(code)); + } + } +} + +void ApplyCommandLineToSettings(WebSettings* settings) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + std::string touch_text_selection_strategy = + command_line.GetSwitchValueASCII(switches::kTouchTextSelectionStrategy); + if (touch_text_selection_strategy == + switches::kTouchTextSelectionStrategy_Character) { + settings->SetSelectionStrategy( + WebSettings::SelectionStrategyType::kCharacter); + } else if (touch_text_selection_strategy == + switches::kTouchTextSelectionStrategy_Direction) { + settings->SetSelectionStrategy( + WebSettings::SelectionStrategyType::kDirection); + } + + WebString network_quiet_timeout = WebString::FromUTF8( + command_line.GetSwitchValueASCII(switches::kNetworkQuietTimeout)); + if (!network_quiet_timeout.IsEmpty()) { + bool ok; + double network_quiet_timeout_seconds = + String(network_quiet_timeout).ToDouble(&ok); + if (ok) + settings->SetNetworkQuietTimeout(network_quiet_timeout_seconds); + } + + if (command_line.HasSwitch(switches::kBlinkSettings)) { + Vector blink_settings; + String command_line_settings = + command_line.GetSwitchValueASCII(switches::kBlinkSettings).c_str(); + command_line_settings.Split(",", blink_settings); + for (const String& setting : blink_settings) { + wtf_size_t pos = setting.find('='); + settings->SetFromStrings( + WebString(setting.Substring(0, pos)), + WebString(pos == kNotFound ? "" : setting.Substring(pos + 1))); + } + } +} + +ui::mojom::blink::WindowOpenDisposition NavigationPolicyToDisposition( + NavigationPolicy policy) { + switch (policy) { + case kNavigationPolicyDownload: + return ui::mojom::blink::WindowOpenDisposition::SAVE_TO_DISK; + case kNavigationPolicyCurrentTab: + return ui::mojom::blink::WindowOpenDisposition::CURRENT_TAB; + case kNavigationPolicyNewBackgroundTab: + return ui::mojom::blink::WindowOpenDisposition::NEW_BACKGROUND_TAB; + case kNavigationPolicyNewForegroundTab: + return ui::mojom::blink::WindowOpenDisposition::NEW_FOREGROUND_TAB; + case kNavigationPolicyNewWindow: + return ui::mojom::blink::WindowOpenDisposition::NEW_WINDOW; + case kNavigationPolicyNewPopup: + return ui::mojom::blink::WindowOpenDisposition::NEW_POPUP; + case kNavigationPolicyPictureInPicture: + return ui::mojom::blink::WindowOpenDisposition::NEW_PICTURE_IN_PICTURE; + case kNavigationPolicyLinkPreview: + NOTREACHED_NORETURN(); + } + NOTREACHED() << "Unexpected NavigationPolicy"; + return ui::mojom::blink::WindowOpenDisposition::IGNORE_ACTION; +} + +// Records the queuing duration for activation IPC. +void RecordPrerenderActivationSignalDelay() { + auto* task = base::TaskAnnotator::CurrentTaskForThread(); + + // It should be a Mojo call, so `RunTask` executes it as a non-delayed task. + CHECK(task); + CHECK(task->delayed_run_time.is_null()); + base::TimeDelta queueing_time = + !task->queue_time.is_null() ? base::TimeTicks::Now() - task->queue_time + : base::TimeDelta(); + base::UmaHistogramTimes("Prerender.Experimental.ActivationIPCDelay", + queueing_time); +} + +#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN) +SkFontHinting RendererPreferencesToSkiaHinting( + const blink::RendererPreferences& prefs) { +// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is +// complete. +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) + if (!prefs.should_antialias_text) { + // When anti-aliasing is off, GTK maps all non-zero hinting settings to + // 'Normal' hinting so we do the same. Otherwise, folks who have 'Slight' + // hinting selected will see readable text in everything expect Chromium. + switch (prefs.hinting) { + case gfx::FontRenderParams::HINTING_NONE: + return SkFontHinting::kNone; + case gfx::FontRenderParams::HINTING_SLIGHT: + case gfx::FontRenderParams::HINTING_MEDIUM: + case gfx::FontRenderParams::HINTING_FULL: + return SkFontHinting::kNormal; + default: + NOTREACHED(); + return SkFontHinting::kNormal; + } + } +#endif + + switch (prefs.hinting) { + case gfx::FontRenderParams::HINTING_NONE: + return SkFontHinting::kNone; + case gfx::FontRenderParams::HINTING_SLIGHT: + return SkFontHinting::kSlight; + case gfx::FontRenderParams::HINTING_MEDIUM: + return SkFontHinting::kNormal; + case gfx::FontRenderParams::HINTING_FULL: + return SkFontHinting::kFull; + default: + NOTREACHED(); + return SkFontHinting::kNormal; + } +} +#endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_WIN) + +} // namespace + +// WebView ---------------------------------------------------------------- + +WebView* WebView::Create( + WebViewClient* client, + bool is_hidden, + bool is_prerendering, + bool is_inside_portal, + std::optional + fenced_frame_mode, + bool compositing_enabled, + bool widgets_never_composited, + WebView* opener, + CrossVariantMojoAssociatedReceiver + page_handle, + scheduler::WebAgentGroupScheduler& agent_group_scheduler, + const SessionStorageNamespaceId& session_storage_namespace_id, + std::optional page_base_background_color, + const BrowsingContextGroupInfo& browsing_context_group_info, + const ColorProviderColorMaps* color_provider_colors) { + return WebViewImpl::Create( + client, + is_hidden ? mojom::blink::PageVisibilityState::kHidden + : mojom::blink::PageVisibilityState::kVisible, + is_prerendering, is_inside_portal, fenced_frame_mode, compositing_enabled, + widgets_never_composited, To(opener), std::move(page_handle), + agent_group_scheduler, session_storage_namespace_id, + std::move(page_base_background_color), browsing_context_group_info, + color_provider_colors); +} + +WebViewImpl* WebViewImpl::Create( + WebViewClient* client, + mojom::blink::PageVisibilityState visibility, + bool is_prerendering, + bool is_inside_portal, + std::optional + fenced_frame_mode, + bool compositing_enabled, + bool widgets_never_composited, + WebViewImpl* opener, + mojo::PendingAssociatedReceiver page_handle, + blink::scheduler::WebAgentGroupScheduler& agent_group_scheduler, + const SessionStorageNamespaceId& session_storage_namespace_id, + std::optional page_base_background_color, + const BrowsingContextGroupInfo& browsing_context_group_info, + const ColorProviderColorMaps* color_provider_colors) { + return new WebViewImpl( + client, visibility, is_prerendering, is_inside_portal, fenced_frame_mode, + compositing_enabled, widgets_never_composited, opener, + std::move(page_handle), agent_group_scheduler, + session_storage_namespace_id, std::move(page_base_background_color), + browsing_context_group_info, color_provider_colors); +} + +size_t WebView::GetWebViewCount() { + return WebViewImpl::AllInstances().size(); +} + +void WebView::UpdateVisitedLinkState(uint64_t link_hash) { + Page::VisitedStateChanged(link_hash); +} + +void WebView::ResetVisitedLinkState(bool invalidate_visited_link_hashes) { + Page::AllVisitedStateChanged(invalidate_visited_link_hashes); +} + +void WebViewImpl::SetNoStatePrefetchClient( + WebNoStatePrefetchClient* no_state_prefetch_client) { + DCHECK(page_); + ProvideNoStatePrefetchClientTo(*page_, + MakeGarbageCollected( + *page_, no_state_prefetch_client)); +} + +void WebViewImpl::CloseWindow() { +#if !(BUILDFLAG(IS_ANDROID) || \ + (BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM64))) + auto close_task_trace = close_task_posted_stack_trace_; + base::debug::Alias(&close_task_trace); + auto close_trace = close_called_stack_trace_; + base::debug::Alias(&close_trace); + auto prev_close_window_trace = close_window_called_stack_trace_; + base::debug::Alias(&prev_close_window_trace); + close_window_called_stack_trace_.emplace(); + auto cur_close_window_trace = close_window_called_stack_trace_; + base::debug::Alias(&cur_close_window_trace); +#endif + SCOPED_CRASH_KEY_BOOL("Bug1499519", "page_exists", !!page_); + + // Have the browser process a close request. We should have either a + // |local_main_frame_host_remote_| or |remote_main_frame_host_remote_|. + // This method will not execute if Close has been called as WeakPtrs + if (GetPage()->MainFrame()->IsLocalFrame()) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->RequestClose(); + } else { + DCHECK(remote_main_frame_host_remote_); + remote_main_frame_host_remote_->RouteCloseEvent(); + } +} + +WebViewImpl::WebViewImpl( + WebViewClient* client, + mojom::blink::PageVisibilityState visibility, + bool is_prerendering, + bool is_inside_portal, + std::optional + fenced_frame_mode, + bool does_composite, + bool widgets_never_composited, + WebViewImpl* opener, + mojo::PendingAssociatedReceiver page_handle, + blink::scheduler::WebAgentGroupScheduler& agent_group_scheduler, + const SessionStorageNamespaceId& session_storage_namespace_id, + std::optional page_base_background_color, + const BrowsingContextGroupInfo& browsing_context_group_info, + const ColorProviderColorMaps* color_provider_colors) + : widgets_never_composited_(widgets_never_composited), + web_view_client_(client), + chrome_client_(MakeGarbageCollected(this)), + minimum_zoom_level_(PageZoomFactorToZoomLevel(kMinimumPageZoomFactor)), + maximum_zoom_level_(PageZoomFactorToZoomLevel(kMaximumPageZoomFactor)), + does_composite_(does_composite), + fullscreen_controller_(std::make_unique(this)), + page_base_background_color_( + page_base_background_color.value_or(SK_ColorWHITE)), + receiver_(this, + std::move(page_handle), + agent_group_scheduler.DefaultTaskRunner()), + session_storage_namespace_id_(session_storage_namespace_id), + web_agent_group_scheduler_(agent_group_scheduler) { + if (receiver_) { + // Typically, the browser process closes the corresponding peer handle + // to signal the renderer process to destroy `this`. In certain + // situations where the lifetime of `this` is not controlled by a + // corresponding browser-side `RenderViewHostImpl` (e.g. tests or + // printing), call `Close()` directly instead to delete `this`. + receiver_.set_disconnect_handler( + WTF::BindOnce(&WebViewImpl::MojoDisconnected, WTF::Unretained(this))); + } + if (!web_view_client_) + DCHECK(!does_composite_); + page_ = Page::CreateOrdinary( + *chrome_client_, opener ? opener->GetPage() : nullptr, + agent_group_scheduler.GetAgentGroupScheduler(), + browsing_context_group_info, color_provider_colors); + CoreInitializer::GetInstance().ProvideModulesToPage( + *page_, session_storage_namespace_id_); + + SetVisibilityState(visibility, /*is_initial_state=*/true); + page_->SetIsPrerendering(is_prerendering); + + // TODO(crbug.com/1498140): Remove the in_inside_portal parameter. + + if (fenced_frame_mode && features::IsFencedFramesEnabled()) { + page_->SetIsMainFrameFencedFrameRoot(); + page_->SetDeprecatedFencedFrameMode(*fenced_frame_mode); + } else { + // `fenced_frame_mode` should only be set if creating an MPArch + // fenced frame. + DCHECK(!fenced_frame_mode); + } + + // When not compositing, keep the Page in the loop so that it will paint all + // content into the root layer, as multiple layers can only be used when + // compositing them together later. + if (does_composite_) + page_->GetSettings().SetAcceleratedCompositingEnabled(true); + + dev_tools_emulator_ = MakeGarbageCollected(this); + + AllInstances().insert(this); + + resize_viewport_anchor_ = MakeGarbageCollected(*page_); + + // Ensure we have valid page scale constraints even if the embedder never + // changes defaults. + GetPageScaleConstraintsSet().ComputeFinalConstraints(); +} + +WebViewImpl::~WebViewImpl() { + DCHECK(!page_); +} + +WebDevToolsAgentImpl* WebViewImpl::MainFrameDevToolsAgentImpl() { + WebLocalFrameImpl* main_frame = MainFrameImpl(); + return main_frame ? main_frame->DevToolsAgentImpl() : nullptr; +} + +void WebViewImpl::SetTabKeyCyclesThroughElements(bool value) { + if (page_) + page_->SetTabKeyCyclesThroughElements(value); +} + +bool WebViewImpl::StartPageScaleAnimation(const gfx::Point& target_position, + bool use_anchor, + float new_scale, + base::TimeDelta duration) { + // PageScaleFactor is a property of the main frame only, and only exists when + // compositing. + DCHECK(MainFrameImpl()); + DCHECK(does_composite_); + + VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); + DCHECK(visual_viewport.IsActiveViewport()); + + gfx::Point clamped_point = target_position; + if (!use_anchor) { + clamped_point = + visual_viewport.ClampDocumentOffsetAtScale(target_position, new_scale); + + // TODO(bokan): Why special case duration zero? PageScaleAnimation should + // work ok for that. + if (duration.is_zero()) { + SetPageScaleFactor(new_scale); + + LocalFrameView* view = MainFrameImpl()->GetFrameView(); + if (view && view->GetScrollableArea()) { + view->GetScrollableArea()->SetScrollOffset( + ScrollOffset(gfx::Vector2dF(clamped_point.OffsetFromOrigin())), + mojom::blink::ScrollType::kProgrammatic); + } + + return false; + } + } + if (use_anchor && new_scale == PageScaleFactor()) + return false; + + if (enable_fake_page_scale_animation_for_testing_) { + fake_page_scale_animation_target_position_ = target_position; + fake_page_scale_animation_use_anchor_ = use_anchor; + fake_page_scale_animation_page_scale_factor_ = new_scale; + } else { + MainFrameImpl()->FrameWidgetImpl()->StartPageScaleAnimation( + target_position, use_anchor, new_scale, duration); + } + return true; +} + +void WebViewImpl::EnableFakePageScaleAnimationForTesting(bool enable) { + enable_fake_page_scale_animation_for_testing_ = enable; + fake_page_scale_animation_target_position_ = gfx::Point(); + fake_page_scale_animation_use_anchor_ = false; + fake_page_scale_animation_page_scale_factor_ = 0; +} + +void WebViewImpl::AcceptLanguagesChanged() { + FontCache::AcceptLanguagesChanged( + String::FromUTF8(renderer_preferences_.accept_languages)); + + if (!GetPage()) + return; + + GetPage()->AcceptLanguagesChanged(); +} + +gfx::Rect WebViewImpl::WidenRectWithinPageBounds(const gfx::Rect& source, + int target_margin, + int minimum_margin) { + // Caller should guarantee that the main frame exists and is local. + DCHECK(MainFrame()); + DCHECK(MainFrame()->IsWebLocalFrame()); + gfx::Size max_size = MainFrame()->ToWebLocalFrame()->DocumentSize(); + gfx::PointF scroll_offset = MainFrame()->ToWebLocalFrame()->GetScrollOffset(); + + int left_margin = target_margin; + int right_margin = target_margin; + + const int absolute_source_x = source.x() + scroll_offset.x(); + if (left_margin > absolute_source_x) { + left_margin = absolute_source_x; + right_margin = std::max(left_margin, minimum_margin); + } + + const int maximum_right_margin = + max_size.width() - (source.width() + absolute_source_x); + if (right_margin > maximum_right_margin) { + right_margin = maximum_right_margin; + left_margin = std::min(left_margin, std::max(right_margin, minimum_margin)); + } + + const int new_width = source.width() + left_margin + right_margin; + const int new_x = source.x() - left_margin; + + DCHECK_GE(new_width, 0); + DCHECK_LE(scroll_offset.x() + new_x + new_width, max_size.width()); + + return gfx::Rect(new_x, source.y(), new_width, source.height()); +} + +float WebViewImpl::MaximumLegiblePageScale() const { + // Pages should be as legible as on desktop when at dpi scale, so no + // need to zoom in further when automatically determining zoom level + // (after double tap, find in page, etc), though the user should still + // be allowed to manually pinch zoom in further if they desire. + if (GetPage()) { + return maximum_legible_scale_ * + GetPage()->GetSettings().GetAccessibilityFontScaleFactor(); + } + return maximum_legible_scale_; +} + +void WebViewImpl::ComputeScaleAndScrollForBlockRect( + const gfx::Point& hit_point_in_root_frame, + const gfx::Rect& block_rect_in_root_frame, + float padding, + float default_scale_when_already_legible, + float& scale, + gfx::Point& scroll) { + DCHECK(GetPage()->GetVisualViewport().IsActiveViewport()); + scale = PageScaleFactor(); + scroll = gfx::Point(); + + gfx::Rect rect = block_rect_in_root_frame; + + if (!rect.IsEmpty()) { + float default_margin = doubleTapZoomContentDefaultMargin; + float minimum_margin = doubleTapZoomContentMinimumMargin; + // We want the margins to have the same physical size, which means we + // need to express them in post-scale size. To do that we'd need to know + // the scale we're scaling to, but that depends on the margins. Instead + // we express them as a fraction of the target rectangle: this will be + // correct if we end up fully zooming to it, and won't matter if we + // don't. + rect = WidenRectWithinPageBounds( + rect, static_cast(default_margin * rect.width() / size_.width()), + static_cast(minimum_margin * rect.width() / size_.width())); + // Fit block to screen, respecting limits. + scale = static_cast(size_.width()) / rect.width(); + scale = std::min(scale, MaximumLegiblePageScale()); + if (PageScaleFactor() < default_scale_when_already_legible) + scale = std::max(scale, default_scale_when_already_legible); + scale = ClampPageScaleFactorToLimits(scale); + } + + // FIXME: If this is being called for auto zoom during find in page, + // then if the user manually zooms in it'd be nice to preserve the + // relative increase in zoom they caused (if they zoom out then it's ok + // to zoom them back in again). This isn't compatible with our current + // double-tap zoom strategy (fitting the containing block to the screen) + // though. + + float screen_width = size_.width() / scale; + float screen_height = size_.height() / scale; + + // Scroll to vertically align the block. + if (rect.height() < screen_height) { + // Vertically center short blocks. + rect.Offset(0, -0.5 * (screen_height - rect.height())); + } else { + // Ensure position we're zooming to (+ padding) isn't off the bottom of + // the screen. + rect.set_y(std::max( + rect.y(), hit_point_in_root_frame.y() + padding - screen_height)); + } // Otherwise top align the block. + + // Do the same thing for horizontal alignment. + if (rect.width() < screen_width) { + rect.Offset(-0.5 * (screen_width - rect.width()), 0); + } else { + rect.set_x(std::max( + rect.x(), hit_point_in_root_frame.x() + padding - screen_width)); + } + scroll.set_x(rect.x()); + scroll.set_y(rect.y()); + + scale = ClampPageScaleFactorToLimits(scale); + scroll = MainFrameImpl()->GetFrameView()->RootFrameToDocument(scroll); + scroll = + GetPage()->GetVisualViewport().ClampDocumentOffsetAtScale(scroll, scale); +} + +static Node* FindLinkHighlightAncestor(Node* node) { + // Go up the tree to find the node that defines a mouse cursor style + while (node) { + const LinkHighlightCandidate type = node->IsLinkHighlightCandidate(); + if (type == LinkHighlightCandidate::kYes) + return node; + if (type == LinkHighlightCandidate::kNo) + return nullptr; + node = LayoutTreeBuilderTraversal::Parent(*node); + } + return nullptr; +} + +// This is for tap (link) highlight and is tested in +// link_highlight_impl_test.cc. +Node* WebViewImpl::BestTapNode( + const GestureEventWithHitTestResults& targeted_tap_event) { + TRACE_EVENT0("input", "WebViewImpl::bestTapNode"); + + Page* page = page_.Get(); + if (!page || !page->MainFrame()) + return nullptr; + + Node* best_touch_node = targeted_tap_event.GetHitTestResult().InnerNode(); + if (!best_touch_node) + return nullptr; + + // We might hit something like an image map that has no layoutObject on it + // Walk up the tree until we have a node with an attached layoutObject + while (!best_touch_node->GetLayoutObject()) { + best_touch_node = LayoutTreeBuilderTraversal::Parent(*best_touch_node); + if (!best_touch_node) + return nullptr; + } + + // Editable nodes should not be highlighted (e.g., ) + if (IsEditable(*best_touch_node)) + return nullptr; + + Node* hand_cursor_ancestor = FindLinkHighlightAncestor(best_touch_node); + // We show a highlight on tap only when the current node shows a hand cursor + if (!hand_cursor_ancestor) { + return nullptr; + } + + // We should pick the largest enclosing node with hand cursor set. We do this + // by first jumping up to the closest ancestor with hand cursor set. Then we + // locate the next ancestor up in the the tree and repeat the jumps as long as + // the node has hand cursor set. + do { + best_touch_node = hand_cursor_ancestor; + hand_cursor_ancestor = FindLinkHighlightAncestor( + LayoutTreeBuilderTraversal::Parent(*best_touch_node)); + } while (hand_cursor_ancestor); + + // This happens in cases like: + //
Text
. + // The text node inherits cursor: pointer and the div doesn't have a + // LayoutObject, so |best_touch_node| is the text node here. We should not + // return the text node because it can't have touch actions. + if (best_touch_node->IsTextNode()) + return nullptr; + + return best_touch_node; +} + +void WebViewImpl::EnableTapHighlightAtPoint( + const GestureEventWithHitTestResults& targeted_tap_event) { + DCHECK(MainFrameImpl()); + Node* touch_node = BestTapNode(targeted_tap_event); + GetPage()->GetLinkHighlight().SetTapHighlight(touch_node); + MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kAll, + DocumentUpdateReason::kTapHighlight); +} + +void WebViewImpl::AnimateDoubleTapZoom(const gfx::Point& point_in_root_frame, + const gfx::Rect& rect_to_zoom) { + DCHECK(MainFrameImpl()); + + float scale; + gfx::Point scroll; + + ComputeScaleAndScrollForBlockRect( + point_in_root_frame, rect_to_zoom, touchPointPadding, + MinimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio, scale, + scroll); + + bool still_at_previous_double_tap_scale = + (PageScaleFactor() == double_tap_zoom_page_scale_factor_ && + double_tap_zoom_page_scale_factor_ != MinimumPageScaleFactor()) || + double_tap_zoom_pending_; + + bool scale_unchanged = fabs(PageScaleFactor() - scale) < minScaleDifference; + bool should_zoom_out = rect_to_zoom.IsEmpty() || scale_unchanged || + still_at_previous_double_tap_scale; + + bool is_animating; + + if (should_zoom_out) { + scale = MinimumPageScaleFactor(); + gfx::Point target_position = + MainFrameImpl()->GetFrameView()->RootFrameToDocument( + gfx::Point(point_in_root_frame.x(), point_in_root_frame.y())); + is_animating = StartPageScaleAnimation(target_position, true, scale, + kDoubleTapZoomAnimationDuration); + } else { + is_animating = StartPageScaleAnimation(scroll, false, scale, + kDoubleTapZoomAnimationDuration); + } + + // TODO(dglazkov): The only reason why we're using isAnimating and not just + // checking for layer_tree_view_->HasPendingPageScaleAnimation() is because of + // fake page scale animation plumbing for testing, which doesn't actually + // initiate a page scale animation. + if (is_animating) { + double_tap_zoom_page_scale_factor_ = scale; + double_tap_zoom_pending_ = true; + } +} + +void WebViewImpl::ZoomToFindInPageRect(const gfx::Rect& rect_in_root_frame) { + DCHECK(MainFrameImpl()); + + gfx::Rect block_bounds = + MainFrameImpl()->FrameWidgetImpl()->ComputeBlockBound( + gfx::Point(rect_in_root_frame.x() + rect_in_root_frame.width() / 2, + rect_in_root_frame.y() + rect_in_root_frame.height() / 2), + true); + + if (block_bounds.IsEmpty()) { + // Keep current scale (no need to scroll as x,y will normally already + // be visible). FIXME: Revisit this if it isn't always true. + return; + } + + float scale; + gfx::Point scroll; + + ComputeScaleAndScrollForBlockRect(rect_in_root_frame.origin(), block_bounds, + nonUserInitiatedPointPadding, + MinimumPageScaleFactor(), scale, scroll); + + StartPageScaleAnimation(scroll, false, scale, kFindInPageAnimationDuration); +} + +#if !BUILDFLAG(IS_MAC) +// Mac has no way to open a context menu based on a keyboard event. +WebInputEventResult WebViewImpl::SendContextMenuEvent() { + // The contextMenuController() holds onto the last context menu that was + // popped up on the page until a new one is created. We need to clear + // this menu before propagating the event through the DOM so that we can + // detect if we create a new menu for this event, since we won't create + // a new menu if the DOM swallows the event and the defaultEventHandler does + // not run. + GetPage()->GetContextMenuController().ClearContextMenu(); + + { + ContextMenuAllowedScope scope; + Frame* focused_frame = GetPage()->GetFocusController().FocusedOrMainFrame(); + auto* focused_local_frame = DynamicTo(focused_frame); + if (!focused_local_frame) + return WebInputEventResult::kNotHandled; + // Firefox reveal focus based on "keydown" event but not "contextmenu" + // event, we match FF. + if (Element* focused_element = + focused_local_frame->GetDocument()->FocusedElement()) + focused_element->scrollIntoViewIfNeeded(); + return focused_local_frame->GetEventHandler().ShowNonLocatedContextMenu( + nullptr, kMenuSourceKeyboard); + } +} +#else +WebInputEventResult WebViewImpl::SendContextMenuEvent() { + return WebInputEventResult::kNotHandled; +} +#endif + +WebPagePopupImpl* WebViewImpl::OpenPagePopup(PagePopupClient* client) { + DCHECK(client); + + // This guarantees there is never more than 1 PagePopup active at a time. + CancelPagePopup(); + DCHECK(!page_popup_); + + LocalFrame* opener_frame = client->OwnerElement().GetDocument().GetFrame(); + WebLocalFrameImpl* web_opener_frame = + WebLocalFrameImpl::FromFrame(opener_frame); + + mojo::PendingAssociatedRemote widget; + mojo::PendingAssociatedReceiver widget_receiver = + widget.InitWithNewEndpointAndPassReceiver(); + + mojo::PendingAssociatedRemote widget_host; + mojo::PendingAssociatedReceiver + widget_host_receiver = widget_host.InitWithNewEndpointAndPassReceiver(); + + mojo::PendingAssociatedRemote + popup_widget_host; + mojo::PendingAssociatedReceiver + popup_widget_host_receiver = + popup_widget_host.InitWithNewEndpointAndPassReceiver(); + + opener_frame->GetLocalFrameHostRemote().CreateNewPopupWidget( + std::move(popup_widget_host_receiver), std::move(widget_host_receiver), + std::move(widget)); + WebFrameWidgetImpl* opener_widget = web_opener_frame->LocalRootFrameWidget(); + + AgentGroupScheduler& agent_group_scheduler = + opener_frame->GetPage()->GetPageScheduler()->GetAgentGroupScheduler(); + // The returned WebPagePopup is self-referencing, so the pointer here is not + // an owning pointer. It is de-referenced by the PopupWidgetHost disconnecting + // and calling Close(). + page_popup_ = WebPagePopupImpl::Create( + std::move(popup_widget_host), std::move(widget_host), + std::move(widget_receiver), this, agent_group_scheduler, + opener_widget->GetOriginalScreenInfos(), client); + EnablePopupMouseWheelEventListener(web_opener_frame->LocalRoot()); + return page_popup_.get(); +} + +void WebViewImpl::CancelPagePopup() { + if (page_popup_) + page_popup_->Cancel(); +} + +void WebViewImpl::ClosePagePopup(PagePopup* popup) { + DCHECK(popup); + auto* popup_impl = To(popup); + DCHECK_EQ(page_popup_.get(), popup_impl); + if (page_popup_.get() != popup_impl) + return; + page_popup_->ClosePopup(); +} + +void WebViewImpl::CleanupPagePopup() { + page_popup_ = nullptr; + DisablePopupMouseWheelEventListener(); +} + +void WebViewImpl::UpdatePagePopup() { + if (page_popup_) + page_popup_->Update(); +} + +void WebViewImpl::EnablePopupMouseWheelEventListener( + WebLocalFrameImpl* local_root) { + DCHECK(!popup_mouse_wheel_event_listener_); + Document* document = local_root->GetDocument(); + DCHECK(document); + // We register an empty event listener, EmptyEventListener, so that mouse + // wheel events get sent to the WebView. + popup_mouse_wheel_event_listener_ = + MakeGarbageCollected(); + document->addEventListener(event_type_names::kMousewheel, + popup_mouse_wheel_event_listener_, false); + local_root_with_empty_mouse_wheel_listener_ = local_root; +} + +void WebViewImpl::DisablePopupMouseWheelEventListener() { + // TODO(kenrb): Concerns the same as in enablePopupMouseWheelEventListener. + // See https://crbug.com/566130 + DCHECK(popup_mouse_wheel_event_listener_); + Document* document = + local_root_with_empty_mouse_wheel_listener_->GetDocument(); + DCHECK(document); + // Document may have already removed the event listener, for instance, due + // to a navigation, but remove it anyway. + document->removeEventListener(event_type_names::kMousewheel, + popup_mouse_wheel_event_listener_.Release(), + false); + local_root_with_empty_mouse_wheel_listener_ = nullptr; +} + +LocalDOMWindow* WebViewImpl::PagePopupWindow() const { + return page_popup_ ? page_popup_->Window() : nullptr; +} + +Frame* WebViewImpl::FocusedCoreFrame() const { + Page* page = page_.Get(); + return page ? page->GetFocusController().FocusedOrMainFrame() : nullptr; +} + +// WebWidget ------------------------------------------------------------------ + +void WebViewImpl::Close() { +#if !(BUILDFLAG(IS_ANDROID) || \ + (BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM64))) + auto close_task_trace = close_task_posted_stack_trace_; + base::debug::Alias(&close_task_trace); + auto prev_close_trace = close_called_stack_trace_; + base::debug::Alias(&prev_close_trace); + close_called_stack_trace_.emplace(); + auto cur_close_trace = close_called_stack_trace_; + base::debug::Alias(&cur_close_trace); + auto close_window_trace = close_window_called_stack_trace_; + base::debug::Alias(&close_window_trace); +#endif + SCOPED_CRASH_KEY_BOOL("Bug1499519", "page_exists", !!page_); + + // Closership is a single relationship, so only 1 call to Close() should + // occur. + CHECK(page_); + DCHECK(AllInstances().Contains(this)); + AllInstances().erase(this); + + // Ensure if we have a page popup we cancel it immediately as we do not + // want page popups to re-enter WebViewImpl during our shutdown. + CancelPagePopup(); + + // Invalidate any weak ptrs as we are starting to shutdown. + weak_ptr_factory_.InvalidateWeakPtrs(); + receiver_.reset(); + + dev_tools_emulator_->Shutdown(); + + // Initiate shutdown for the entire frameset. This will cause a lot of + // notifications to be sent. This will detach all frames in this WebView's + // frame tree. + page_->WillBeDestroyed(); + page_.Clear(); + + if (web_view_client_) + web_view_client_->OnDestruct(); + + // Reset the delegate to prevent notifications being sent as we're being + // deleted. + web_view_client_ = nullptr; + + for (auto& observer : observers_) + observer.WebViewDestroyed(); + + delete this; +} + +gfx::Size WebViewImpl::Size() { + return size_; +} + +void WebViewImpl::ResizeVisualViewport(const gfx::Size& new_size) { + GetPage()->GetVisualViewport().SetSize(new_size); + GetPage()->GetVisualViewport().ClampToBoundaries(); +} + +void WebViewImpl::DidFirstVisuallyNonEmptyPaint() { + DCHECK(MainFrameImpl()); + local_main_frame_host_remote_->DidFirstVisuallyNonEmptyPaint(); +} + +void WebViewImpl::UpdateICBAndResizeViewport( + const gfx::Size& visible_viewport_size) { + // We'll keep the initial containing block size from changing when the top + // controls hide so that the ICB will always be the same size as the + // viewport with the browser controls shown. + gfx::Size icb_size = size_; + if (GetBrowserControls().PermittedState() == + cc::BrowserControlsState::kBoth && + !GetBrowserControls().ShrinkViewport()) { + icb_size.Enlarge(0, -(GetBrowserControls().TotalHeight() - + GetBrowserControls().TotalMinHeight())); + } + + GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(icb_size); + + UpdatePageDefinedViewportConstraints(MainFrameImpl() + ->GetFrame() + ->GetDocument() + ->GetViewportData() + .GetViewportDescription()); + UpdateMainFrameLayoutSize(); + + GetPage()->GetVisualViewport().SetSize(visible_viewport_size); + + if (MainFrameImpl()->GetFrameView()) { + if (!MainFrameImpl()->GetFrameView()->NeedsLayout()) + resize_viewport_anchor_->ResizeFrameView(MainFrameSize()); + } + + // The boundaries are not properly established until after the frame view is + // also resized, as demonstrated by + // VisualViewportTest.TestBrowserControlsAdjustmentAndResize. + GetPage()->GetVisualViewport().ClampToBoundaries(); +} + +void WebViewImpl::UpdateBrowserControlsConstraint( + cc::BrowserControlsState constraint) { + cc::BrowserControlsState old_permitted_state = + GetBrowserControls().PermittedState(); + + GetBrowserControls().UpdateConstraintsAndState( + constraint, cc::BrowserControlsState::kBoth); + + // If the controls are going from a locked hidden to unlocked state, or vice + // versa, the ICB size needs to change but we can't rely on getting a + // WebViewImpl::resize since the top controls shown state may not have + // changed. + if ((old_permitted_state == cc::BrowserControlsState::kHidden && + constraint == cc::BrowserControlsState::kBoth) || + (old_permitted_state == cc::BrowserControlsState::kBoth && + constraint == cc::BrowserControlsState::kHidden)) { + UpdateICBAndResizeViewport(GetPage()->GetVisualViewport().Size()); + } +} + +void WebViewImpl::DidUpdateBrowserControls() { + // BrowserControls are a feature whereby the browser can introduce an + // interactable element [e.g. search box] that grows/shrinks in height as the + // user scrolls the web contents. + // + // This method is called by the BrowserControls class to let the compositor + // know that the browser controls have been updated. This is only relevant if + // the main frame is local because BrowserControls only affects the main + // frame's viewport, and are only affected by main frame scrolling. + // + // The relevant state is stored on the BrowserControls object even if the main + // frame is remote. If the main frame becomes local, the state will be + // restored by the first commit, since the state is checked in every call to + // ApplyScrollAndScale(). + WebLocalFrameImpl* main_frame = MainFrameImpl(); + if (!main_frame || !main_frame->IsOutermostMainFrame()) + return; + + WebFrameWidgetImpl* widget = main_frame->LocalRootFrameWidget(); + widget->SetBrowserControlsShownRatio(GetBrowserControls().TopShownRatio(), + GetBrowserControls().BottomShownRatio()); + widget->SetBrowserControlsParams(GetBrowserControls().Params()); + + VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); + DCHECK(visual_viewport.IsActiveViewport()); + + { + // This object will save the current visual viewport offset w.r.t. the + // document and restore it when the object goes out of scope. It's + // needed since the browser controls adjustment will change the maximum + // scroll offset and we may need to reposition them to keep the user's + // apparent position unchanged. + ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_); + + visual_viewport.SetBrowserControlsAdjustment( + GetBrowserControls().UnreportedSizeAdjustment()); + } +} + +BrowserControls& WebViewImpl::GetBrowserControls() { + return GetPage()->GetBrowserControls(); +} + +void WebViewImpl::ResizeViewWhileAnchored( + cc::BrowserControlsParams params, + const gfx::Size& visible_viewport_size) { + DCHECK(MainFrameImpl()); + + bool old_viewport_shrink = GetBrowserControls().ShrinkViewport(); + + GetBrowserControls().SetParams(params); + + if (old_viewport_shrink != GetBrowserControls().ShrinkViewport()) + MainFrameImpl()->GetFrameView()->DynamicViewportUnitsChanged(); + + { + // Avoids unnecessary invalidations while various bits of state in + // TextAutosizer are updated. + TextAutosizer::DeferUpdatePageInfo defer_update_page_info(GetPage()); + LocalFrameView* frame_view = MainFrameImpl()->GetFrameView(); + gfx::Size old_size = frame_view->Size(); + UpdateICBAndResizeViewport(visible_viewport_size); + if (old_size != frame_view->Size()) { + frame_view->InvalidateLayoutForViewportConstrainedObjects(); + } + } + + fullscreen_controller_->UpdateSize(); + + if (!scoped_defer_main_frame_update_) { + // Page scale constraints may need to be updated; running layout now will + // do that. + MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kLayout, + DocumentUpdateReason::kSizeChange); + } +} + +void WebViewImpl::ResizeWithBrowserControls( + const gfx::Size& new_size, + float top_controls_height, + float bottom_controls_height, + bool browser_controls_shrink_layout) { + ResizeWithBrowserControls( + new_size, new_size, + {top_controls_height, GetBrowserControls().TopMinHeight(), + bottom_controls_height, GetBrowserControls().BottomMinHeight(), + GetBrowserControls().AnimateHeightChanges(), + browser_controls_shrink_layout}); +} + +void WebViewImpl::ResizeWithBrowserControls( + const gfx::Size& main_frame_widget_size, + const gfx::Size& visible_viewport_size, + cc::BrowserControlsParams browser_controls_params) { + if (should_auto_resize_) { + // When auto-resizing only the viewport size comes from the browser, while + // the widget size is determined in the renderer. + ResizeVisualViewport(visible_viewport_size); + return; + } + + if (size_ == main_frame_widget_size && + GetPage()->GetVisualViewport().Size() == visible_viewport_size && + GetBrowserControls().Params() == browser_controls_params) + return; + + if (GetPage()->MainFrame() && !GetPage()->MainFrame()->IsLocalFrame()) { + // Viewport resize for a remote main frame does not require any + // particular action, but the state needs to reflect the correct size + // so that it can be used for initialization if the main frame gets + // swapped to a LocalFrame at a later time. + size_ = main_frame_widget_size; + GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_); + GetPage()->GetVisualViewport().SetSize(size_); + GetPage()->GetBrowserControls().SetParams(browser_controls_params); + return; + } + + WebLocalFrameImpl* main_frame = MainFrameImpl(); + if (!main_frame) + return; + + LocalFrameView* view = main_frame->GetFrameView(); + if (!view) + return; + + VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); + + bool is_rotation = + GetPage()->GetSettings().GetMainFrameResizesAreOrientationChanges() && + size_.width() && ContentsSize().width() && + main_frame_widget_size.width() != size_.width() && + !fullscreen_controller_->IsFullscreenOrTransitioning(); + size_ = main_frame_widget_size; + + if (!main_frame->IsOutermostMainFrame()) { + // Anchoring should not be performed from embedded frames as anchoring + // should only be performed when the size/orientation is user controlled. + ResizeViewWhileAnchored(browser_controls_params, visible_viewport_size); + } else if (is_rotation) { + gfx::PointF viewport_anchor_coords(viewportAnchorCoordX, + viewportAnchorCoordY); + RotationViewportAnchor anchor(*view, visual_viewport, + viewport_anchor_coords, + GetPageScaleConstraintsSet()); + ResizeViewWhileAnchored(browser_controls_params, visible_viewport_size); + } else { + DCHECK(visual_viewport.IsActiveViewport()); + ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_); + ResizeViewWhileAnchored(browser_controls_params, visible_viewport_size); + } + + // TODO(bokan): This will send a resize event even if the innerHeight on the + // page didn't change (e.g. virtual keyboard causes resize of only visual + // viewport). Lets remove this and have the frame send this event when its + // frame rect is resized (as noted by the ancient FIXME inside this method). + // https://crbug.com/1353728. + SendResizeEventForMainFrame(); +} + +void WebViewImpl::Resize(const gfx::Size& new_size) { + if (should_auto_resize_ || size_ == new_size) + return; + + ResizeWithBrowserControls(new_size, GetBrowserControls().TopHeight(), + GetBrowserControls().BottomHeight(), + GetBrowserControls().ShrinkViewport()); +} + +void WebViewImpl::SetScreenOrientationOverrideForTesting( + std::optional orientation) { + screen_orientation_override_ = orientation; + + // Since we updated the override value, notify all widgets. + for (WebFrame* frame = MainFrame(); frame; frame = frame->TraverseNext()) { + if (frame->IsWebLocalFrame()) { + if (WebFrameWidgetImpl* widget = static_cast( + frame->ToWebLocalFrame()->FrameWidget())) { + widget->UpdateScreenInfo(widget->GetScreenInfos()); + } + } + } +} + +void WebViewImpl::SetWindowRectSynchronouslyForTesting( + const gfx::Rect& new_window_rect) { + // We need to call UpdateScreenRects to ensure the 'move' event is enqueued. + // TODO(jfernandez): Ideally updating the window rect should do that + // automatically. + web_widget_->UpdateScreenRects(new_window_rect, new_window_rect); + web_widget_->SetWindowRectSynchronouslyForTesting(new_window_rect); +} + +std::optional +WebViewImpl::ScreenOrientationOverride() { + return screen_orientation_override_; +} + +void WebViewImpl::DidEnterFullscreen() { + fullscreen_controller_->DidEnterFullscreen(); +} + +void WebViewImpl::DidExitFullscreen() { + fullscreen_controller_->DidExitFullscreen(); +} + +void WebViewImpl::SetMainFrameViewWidget(WebFrameWidgetImpl* widget) { + DCHECK(!widget || widget->ForMainFrame()); + web_widget_ = widget; +} + +void WebViewImpl::SetMouseOverURL(const KURL& url) { + mouse_over_url_ = url; + UpdateTargetURL(mouse_over_url_, focus_url_); +} + +void WebViewImpl::SetKeyboardFocusURL(const KURL& url) { + focus_url_ = url; + UpdateTargetURL(focus_url_, mouse_over_url_); +} + +WebFrameWidgetImpl* WebViewImpl::MainFrameViewWidget() { + return web_widget_; +} + +void WebViewImpl::PaintContent(cc::PaintCanvas* canvas, const gfx::Rect& rect) { + // This should only be used when compositing is not being used for this + // WebView, and it is painting into the recording of its parent. + DCHECK(!does_composite_); + // Non-composited WebViews always have a local main frame. + DCHECK(MainFrameImpl()); + + if (rect.IsEmpty()) + return; + + LocalFrameView& main_view = *MainFrameImpl()->GetFrame()->View(); + // TODO(crbug.com/1442088): Investigate the reason. + if (!main_view.GetLayoutView() + ->FirstFragment() + .HasLocalBorderBoxProperties()) { + return; + } + DCHECK_EQ(main_view.GetLayoutView()->GetDocument().Lifecycle().GetState(), + DocumentLifecycle::kPaintClean); + + PaintRecordBuilder builder; + main_view.PaintOutsideOfLifecycleWithThrottlingAllowed( + builder.Context(), PaintFlag::kNoFlag, CullRect(rect)); + // Don't bother to save/restore here as the caller is expecting the canvas + // to be modified and take care of it. + canvas->clipRect(gfx::RectToSkRect(rect)); + builder.EndRecording(*canvas, main_view.GetLayoutView() + ->FirstFragment() + .LocalBorderBoxProperties() + .Unalias()); +} + +// static +void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs, + WebView* web_view) { + WebViewImpl* web_view_impl = To(web_view); + WebSettings* settings = web_view->GetSettings(); + ApplyFontsFromMap(prefs.standard_font_family_map, + SetStandardFontFamilyWrapper, settings); + ApplyFontsFromMap(prefs.fixed_font_family_map, SetFixedFontFamilyWrapper, + settings); + ApplyFontsFromMap(prefs.serif_font_family_map, SetSerifFontFamilyWrapper, + settings); + ApplyFontsFromMap(prefs.sans_serif_font_family_map, + SetSansSerifFontFamilyWrapper, settings); + ApplyFontsFromMap(prefs.cursive_font_family_map, SetCursiveFontFamilyWrapper, + settings); + ApplyFontsFromMap(prefs.fantasy_font_family_map, SetFantasyFontFamilyWrapper, + settings); + ApplyFontsFromMap(prefs.math_font_family_map, SetMathFontFamilyWrapper, + settings); + settings->SetDefaultFontSize(prefs.default_font_size); + settings->SetDefaultFixedFontSize(prefs.default_fixed_font_size); + settings->SetMinimumFontSize(prefs.minimum_font_size); + settings->SetMinimumLogicalFontSize(prefs.minimum_logical_font_size); + settings->SetDefaultTextEncodingName( + WebString::FromASCII(prefs.default_encoding)); + settings->SetJavaScriptEnabled(prefs.javascript_enabled); + settings->SetWebSecurityEnabled(prefs.web_security_enabled); + settings->SetLoadsImagesAutomatically(prefs.loads_images_automatically); + settings->SetImagesEnabled(prefs.images_enabled); + settings->SetPluginsEnabled(prefs.plugins_enabled); + settings->SetDOMPasteAllowed(prefs.dom_paste_enabled); + settings->SetTextAreasAreResizable(prefs.text_areas_are_resizable); + settings->SetAllowScriptsToCloseWindows(prefs.allow_scripts_to_close_windows); + settings->SetDownloadableBinaryFontsEnabled(prefs.remote_fonts_enabled); + settings->SetJavaScriptCanAccessClipboard( + prefs.javascript_can_access_clipboard); + settings->SetDNSPrefetchingEnabled(prefs.dns_prefetching_enabled); + blink::WebNetworkStateNotifier::SetSaveDataEnabled(prefs.data_saver_enabled); + settings->SetLocalStorageEnabled(prefs.local_storage_enabled); + settings->SetSyncXHRInDocumentsEnabled(prefs.sync_xhr_in_documents_enabled); + settings->SetTargetBlankImpliesNoOpenerEnabledWillBeRemoved( + prefs.target_blank_implies_no_opener_enabled_will_be_removed); + settings->SetAllowNonEmptyNavigatorPlugins( + prefs.allow_non_empty_navigator_plugins); + RuntimeEnabledFeatures::SetDatabaseEnabled(prefs.databases_enabled); + settings->SetShouldProtectAgainstIpcFlooding( + !prefs.disable_ipc_flooding_protection); + settings->SetHyperlinkAuditingEnabled(prefs.hyperlink_auditing_enabled); + settings->SetCookieEnabled(prefs.cookie_enabled); + + // By default, allow_universal_access_from_file_urls is set to false and thus + // we mitigate attacks from local HTML files by not granting file:// URLs + // universal access. Only test shell will enable this. + settings->SetAllowUniversalAccessFromFileURLs( + prefs.allow_universal_access_from_file_urls); + settings->SetAllowFileAccessFromFileURLs( + prefs.allow_file_access_from_file_urls); + + settings->SetWebGL1Enabled(prefs.webgl1_enabled); + settings->SetWebGL2Enabled(prefs.webgl2_enabled); + + // Enable WebGL errors to the JS console if requested. + settings->SetWebGLErrorsToConsoleEnabled( + prefs.webgl_errors_to_console_enabled); + + settings->SetHideScrollbars(prefs.hide_scrollbars); + + settings->SetPrefersDefaultScrollbarStyles( + prefs.prefers_default_scrollbar_styles); + + // Enable gpu-accelerated 2d canvas if requested on the command line. + RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled( + prefs.accelerated_2d_canvas_enabled); + + RuntimeEnabledFeatures::SetCanvas2dLayersEnabled( + prefs.canvas_2d_layers_enabled); + + // Disable antialiasing for 2d canvas if requested on the command line. + settings->SetAntialiased2dCanvasEnabled( + !prefs.antialiased_2d_canvas_disabled); + + // Disable antialiasing of clips for 2d canvas if requested on the command + // line. + settings->SetAntialiasedClips2dCanvasEnabled( + prefs.antialiased_clips_2d_canvas_enabled); + + // Tabs to link is not part of the settings. WebCore calls + // ChromeClient::tabsToLinks which is part of the glue code. + web_view_impl->SetTabsToLinks(prefs.tabs_to_links); + + DCHECK(!(web_view_impl->IsFencedFrameRoot() && + prefs.allow_running_insecure_content)); + settings->SetAllowRunningOfInsecureContent( + prefs.allow_running_insecure_content); + settings->SetDisableReadingFromCanvas(prefs.disable_reading_from_canvas); + settings->SetStrictMixedContentChecking(prefs.strict_mixed_content_checking); + + settings->SetStrictlyBlockBlockableMixedContent( + prefs.strictly_block_blockable_mixed_content); + + settings->SetStrictMixedContentCheckingForPlugin( + prefs.block_mixed_plugin_content); + + settings->SetStrictPowerfulFeatureRestrictions( + prefs.strict_powerful_feature_restrictions); + settings->SetAllowGeolocationOnInsecureOrigins( + prefs.allow_geolocation_on_insecure_origins); + settings->SetPasswordEchoEnabled(prefs.password_echo_enabled); + settings->SetShouldPrintBackgrounds(prefs.should_print_backgrounds); + settings->SetShouldClearDocumentBackground( + prefs.should_clear_document_background); + settings->SetEnableScrollAnimator(prefs.enable_scroll_animator); + settings->SetPrefersReducedMotion(prefs.prefers_reduced_motion); + settings->SetPrefersReducedTransparency(prefs.prefers_reduced_transparency); + settings->SetInvertedColors(prefs.inverted_colors); + + RuntimeEnabledFeatures::SetTouchEventFeatureDetectionEnabled( + prefs.touch_event_feature_detection_enabled); + settings->SetMaxTouchPoints(prefs.pointer_events_max_touch_points); + settings->SetAvailablePointerTypes(prefs.available_pointer_types); + settings->SetPrimaryPointerType(prefs.primary_pointer_type); + settings->SetAvailableHoverTypes(prefs.available_hover_types); + settings->SetPrimaryHoverType(prefs.primary_hover_type); + settings->SetOutputDeviceUpdateAbilityType( + prefs.output_device_update_ability_type); + settings->SetBarrelButtonForDragEnabled(prefs.barrel_button_for_drag_enabled); + + settings->SetEditingBehavior(prefs.editing_behavior); + + settings->SetSupportsMultipleWindows(prefs.supports_multiple_windows); + + settings->SetMainFrameClipsContent(!prefs.record_whole_document); + + RuntimeEnabledFeatures::SetStylusHandwritingEnabled( + prefs.stylus_handwriting_enabled); + + settings->SetSmartInsertDeleteEnabled(prefs.smart_insert_delete_enabled); + + settings->SetSpatialNavigationEnabled(prefs.spatial_navigation_enabled); + // Spatnav depends on KeyboardFocusableScrollers. The WebUI team has + // disabled KFS because they need more time to update their custom elements, + // crbug.com/907284. Meanwhile, we pre-ship KFS to spatnav users. + if (prefs.spatial_navigation_enabled) + RuntimeEnabledFeatures::SetKeyboardFocusableScrollersEnabled(true); + + settings->SetSelectionIncludesAltImageText(true); + + RuntimeEnabledFeatures::SetFakeNoAllocDirectCallForTestingEnabled( + prefs.fake_no_alloc_direct_call_for_testing_enabled); + + settings->SetV8CacheOptions(prefs.v8_cache_options); + + settings->SetImageAnimationPolicy(prefs.animation_policy); + + settings->SetPresentationRequiresUserGesture( + prefs.user_gesture_required_for_presentation); + + if (prefs.text_tracks_enabled) { + settings->SetTextTrackKindUserPreference( + WebSettings::TextTrackKindUserPreference::kCaptions); + } else { + settings->SetTextTrackKindUserPreference( + WebSettings::TextTrackKindUserPreference::kDefault); + } + settings->SetTextTrackBackgroundColor( + WebString::FromASCII(prefs.text_track_background_color)); + settings->SetTextTrackTextColor( + WebString::FromASCII(prefs.text_track_text_color)); + settings->SetTextTrackTextSize( + WebString::FromASCII(prefs.text_track_text_size)); + settings->SetTextTrackTextShadow( + WebString::FromASCII(prefs.text_track_text_shadow)); + settings->SetTextTrackFontFamily( + WebString::FromASCII(prefs.text_track_font_family)); + settings->SetTextTrackFontStyle( + WebString::FromASCII(prefs.text_track_font_style)); + settings->SetTextTrackFontVariant( + WebString::FromASCII(prefs.text_track_font_variant)); + settings->SetTextTrackMarginPercentage(prefs.text_track_margin_percentage); + settings->SetTextTrackWindowColor( + WebString::FromASCII(prefs.text_track_window_color)); + settings->SetTextTrackWindowRadius( + WebString::FromASCII(prefs.text_track_window_radius)); + + // Needs to happen before SetDefaultPageScaleLimits below since that'll + // recalculate the final page scale limits and that depends on this setting. + settings->SetShrinksViewportContentToFit( + prefs.shrinks_viewport_contents_to_fit); + + // Needs to happen before SetIgnoreViewportTagScaleLimits below. + web_view->SetDefaultPageScaleLimits(prefs.default_minimum_page_scale_factor, + prefs.default_maximum_page_scale_factor); + + settings->SetFullscreenSupported(prefs.fullscreen_supported); + settings->SetTextAutosizingEnabled(prefs.text_autosizing_enabled); + settings->SetDoubleTapToZoomEnabled(prefs.double_tap_to_zoom_enabled); + blink::WebNetworkStateNotifier::SetNetworkQualityWebHoldback( + static_cast( + prefs.network_quality_estimator_web_holdback)); + + settings->SetDontSendKeyEventsToJavascript( + prefs.dont_send_key_events_to_javascript); + settings->SetWebAppScope(WebString::FromASCII(prefs.web_app_scope.spec())); + +#if BUILDFLAG(IS_ANDROID) + settings->SetAllowCustomScrollbarInMainFrame(false); + settings->SetAccessibilityFontScaleFactor(prefs.font_scale_factor); + settings->SetAccessibilityFontWeightAdjustment(prefs.font_weight_adjustment); + settings->SetAccessibilityTextSizeContrastFactor( + prefs.text_size_contrast_factor); + settings->SetDeviceScaleAdjustment(prefs.device_scale_adjustment); + web_view_impl->SetIgnoreViewportTagScaleLimits(prefs.force_enable_zoom); + settings->SetDefaultVideoPosterURL( + WebString::FromASCII(prefs.default_video_poster_url.spec())); + settings->SetSupportDeprecatedTargetDensityDPI( + prefs.support_deprecated_target_density_dpi); + settings->SetWideViewportQuirkEnabled(prefs.wide_viewport_quirk); + settings->SetUseWideViewport(prefs.use_wide_viewport); + settings->SetForceZeroLayoutHeight(prefs.force_zero_layout_height); + settings->SetViewportMetaMergeContentQuirk( + prefs.viewport_meta_merge_content_quirk); + settings->SetViewportMetaNonUserScalableQuirk( + prefs.viewport_meta_non_user_scalable_quirk); + settings->SetViewportMetaZeroValuesQuirk( + prefs.viewport_meta_zero_values_quirk); + settings->SetClobberUserAgentInitialScaleQuirk( + prefs.clobber_user_agent_initial_scale_quirk); + settings->SetIgnoreMainFrameOverflowHiddenQuirk( + prefs.ignore_main_frame_overflow_hidden_quirk); + settings->SetReportScreenSizeInPhysicalPixelsQuirk( + prefs.report_screen_size_in_physical_pixels_quirk); + settings->SetShouldReuseGlobalForUnownedMainFrame( + prefs.reuse_global_for_unowned_main_frame); + settings->SetPreferHiddenVolumeControls(true); + settings->SetSpellCheckEnabledByDefault(prefs.spellcheck_enabled_by_default); + + RuntimeEnabledFeatures::SetVideoFullscreenOrientationLockEnabled( + prefs.video_fullscreen_orientation_lock_enabled); + RuntimeEnabledFeatures::SetVideoRotateToFullscreenEnabled( + prefs.video_rotate_to_fullscreen_enabled); + settings->SetEmbeddedMediaExperienceEnabled( + prefs.embedded_media_experience_enabled); + settings->SetImmersiveModeEnabled(prefs.immersive_mode_enabled); + settings->SetDoNotUpdateSelectionOnMutatingSelectionRange( + prefs.do_not_update_selection_on_mutating_selection_range); + RuntimeEnabledFeatures::SetCSSHexAlphaColorEnabled( + prefs.css_hex_alpha_color_enabled); + RuntimeEnabledFeatures::SetScrollTopLeftInteropEnabled( + prefs.scroll_top_left_interop_enabled); + RuntimeEnabledFeatures::SetAcceleratedSmallCanvasesEnabled( + !prefs.disable_accelerated_small_canvases); +#endif // BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) + RuntimeEnabledFeatures::SetWebAuthEnabled(!prefs.disable_webauthn); +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) + + settings->SetForceDarkModeEnabled(prefs.force_dark_mode_enabled); + + settings->SetAccessibilityAlwaysShowFocus(prefs.always_show_focus); + settings->SetAutoplayPolicy(prefs.autoplay_policy); + settings->SetRequireTransientActivationForGetDisplayMedia( + prefs.require_transient_activation_for_get_display_media); + settings->SetRequireTransientActivationForShowFileOrDirectoryPicker( + prefs.require_transient_activation_for_show_file_or_directory_picker); + settings->SetRequireTransientActivationForHtmlFullscreen( + prefs.require_transient_activation_for_html_fullscreen); + settings->SetViewportEnabled(prefs.viewport_enabled); + settings->SetViewportMetaEnabled(prefs.viewport_meta_enabled); + settings->SetViewportStyle(prefs.viewport_style); + settings->SetAutoZoomFocusedEditableToLegibleScale( + prefs.auto_zoom_focused_editable_to_legible_scale); + + settings->SetLoadWithOverviewMode(prefs.initialize_at_minimum_page_scale); + settings->SetMainFrameResizesAreOrientationChanges( + prefs.main_frame_resizes_are_orientation_changes); + + settings->SetShowContextMenuOnMouseUp(prefs.context_menu_on_mouse_up); + settings->SetAlwaysShowContextMenuOnTouch( + prefs.always_show_context_menu_on_touch); + settings->SetSmoothScrollForFindEnabled(prefs.smooth_scroll_for_find_enabled); + + settings->SetHideDownloadUI(prefs.hide_download_ui); + + settings->SetPresentationReceiver(prefs.presentation_receiver); + + settings->SetMediaControlsEnabled(prefs.media_controls_enabled); + + settings->SetLowPriorityIframesThreshold( + static_cast( + prefs.low_priority_iframes_threshold)); + + settings->SetPictureInPictureEnabled(prefs.picture_in_picture_enabled && + ::features::UseSurfaceLayerForVideo()); + + settings->SetLazyLoadEnabled(prefs.lazy_load_enabled); + settings->SetInForcedColors(prefs.in_forced_colors); + settings->SetPreferredColorScheme(prefs.preferred_color_scheme); + settings->SetPreferredContrast(prefs.preferred_contrast); + + settings->SetTouchDragDropEnabled(prefs.touch_drag_drop_enabled); + settings->SetTouchDragEndContextMenu(prefs.touch_dragend_context_menu); + settings->SetWebXRImmersiveArAllowed(prefs.webxr_immersive_ar_allowed); + settings->SetModalContextMenu(prefs.modal_context_menu); + settings->SetRequireTransientActivationAndAuthorizationForSubAppsAPIs( + prefs.subapps_apis_require_user_gesture_and_authorization); + +#if BUILDFLAG(IS_MAC) + web_view_impl->SetMaximumLegibleScale( + prefs.default_maximum_page_scale_factor); +#endif + +#if BUILDFLAG(IS_WIN) + RuntimeEnabledFeatures::SetMiddleClickAutoscrollEnabled(true); +#endif + + RuntimeEnabledFeatures::SetTranslateServiceEnabled( + prefs.translate_service_available); + +#if BUILDFLAG(IS_WIN) + if (web_view_impl->GetPage() && + base::FeatureList::IsEnabled(features::kPrewarmDefaultFontFamilies)) { + if (auto* prewarmer = WebFontRendering::GetFontPrewarmer()) { + GenericFontFamilySettings& font_settings = + web_view_impl->GetPage() + ->GetSettings() + .GetGenericFontFamilySettings(); + if (features::kPrewarmStandard.Get()) + prewarmer->PrewarmFamily(font_settings.Standard()); + if (features::kPrewarmFixed.Get()) + prewarmer->PrewarmFamily(font_settings.Fixed()); + if (features::kPrewarmSerif.Get()) + prewarmer->PrewarmFamily(font_settings.Serif()); + if (features::kPrewarmSansSerif.Get()) + prewarmer->PrewarmFamily(font_settings.SansSerif()); + if (features::kPrewarmCursive.Get()) + prewarmer->PrewarmFamily(font_settings.Cursive()); + if (features::kPrewarmFantasy.Get()) + prewarmer->PrewarmFamily(font_settings.Fantasy()); + } + } +#endif + + // Disabling the StrictMimetypeCheckForWorkerScriptsEnabled enterprise policy + // overrides the corresponding RuntimeEnabledFeature (via its Pref). + if (!prefs.strict_mime_type_check_for_worker_scripts_enabled) { + RuntimeEnabledFeatures::SetStrictMimeTypesForWorkersEnabled(false); + } +} + +void WebViewImpl::ThemeChanged() { + if (auto* page = GetPage()) + page->InvalidatePaint(); +} + +void WebViewImpl::EnterFullscreen(LocalFrame& frame, + const FullscreenOptions* options, + FullscreenRequestType request_type) { + fullscreen_controller_->EnterFullscreen(frame, options, request_type); +} + +void WebViewImpl::ExitFullscreen(LocalFrame& frame) { + fullscreen_controller_->ExitFullscreen(frame); +} + +void WebViewImpl::FullscreenElementChanged(Element* old_element, + Element* new_element, + const FullscreenOptions* options, + FullscreenRequestType request_type) { + fullscreen_controller_->FullscreenElementChanged(old_element, new_element, + options, request_type); +} + +bool WebViewImpl::HasHorizontalScrollbar() { + return MainFrameImpl() + ->GetFrameView() + ->LayoutViewport() + ->HorizontalScrollbar(); +} + +bool WebViewImpl::HasVerticalScrollbar() { + return MainFrameImpl()->GetFrameView()->LayoutViewport()->VerticalScrollbar(); +} + +void WebViewImpl::SetPageFocus(bool enable) { + page_->GetFocusController().SetFocused(enable); + if (enable) { + LocalFrame* focused_frame = page_->GetFocusController().FocusedFrame(); + if (focused_frame) { + Element* element = focused_frame->GetDocument()->FocusedElement(); + if (element && focused_frame->Selection() + .ComputeVisibleSelectionInDOMTreeDeprecated() + .IsNone()) { + // If the selection was cleared while the WebView was not + // focused, then the focus element shows with a focus ring but + // no caret and does respond to keyboard inputs. + focused_frame->GetDocument()->UpdateStyleAndLayoutTree(); + if (element->IsTextControl()) { + element->UpdateSelectionOnFocus(SelectionBehaviorOnFocus::kRestore); + } else if (IsEditable(*element)) { + // updateFocusAppearance() selects all the text of + // contentseditable DIVs. So we set the selection explicitly + // instead. Note that this has the side effect of moving the + // caret back to the beginning of the text. + Position position(element, 0); + focused_frame->Selection().SetSelection( + SelectionInDOMTree::Builder().Collapse(position).Build(), + SetSelectionOptions()); + } + } + } + } else { + CancelPagePopup(); + + LocalFrame* focused_frame = page_->GetFocusController().FocusedFrame(); + if (focused_frame) { + // Finish an ongoing composition to delete the composition node. + if (focused_frame->GetInputMethodController().GetActiveEditContext()) { + focused_frame->GetInputMethodController() + .GetActiveEditContext() + ->FinishComposingText(WebInputMethodController::kKeepSelection); + } else if (focused_frame->GetInputMethodController().HasComposition()) { + // TODO(editing-dev): The use of + // UpdateStyleAndLayout needs to be audited. + // See http://crbug.com/590369 for more details. + focused_frame->GetDocument()->UpdateStyleAndLayout( + DocumentUpdateReason::kFocus); + + focused_frame->GetInputMethodController().FinishComposingText( + InputMethodController::kKeepSelection); + } + } + } +} + +// WebView -------------------------------------------------------------------- + +WebSettingsImpl* WebViewImpl::SettingsImpl() { + if (!web_settings_) { + web_settings_ = std::make_unique( + &page_->GetSettings(), dev_tools_emulator_.Get()); + } + DCHECK(web_settings_); + return web_settings_.get(); +} + +WebSettings* WebViewImpl::GetSettings() { + return SettingsImpl(); +} + +WebString WebViewImpl::PageEncoding() const { + if (!page_) + return WebString(); + + auto* main_frame = DynamicTo(page_->MainFrame()); + if (!main_frame) + return WebString(); + + // FIXME: Is this check needed? + if (!main_frame->GetDocument()->Loader()) + return WebString(); + + return main_frame->GetDocument()->EncodingName(); +} + +WebFrame* WebViewImpl::MainFrame() { + Page* page = page_.Get(); + return WebFrame::FromCoreFrame(page ? page->MainFrame() : nullptr); +} + +const WebFrame* WebViewImpl::MainFrame() const { + Page* page = page_.Get(); + return WebFrame::FromCoreFrame(page ? page->MainFrame() : nullptr); +} + +WebLocalFrameImpl* WebViewImpl::MainFrameImpl() const { + Page* page = page_.Get(); + if (!page) + return nullptr; + return WebLocalFrameImpl::FromFrame(DynamicTo(page->MainFrame())); +} + +std::string WebViewImpl::GetNullFrameReasonForBug1139104() const { + Page* page = page_.Get(); + if (!page) + return "WebViewImpl::page"; + if (!page->MainFrame()) + return "WebViewImpl::page->MainFrame"; + LocalFrame* local_frame = DynamicTo(page->MainFrame()); + if (!local_frame) + return "WebViewImpl::local_frame"; + return WebLocalFrameImpl::GetNullFrameReasonForBug1139104(local_frame); +} + +void WebViewImpl::DidAttachLocalMainFrame() { + DCHECK(MainFrameImpl()); + DCHECK(!remote_main_frame_host_remote_); + + LocalFrame* local_frame = MainFrameImpl()->GetFrame(); + local_frame->WasAttachedAsLocalMainFrame(); + + local_frame->GetRemoteNavigationAssociatedInterfaces()->GetInterface( + local_main_frame_host_remote_.BindNewEndpointAndPassReceiver( + GetPage() + ->GetPageScheduler() + ->GetAgentGroupScheduler() + .DefaultTaskRunner())); + + auto& viewport = GetPage()->GetVisualViewport(); + if (does_composite_) { + // When attaching a local main frame, set up any state on the compositor. + MainFrameImpl()->FrameWidgetImpl()->SetBackgroundColor(BackgroundColor()); + MainFrameImpl()->FrameWidgetImpl()->SetPrefersReducedMotion( + web_preferences_.prefers_reduced_motion); + MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits( + viewport.Scale(), viewport.IsPinchGestureActive(), + MinimumPageScaleFactor(), MaximumPageScaleFactor()); + // Prevent main frame updates while the main frame is loading until enough + // progress is made and BeginMainFrames are explicitly asked for. + scoped_defer_main_frame_update_ = + MainFrameImpl()->FrameWidgetImpl()->DeferMainFrameUpdate(); + } + + // It's possible that at the time that `local_frame` attached its document it + // was provisional so it couldn't initialize the root scroller. Try again now + // that the frame has been attached; this is a no-op if the root scroller is + // already initialized. + if (viewport.IsActiveViewport()) { + DCHECK(local_frame->GetDocument()); + // DidAttachLocalMainFrame can be called before a new document is attached + // so ensure we don't try to initialize the root scroller on a stopped + // document. + if (local_frame->GetDocument()->IsActive()) + local_frame->View()->InitializeRootScroller(); + } +} + +void WebViewImpl::DidAttachRemoteMainFrame( + CrossVariantMojoAssociatedRemote< + mojom::blink::RemoteMainFrameHostInterfaceBase> main_frame_host, + CrossVariantMojoAssociatedReceiver< + mojom::blink::RemoteMainFrameInterfaceBase> main_frame) { + DCHECK(!MainFrameImpl()); + DCHECK(!local_main_frame_host_remote_); + // Note that we didn't DCHECK the `main_frame_host` and `main_frame`, because + // it's possible for those to be null, in case the remote main frame is a + // placeholder RemoteFrame that does not have any browser-side counterpart. + // This is possible when the WebView is created in preparation for a main + // frame LocalFrame <-> LocalFrame swap. See the comments in + // `AgentSchedulingGroup::CreateWebView()` for more details. + + RemoteFrame* remote_frame = DynamicTo(GetPage()->MainFrame()); + remote_frame->WasAttachedAsRemoteMainFrame(std::move(main_frame)); + + remote_main_frame_host_remote_.Bind(std::move(main_frame_host)); + + auto& viewport = GetPage()->GetVisualViewport(); + DCHECK(!viewport.IsActiveViewport()); + viewport.Reset(); +} + +void WebViewImpl::DidDetachLocalMainFrame() { + // The WebFrameWidget that generated the |scoped_defer_main_frame_update_| + // for a local main frame is going away. + scoped_defer_main_frame_update_ = nullptr; + local_main_frame_host_remote_.reset(); +} + +void WebViewImpl::DidDetachRemoteMainFrame() { + remote_main_frame_host_remote_.reset(); +} + +WebLocalFrame* WebViewImpl::FocusedFrame() { + Frame* frame = FocusedCoreFrame(); + // TODO(yabinh): focusedCoreFrame() should always return a local frame, and + // the following check should be unnecessary. + // See crbug.com/625068 + return WebLocalFrameImpl::FromFrame(DynamicTo(frame)); +} + +void WebViewImpl::SetFocusedFrame(WebFrame* frame) { + if (!frame) { + // Clears the focused frame if any. + Frame* focused_frame = FocusedCoreFrame(); + if (auto* focused_local_frame = DynamicTo(focused_frame)) + focused_local_frame->Selection().SetFrameIsFocused(false); + return; + } + LocalFrame* core_frame = To(frame)->GetFrame(); + core_frame->GetPage()->GetFocusController().SetFocusedFrame(core_frame); +} + +void WebViewImpl::FinishScrollFocusedEditableIntoView( + const gfx::RectF& caret_rect_in_root_frame, + mojom::blink::ScrollIntoViewParamsPtr params) { + DCHECK(MainFrameImpl()); + DCHECK(!IsFencedFrameRoot()); + DCHECK(!caret_rect_in_root_frame.IsEmpty()); + DCHECK(params->for_focused_editable); + + // Zoom if: + // (1) Zoom to legible scale is enabled (i.e. Android) + // (2) We're on a non-mobile-friendly page + // (3) The element doesn't explicitly block pinch-zoom gestures so the user + // can zoom back out. + bool zoom_into_legible_scale = + web_settings_->AutoZoomFocusedEditableToLegibleScale() && + !GetPage()->GetVisualViewport().ShouldDisableDesktopWorkarounds() && + params->for_focused_editable->can_zoom; + + // Reconstruct the editable element's absolute rect from the caret-relative + // location. + gfx::RectF editable_rect_in_root_frame = + scroll_into_view_util::FocusedEditableBoundsFromParams( + caret_rect_in_root_frame, params); + + DCHECK(!editable_rect_in_root_frame.IsEmpty()); + + float scale; + gfx::Point scroll; + bool need_animation = false; + ComputeScaleAndScrollForEditableElementRects( + gfx::ToEnclosedRect(editable_rect_in_root_frame), + gfx::ToEnclosedRect(caret_rect_in_root_frame), zoom_into_legible_scale, + scale, scroll, need_animation); + + if (need_animation) { + StartPageScaleAnimation(scroll, false, scale, + kScrollAndScaleAnimationDuration); + } +} + +void WebViewImpl::SmoothScroll(int target_x, + int target_y, + base::TimeDelta duration) { + gfx::Point target_position(target_x, target_y); + StartPageScaleAnimation(target_position, false, PageScaleFactor(), duration); +} + +void WebViewImpl::ComputeScaleAndScrollForEditableElementRects( + const gfx::Rect& element_bounds_in_root_frame, + const gfx::Rect& caret_bounds_in_root_frame, + bool zoom_into_legible_scale, + float& new_scale, + gfx::Point& new_scroll_position, + bool& need_animation) { + VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); + + TopDocumentRootScrollerController& controller = + GetPage()->GlobalRootScrollerController(); + Node* root_scroller = controller.GlobalRootScroller(); + + gfx::Rect element_bounds_in_content; + gfx::Rect caret_bounds_in_content; + + // If the page has a non-default root scroller then we need to put the + // "in_content" coordinates into that scroller's coordinate space, rather + // than the root frame's. + if (root_scroller != MainFrameImpl()->GetFrame()->GetDocument() && + controller.RootScrollerArea()) { + ScrollOffset offset = controller.RootScrollerArea()->GetScrollOffset(); + + element_bounds_in_content = element_bounds_in_root_frame; + caret_bounds_in_content = caret_bounds_in_root_frame; + + element_bounds_in_content.Offset(gfx::ToFlooredVector2d(offset)); + caret_bounds_in_content.Offset(gfx::ToFlooredVector2d(offset)); + } else { + element_bounds_in_content = + MainFrameImpl()->GetFrameView()->RootFrameToDocument( + element_bounds_in_root_frame); + caret_bounds_in_content = + MainFrameImpl()->GetFrameView()->RootFrameToDocument( + caret_bounds_in_root_frame); + } + + if (!zoom_into_legible_scale) { + new_scale = PageScaleFactor(); + } else { + // Pick a scale which is reasonably readable. This is the scale at which + // the caret height will become minReadableCaretHeightForNode (adjusted + // for dpi and font scale factor). + const int min_readable_caret_height_for_node = + (element_bounds_in_content.height() >= + 2 * caret_bounds_in_content.height() + ? minReadableCaretHeightForTextArea + : minReadableCaretHeight) * + MainFrameImpl()->GetFrame()->PageZoomFactor(); + new_scale = ClampPageScaleFactorToLimits( + MaximumLegiblePageScale() * min_readable_caret_height_for_node / + caret_bounds_in_content.height()); + new_scale = std::max(new_scale, PageScaleFactor()); + } + const float delta_scale = new_scale / PageScaleFactor(); + + need_animation = false; + + // If we are at less than the target zoom level, zoom in. + if (delta_scale > minScaleChangeToTriggerZoom) + need_animation = true; + else + new_scale = PageScaleFactor(); + + ScrollableArea* root_viewport = + MainFrameImpl()->GetFrame()->View()->GetScrollableArea(); + + // If the caret is offscreen, then animate. + if (!root_viewport->VisibleContentRect().Contains(caret_bounds_in_content)) + need_animation = true; + + // If the box is partially offscreen and it's possible to bring it fully + // onscreen, then animate. + if (visual_viewport.VisibleRect().width() >= + element_bounds_in_content.width() && + visual_viewport.VisibleRect().height() >= + element_bounds_in_content.height() && + !root_viewport->VisibleContentRect().Contains(element_bounds_in_content)) + need_animation = true; + + if (!need_animation) + return; + + gfx::SizeF target_viewport_size(visual_viewport.Size()); + target_viewport_size.Scale(1 / new_scale); + + // TODO(bokan): The logic below is all tailored assuming LTR writing mode. + // Ideally, it'd perform its computations based on writing mode. + ScrollOffset scroll_offset; + if (element_bounds_in_content.width() <= target_viewport_size.width()) { + // Field is narrower than screen. Try to leave padding on left so field's + // label is visible, but it's more important to ensure entire field is + // onscreen. + int ideal_left_padding = target_viewport_size.width() * leftBoxRatio; + int max_left_padding_keeping_box_onscreen = + target_viewport_size.width() - element_bounds_in_content.width(); + scroll_offset.set_x(element_bounds_in_content.x() - + std::min(ideal_left_padding, + max_left_padding_keeping_box_onscreen)); + } else { + // Field is wider than screen. Try to left-align field, unless caret would + // be offscreen, in which case right-align the caret. + scroll_offset.set_x(std::max( + element_bounds_in_content.x(), + caret_bounds_in_content.x() + caret_bounds_in_content.width() + + caretPadding - target_viewport_size.width())); + } + if (element_bounds_in_content.height() <= target_viewport_size.height()) { + // Field is shorter than screen. Vertically center it. + scroll_offset.set_y( + element_bounds_in_content.y() - + (target_viewport_size.height() - element_bounds_in_content.height()) / + 2); + } else { + // Field is taller than screen. Try to top align field, unless caret would + // be offscreen, in which case bottom-align the caret. + scroll_offset.set_y(std::max( + element_bounds_in_content.y(), + caret_bounds_in_content.y() + caret_bounds_in_content.height() + + caretPadding - target_viewport_size.height())); + } + + // The output scroll will be used by the compositor so we must convert the + // scroll-origin relative (i.e. writing-mode dependent) ScrollOffset with a + // top-left relative scroll position. + new_scroll_position = + ToFlooredPoint(root_viewport->ScrollOffsetToPosition(scroll_offset)); +} + +void WebViewImpl::AdvanceFocus(bool reverse) { + GetPage()->GetFocusController().AdvanceFocus( + reverse ? mojom::blink::FocusType::kBackward + : mojom::blink::FocusType::kForward); +} + +double WebViewImpl::ZoomLevel() { + return zoom_level_; +} + +void WebViewImpl::PropagateZoomFactorToLocalFrameRoots(Frame* frame, + float zoom_factor) { + auto* local_frame = DynamicTo(frame); + if (local_frame && local_frame->IsLocalRoot()) { + if (Document* document = local_frame->GetDocument()) { + auto* plugin_document = DynamicTo(document); + if (!plugin_document || !plugin_document->GetPluginView()) { + local_frame->SetPageZoomFactor(zoom_factor); + } + } + } + + for (Frame* child = frame->Tree().FirstChild(); child; + child = child->Tree().NextSibling()) + PropagateZoomFactorToLocalFrameRoots(child, zoom_factor); +} + +double WebViewImpl::SetZoomLevel(double zoom_level) { + double old_zoom_level = zoom_level_; + if (zoom_level < minimum_zoom_level_) + zoom_level_ = minimum_zoom_level_; + else if (zoom_level > maximum_zoom_level_) + zoom_level_ = maximum_zoom_level_; + else + zoom_level_ = zoom_level; + + float zoom_factor = + zoom_factor_override_ + ? zoom_factor_override_ + : static_cast(PageZoomLevelToZoomFactor(zoom_level_)); + if (zoom_factor_for_device_scale_factor_) { + if (compositor_device_scale_factor_override_) { + page_->SetInspectorDeviceScaleFactorOverride( + zoom_factor_for_device_scale_factor_ / + compositor_device_scale_factor_override_); + + zoom_factor *= compositor_device_scale_factor_override_; + } else { + page_->SetInspectorDeviceScaleFactorOverride(1.0f); + zoom_factor *= zoom_factor_for_device_scale_factor_; + } + } + PropagateZoomFactorToLocalFrameRoots(page_->MainFrame(), zoom_factor); + + if (old_zoom_level != zoom_level_) { + for (auto& observer : observers_) + observer.OnZoomLevelChanged(); + CancelPagePopup(); + } + + return zoom_level_; +} + +float WebViewImpl::PageScaleFactor() const { + if (!GetPage()) + return 1; + + return GetPage()->GetVisualViewport().Scale(); +} + +float WebViewImpl::ClampPageScaleFactorToLimits(float scale_factor) const { + return GetPageScaleConstraintsSet().FinalConstraints().ClampToConstraints( + scale_factor); +} + +void WebViewImpl::SetVisualViewportOffset(const gfx::PointF& offset) { + DCHECK(GetPage()); + GetPage()->GetVisualViewport().SetLocation(offset); +} + +gfx::PointF WebViewImpl::VisualViewportOffset() const { + DCHECK(GetPage()); + return GetPage()->GetVisualViewport().VisibleRect().origin(); +} + +gfx::SizeF WebViewImpl::VisualViewportSize() const { + DCHECK(GetPage()); + return GetPage()->GetVisualViewport().VisibleRect().size(); +} + +void WebViewImpl::SetPageScaleFactorAndLocation(float scale_factor, + bool is_pinch_gesture_active, + const gfx::PointF& location) { + DCHECK(GetPage()); + + GetPage()->GetVisualViewport().SetScaleAndLocation( + ClampPageScaleFactorToLimits(scale_factor), is_pinch_gesture_active, + location); +} + +void WebViewImpl::SetPageScaleFactor(float scale_factor) { + DCHECK(GetPage()); + DCHECK(MainFrameImpl()); + + if (LocalFrame* frame = MainFrameImpl()->GetFrame()) { + frame->SetScaleFactor(scale_factor); + } +} + +void WebViewImpl::SetZoomFactorForDeviceScaleFactor( + float zoom_factor_for_device_scale_factor) { + DCHECK(does_composite_); + // We can't early-return here if these are already equal, because we may + // need to propagate the correct zoom factor to newly navigated frames. + zoom_factor_for_device_scale_factor_ = zoom_factor_for_device_scale_factor; + SetZoomLevel(zoom_level_); +} + +void WebViewImpl::SetPageLifecycleStateFromNewPageCommit( + mojom::blink::PageVisibilityState visibility, + mojom::blink::PagehideDispatch pagehide_dispatch) { + TRACE_EVENT0("navigation", + "WebViewImpl::SetPageLifecycleStateFromNewPageCommit"); + mojom::blink::PageLifecycleStatePtr state = + GetPage()->GetPageLifecycleState().Clone(); + state->visibility = visibility; + state->pagehide_dispatch = pagehide_dispatch; + SetPageLifecycleStateInternal(std::move(state), + /*page_restore_params=*/nullptr); +} + +void WebViewImpl::SetPageLifecycleState( + mojom::blink::PageLifecycleStatePtr state, + mojom::blink::PageRestoreParamsPtr page_restore_params, + SetPageLifecycleStateCallback callback) { + TRACE_EVENT0("navigation", "WebViewImpl::SetPageLifecycleState"); + SetPageLifecycleStateInternal(std::move(state), + std::move(page_restore_params)); + // Tell the browser that the lifecycle update was successful. + std::move(callback).Run(); +} + +// Returns true if this state update is for the page being restored from +// back-forward cache, causing the pageshow event to fire with persisted=true. +bool IsRestoredFromBackForwardCache( + const mojom::blink::PageLifecycleStatePtr& old_state, + const mojom::blink::PageLifecycleStatePtr& new_state) { + if (!old_state) + return false; + bool old_state_hidden = old_state->pagehide_dispatch != + mojom::blink::PagehideDispatch::kNotDispatched; + bool new_state_shown = new_state->pagehide_dispatch == + mojom::blink::PagehideDispatch::kNotDispatched; + // It's a pageshow but it can't be the initial pageshow since it was already + // hidden. So it must be a back-forward cache restore. + return old_state_hidden && new_state_shown; +} + +void WebViewImpl::SetPageLifecycleStateInternal( + mojom::blink::PageLifecycleStatePtr new_state, + mojom::blink::PageRestoreParamsPtr page_restore_params) { + Page* page = GetPage(); + if (!page) + return; + auto& old_state = page->GetPageLifecycleState(); + TRACE_EVENT2("navigation", "WebViewImpl::SetPageLifecycleStateInternal", + "old_state", old_state, "new_state", new_state); + + bool storing_in_bfcache = new_state->is_in_back_forward_cache && + !old_state->is_in_back_forward_cache; + bool restoring_from_bfcache = !new_state->is_in_back_forward_cache && + old_state->is_in_back_forward_cache; + // `hiding_page` indicates that the page is switching visibility states in a + // way that we should treat as a change. There are two definitions of this + // (see below), but both require that the new state is not `kVisible`. + bool hiding_page = + new_state->visibility != mojom::blink::PageVisibilityState::kVisible; + if (RuntimeEnabledFeatures::DispatchHiddenVisibilityTransitionsEnabled()) { + // Dispatch a visibility change from `kVisible` to either hidden state, and + // also between the two hidden states. + hiding_page &= (old_state->visibility != new_state->visibility); + } else { + // Dispatch a visibility change only when entering or leaving `kVisible` to + // one of the two hidden states, but not when switching between `kHidden` + // and `kHiddenButPainting` in either direction. + hiding_page &= + (old_state->visibility == mojom::blink::PageVisibilityState::kVisible); + } + bool showing_page = + (new_state->visibility == mojom::blink::PageVisibilityState::kVisible) && + (old_state->visibility != mojom::blink::PageVisibilityState::kVisible); + bool freezing_page = new_state->is_frozen && !old_state->is_frozen; + bool resuming_page = !new_state->is_frozen && old_state->is_frozen; + bool dispatching_pagehide = + (new_state->pagehide_dispatch != + mojom::blink::PagehideDispatch::kNotDispatched) && + !GetPage()->DispatchedPagehideAndStillHidden(); + bool dispatching_pageshow = + IsRestoredFromBackForwardCache(old_state, new_state); + bool eviction_changed = + new_state->eviction_enabled != old_state->eviction_enabled; + + if (dispatching_pagehide) { + RemoveFocusAndTextInputState(); + } + if (dispatching_pagehide) { + // Note that |dispatching_pagehide| is different than |hiding_page|. + // |dispatching_pagehide| will only be true when we're navigating away from + // a page, while |hiding_page| might be true in other cases too such as when + // the tab containing a page is backgrounded, and might be false even when + // we're navigating away from a page, if the page is already hidden. + DispatchPagehide(new_state->pagehide_dispatch); + } + if (hiding_page) { + SetVisibilityState(new_state->visibility, /*is_initial_state=*/false); + } + if (storing_in_bfcache) { + // TODO(https://crbug.com/1378279): Consider moving this to happen earlier + // and together with other page state updates so that the ordering is clear. + Scheduler()->SetPageBackForwardCached(new_state->is_in_back_forward_cache); + } + + if (freezing_page) { + // Notify all local frames that we are about to freeze. + for (WebFrame* frame = MainFrame(); frame; frame = frame->TraverseNext()) { + if (frame->IsWebLocalFrame()) { + frame->ToWebLocalFrame()->Client()->WillFreezePage(); + } + } + + // TODO(https://crbug.com/1378279): Consider moving this to happen earlier + // and together with other page state updates so that the ordering is clear. + SetPageFrozen(true); + } + + if (restoring_from_bfcache) { + DCHECK(page_restore_params); + // Update the history offset and length value, as pages that are kept in + // the back-forward cache do not get notified about updates on these + // values, so the currently saved value might be stale. + SetHistoryOffsetAndLength(page_restore_params->pending_history_list_offset, + page_restore_params->current_history_list_length); + } + if (eviction_changed) + HookBackForwardCacheEviction(new_state->eviction_enabled); + if (resuming_page) { + // TODO(https://crbug.com/1378279): Consider moving this to happen earlier + // and together with other page state updates so that the ordering is clear. + SetPageFrozen(false); + } + if (showing_page) { + SetVisibilityState(new_state->visibility, /*is_initial_state=*/false); + } + if (restoring_from_bfcache) { + DCHECK(dispatching_pageshow); + DCHECK(page_restore_params); + // Increment the navigation counter on the main frame and all nested frames + // in its frame tree. + // Navigation Id increment should happen before a + // BackForwardCacheRestoration instance is created which happens inside the + // DispatchPageshow method. + for (Frame* frame = page->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + auto* local_frame = DynamicTo(frame); + if (local_frame && local_frame->View()) { + DCHECK(local_frame->DomWindow()); + local_frame->DomWindow()->GenerateNewNavigationId(); + } + } + + DispatchPersistedPageshow(page_restore_params->navigation_start); + + // TODO(https://crbug.com/1378279): Consider moving this to happen earlier + // and together with other page state updates so that the ordering is clear. + Scheduler()->SetPageBackForwardCached(new_state->is_in_back_forward_cache); + if (MainFrame()->IsWebLocalFrame()) { + LocalFrame* local_frame = To(page->MainFrame()); + probe::DidRestoreFromBackForwardCache(local_frame); + + if (local_frame->IsOutermostMainFrame()) { + Document* document = local_frame->GetDocument(); + if (auto* document_rules = + DocumentSpeculationRules::FromIfExists(*document)) { + document_rules->DocumentRestoredFromBFCache(); + } + } + } + } + + // Make sure no TrackedFeaturesUpdate message is sent after the ACK + // TODO(carlscab): Do we really need to go through LocalFrame => + // platform/scheduler/ => LocalFrame to report the features? We can probably + // move SchedulerTrackedFeatures to core/ and remove the back and forth. + ReportActiveSchedulerTrackedFeatures(); + + // TODO(https://crbug.com/1378279): Consider moving this to happen earlier + // and together with other page state updates so that the ordering is clear. + GetPage()->SetPageLifecycleState(std::move(new_state)); + + // Notify all local frames that we've updated the page lifecycle state. + for (WebFrame* frame = MainFrame(); frame; frame = frame->TraverseNext()) { + if (frame->IsWebLocalFrame()) { + frame->ToWebLocalFrame()->Client()->DidSetPageLifecycleState( + restoring_from_bfcache); + } + } + + UpdateViewTransitionState(restoring_from_bfcache, storing_in_bfcache, + page_restore_params); + + if (RuntimeEnabledFeatures::PageRevealEventEnabled()) { + if (restoring_from_bfcache) { + if (auto* main_frame = DynamicTo(GetPage()->MainFrame())) { + main_frame->GetDocument()->EnqueuePageRevealEvent(); + } + } + } +} + +void WebViewImpl::UpdateViewTransitionState( + bool restoring_from_bfcache, + bool storing_in_bfcache, + const mojom::blink::PageRestoreParamsPtr& page_restore_params) { + // If we have view_transition_state, then we must be a main frame. + DCHECK(!page_restore_params || !page_restore_params->view_transition_state || + MainFrame()->IsWebLocalFrame()); + // We can't be both restoring and storing things. + DCHECK(!restoring_from_bfcache || !storing_in_bfcache); + + if (!MainFrame()->IsWebLocalFrame()) { + return; + } + LocalFrame* local_frame = To(GetPage()->MainFrame()); + DCHECK(local_frame); + + // When restoring from BFCache, start a transition if we have a view + // transition state. + if (restoring_from_bfcache && page_restore_params->view_transition_state) { + if (auto* document = local_frame->GetDocument()) { + ViewTransitionSupplement::CreateFromSnapshotForNavigation( + *document, std::move(*page_restore_params->view_transition_state)); + } + } + + // If we're storing the page in BFCache, abort any pending transitions. This + // is important since when we bring the page back from BFCache, we might + // attempt to create a transition and fail if there is one already happening. + // Note that even if we won't be creating a transition, it's harmless to abort + // the main frame transition when going into BFCache. + if (storing_in_bfcache) { + if (auto* document = local_frame->GetDocument()) { + ViewTransitionSupplement::AbortTransition(*document); + } + } +} + +void WebViewImpl::ReportActiveSchedulerTrackedFeatures() { + Page* page = GetPage(); + if (!page) + return; + + for (Frame* frame = page->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + if (!frame->IsLocalFrame()) + continue; + auto* local_frame = DynamicTo(frame); + if (!local_frame->GetFrameScheduler()) + continue; + local_frame->GetFrameScheduler()->ReportActiveSchedulerTrackedFeatures(); + } +} + +void WebViewImpl::AudioStateChanged(bool is_audio_playing) { + GetPage()->GetPageScheduler()->AudioStateChanged(is_audio_playing); +} + +void WebViewImpl::RemoveFocusAndTextInputState() { + auto& focus_controller = GetPage()->GetFocusController(); + auto* focused_frame = focus_controller.FocusedFrame(); + if (!focused_frame) + return; + // Remove focus from the currently focused element and frame. + focus_controller.SetFocusedElement(nullptr, nullptr); + // Clear composing state, and make sure we send a TextInputState update. + // Note that the TextInputState itself is cleared when we clear the focus, + // but no updates to the browser will be triggered until the next animation + // frame, which won't happen if we're freezing the page. + if (auto* widget = static_cast( + focused_frame->GetWidgetForLocalRoot())) { + widget->FinishComposingText(false /* keep_selection */); + widget->UpdateTextInputState(); + } +} + +void WebViewImpl::DispatchPagehide( + mojom::blink::PagehideDispatch pagehide_dispatch) { + DCHECK_NE(pagehide_dispatch, mojom::blink::PagehideDispatch::kNotDispatched); + bool persisted = (pagehide_dispatch == + mojom::blink::PagehideDispatch::kDispatchedPersisted); + // Dispatch pagehide on all frames. + for (Frame* frame = GetPage()->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + if (frame->DomWindow() && frame->DomWindow()->IsLocalDOMWindow()) { + frame->DomWindow()->ToLocalDOMWindow()->DispatchPagehideEvent( + persisted + ? PageTransitionEventPersistence::kPageTransitionEventPersisted + : PageTransitionEventPersistence:: + kPageTransitionEventNotPersisted); + } + } +} + +void WebViewImpl::DispatchPersistedPageshow(base::TimeTicks navigation_start) { + for (Frame* frame = GetPage()->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + auto* local_frame = DynamicTo(frame); + // Record the metics. + if (local_frame && local_frame->View()) { + Document* document = local_frame->GetDocument(); + if (document) { + PaintTiming::From(*document).OnRestoredFromBackForwardCache(); + InteractiveDetector::From(*document)->OnRestoredFromBackForwardCache(); + } + DocumentLoader* loader = local_frame->Loader().GetDocumentLoader(); + if (loader) { + loader->GetTiming().SetBackForwardCacheRestoreNavigationStart( + navigation_start); + } + } + if (frame->DomWindow() && frame->DomWindow()->IsLocalDOMWindow()) { + auto pageshow_start_time = base::TimeTicks::Now(); + LocalDOMWindow* window = frame->DomWindow()->ToLocalDOMWindow(); + + window->DispatchPersistedPageshowEvent(navigation_start); + + if (RuntimeEnabledFeatures::NavigationIdEnabled(window)) { + auto pageshow_end_time = base::TimeTicks::Now(); + + WindowPerformance* performance = + DOMWindowPerformance::performance(*window); + DCHECK(performance); + + performance->AddBackForwardCacheRestoration( + navigation_start, pageshow_start_time, pageshow_end_time); + } + if (frame->IsOutermostMainFrame()) { + UMA_HISTOGRAM_BOOLEAN( + "BackForwardCache.MainFrameHasPageshowListenersOnRestore", + window->HasEventListeners(event_type_names::kPageshow)); + } + } + } +} + +void WebViewImpl::HookBackForwardCacheEviction(bool hook) { + DCHECK(GetPage()); + for (Frame* frame = GetPage()->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + auto* local_frame = DynamicTo(frame); + if (!local_frame) + continue; + if (hook) + local_frame->HookBackForwardCacheEviction(); + else + local_frame->RemoveBackForwardCacheEviction(); + } +} + +void WebViewImpl::EnableAutoResizeMode(const gfx::Size& min_size, + const gfx::Size& max_size) { + should_auto_resize_ = true; + min_auto_size_ = min_size; + max_auto_size_ = max_size; + ConfigureAutoResizeMode(); +} + +void WebViewImpl::DisableAutoResizeMode() { + should_auto_resize_ = false; + ConfigureAutoResizeMode(); +} + +bool WebViewImpl::AutoResizeMode() { + return should_auto_resize_; +} + +void WebViewImpl::EnableAutoResizeForTesting(const gfx::Size& min_window_size, + const gfx::Size& max_window_size) { + EnableAutoResizeMode(web_widget_->DIPsToCeiledBlinkSpace(min_window_size), + web_widget_->DIPsToCeiledBlinkSpace(max_window_size)); +} + +void WebViewImpl::DisableAutoResizeForTesting( + const gfx::Size& new_window_size) { + if (!should_auto_resize_) + return; + DisableAutoResizeMode(); + + // The |new_size| is empty when resetting auto resize in between tests. In + // this case the current size should just be preserved. + if (!new_window_size.IsEmpty()) { + web_widget_->Resize(web_widget_->DIPsToCeiledBlinkSpace(new_window_size)); + } +} + +void WebViewImpl::SetDefaultPageScaleLimits(float min_scale, float max_scale) { + dev_tools_emulator_->SetDefaultPageScaleLimits(min_scale, max_scale); +} + +void WebViewImpl::SetInitialPageScaleOverride( + float initial_page_scale_factor_override) { + PageScaleConstraints constraints = + GetPageScaleConstraintsSet().UserAgentConstraints(); + constraints.initial_scale = initial_page_scale_factor_override; + + if (constraints == GetPageScaleConstraintsSet().UserAgentConstraints()) + return; + + GetPageScaleConstraintsSet().SetNeedsReset(true); + GetPage()->SetUserAgentPageScaleConstraints(constraints); +} + +void WebViewImpl::SetMaximumLegibleScale(float maximum_legible_scale) { + maximum_legible_scale_ = maximum_legible_scale; +} + +void WebViewImpl::SetIgnoreViewportTagScaleLimits(bool ignore) { + PageScaleConstraints constraints = + GetPageScaleConstraintsSet().UserAgentConstraints(); + if (ignore) { + constraints.minimum_scale = + GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale; + constraints.maximum_scale = + GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale; + } else { + constraints.minimum_scale = -1; + constraints.maximum_scale = -1; + } + GetPage()->SetUserAgentPageScaleConstraints(constraints); +} + +gfx::Size WebViewImpl::MainFrameSize() { + // The frame size should match the viewport size at minimum scale, since the + // viewport must always be contained by the frame. + return gfx::ScaleToCeiledSize(size_, 1 / MinimumPageScaleFactor()); +} + +PageScaleConstraintsSet& WebViewImpl::GetPageScaleConstraintsSet() const { + return GetPage()->GetPageScaleConstraintsSet(); +} + +void WebViewImpl::RefreshPageScaleFactor() { + if (!MainFrame() || !GetPage() || !GetPage()->MainFrame() || + !GetPage()->MainFrame()->IsLocalFrame() || + !GetPage()->DeprecatedLocalMainFrame()->View()) + return; + UpdatePageDefinedViewportConstraints(MainFrameImpl() + ->GetFrame() + ->GetDocument() + ->GetViewportData() + .GetViewportDescription()); + GetPageScaleConstraintsSet().ComputeFinalConstraints(); + + float new_page_scale_factor = PageScaleFactor(); + if (GetPageScaleConstraintsSet().NeedsReset() && + GetPageScaleConstraintsSet().FinalConstraints().initial_scale != -1) { + new_page_scale_factor = + GetPageScaleConstraintsSet().FinalConstraints().initial_scale; + GetPageScaleConstraintsSet().SetNeedsReset(false); + } + SetPageScaleFactor(new_page_scale_factor); + + // The constraints may have changed above which affects the page scale limits, + // so we must update those even though SetPageScaleFactor() may do the same if + // the scale factor is changed. + if (does_composite_) { + auto& viewport = GetPage()->GetVisualViewport(); + MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits( + viewport.Scale(), viewport.IsPinchGestureActive(), + MinimumPageScaleFactor(), MaximumPageScaleFactor()); + } +} + +void WebViewImpl::UpdatePageDefinedViewportConstraints( + const ViewportDescription& description) { + if (!GetPage() || (!size_.width() && !size_.height())) + return; + // The viewport is a property of the main frame and its widget, so ignore it + // when the main frame is remote. + // TODO(danakj): Remove calls to this method from ChromeClient and DCHECK this + // instead. + if (!GetPage()->MainFrame()->IsLocalFrame()) + return; + + if (virtual_keyboard_mode_ != description.virtual_keyboard_mode) { + DCHECK(MainFrameImpl()->IsOutermostMainFrame()); + virtual_keyboard_mode_ = description.virtual_keyboard_mode; + mojom::blink::LocalFrameHost& frame_host = + MainFrameImpl()->GetFrame()->GetLocalFrameHostRemote(); + + frame_host.SetVirtualKeyboardMode(virtual_keyboard_mode_); + } + + if (!GetSettings()->ViewportEnabled()) { + GetPageScaleConstraintsSet().ClearPageDefinedConstraints(); + UpdateMainFrameLayoutSize(); + return; + } + + Document* document = GetPage()->DeprecatedLocalMainFrame()->GetDocument(); + + Length default_min_width = + document->GetViewportData().ViewportDefaultMinWidth(); + if (default_min_width.IsAuto()) + default_min_width = Length::ExtendToZoom(); + + float old_initial_scale = + GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale; + GetPageScaleConstraintsSet().UpdatePageDefinedConstraints(description, + default_min_width); + + if (SettingsImpl()->ClobberUserAgentInitialScaleQuirk() && + GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale != -1 && + GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale <= 1) { + if (description.max_width == Length::DeviceWidth() || + (description.max_width.IsAuto() && + GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale == + 1.0f)) + SetInitialPageScaleOverride(-1); + } + + Settings& page_settings = GetPage()->GetSettings(); + GetPageScaleConstraintsSet().AdjustForAndroidWebViewQuirks( + description, default_min_width.IntValue(), + SettingsImpl()->SupportDeprecatedTargetDensityDPI(), + page_settings.GetWideViewportQuirkEnabled(), + page_settings.GetUseWideViewport(), + page_settings.GetLoadWithOverviewMode(), + SettingsImpl()->ViewportMetaNonUserScalableQuirk()); + float new_initial_scale = + GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale; + if (old_initial_scale != new_initial_scale && new_initial_scale != -1) { + GetPageScaleConstraintsSet().SetNeedsReset(true); + if (MainFrameImpl() && MainFrameImpl()->GetFrameView()) + MainFrameImpl()->GetFrameView()->SetNeedsLayout(); + } + + if (does_composite_) { + MainFrameImpl()->FrameWidgetImpl()->UpdateViewportDescription(description); + } + + UpdateMainFrameLayoutSize(); +} + +void WebViewImpl::UpdateMainFrameLayoutSize() { + if (should_auto_resize_ || !MainFrameImpl()) + return; + + LocalFrameView* view = MainFrameImpl()->GetFrameView(); + if (!view) + return; + + gfx::Size layout_size = size_; + + if (GetSettings()->ViewportEnabled()) + layout_size = GetPageScaleConstraintsSet().GetLayoutSize(); + + if (GetPage()->GetSettings().GetForceZeroLayoutHeight()) + layout_size.set_height(0); + + view->SetLayoutSize(layout_size); +} + +gfx::Size WebViewImpl::ContentsSize() const { + if (!GetPage()->MainFrame()->IsLocalFrame()) + return gfx::Size(); + auto* layout_view = + GetPage()->DeprecatedLocalMainFrame()->ContentLayoutObject(); + if (!layout_view) + return gfx::Size(); + return ToPixelSnappedRect(layout_view->DocumentRect()).size(); +} + +gfx::Size WebViewImpl::ContentsPreferredMinimumSize() { + DCHECK(page_->MainFrame()->IsLocalFrame()); + + auto* main_local_frame = DynamicTo(page_->MainFrame()); + Document* document = main_local_frame->GetDocument(); + if (!document || !document->GetLayoutView() || !document->documentElement() || + !document->documentElement()->GetLayoutBox()) + return gfx::Size(); + + // The preferred size requires an up-to-date layout tree. + DCHECK(!document->NeedsLayoutTreeUpdate() && + !document->View()->NeedsLayout()); + + // Needed for computing MinPreferredWidth. + FontCachePurgePreventer fontCachePurgePreventer; + // Already accounts for zoom. + int width_scaled = document->GetLayoutView()->ComputeMinimumWidth().Round(); + int height_scaled = + document->documentElement()->GetLayoutBox()->ScrollHeight().Round(); + return gfx::Size(width_scaled, height_scaled); +} + +void WebViewImpl::UpdatePreferredSize() { + // We don't always want to send the change messages over IPC, only if we've + // been put in that mode by getting a |ViewMsg_EnablePreferredSizeChangedMode| + // message. + if (!send_preferred_size_changes_ || !MainFrameImpl()) + return; + + if (!needs_preferred_size_update_) + return; + needs_preferred_size_update_ = false; + + gfx::Size size_in_dips = + MainFrameImpl()->LocalRootFrameWidget()->BlinkSpaceToFlooredDIPs( + gfx::Size(ContentsPreferredMinimumSize())); + + if (size_in_dips != preferred_size_in_dips_) { + preferred_size_in_dips_ = size_in_dips; + local_main_frame_host_remote_->ContentsPreferredSizeChanged(size_in_dips); + } +} + +void WebViewImpl::EnablePreferredSizeChangedMode() { + if (send_preferred_size_changes_) + return; + send_preferred_size_changes_ = true; + needs_preferred_size_update_ = true; + + // We need to ensure |UpdatePreferredSize| gets called. If a layout is needed, + // force an update here which will call |DidUpdateMainFrameLayout|. + if (MainFrameWidget()) { + MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kLayout, + DocumentUpdateReason::kSizeChange); + } + + // If a layout was not needed, |DidUpdateMainFrameLayout| will not be called. + // We explicitly update the preferred size here to ensure the preferred size + // notification is sent. + UpdatePreferredSize(); +} + +void WebViewImpl::Focus() { + if (GetPage()->MainFrame()->IsLocalFrame()) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->FocusPage(); + } else { + DCHECK(remote_main_frame_host_remote_); + remote_main_frame_host_remote_->FocusPage(); + } +} + +void WebViewImpl::TakeFocus(bool reverse) { + if (GetPage()->MainFrame()->IsLocalFrame()) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->TakeFocus(reverse); + } else { + DCHECK(remote_main_frame_host_remote_); + remote_main_frame_host_remote_->TakeFocus(reverse); + } +} + +void WebViewImpl::Show(const LocalFrameToken& opener_frame_token, + NavigationPolicy policy, + const gfx::Rect& requested_rect, + const gfx::Rect& adjusted_rect, + bool opened_by_user_gesture) { + // This is only called on local main frames. + DCHECK(local_main_frame_host_remote_); + DCHECK(web_widget_); + web_widget_->SetPendingWindowRect(adjusted_rect); + const WebWindowFeatures& web_window_features = page_->GetWindowFeatures(); + mojom::blink::WindowFeaturesPtr window_features = + mojom::blink::WindowFeatures::New(); + window_features->bounds = requested_rect; + window_features->has_x = web_window_features.x_set; + window_features->has_y = web_window_features.y_set; + window_features->has_width = web_window_features.width_set; + window_features->has_height = web_window_features.height_set; + window_features->is_popup = web_window_features.is_popup; + local_main_frame_host_remote_->ShowCreatedWindow( + opener_frame_token, NavigationPolicyToDisposition(policy), + std::move(window_features), opened_by_user_gesture, + WTF::BindOnce(&WebViewImpl::DidShowCreatedWindow, WTF::Unretained(this))); + + MainFrameDevToolsAgentImpl()->DidShowNewWindow(); +} + +void WebViewImpl::DidShowCreatedWindow() { + web_widget_->AckPendingWindowRect(); +} + +void WebViewImpl::SendWindowRectToMainFrameHost( + const gfx::Rect& bounds, + base::OnceClosure ack_callback) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->SetWindowRect(bounds, std::move(ack_callback)); +} + +void WebViewImpl::DidAccessInitialMainDocument() { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->DidAccessInitialMainDocument(); +} + +void WebViewImpl::Minimize() { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->Minimize(); +} + +void WebViewImpl::Maximize() { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->Maximize(); +} + +void WebViewImpl::Restore() { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->Restore(); +} + +void WebViewImpl::SetResizable(bool resizable) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->SetResizable(resizable); +} + +void WebViewImpl::UpdateTargetURL(const WebURL& url, + const WebURL& fallback_url) { + KURL latest_url = KURL(url.IsEmpty() ? fallback_url : url); + if (latest_url == target_url_) + return; + + // Tell the browser to display a destination link. + if (target_url_status_ == TARGET_INFLIGHT || + target_url_status_ == TARGET_PENDING) { + // If we have a request in-flight, save the URL to be sent when we + // receive an ACK to the in-flight request. We can happily overwrite + // any existing pending sends. + pending_target_url_ = latest_url; + target_url_status_ = TARGET_PENDING; + } else { + // URLs larger than |kMaxURLChars| cannot be sent through IPC - + // see |ParamTraits|. + if (latest_url.GetString().length() > url::kMaxURLChars) + latest_url = KURL(); + SendUpdatedTargetURLToBrowser(latest_url); + target_url_ = latest_url; + target_url_status_ = TARGET_INFLIGHT; + } +} + +void WebViewImpl::SendUpdatedTargetURLToBrowser(const KURL& target_url) { + // Note: WTF::Unretained() usage below is safe, since `this` owns both + // `mojo::Remote` objects. + if (GetPage()->MainFrame()->IsLocalFrame()) { + DCHECK(local_main_frame_host_remote_); + local_main_frame_host_remote_->UpdateTargetURL( + target_url, WTF::BindOnce(&WebViewImpl::TargetURLUpdatedInBrowser, + WTF::Unretained(this))); + } else { + DCHECK(remote_main_frame_host_remote_); + remote_main_frame_host_remote_->UpdateTargetURL( + target_url, WTF::BindOnce(&WebViewImpl::TargetURLUpdatedInBrowser, + WTF::Unretained(this))); + } +} + +void WebViewImpl::TargetURLUpdatedInBrowser() { + // Check if there is a targeturl waiting to be sent. + if (target_url_status_ == TARGET_PENDING) + SendUpdatedTargetURLToBrowser(pending_target_url_); + + target_url_status_ = TARGET_NONE; +} + +float WebViewImpl::DefaultMinimumPageScaleFactor() const { + return GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale; +} + +float WebViewImpl::DefaultMaximumPageScaleFactor() const { + return GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale; +} + +float WebViewImpl::MinimumPageScaleFactor() const { + return GetPageScaleConstraintsSet().FinalConstraints().minimum_scale; +} + +float WebViewImpl::MaximumPageScaleFactor() const { + return GetPageScaleConstraintsSet().FinalConstraints().maximum_scale; +} + +void WebViewImpl::ResetScaleStateImmediately() { + GetPageScaleConstraintsSet().SetNeedsReset(true); +} + +void WebViewImpl::ResetScrollAndScaleState() { + GetPage()->GetVisualViewport().Reset(); + + auto* main_local_frame = DynamicTo(GetPage()->MainFrame()); + if (!main_local_frame) + return; + + if (LocalFrameView* frame_view = main_local_frame->View()) { + ScrollableArea* scrollable_area = frame_view->LayoutViewport(); + + if (!scrollable_area->GetScrollOffset().IsZero()) { + scrollable_area->SetScrollOffset(ScrollOffset(), + mojom::blink::ScrollType::kProgrammatic); + } + } + + if (Document* document = main_local_frame->GetDocument()) { + if (DocumentLoader* loader = document->Loader()) { + if (HistoryItem* item = loader->GetHistoryItem()) + item->ClearViewState(); + } + } + + GetPageScaleConstraintsSet().SetNeedsReset(true); +} + +void WebViewImpl::SendResizeEventForMainFrame() { + // FIXME: This is wrong. The LocalFrameView is responsible sending a + // resizeEvent as part of layout. Layout is also responsible for sending + // invalidations to the embedder. This method and all callers may be wrong. -- + // eseidel. + if (MainFrameImpl()->GetFrameView()) { + // Enqueues the resize event. + MainFrameImpl()->GetFrame()->GetDocument()->EnqueueResizeEvent(); + } + + // A resized main frame can change the page scale limits. + if (does_composite_) { + auto& viewport = GetPage()->GetVisualViewport(); + MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits( + viewport.Scale(), viewport.IsPinchGestureActive(), + MinimumPageScaleFactor(), MaximumPageScaleFactor()); + } +} + +void WebViewImpl::ConfigureAutoResizeMode() { + if (!MainFrameImpl() || !MainFrameImpl()->GetFrame() || + !MainFrameImpl()->GetFrame()->View()) + return; + + if (should_auto_resize_) { + MainFrameImpl()->GetFrame()->View()->EnableAutoSizeMode(min_auto_size_, + max_auto_size_); + } else { + MainFrameImpl()->GetFrame()->View()->DisableAutoSizeMode(); + } +} + +void WebViewImpl::SetCompositorDeviceScaleFactorOverride( + float device_scale_factor) { + if (compositor_device_scale_factor_override_ == device_scale_factor) + return; + compositor_device_scale_factor_override_ = device_scale_factor; + if (zoom_factor_for_device_scale_factor_) { + SetZoomLevel(ZoomLevel()); + return; + } +} + +void WebViewImpl::SetDeviceEmulationTransform(const gfx::Transform& transform) { + if (transform == device_emulation_transform_) + return; + device_emulation_transform_ = transform; + UpdateDeviceEmulationTransform(); +} + +gfx::Transform WebViewImpl::GetDeviceEmulationTransform() const { + return device_emulation_transform_; +} + +void WebViewImpl::EnableDeviceEmulation(const DeviceEmulationParams& params) { + web_widget_->EnableDeviceEmulation(params); +} + +void WebViewImpl::ActivateDevToolsTransform( + const DeviceEmulationParams& params) { + gfx::Transform device_emulation_transform = + dev_tools_emulator_->EnableDeviceEmulation(params); + SetDeviceEmulationTransform(device_emulation_transform); +} + +void WebViewImpl::DisableDeviceEmulation() { + web_widget_->DisableDeviceEmulation(); +} + +void WebViewImpl::DeactivateDevToolsTransform() { + dev_tools_emulator_->DisableDeviceEmulation(); + SetDeviceEmulationTransform(gfx::Transform()); +} + +void WebViewImpl::PerformCustomContextMenuAction(unsigned action) { + if (page_) { + page_->GetContextMenuController().CustomContextMenuItemSelected(action); + } +} + +void WebViewImpl::DidCloseContextMenu() { + LocalFrame* frame = page_->GetFocusController().FocusedFrame(); + if (frame) + frame->Selection().SetCaretBlinkingSuspended(false); +} + +SkColor WebViewImpl::BackgroundColor() const { + if (background_color_override_for_fullscreen_controller_) + return background_color_override_for_fullscreen_controller_.value(); + Page* page = page_.Get(); + if (!page) + return BaseBackgroundColor().Rgb(); + if (auto* main_local_frame = DynamicTo(page->MainFrame())) { + LocalFrameView* view = main_local_frame->View(); + if (view) + return view->DocumentBackgroundColor().Rgb(); + } + return BaseBackgroundColor().Rgb(); +} + +Color WebViewImpl::BaseBackgroundColor() const { + if (override_base_background_color_to_transparent_) + return Color::kTransparent; + // TODO(https://crbug.com/1351544): The base background color override should + // be an SkColor4f or a Color. + if (base_background_color_override_for_inspector_) { + return Color::FromSkColor( + base_background_color_override_for_inspector_.value()); + } + // Use the page background color if this is the WebView of the main frame. + if (MainFrameImpl()) + return Color::FromSkColor(page_base_background_color_); + return Color::kWhite; +} + +void WebViewImpl::SetPageBaseBackgroundColor(std::optional color) { + SkColor new_color = color.value_or(SK_ColorWHITE); + if (page_base_background_color_ == new_color) + return; + page_base_background_color_ = new_color; + UpdateBaseBackgroundColor(); +} + +void WebViewImpl::UpdateColorProviders( + const ColorProviderColorMaps& color_provider_colors) { + bool color_providers_did_change = + page_->UpdateColorProviders(color_provider_colors); + if (color_providers_did_change) { + Page::PlatformColorsChanged(); + Page::ColorSchemeChanged(); + } +} + +void WebViewImpl::SetBaseBackgroundColorOverrideTransparent( + bool override_to_transparent) { + DCHECK(does_composite_); + if (override_base_background_color_to_transparent_ == override_to_transparent) + return; + override_base_background_color_to_transparent_ = override_to_transparent; + UpdateBaseBackgroundColor(); +} + +void WebViewImpl::SetBaseBackgroundColorOverrideForInspector( + std::optional optional_color) { + if (base_background_color_override_for_inspector_ == optional_color) + return; + base_background_color_override_for_inspector_ = optional_color; + UpdateBaseBackgroundColor(); +} + +void WebViewImpl::UpdateBaseBackgroundColor() { + if (MainFrameImpl()) { + // Force lifecycle update to ensure we're good to call + // LocalFrameView::setBaseBackgroundColor(). + MainFrameImpl()->GetFrame()->View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kBaseColor); + } + + Color color = BaseBackgroundColor(); + if (auto* local_frame = DynamicTo(page_->MainFrame())) { + LocalFrameView* view = local_frame->View(); + view->UpdateBaseBackgroundColorRecursively(color); + } +} + +void WebViewImpl::UpdateFontRenderingFromRendererPrefs() { +#if !BUILDFLAG(IS_MAC) + skia::LegacyDisplayGlobals::SetCachedParams( + gfx::FontRenderParams::SubpixelRenderingToSkiaPixelGeometry( + renderer_preferences_.subpixel_rendering), + renderer_preferences_.text_contrast, renderer_preferences_.text_gamma); +#if BUILDFLAG(IS_WIN) + // Cache the system font metrics in blink. + WebFontRendering::SetMenuFontMetrics( + WebString::FromUTF16(renderer_preferences_.menu_font_family_name), + renderer_preferences_.menu_font_height); + WebFontRendering::SetSmallCaptionFontMetrics( + WebString::FromUTF16( + renderer_preferences_.small_caption_font_family_name), + renderer_preferences_.small_caption_font_height); + WebFontRendering::SetStatusFontMetrics( + WebString::FromUTF16(renderer_preferences_.status_font_family_name), + renderer_preferences_.status_font_height); + WebFontRendering::SetAntialiasedTextEnabled( + renderer_preferences_.should_antialias_text); + WebFontRendering::SetLCDTextEnabled( + renderer_preferences_.subpixel_rendering != + gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE); +#else + WebFontRenderStyle::SetHinting( + RendererPreferencesToSkiaHinting(renderer_preferences_)); + WebFontRenderStyle::SetAutoHint(renderer_preferences_.use_autohinter); + WebFontRenderStyle::SetUseBitmaps(renderer_preferences_.use_bitmaps); + WebFontRenderStyle::SetAntiAlias(renderer_preferences_.should_antialias_text); + WebFontRenderStyle::SetSubpixelRendering( + renderer_preferences_.subpixel_rendering != + gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE); + WebFontRenderStyle::SetSubpixelPositioning( + renderer_preferences_.use_subpixel_positioning); +// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is +// complete. +#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \ + !BUILDFLAG(IS_ANDROID) + if (!renderer_preferences_.system_font_family_name.empty()) { + WebFontRenderStyle::SetSystemFontFamily(blink::WebString::FromUTF8( + renderer_preferences_.system_font_family_name)); + } +#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && + // !BUILDFLAG(IS_ANDROID) +#endif // BUILDFLAG(IS_WIN) +#endif // !BUILDFLAG(IS_MAC) +} + +void WebViewImpl::ActivatePrerenderedPage( + mojom::blink::PrerenderPageActivationParamsPtr + prerender_page_activation_params, + ActivatePrerenderedPageCallback callback) { + TRACE_EVENT0("navigation", "WebViewImpl::ActivatePrerenderedPage"); + + // From here all new documents will have prerendering false. + GetPage()->SetIsPrerendering(false); + + // Collect local documents. This is because we are about to run the + // prerenderchange event and post-prerendering activation steps on each + // document, which could mutate the frame tree and make iteration over it + // complicated. + HeapVector> child_frame_documents; + Member main_frame_document; + if (auto* local_frame = DynamicTo(GetPage()->MainFrame())) { + main_frame_document = local_frame->GetDocument(); + } + if (main_frame_document) { + RecordPrerenderActivationSignalDelay(); + } + + for (Frame* frame = GetPage()->MainFrame(); frame; + frame = frame->Tree().TraverseNext()) { + if (auto* local_frame = DynamicTo(frame)) { + if (local_frame->GetDocument() != main_frame_document) { + child_frame_documents.push_back(local_frame->GetDocument()); + } + } + } + + // A null `activation_start` is sent to the WebViewImpl that does not host the + // main frame, in which case we expect that it does not have any documents + // since cross-origin documents are not loaded during prerendering. + DCHECK((!main_frame_document && child_frame_documents.size() == 0) || + !prerender_page_activation_params->activation_start.is_null()); + // We also only send view_transition_state to the main frame. + DCHECK(main_frame_document || + !prerender_page_activation_params->view_transition_state); + + if (main_frame_document) { + main_frame_document->ActivateForPrerendering( + *prerender_page_activation_params); + prerender_page_activation_params->view_transition_state.reset(); + } + + // While the spec says to post a task on the networking task source for each + // document, we don't post a task here for simplicity. This allows dispatching + // the event on all documents without a chance for other IPCs from the browser + // to arrive in the intervening time, resulting in an unclear state. + for (auto& document : child_frame_documents) { + document->ActivateForPrerendering(*prerender_page_activation_params); + } + + std::move(callback).Run(); +} + +void WebViewImpl::RegisterRendererPreferenceWatcher( + CrossVariantMojoRemote + watcher) { + renderer_preference_watchers_.Add(std::move(watcher)); +} + +void WebViewImpl::SetRendererPreferences( + const RendererPreferences& preferences) { + UpdateRendererPreferences(preferences); +} + +const RendererPreferences& WebViewImpl::GetRendererPreferences() const { + return renderer_preferences_; +} + +void WebViewImpl::UpdateRendererPreferences( + const RendererPreferences& preferences) { + std::string old_accept_languages = renderer_preferences_.accept_languages; + renderer_preferences_ = preferences; + + for (auto& watcher : renderer_preference_watchers_) + watcher->NotifyUpdate(renderer_preferences_); + + WebThemeEngineHelper::DidUpdateRendererPreferences(preferences); + UpdateFontRenderingFromRendererPrefs(); + + blink::SetCaretBlinkInterval( + renderer_preferences_.caret_blink_interval.has_value() + ? renderer_preferences_.caret_blink_interval.value() + : base::Milliseconds( + mojom::blink::kDefaultCaretBlinkIntervalInMilliseconds)); + +#if defined(USE_AURA) + if (renderer_preferences_.use_custom_colors) { + SetFocusRingColor(renderer_preferences_.focus_ring_color); + SetSelectionColors(renderer_preferences_.active_selection_bg_color, + renderer_preferences_.active_selection_fg_color, + renderer_preferences_.inactive_selection_bg_color, + renderer_preferences_.inactive_selection_fg_color); + ThemeChanged(); + } +#endif + + if (renderer_preferences_.use_custom_colors) { + SetFocusRingColor(renderer_preferences_.focus_ring_color); + } + + if (old_accept_languages != renderer_preferences_.accept_languages) + AcceptLanguagesChanged(); + + GetSettings()->SetCaretBrowsingEnabled( + renderer_preferences_.caret_browsing_enabled); + +#if BUILDFLAG(IS_OZONE) + GetSettings()->SetSelectionClipboardBufferAvailable( + renderer_preferences_.selection_clipboard_buffer_available); +#endif // BUILDFLAG(IS_OZONE) + + SetExplicitlyAllowedPorts( + renderer_preferences_.explicitly_allowed_network_ports); + + if (renderer_preferences_.prefixed_fullscreen_video_api_availability + .has_value()) { + WebRuntimeFeatures::EnableFeatureFromString( + "PrefixedVideoFullscreen", + renderer_preferences_.prefixed_fullscreen_video_api_availability + .value()); + } +} + +void WebViewImpl::SetHistoryOffsetAndLength(int32_t history_offset, + int32_t history_length) { + // -1 <= history_offset < history_length <= kMaxSessionHistoryEntries. + DCHECK_LE(-1, history_offset); + DCHECK_LT(history_offset, history_length); + DCHECK_LE(history_length, kMaxSessionHistoryEntries); + + history_list_offset_ = history_offset; + history_list_length_ = history_length; +} + +void WebViewImpl::SetHistoryListFromNavigation( + int32_t history_offset, + std::optional history_length) { + if (!history_length.has_value()) { + history_list_offset_ = history_offset; + return; + } + + SetHistoryOffsetAndLength(history_offset, *history_length); +} + +void WebViewImpl::IncreaseHistoryListFromNavigation() { + // Advance our offset in session history, applying the length limit. + // There is now no forward history. + history_list_offset_ = + std::min(history_list_offset_ + 1, kMaxSessionHistoryEntries - 1); + history_list_length_ = history_list_offset_ + 1; +} + +int32_t WebViewImpl::HistoryBackListCount() const { + return std::max(history_list_offset_, 0); +} + +int32_t WebViewImpl::HistoryForwardListCount() const { + return history_list_length_ - HistoryBackListCount() - 1; +} + +void WebViewImpl::SetWebPreferences( + const web_pref::WebPreferences& preferences) { + UpdateWebPreferences(preferences); +} + +const web_pref::WebPreferences& WebViewImpl::GetWebPreferences() { + return web_preferences_; +} + +void WebViewImpl::UpdateWebPreferences( + const blink::web_pref::WebPreferences& preferences) { + web_preferences_ = preferences; + + if (IsFencedFrameRoot()) { + // The main frame of a fenced frame should not behave like a top level + // frame in terms of viewport behavior. i.e. It shouldn't allow zooming, + // either explicitly or to fit content, and it should not interpret the + // viewport tag. Text autosizing is disabled since it is only + // determined by the outermost page and having the outermost page pass + // it into the fenced frame can create a communication channel. + web_preferences_.viewport_enabled = false; + web_preferences_.viewport_meta_enabled = false; + web_preferences_.default_minimum_page_scale_factor = 1.f; + web_preferences_.default_maximum_page_scale_factor = 1.f; + web_preferences_.shrinks_viewport_contents_to_fit = false; + web_preferences_.main_frame_resizes_are_orientation_changes = false; + web_preferences_.text_autosizing_enabled = false; + + // Insecure content should not be allowed in a fenced frame. + web_preferences_.allow_running_insecure_content = false; + +#if BUILDFLAG(IS_ANDROID) + // Reusing the global for unowned main frame is only used for + // Android WebView. Since this is a fenced frame it is not the + // outermost main frame so we can safely disable this feature. + web_preferences_.reuse_global_for_unowned_main_frame = false; +#endif + } + + if (MainFrameImpl()) { + MainFrameImpl()->FrameWidgetImpl()->SetPrefersReducedMotion( + web_preferences_.prefers_reduced_motion); + } + + ApplyWebPreferences(web_preferences_, this); + ApplyCommandLineToSettings(SettingsImpl()); +} + +void WebViewImpl::AddObserver(WebViewObserver* observer) { + observers_.AddObserver(observer); +} + +void WebViewImpl::RemoveObserver(WebViewObserver* observer) { + observers_.RemoveObserver(observer); +} + +void WebViewImpl::SetIsActive(bool active) { + if (GetPage()) + GetPage()->GetFocusController().SetActive(active); +} + +bool WebViewImpl::IsActive() const { + return GetPage() ? GetPage()->GetFocusController().IsActive() : false; +} + +void WebViewImpl::SetWindowFeatures(const WebWindowFeatures& features) { + page_->SetWindowFeatures(features); +} + +void WebViewImpl::SetOpenedByDOM() { + page_->SetOpenedByDOM(); +} + +void WebViewImpl::DidCommitLoad(bool is_new_navigation, + bool is_navigation_within_page) { + if (!is_navigation_within_page) { + if (web_widget_) + web_widget_->ResetMeaningfulLayoutStateForMainFrame(); + + if (is_new_navigation) + GetPageScaleConstraintsSet().SetNeedsReset(true); + } + + // Give the visual viewport's scroll layer its initial size. + GetPage()->GetVisualViewport().MainFrameDidChangeSize(); +} + +void WebViewImpl::DidCommitCompositorFrameForLocalMainFrame() { + for (auto& observer : observers_) + observer.DidCommitCompositorFrame(); +} + +void WebViewImpl::ResizeAfterLayout() { + DCHECK(MainFrameImpl()); + + if (!web_view_client_) + return; + + if (should_auto_resize_) { + LocalFrameView* view = MainFrameImpl()->GetFrame()->View(); + gfx::Size frame_size = view->Size(); + if (frame_size != size_) { + size_ = frame_size; + + GetPage()->GetVisualViewport().SetSize(size_); + GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_); + + web_view_client_->DidAutoResize(size_); + web_widget_->DidAutoResize(size_); + SendResizeEventForMainFrame(); + } + } + + if (does_composite_ && GetPageScaleConstraintsSet().ConstraintsDirty()) + RefreshPageScaleFactor(); + + resize_viewport_anchor_->ResizeFrameView(MainFrameSize()); +} + +void WebViewImpl::MainFrameLayoutUpdated() { + DCHECK(MainFrameImpl()); + if (!web_view_client_) + return; + + for (auto& observer : observers_) + observer.DidUpdateMainFrameLayout(); + needs_preferred_size_update_ = true; +} + +void WebViewImpl::DidChangeContentsSize() { + auto* local_frame = DynamicTo(GetPage()->MainFrame()); + if (!local_frame) + return; + + LocalFrameView* view = local_frame->View(); + + int vertical_scrollbar_width = 0; + if (view && view->LayoutViewport()) { + Scrollbar* vertical_scrollbar = view->LayoutViewport()->VerticalScrollbar(); + if (vertical_scrollbar && !vertical_scrollbar->IsOverlayScrollbar()) + vertical_scrollbar_width = vertical_scrollbar->Width(); + } + + GetPageScaleConstraintsSet().DidChangeContentsSize( + ContentsSize(), vertical_scrollbar_width, PageScaleFactor()); +} + +void WebViewImpl::PageScaleFactorChanged() { + // This is called from the VisualViewport which only is used to control the + // page scale/scroll viewport for a local main frame, and only when + // compositing as PageScaleFactor doesn't exist otherwise. + DCHECK(MainFrameImpl()); + DCHECK(does_composite_); + + GetPageScaleConstraintsSet().SetNeedsReset(false); + // Set up the compositor and inform the browser of the PageScaleFactor, + // which is tracked per-view. + auto& viewport = GetPage()->GetVisualViewport(); + DCHECK(viewport.IsActiveViewport()); + MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits( + viewport.Scale(), viewport.IsPinchGestureActive(), + MinimumPageScaleFactor(), MaximumPageScaleFactor()); + + local_main_frame_host_remote_->ScaleFactorChanged(viewport.Scale()); + + if (dev_tools_emulator_->HasViewportOverride()) { + // TODO(bokan): Can HasViewportOverride be set on a nested main frame? If + // not, we can enforce that when setting it and DCHECK IsOutermostMainFrame + // instead. + if (MainFrameImpl()->IsOutermostMainFrame()) { + gfx::Transform device_emulation_transform = + dev_tools_emulator_->OutermostMainFrameScrollOrScaleChanged(); + SetDeviceEmulationTransform(device_emulation_transform); + } + } +} + +void WebViewImpl::OutermostMainFrameScrollOffsetChanged() { + DCHECK(MainFrameImpl()); + DCHECK(MainFrameImpl()->IsOutermostMainFrame()); + if (dev_tools_emulator_->HasViewportOverride()) { + gfx::Transform device_emulation_transform = + dev_tools_emulator_->OutermostMainFrameScrollOrScaleChanged(); + SetDeviceEmulationTransform(device_emulation_transform); + } +} + +void WebViewImpl::TextAutosizerPageInfoChanged( + const mojom::blink::TextAutosizerPageInfo& page_info) { + DCHECK(MainFrameImpl()); + local_main_frame_host_remote_->TextAutosizerPageInfoChanged( + page_info.Clone()); +} + +void WebViewImpl::SetBackgroundColorOverrideForFullscreenController( + std::optional optional_color) { + DCHECK(does_composite_); + + background_color_override_for_fullscreen_controller_ = optional_color; + if (MainFrameImpl()) { + MainFrameImpl()->FrameWidgetImpl()->SetBackgroundColor(BackgroundColor()); + } +} + +void WebViewImpl::SetZoomFactorOverride(float zoom_factor) { + zoom_factor_override_ = zoom_factor; + SetZoomLevel(ZoomLevel()); +} + +Element* WebViewImpl::FocusedElement() const { + LocalFrame* frame = page_->GetFocusController().FocusedFrame(); + if (!frame) + return nullptr; + + Document* document = frame->GetDocument(); + if (!document) + return nullptr; + + return document->FocusedElement(); +} + +WebHitTestResult WebViewImpl::HitTestResultForTap( + const gfx::Point& tap_point_window_pos, + const gfx::Size& tap_area) { + auto* main_frame = DynamicTo(page_->MainFrame()); + if (!main_frame) + return HitTestResult(); + + WebGestureEvent tap_event(WebInputEvent::Type::kGestureTap, + WebInputEvent::kNoModifiers, base::TimeTicks::Now(), + WebGestureDevice::kTouchscreen); + // GestureTap is only ever from a touchscreen. + tap_event.SetPositionInWidget(gfx::PointF(tap_point_window_pos)); + tap_event.data.tap.tap_count = 1; + tap_event.data.tap.width = tap_area.width(); + tap_event.data.tap.height = tap_area.height(); + + WebGestureEvent scaled_event = + TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), tap_event); + + HitTestResult result = + main_frame->GetEventHandler() + .HitTestResultForGestureEvent( + scaled_event, HitTestRequest::kReadOnly | HitTestRequest::kActive) + .GetHitTestResult(); + + result.SetToShadowHostIfInUAShadowRoot(); + return result; +} + +void WebViewImpl::SetTabsToLinks(bool enable) { + tabs_to_links_ = enable; +} + +bool WebViewImpl::TabsToLinks() const { + return tabs_to_links_; +} + +void WebViewImpl::DidChangeRootLayer(bool root_layer_exists) { + // The Layer is removed when the main frame's `Document` changes. It also is + // removed when the whole `LocalFrame` goes away, in which case we don't + // need to DeferMainFrameUpdate() as we will do so if a local MainFrame is + // attached in the future. + if (!MainFrameImpl()) { + DCHECK(!root_layer_exists); + return; + } + if (root_layer_exists) { + if (!device_emulation_transform_.IsIdentity()) + UpdateDeviceEmulationTransform(); + } else if (!MainFrameImpl()->FrameWidgetImpl()->WillBeDestroyed()) { + // When the document in an already-attached main frame is being replaced + // by a navigation then DidChangeRootLayer(false) will be called. Since we + // are navigating, defer BeginMainFrames until the new document is ready + // for them. + // + // If WillBeDestroyed() is true, it means we're swapping the frame as well + // as the document for this navigation. BeginMainFrames are instead + // deferred for a newly attached frame via DidAttachLocalMainFrame(). See + // crbug.com/936696. + scoped_defer_main_frame_update_ = + MainFrameImpl()->FrameWidgetImpl()->DeferMainFrameUpdate(); + } +} + +void WebViewImpl::InvalidateContainer() { + // This is only for non-composited WebViewPlugin. + if (!does_composite_ && web_view_client_) + web_view_client_->InvalidateContainer(); +} + +void WebViewImpl::ApplyViewportChanges(const ApplyViewportChangesArgs& args) { + // TODO(https://crbug.com/1160652): Figure out if Page is null. + CHECK(page_); + + VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); + DCHECK(visual_viewport.IsActiveViewport()); + + // Store the desired offsets the visual viewport before setting the top + // controls ratio since doing so will change the bounds and move the + // viewports to keep the offsets valid. The compositor may have already + // done that so we don't want to double apply the deltas here. + gfx::PointF visual_viewport_offset = visual_viewport.VisibleRect().origin(); + visual_viewport_offset.Offset(args.inner_delta.x(), args.inner_delta.y()); + + GetBrowserControls().SetShownRatio( + GetBrowserControls().TopShownRatio() + args.top_controls_delta, + GetBrowserControls().BottomShownRatio() + args.bottom_controls_delta); + + SetPageScaleFactorAndLocation(PageScaleFactor() * args.page_scale_delta, + args.is_pinch_gesture_active, + visual_viewport_offset); + + if (args.page_scale_delta != 1) { + double_tap_zoom_pending_ = false; + } + + elastic_overscroll_ += args.elastic_overscroll_delta; + UpdateBrowserControlsConstraint(args.browser_controls_constraint); + + if (args.scroll_gesture_did_end) { + // TODO(https://crbug.com/1160652): Figure out if MainFrameImpl is null. + CHECK(MainFrameImpl()); + MainFrameImpl()->GetFrame()->GetEventHandler().MarkHoverStateDirty(); + } +} + +Node* WebViewImpl::FindNodeFromScrollableCompositorElementId( + cc::ElementId element_id) const { + if (!GetPage()) + return nullptr; + + if (element_id == GetPage()->GetVisualViewport().GetScrollElementId()) { + // Return the Document in this case since the window.visualViewport DOM + // object is not a node. + if (MainFrameImpl()) + return MainFrameImpl()->GetDocument(); + } + + if (!GetPage()->GetScrollingCoordinator()) + return nullptr; + ScrollableArea* scrollable_area = + GetPage() + ->GetScrollingCoordinator() + ->ScrollableAreaWithElementIdInAllLocalFrames(element_id); + if (!scrollable_area || !scrollable_area->GetLayoutBox()) + return nullptr; + + return scrollable_area->GetLayoutBox()->GetNode(); +} + +void WebViewImpl::UpdateDeviceEmulationTransform() { + if (GetPage()->GetVisualViewport().IsActiveViewport()) + GetPage()->GetVisualViewport().SetNeedsPaintPropertyUpdate(); + + if (auto* main_frame = MainFrameImpl()) { + // When the device emulation transform is updated, to avoid incorrect + // scales and fuzzy raster from the compositor, force all content to + // pick ideal raster scales. + // TODO(wjmaclean): This is only done on the main frame's widget currently, + // it should update all local frames. + main_frame->FrameWidgetImpl()->SetNeedsRecalculateRasterScales(); + + // Device emulation transform also affects the overriding visible rect + // which is used as the overflow rect of the main frame layout view. + if (auto* view = main_frame->GetFrameView()) + view->SetNeedsPaintPropertyUpdate(); + } +} + +PageScheduler* WebViewImpl::Scheduler() const { + DCHECK(GetPage()); + return GetPage()->GetPageScheduler(); +} + +void WebViewImpl::SetVisibilityState( + mojom::blink::PageVisibilityState visibility_state, + bool is_initial_state) { + DCHECK(GetPage()); + GetPage()->SetVisibilityState(visibility_state, is_initial_state); + // Do not throttle if the page should be painting. + bool is_visible = + visibility_state == mojom::blink::PageVisibilityState::kVisible; + if (RuntimeEnabledFeatures::DispatchHiddenVisibilityTransitionsEnabled()) { + // Treat `kHiddenButPainting` as visible for page scheduling; we don't want + // to throttle timers, etc. + is_visible |= visibility_state == + mojom::blink::PageVisibilityState::kHiddenButPainting; + } + GetPage()->GetPageScheduler()->SetPageVisible(is_visible); + // Notify observers of the change. + if (!is_initial_state) { + for (auto& observer : observers_) + observer.OnPageVisibilityChanged(visibility_state); + } +} + +mojom::blink::PageVisibilityState WebViewImpl::GetVisibilityState() { + DCHECK(GetPage()); + return GetPage()->GetVisibilityState(); +} + +LocalFrame* WebViewImpl::FocusedLocalFrameInWidget() const { + if (!MainFrameImpl()) + return nullptr; + + auto* focused_frame = To(FocusedCoreFrame()); + if (focused_frame->LocalFrameRoot() != MainFrameImpl()->GetFrame()) + return nullptr; + return focused_frame; +} + +void WebViewImpl::SetPageFrozen(bool frozen) { + Scheduler()->SetPageFrozen(frozen); +} + +WebFrameWidget* WebViewImpl::MainFrameWidget() { + return web_widget_; +} + +void WebViewImpl::AddAutoplayFlags(int32_t value) { + page_->AddAutoplayFlags(value); +} + +void WebViewImpl::ClearAutoplayFlags() { + page_->ClearAutoplayFlags(); +} + +int32_t WebViewImpl::AutoplayFlagsForTest() const { + return page_->AutoplayFlags(); +} + +gfx::Size WebViewImpl::GetPreferredSizeForTest() { + return preferred_size_in_dips_; +} + +void WebViewImpl::StopDeferringMainFrameUpdate() { + scoped_defer_main_frame_update_ = nullptr; +} + +void WebViewImpl::SetDeviceColorSpaceForTesting( + const gfx::ColorSpace& color_space) { + web_widget_->SetDeviceColorSpaceForTesting(color_space); +} + +const SessionStorageNamespaceId& WebViewImpl::GetSessionStorageNamespaceId() { + CHECK(!session_storage_namespace_id_.empty()); + return session_storage_namespace_id_; +} + +bool WebViewImpl::IsFencedFrameRoot() const { + return GetPage()->IsMainFrameFencedFrameRoot(); +} + +void WebViewImpl::SetSupportsAppRegion(bool supports_app_region) { + supports_app_region_ = supports_app_region; + if (!MainFrameImpl() || !MainFrameImpl()->GetFrame()) { + return; + } + + LocalFrame* local_frame = MainFrameImpl()->GetFrame(); + + if (supports_app_region_) { + local_frame->View()->UpdateDocumentAnnotatedRegions(); + } else { + local_frame->GetDocument()->SetAnnotatedRegions( + Vector()); + local_frame->Client()->AnnotatedRegionsChanged(); + } +} + +bool WebViewImpl::SupportsAppRegion() { + return supports_app_region_; +} + +void WebViewImpl::MojoDisconnected() { +#if !(BUILDFLAG(IS_ANDROID) || \ + (BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM64))) + auto prev_close_task_trace = close_task_posted_stack_trace_; + base::debug::Alias(&prev_close_task_trace); + close_task_posted_stack_trace_.emplace(); + auto cur_close_task_trace = close_task_posted_stack_trace_; + base::debug::Alias(&cur_close_task_trace); + auto close_trace = close_called_stack_trace_; + base::debug::Alias(&close_trace); + auto close_window_trace = close_window_called_stack_trace_; + base::debug::Alias(&close_window_trace); +#endif + // This IPC can be called from re-entrant contexts. We can't destroy a + // RenderViewImpl while references still exist on the stack, so we dispatch a + // non-nestable task. This method is called exactly once by the browser + // process, and is used to release ownership of the corresponding + // RenderViewImpl instance. https://crbug.com/1000035. + GetPage()->GetAgentGroupScheduler().DefaultTaskRunner()->PostNonNestableTask( + FROM_HERE, WTF::BindOnce(&WebViewImpl::Close, WTF::Unretained(this))); +} + +void WebViewImpl::CreateRemoteMainFrame( + const RemoteFrameToken& frame_token, + const std::optional& opener_frame_token, + mojom::blink::FrameReplicationStatePtr replicated_state, + bool is_loading, + const base::UnguessableToken& devtools_frame_token, + mojom::blink::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces, + mojom::blink::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) { + blink::WebFrame* opener = nullptr; + if (opener_frame_token) + opener = WebFrame::FromFrameToken(*opener_frame_token); + // Create a top level WebRemoteFrame. + WebRemoteFrameImpl::CreateMainFrame( + this, frame_token, is_loading, devtools_frame_token, opener, + std::move(remote_frame_interfaces->frame_host), + std::move(remote_frame_interfaces->frame_receiver), + std::move(replicated_state)); + // Root frame proxy has no ancestors to point to their RenderWidget. + + // The WebRemoteFrame created here was already attached to the Page as its + // main frame, so we can call WebView's DidAttachRemoteMainFrame(). + DidAttachRemoteMainFrame( + std::move(remote_main_frame_interfaces->main_frame_host), + std::move(remote_main_frame_interfaces->main_frame)); +} + +scheduler::WebAgentGroupScheduler& WebViewImpl::GetWebAgentGroupScheduler() { + return web_agent_group_scheduler_; +} + +void WebViewImpl::UpdatePageBrowsingContextGroup( + const BrowsingContextGroupInfo& browsing_context_group_info) { + Page* page = GetPage(); + CHECK(page); + + page->UpdateBrowsingContextGroup(browsing_context_group_info); +} + +void WebViewImpl::SetPageAttributionSupport( + network::mojom::AttributionSupport support) { + Page* page = GetPage(); + CHECK(page); + + page->SetAttributionSupport(support); +} + +} // namespace blink