| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/shell/browser/web_test/web_test_content_browser_client.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/pattern.h" |
| #include "cc/base/switches.h" |
| #include "content/public/browser/browser_child_process_observer.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_data.h" |
| #include "content/public/browser/client_hints_controller_delegate.h" |
| #include "content/public/browser/login_delegate.h" |
| #include "content/public/browser/overlay_window.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/site_isolation_policy.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/shell/browser/shell_browser_context.h" |
| #include "content/shell/browser/shell_content_browser_client.h" |
| #include "content/shell/browser/web_test/fake_bluetooth_chooser.h" |
| #include "content/shell/browser/web_test/fake_bluetooth_chooser_factory.h" |
| #include "content/shell/browser/web_test/fake_bluetooth_delegate.h" |
| #include "content/shell/browser/web_test/mojo_web_test_helper.h" |
| #include "content/shell/browser/web_test/web_test_bluetooth_fake_adapter_setter_impl.h" |
| #include "content/shell/browser/web_test/web_test_browser_context.h" |
| #include "content/shell/browser/web_test/web_test_browser_main_parts.h" |
| #include "content/shell/browser/web_test/web_test_client_impl.h" |
| #include "content/shell/browser/web_test/web_test_control_host.h" |
| #include "content/shell/browser/web_test/web_test_permission_manager.h" |
| #include "content/shell/browser/web_test/web_test_tts_platform.h" |
| #include "content/shell/common/web_test/web_test_bluetooth_fake_adapter_setter.mojom.h" |
| #include "content/shell/common/web_test/web_test_switches.h" |
| #include "content/test/data/mojo_web_test_helper_test.mojom.h" |
| #include "content/test/mock_badge_service.h" |
| #include "content/test/mock_clipboard_host.h" |
| #include "content/test/mock_platform_notification_service.h" |
| #include "content/test/mock_raw_clipboard_host.h" |
| #include "device/bluetooth/public/mojom/test/fake_bluetooth.mojom.h" |
| #include "device/bluetooth/test/fake_bluetooth.h" |
| #include "gpu/config/gpu_switches.h" |
| #include "mojo/public/cpp/bindings/binder_map.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "services/network/public/mojom/network_service.mojom.h" |
| #include "services/service_manager/public/cpp/manifest.h" |
| #include "services/service_manager/public/cpp/manifest_builder.h" |
| #include "storage/browser/quota/quota_settings.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" |
| #include "ui/base/ui_base_switches.h" |
| #include "url/origin.h" |
| |
| #if defined(OS_WIN) |
| #include "base/strings/utf_string_conversions.h" |
| #include "sandbox/win/src/sandbox.h" |
| #include "services/service_manager/sandbox/win/sandbox_win.h" |
| #endif |
| |
| namespace content { |
| namespace { |
| |
| WebTestContentBrowserClient* g_web_test_browser_client; |
| |
| void BindWebTestHelper( |
| RenderFrameHost* render_frame_host, |
| mojo::PendingReceiver<mojom::MojoWebTestHelper> receiver) { |
| MojoWebTestHelper::Create(std::move(receiver)); |
| } |
| |
| const service_manager::Manifest& GetWebTestContentBrowserOverlayManifest() { |
| static base::NoDestructor<service_manager::Manifest> manifest{ |
| service_manager::ManifestBuilder() |
| .ExposeCapability( |
| "renderer", |
| service_manager::Manifest::InterfaceList< |
| mojom::MojoWebTestHelper, mojom::FakeBluetoothChooser, |
| mojom::FakeBluetoothChooserFactory, |
| mojom::WebTestBluetoothFakeAdapterSetter, |
| bluetooth::mojom::FakeBluetooth>()) |
| .Build()}; |
| return *manifest; |
| } |
| |
| // An OverlayWindow that returns the last given video natural size as the |
| // window's bounds. |
| class BoundsMatchVideoSizeOverlayWindow : public OverlayWindow { |
| public: |
| BoundsMatchVideoSizeOverlayWindow() = default; |
| ~BoundsMatchVideoSizeOverlayWindow() override = default; |
| |
| BoundsMatchVideoSizeOverlayWindow(const BoundsMatchVideoSizeOverlayWindow&) = |
| delete; |
| BoundsMatchVideoSizeOverlayWindow& operator=( |
| const BoundsMatchVideoSizeOverlayWindow&) = delete; |
| |
| bool IsActive() override { return false; } |
| void Close() override {} |
| void ShowInactive() override {} |
| void Hide() override {} |
| bool IsVisible() override { return false; } |
| bool IsAlwaysOnTop() override { return false; } |
| gfx::Rect GetBounds() override { return gfx::Rect(size_); } |
| void UpdateVideoSize(const gfx::Size& natural_size) override { |
| size_ = natural_size; |
| } |
| void SetPlaybackState(PlaybackState playback_state) override {} |
| void SetPlayPauseButtonVisibility(bool is_visible) override {} |
| void SetSkipAdButtonVisibility(bool is_visible) override {} |
| void SetNextTrackButtonVisibility(bool is_visible) override {} |
| void SetPreviousTrackButtonVisibility(bool is_visible) override {} |
| void SetSurfaceId(const viz::SurfaceId& surface_id) override {} |
| cc::Layer* GetLayerForTesting() override { return nullptr; } |
| |
| private: |
| gfx::Size size_; |
| }; |
| |
| void CreateChildProcessCrashWatcher() { |
| class ChildProcessCrashWatcher : public BrowserChildProcessObserver { |
| public: |
| ChildProcessCrashWatcher() { BrowserChildProcessObserver::Add(this); } |
| ~ChildProcessCrashWatcher() override { |
| BrowserChildProcessObserver::Remove(this); |
| } |
| |
| private: |
| // BrowserChildProcessObserver implementation. |
| void BrowserChildProcessCrashed( |
| const ChildProcessData& data, |
| const ChildProcessTerminationInfo& info) override { |
| // Child processes should not crash in web tests. |
| LOG(ERROR) << "Child process crashed with\n" |
| " process_type: " |
| << data.process_type << "\n" |
| << " name: " << data.name; |
| CHECK(false); |
| } |
| }; |
| |
| // This creates the singleton object which will now watch for crashes from |
| // any BrowserChildProcessHost. |
| static base::NoDestructor<ChildProcessCrashWatcher> watcher; |
| } |
| |
| } // namespace |
| |
| WebTestContentBrowserClient::WebTestContentBrowserClient() { |
| DCHECK(!g_web_test_browser_client); |
| |
| g_web_test_browser_client = this; |
| |
| // The 1GB limit is intended to give a large headroom to tests that need to |
| // build up a large data set and issue many concurrent reads or writes. |
| static storage::QuotaSettings quota_settings( |
| storage::GetHardCodedSettings(1024 * 1024 * 1024)); |
| StoragePartition::SetDefaultQuotaSettingsForTesting("a_settings); |
| } |
| |
| WebTestContentBrowserClient::~WebTestContentBrowserClient() { |
| g_web_test_browser_client = nullptr; |
| } |
| |
| WebTestContentBrowserClient* WebTestContentBrowserClient::Get() { |
| return g_web_test_browser_client; |
| } |
| |
| WebTestBrowserContext* WebTestContentBrowserClient::GetWebTestBrowserContext() { |
| return static_cast<WebTestBrowserContext*>(browser_context()); |
| } |
| |
| void WebTestContentBrowserClient::SetPopupBlockingEnabled(bool block_popups) { |
| block_popups_ = block_popups; |
| } |
| |
| void WebTestContentBrowserClient::ResetMockClipboardHosts() { |
| if (mock_clipboard_host_) |
| mock_clipboard_host_->Reset(); |
| if (mock_raw_clipboard_host_) |
| mock_raw_clipboard_host_->Reset(); |
| } |
| |
| void WebTestContentBrowserClient::SetScreenOrientationChanged( |
| bool screen_orientation_changed) { |
| screen_orientation_changed_ = screen_orientation_changed; |
| } |
| |
| std::unique_ptr<FakeBluetoothChooser> |
| WebTestContentBrowserClient::GetNextFakeBluetoothChooser() { |
| if (!fake_bluetooth_chooser_factory_) |
| return nullptr; |
| return fake_bluetooth_chooser_factory_->GetNextFakeBluetoothChooser(); |
| } |
| |
| void WebTestContentBrowserClient::BrowserChildProcessHostCreated( |
| BrowserChildProcessHost* host) { |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = |
| content::GetUIThreadTaskRunner({}); |
| ui_task_runner->PostTask(FROM_HERE, |
| base::BindOnce(&CreateChildProcessCrashWatcher)); |
| } |
| |
| void WebTestContentBrowserClient::ExposeInterfacesToRenderer( |
| service_manager::BinderRegistry* registry, |
| blink::AssociatedInterfaceRegistry* associated_registry, |
| RenderProcessHost* render_process_host) { |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner = |
| content::GetUIThreadTaskRunner({}); |
| registry->AddInterface( |
| base::BindRepeating(&WebTestBluetoothFakeAdapterSetterImpl::Create), |
| ui_task_runner); |
| registry->AddInterface(base::BindRepeating(&bluetooth::FakeBluetooth::Create), |
| ui_task_runner); |
| // This class outlives |render_process_host|, which owns |registry|. Since |
| // any binders will not be called after |registry| is deleted |
| // and |registry| is outlived by this class, it is safe to use |
| // base::Unretained in all binders. |
| registry->AddInterface( |
| base::BindRepeating( |
| &WebTestContentBrowserClient::CreateFakeBluetoothChooserFactory, |
| base::Unretained(this)), |
| ui_task_runner); |
| registry->AddInterface(base::BindRepeating(&MojoWebTestHelper::Create)); |
| |
| registry->AddInterface( |
| base::BindRepeating( |
| &WebTestContentBrowserClient::BindPermissionAutomation, |
| base::Unretained(this)), |
| ui_task_runner); |
| |
| associated_registry->AddInterface( |
| base::BindRepeating(&WebTestContentBrowserClient::BindWebTestControlHost, |
| base::Unretained(this))); |
| associated_registry->AddInterface(base::BindRepeating( |
| &WebTestContentBrowserClient::BindWebTestClient, |
| render_process_host->GetID(), |
| BrowserContext::GetDefaultStoragePartition(browser_context()))); |
| } |
| |
| base::Optional<service_manager::Manifest> |
| WebTestContentBrowserClient::GetServiceManifestOverlay(base::StringPiece name) { |
| if (name == content::mojom::kBrowserServiceName) |
| return GetWebTestContentBrowserOverlayManifest(); |
| |
| return base::nullopt; |
| } |
| |
| void WebTestContentBrowserClient::BindPermissionAutomation( |
| mojo::PendingReceiver<blink::test::mojom::PermissionAutomation> receiver) { |
| GetWebTestBrowserContext()->GetWebTestPermissionManager()->Bind( |
| std::move(receiver)); |
| } |
| |
| void WebTestContentBrowserClient::OverrideWebkitPrefs( |
| RenderViewHost* render_view_host, |
| WebPreferences* prefs) { |
| if (WebTestControlHost::Get()) |
| WebTestControlHost::Get()->OverrideWebkitPrefs(prefs); |
| } |
| |
| void WebTestContentBrowserClient::AppendExtraCommandLineSwitches( |
| base::CommandLine* command_line, |
| int child_process_id) { |
| ShellContentBrowserClient::AppendExtraCommandLineSwitches(command_line, |
| child_process_id); |
| |
| static const char* kForwardSwitches[] = { |
| // Switches from web_test_switches.h that are used in the renderer. |
| switches::kEnableAccelerated2DCanvas, |
| switches::kEnableFontAntialiasing, |
| switches::kAlwaysUseComplexText, |
| switches::kStableReleaseMode, |
| #if defined(OS_WIN) |
| switches::kRegisterFontFiles, |
| #endif |
| }; |
| |
| command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), |
| kForwardSwitches, |
| base::size(kForwardSwitches)); |
| } |
| |
| std::unique_ptr<BrowserMainParts> |
| WebTestContentBrowserClient::CreateBrowserMainParts( |
| const MainFunctionParams& parameters) { |
| auto browser_main_parts = |
| std::make_unique<WebTestBrowserMainParts>(parameters); |
| |
| set_browser_main_parts(browser_main_parts.get()); |
| |
| return browser_main_parts; |
| } |
| |
| std::unique_ptr<OverlayWindow> |
| WebTestContentBrowserClient::CreateWindowForPictureInPicture( |
| PictureInPictureWindowController* controller) { |
| return std::make_unique<BoundsMatchVideoSizeOverlayWindow>(); |
| } |
| |
| std::vector<url::Origin> |
| WebTestContentBrowserClient::GetOriginsRequiringDedicatedProcess() { |
| // Unconditionally (with and without --site-per-process) isolate some origins |
| // that may be used by tests that only make sense in presence of an OOPIF. |
| std::vector<std::string> origins_to_isolate = { |
| "http://devtools.oopif.test/", |
| "http://devtools-extensions.oopif.test/", |
| "https://devtools.oopif.test/", |
| }; |
| |
| // When appropriate, we isolate WPT origins for additional OOPIF coverage. |
| // |
| // We don't isolate WPT origins: |
| // 1) on platforms where strict Site Isolation is not the default. |
| // 2) in web tests under virtual/not-site-per-process where the |
| // --disable-site-isolation-trials switch is used. |
| // 3) in web tests under virtual/no-auto-wpt-origin-isolation where the |
| // --disable-auto-wpt-origin-isolation switch is used. |
| if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites() && |
| !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableAutoWPTOriginIsolation)) { |
| // The list of hostnames below is based on |
| // https://web-platform-tests.org/writing-tests/server-features.html |
| const char* kWptHostnames[] = { |
| "www.web-platform.test", |
| "www1.web-platform.test", |
| "www2.web-platform.test", |
| "xn--n8j6ds53lwwkrqhv28a.web-platform.test", |
| "xn--lve-6lad.web-platform.test", |
| }; |
| |
| // The list of schemes below is based on |
| // third_party/blink/tools/blinkpy/third_party/wpt/wpt.config.json |
| const char* kOriginTemplates[] = { |
| "http://%s/", |
| "https://%s/", |
| }; |
| |
| origins_to_isolate.reserve(origins_to_isolate.size() + |
| base::size(kWptHostnames) * |
| base::size(kOriginTemplates)); |
| for (const char* kWptHostname : kWptHostnames) { |
| for (const char* kOriginTemplate : kOriginTemplates) { |
| std::string origin = base::StringPrintf(kOriginTemplate, kWptHostname); |
| origins_to_isolate.push_back(origin); |
| } |
| } |
| } |
| |
| // Translate std::vector<std::string> into std::vector<url::Origin>. |
| std::vector<url::Origin> result; |
| result.reserve(origins_to_isolate.size()); |
| for (const std::string& s : origins_to_isolate) |
| result.push_back(url::Origin::Create(GURL(s))); |
| return result; |
| } |
| |
| PlatformNotificationService* |
| WebTestContentBrowserClient::GetPlatformNotificationService( |
| content::BrowserContext* browser_context) { |
| if (!mock_platform_notification_service_) { |
| mock_platform_notification_service_.reset( |
| new MockPlatformNotificationService(browser_context)); |
| } |
| |
| return mock_platform_notification_service_.get(); |
| } |
| |
| bool WebTestContentBrowserClient::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) { |
| *no_javascript_access = false; |
| return !block_popups_ || user_gesture; |
| } |
| |
| void WebTestContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( |
| RenderFrameHost* render_frame_host, |
| mojo::BinderMapWithContext<content::RenderFrameHost*>* map) { |
| map->Add<mojom::MojoWebTestHelper>(base::BindRepeating(&BindWebTestHelper)); |
| map->Add<blink::mojom::ClipboardHost>(base::BindRepeating( |
| &WebTestContentBrowserClient::BindClipboardHost, base::Unretained(this))); |
| map->Add<blink::mojom::RawClipboardHost>( |
| base::BindRepeating(&WebTestContentBrowserClient::BindRawClipboardHost, |
| base::Unretained(this))); |
| map->Add<blink::mojom::BadgeService>(base::BindRepeating( |
| &WebTestContentBrowserClient::BindBadgeService, base::Unretained(this))); |
| } |
| |
| bool WebTestContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() { |
| return true; |
| } |
| |
| BluetoothDelegate* WebTestContentBrowserClient::GetBluetoothDelegate() { |
| if (!fake_bluetooth_delegate_) |
| fake_bluetooth_delegate_ = std::make_unique<FakeBluetoothDelegate>(); |
| return fake_bluetooth_delegate_.get(); |
| } |
| |
| void WebTestContentBrowserClient::ResetFakeBluetoothDelegate() { |
| fake_bluetooth_delegate_.reset(); |
| } |
| |
| content::TtsPlatform* WebTestContentBrowserClient::GetTtsPlatform() { |
| return WebTestTtsPlatform::GetInstance(); |
| } |
| |
| bool WebTestContentBrowserClient::CanEnterFullscreenWithoutUserActivation() { |
| return screen_orientation_changed_; |
| } |
| |
| void WebTestContentBrowserClient::BindClipboardHost( |
| RenderFrameHost* render_frame_host, |
| mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver) { |
| if (!mock_clipboard_host_) |
| mock_clipboard_host_ = std::make_unique<MockClipboardHost>(); |
| mock_clipboard_host_->Bind(std::move(receiver)); |
| } |
| |
| void WebTestContentBrowserClient::BindRawClipboardHost( |
| RenderFrameHost* render_frame_host, |
| mojo::PendingReceiver<blink::mojom::RawClipboardHost> receiver) { |
| if (!mock_clipboard_host_) |
| mock_clipboard_host_ = std::make_unique<MockClipboardHost>(); |
| if (!mock_raw_clipboard_host_) { |
| mock_raw_clipboard_host_ = |
| std::make_unique<MockRawClipboardHost>(mock_clipboard_host_.get()); |
| } |
| mock_raw_clipboard_host_->Bind(std::move(receiver)); |
| } |
| |
| void WebTestContentBrowserClient::BindBadgeService( |
| RenderFrameHost* render_frame_host, |
| mojo::PendingReceiver<blink::mojom::BadgeService> receiver) { |
| if (!mock_badge_service_) |
| mock_badge_service_ = std::make_unique<MockBadgeService>(); |
| mock_badge_service_->Bind(std::move(receiver)); |
| } |
| |
| std::unique_ptr<LoginDelegate> WebTestContentBrowserClient::CreateLoginDelegate( |
| const net::AuthChallengeInfo& auth_info, |
| content::WebContents* web_contents, |
| const content::GlobalRequestID& request_id, |
| bool is_main_frame, |
| const GURL& url, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| bool first_auth_attempt, |
| LoginAuthRequiredCallback auth_required_callback) { |
| return nullptr; |
| } |
| |
| void WebTestContentBrowserClient::ConfigureNetworkContextParamsForShell( |
| BrowserContext* context, |
| network::mojom::NetworkContextParams* context_params, |
| network::mojom::CertVerifierCreationParams* cert_verifier_creation_params) { |
| ShellContentBrowserClient::ConfigureNetworkContextParamsForShell( |
| context, context_params, cert_verifier_creation_params); |
| |
| #if BUILDFLAG(ENABLE_REPORTING) |
| // Configure the Reporting service in a manner expected by certain Web |
| // Platform Tests (network-error-logging and reporting-api). |
| // |
| // (1) Always send reports (irrespective of BACKGROUND_SYNC permission) |
| // (2) Lower the timeout for sending reports. |
| context_params->reporting_delivery_interval = |
| kReportingDeliveryIntervalTimeForWebTests; |
| context_params->skip_reporting_send_permission_check = true; |
| #endif |
| } |
| |
| void WebTestContentBrowserClient::CreateFakeBluetoothChooserFactory( |
| mojo::PendingReceiver<mojom::FakeBluetoothChooserFactory> receiver) { |
| DCHECK(!fake_bluetooth_chooser_factory_); |
| fake_bluetooth_chooser_factory_ = |
| FakeBluetoothChooserFactory::Create(std::move(receiver)); |
| } |
| |
| void WebTestContentBrowserClient::BindWebTestControlHost( |
| mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver) { |
| if (WebTestControlHost::Get()) |
| WebTestControlHost::Get()->AddWebTestControlHostReceiver( |
| std::move(receiver)); |
| } |
| |
| // static |
| void WebTestContentBrowserClient::BindWebTestClient( |
| int render_process_id, |
| StoragePartition* partition, |
| mojo::PendingAssociatedReceiver<mojom::WebTestClient> receiver) { |
| WebTestClientImpl::Create(render_process_id, std::move(receiver)); |
| } |
| |
| #if defined(OS_WIN) |
| bool WebTestContentBrowserClient::PreSpawnRenderer( |
| sandbox::TargetPolicy* policy, |
| RendererSpawnFlags flags) { |
| // Add sideloaded font files for testing. See also DIR_WINDOWS_FONTS |
| // addition in |StartSandboxedProcess|. |
| std::vector<std::string> font_files = switches::GetSideloadFontFiles(); |
| for (std::vector<std::string>::const_iterator i(font_files.begin()); |
| i != font_files.end(); ++i) { |
| policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, |
| sandbox::TargetPolicy::FILES_ALLOW_READONLY, |
| base::UTF8ToWide(*i).c_str()); |
| } |
| return true; |
| } |
| #endif // OS_WIN |
| |
| std::string WebTestContentBrowserClient::GetAcceptLangs( |
| BrowserContext* context) { |
| return content::GetShellLanguage(); |
| } |
| |
| } // namespace content |