| /* |
| * Copyright (C) 2011 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/frame/frame_test_helpers.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "build/build_config.h" |
| #include "cc/test/test_ukm_recorder_factory.h" |
| #include "cc/trees/layer_tree_host.h" |
| #include "cc/trees/layer_tree_settings.h" |
| #include "cc/trees/render_frame_metadata_observer.h" |
| #include "mojo/public/cpp/bindings/pending_associated_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| #include "third_party/blink/public/common/frame/frame_policy.h" |
| #include "third_party/blink/public/common/tokens/tokens.h" |
| #include "third_party/blink/public/mojom/frame/tree_scope_type.mojom-blink.h" |
| #include "third_party/blink/public/mojom/page/widget.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/test/renderer_scheduler_test_support.h" |
| #include "third_party/blink/public/platform/web_data.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" |
| #include "third_party/blink/public/platform/web_url_request.h" |
| #include "third_party/blink/public/platform/web_url_response.h" |
| #include "third_party/blink/public/web/web_console_message.h" |
| #include "third_party/blink/public/web/web_frame_widget.h" |
| #include "third_party/blink/public/web/web_navigation_params.h" |
| #include "third_party/blink/public/web/web_settings.h" |
| #include "third_party/blink/public/web/web_view_client.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/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" |
| #include "third_party/blink/renderer/core/testing/fake_web_plugin.h" |
| #include "third_party/blink/renderer/core/testing/mock_policy_container_host.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread.h" |
| #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" |
| #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" |
| #include "ui/base/ime/mojom/text_input_state.mojom-blink.h" |
| |
| namespace blink { |
| namespace frame_test_helpers { |
| |
| namespace { |
| |
| // The frame test helpers coordinate frame loads in a carefully choreographed |
| // dance. Since the parser is threaded, simply spinning the run loop once is not |
| // enough to ensure completion of a load. Instead, the following pattern is |
| // used to ensure that tests see the final state: |
| // 1. Starts a load. |
| // 2. Enter the run loop. |
| // 3. Posted task triggers the load, and starts pumping pending resource |
| // requests using runServeAsyncRequestsTask(). |
| // 4. TestWebFrameClient watches for DidStartLoading/DidStopLoading calls, |
| // keeping track of how many loads it thinks are in flight. |
| // 5. While RunServeAsyncRequestsTask() observes TestWebFrameClient to still |
| // have loads in progress, it posts itself back to the run loop. |
| // 6. When RunServeAsyncRequestsTask() notices there are no more loads in |
| // progress, it exits the run loop. |
| // 7. At this point, all parsing, resource loads, and layout should be finished. |
| |
| void RunServeAsyncRequestsTask(scoped_refptr<base::TaskRunner> task_runner) { |
| // TODO(kinuko,toyoshim): Create a mock factory and use it instead of |
| // getting the platform's one. (crbug.com/751425) |
| WebURLLoaderMockFactory::GetSingletonInstance()->ServeAsynchronousRequests(); |
| if (TestWebFrameClient::IsLoading()) { |
| task_runner->PostTask(FROM_HERE, |
| WTF::Bind(&RunServeAsyncRequestsTask, task_runner)); |
| } else { |
| test::ExitRunLoop(); |
| } |
| } |
| |
| // Helper to create a default test client if the supplied client pointer is |
| // null. The |owned_client| is used to store the client if it must be created. |
| // In both cases the client to be used is returned. |
| template <typename T> |
| T* CreateDefaultClientIfNeeded(T* client, std::unique_ptr<T>& owned_client) { |
| if (client) |
| return client; |
| owned_client = std::make_unique<T>(); |
| return owned_client.get(); |
| } |
| |
| viz::FrameSinkId AllocateFrameSinkId() { |
| // A static increasing count of frame sinks created so they are all unique. |
| static uint32_t s_frame_sink_count = 0; |
| return viz::FrameSinkId(++s_frame_sink_count, 1); |
| } |
| |
| // Installs a create hook and uninstalls it when this object is |
| // destroyed. |
| class ScopedCreateWebFrameWidget { |
| public: |
| explicit ScopedCreateWebFrameWidget(CreateWebFrameWidgetCallback* hook) { |
| InstallCreateWebFrameWidgetHook(hook); |
| } |
| |
| ~ScopedCreateWebFrameWidget() { InstallCreateWebFrameWidgetHook(nullptr); } |
| }; |
| |
| } // namespace |
| |
| cc::LayerTreeSettings GetSynchronousSingleThreadLayerTreeSettings() { |
| cc::LayerTreeSettings settings; |
| // Use synchronous compositing so that the MessageLoop becomes idle and the |
| // test makes progress. |
| settings.single_thread_proxy_scheduler = false; |
| settings.use_layer_lists = true; |
| #if defined(OS_MAC) |
| settings.enable_elastic_overscroll = true; |
| #endif |
| return settings; |
| } |
| |
| void LoadFrameDontWait(WebLocalFrame* frame, const WebURL& url) { |
| auto* impl = To<WebLocalFrameImpl>(frame); |
| if (url.ProtocolIs("javascript")) { |
| impl->LoadJavaScriptURL(url); |
| } else { |
| auto params = std::make_unique<WebNavigationParams>(); |
| params->url = url; |
| params->navigation_timings.navigation_start = base::TimeTicks::Now(); |
| params->navigation_timings.fetch_start = base::TimeTicks::Now(); |
| params->is_browser_initiated = true; |
| MockPolicyContainerHost mock_policy_container_host; |
| params->policy_container = std::make_unique<WebPolicyContainer>( |
| WebPolicyContainerPolicies(), |
| mock_policy_container_host.BindNewEndpointAndPassDedicatedRemote()); |
| FillNavigationParamsResponse(params.get()); |
| impl->CommitNavigation(std::move(params), nullptr /* extra_data */); |
| } |
| } |
| |
| void LoadFrame(WebLocalFrame* frame, const std::string& url) { |
| LoadFrameDontWait(frame, url_test_helpers::ToKURL(url)); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void LoadHTMLString(WebLocalFrame* frame, |
| const std::string& html, |
| const WebURL& base_url, |
| const base::TickClock* clock) { |
| auto* impl = To<WebLocalFrameImpl>(frame); |
| std::unique_ptr<WebNavigationParams> navigation_params = |
| WebNavigationParams::CreateWithHTMLStringForTesting(html, base_url); |
| navigation_params->tick_clock = clock; |
| impl->CommitNavigation(std::move(navigation_params), |
| nullptr /* extra_data */); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void LoadHistoryItem(WebLocalFrame* frame, |
| const WebHistoryItem& item, |
| mojom::FetchCacheMode cache_mode) { |
| auto* impl = To<WebLocalFrameImpl>(frame); |
| HistoryItem* history_item = item; |
| auto params = std::make_unique<WebNavigationParams>(); |
| params->url = history_item->Url(); |
| params->frame_load_type = WebFrameLoadType::kBackForward; |
| params->history_item = item; |
| params->navigation_timings.navigation_start = base::TimeTicks::Now(); |
| params->navigation_timings.fetch_start = base::TimeTicks::Now(); |
| FillNavigationParamsResponse(params.get()); |
| impl->CommitNavigation(std::move(params), nullptr /* extra_data */); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void ReloadFrame(WebLocalFrame* frame) { |
| frame->StartReload(WebFrameLoadType::kReload); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void ReloadFrameBypassingCache(WebLocalFrame* frame) { |
| frame->StartReload(WebFrameLoadType::kReloadBypassingCache); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void PumpPendingRequestsForFrameToLoad(WebLocalFrame* frame) { |
| scoped_refptr<base::TaskRunner> task_runner = |
| frame->GetTaskRunner(blink::TaskType::kInternalTest); |
| task_runner->PostTask(FROM_HERE, |
| WTF::Bind(&RunServeAsyncRequestsTask, task_runner)); |
| test::EnterRunLoop(); |
| } |
| |
| void FillNavigationParamsResponse(WebNavigationParams* params) { |
| KURL kurl(params->url); |
| // Empty documents and srcdoc will be handled by DocumentLoader. |
| if (DocumentLoader::WillLoadUrlAsEmpty(kurl) || kurl.IsAboutSrcdocURL()) |
| return; |
| WebURLLoaderMockFactory::GetSingletonInstance()->FillNavigationParamsResponse( |
| params); |
| } |
| |
| WebMouseEvent CreateMouseEvent(WebInputEvent::Type type, |
| WebMouseEvent::Button button, |
| const IntPoint& point, |
| int modifiers) { |
| WebMouseEvent result(type, modifiers, |
| WebInputEvent::GetStaticTimeStampForTests()); |
| result.pointer_type = WebPointerProperties::PointerType::kMouse; |
| result.SetPositionInWidget(point.X(), point.Y()); |
| result.SetPositionInScreen(point.X(), point.Y()); |
| result.button = button; |
| result.click_count = 1; |
| return result; |
| } |
| |
| WebLocalFrameImpl* CreateLocalChild( |
| WebLocalFrame& parent, |
| mojom::blink::TreeScopeType scope, |
| TestWebFrameClient* client, |
| WebPolicyContainerBindParams policy_container_bind_params) { |
| MockPolicyContainerHost mock_policy_container_host; |
| mock_policy_container_host.BindWithNewEndpoint( |
| std::move(policy_container_bind_params.receiver)); |
| std::unique_ptr<TestWebFrameClient> owned_client; |
| client = CreateDefaultClientIfNeeded(client, owned_client); |
| auto* frame = To<WebLocalFrameImpl>( |
| parent.CreateLocalChild(scope, client, nullptr, LocalFrameToken())); |
| client->Bind(frame, std::move(owned_client)); |
| return frame; |
| } |
| |
| WebLocalFrameImpl* CreateLocalChild( |
| WebLocalFrame& parent, |
| mojom::blink::TreeScopeType scope, |
| std::unique_ptr<TestWebFrameClient> self_owned, |
| WebPolicyContainerBindParams policy_container_bind_params) { |
| MockPolicyContainerHost mock_policy_container_host; |
| mock_policy_container_host.BindWithNewEndpoint( |
| std::move(policy_container_bind_params.receiver)); |
| DCHECK(self_owned); |
| TestWebFrameClient* client = self_owned.get(); |
| auto* frame = To<WebLocalFrameImpl>( |
| parent.CreateLocalChild(scope, client, nullptr, LocalFrameToken())); |
| client->Bind(frame, std::move(self_owned)); |
| return frame; |
| } |
| |
| WebRemoteFrameImpl* CreateRemote(TestWebRemoteFrameClient* client) { |
| std::unique_ptr<TestWebRemoteFrameClient> owned_client; |
| client = CreateDefaultClientIfNeeded(client, owned_client); |
| auto* frame = MakeGarbageCollected<WebRemoteFrameImpl>( |
| mojom::blink::TreeScopeType::kDocument, client, |
| InterfaceRegistry::GetEmptyInterfaceRegistry(), |
| client->GetRemoteAssociatedInterfaces(), RemoteFrameToken()); |
| client->Bind(frame, std::move(owned_client)); |
| return frame; |
| } |
| |
| WebRemoteFrameImpl* CreateRemoteChild( |
| WebRemoteFrame& parent, |
| const WebString& name, |
| scoped_refptr<SecurityOrigin> security_origin, |
| TestWebRemoteFrameClient* client) { |
| std::unique_ptr<TestWebRemoteFrameClient> owned_client; |
| client = CreateDefaultClientIfNeeded(client, owned_client); |
| auto* frame = To<WebRemoteFrameImpl>(parent.CreateRemoteChild( |
| mojom::blink::TreeScopeType::kDocument, name, FramePolicy(), |
| mojom::blink::FrameOwnerElementType::kIframe, client, |
| InterfaceRegistry::GetEmptyInterfaceRegistry(), |
| client->GetRemoteAssociatedInterfaces(), RemoteFrameToken(), nullptr)); |
| client->Bind(frame, std::move(owned_client)); |
| if (!security_origin) |
| security_origin = SecurityOrigin::CreateUniqueOpaque(); |
| frame->GetFrame()->SetReplicatedOrigin(std::move(security_origin), false); |
| return frame; |
| } |
| |
| WebViewHelper::WebViewHelper( |
| CreateTestWebFrameWidgetCallback create_web_frame_callback) |
| : web_view_(nullptr), |
| agent_group_scheduler_( |
| blink::ThreadScheduler::Current()->CreateAgentGroupScheduler()), |
| platform_(Platform::Current()) { |
| CreateTestWebFrameWidgetCallback create_callback = |
| std::move(create_web_frame_callback); |
| if (!create_callback) { |
| create_callback = |
| base::BindRepeating(&WebViewHelper::CreateTestWebFrameWidget<>); |
| } |
| // Due to return type differences we need to bind the RepeatingCallback |
| // in a wrapper. |
| create_widget_callback_wrapper_ = base::BindRepeating( |
| [](const CreateTestWebFrameWidgetCallback& create_test_web_widget, |
| base::PassKey<WebLocalFrame> pass_key, |
| CrossVariantMojoAssociatedRemote< |
| mojom::blink::FrameWidgetHostInterfaceBase> frame_widget_host, |
| CrossVariantMojoAssociatedReceiver< |
| mojom::blink::FrameWidgetInterfaceBase> frame_widget, |
| CrossVariantMojoAssociatedRemote<mojom::blink::WidgetHostInterfaceBase> |
| widget_host, |
| CrossVariantMojoAssociatedReceiver<mojom::blink::WidgetInterfaceBase> |
| widget, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| const viz::FrameSinkId& frame_sink_id, bool hidden, |
| bool never_composited, bool is_for_child_local_root, |
| bool is_for_nested_main_frame) -> WebFrameWidget* { |
| return create_test_web_widget.Run( |
| std::move(pass_key), std::move(frame_widget_host), |
| std::move(frame_widget), std::move(widget_host), std::move(widget), |
| std::move(task_runner), frame_sink_id, hidden, never_composited, |
| is_for_child_local_root, is_for_nested_main_frame); |
| }, |
| std::move(create_callback)); |
| } |
| |
| WebViewHelper::~WebViewHelper() { |
| // Close the WebViewImpl before the WebViewClient is destroyed. |
| Reset(); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeWithOpener( |
| WebFrame* opener, |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| void (*update_settings_func)(WebSettings*)) { |
| Reset(); |
| |
| InitializeWebView(web_view_client, opener ? opener->View() : nullptr); |
| if (update_settings_func) |
| update_settings_func(web_view_->GetSettings()); |
| |
| std::unique_ptr<TestWebFrameClient> owned_web_frame_client; |
| web_frame_client = |
| CreateDefaultClientIfNeeded(web_frame_client, owned_web_frame_client); |
| MockPolicyContainerHost mock_policy_container_host; |
| WebLocalFrame* frame = WebLocalFrame::CreateMainFrame( |
| web_view_, web_frame_client, nullptr, LocalFrameToken(), |
| std::make_unique<WebPolicyContainer>( |
| WebPolicyContainerPolicies(), |
| mock_policy_container_host.BindNewEndpointAndPassDedicatedRemote()), |
| opener); |
| web_frame_client->Bind(frame, std::move(owned_web_frame_client)); |
| |
| TestWebFrameWidget* frame_widget = |
| CreateFrameWidgetAndInitializeCompositing(frame); |
| |
| // We inform the WebView when it has a local main frame attached once the |
| // WebFrame is fully set up and the WebFrameWidget is initialized (which is |
| // the case by this point). |
| web_view_->DidAttachLocalMainFrame(); |
| |
| // Set an initial size for subframes. |
| if (frame->Parent()) |
| frame_widget->Resize(gfx::Size()); |
| return web_view_; |
| } |
| |
| WebViewImpl* WebViewHelper::Initialize( |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| void (*update_settings_func)(WebSettings*)) { |
| return InitializeWithOpener(nullptr, web_frame_client, web_view_client, |
| update_settings_func); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeWithSettings( |
| void (*update_settings_func)(WebSettings*)) { |
| return InitializeWithOpener(nullptr, nullptr, nullptr, update_settings_func); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeAndLoad( |
| const std::string& url, |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| void (*update_settings_func)(WebSettings*)) { |
| Initialize(web_frame_client, web_view_client, update_settings_func); |
| |
| LoadFrame(GetWebView()->MainFrameImpl(), url); |
| |
| return GetWebView(); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeRemote( |
| TestWebRemoteFrameClient* client, |
| scoped_refptr<SecurityOrigin> security_origin, |
| TestWebViewClient* web_view_client) { |
| return InitializeRemoteWithOpener(nullptr, client, security_origin, |
| web_view_client); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeRemoteWithOpener( |
| WebFrame* opener, |
| TestWebRemoteFrameClient* web_remote_frame_client, |
| scoped_refptr<SecurityOrigin> security_origin, |
| TestWebViewClient* web_view_client) { |
| Reset(); |
| |
| InitializeWebView(web_view_client, nullptr); |
| |
| std::unique_ptr<TestWebRemoteFrameClient> owned_web_remote_frame_client; |
| web_remote_frame_client = CreateDefaultClientIfNeeded( |
| web_remote_frame_client, owned_web_remote_frame_client); |
| WebRemoteFrameImpl* frame = WebRemoteFrameImpl::CreateMainFrame( |
| web_view_, web_remote_frame_client, |
| InterfaceRegistry::GetEmptyInterfaceRegistry(), |
| web_remote_frame_client->GetRemoteAssociatedInterfaces(), |
| RemoteFrameToken(), opener); |
| web_remote_frame_client->Bind(frame, |
| std::move(owned_web_remote_frame_client)); |
| if (!security_origin) |
| security_origin = SecurityOrigin::CreateUniqueOpaque(); |
| frame->GetFrame()->SetReplicatedOrigin(std::move(security_origin), false); |
| return web_view_; |
| } |
| |
| void WebViewHelper::CheckFrameIsAssociatedWithWebView(WebFrame* frame) { |
| // Find the main frame and assert that it is the same. |
| while (frame->Parent()) { |
| frame = frame->Parent(); |
| } |
| CHECK_EQ(web_view_->MainFrame(), frame); |
| } |
| |
| WebLocalFrameImpl* WebViewHelper::CreateLocalChild( |
| WebRemoteFrame& parent, |
| const WebString& name, |
| const WebFrameOwnerProperties& properties, |
| WebFrame* previous_sibling, |
| TestWebFrameClient* client) { |
| CheckFrameIsAssociatedWithWebView(&parent); |
| std::unique_ptr<TestWebFrameClient> owned_client; |
| client = CreateDefaultClientIfNeeded(client, owned_client); |
| auto* frame = To<WebLocalFrameImpl>(parent.CreateLocalChild( |
| mojom::blink::TreeScopeType::kDocument, name, FramePolicy(), client, |
| nullptr, previous_sibling, properties, |
| mojom::blink::FrameOwnerElementType::kIframe, LocalFrameToken(), nullptr, |
| std::make_unique<WebPolicyContainer>(WebPolicyContainerPolicies(), |
| mojo::NullAssociatedRemote()))); |
| client->Bind(frame, std::move(owned_client)); |
| |
| TestWebFrameWidget* frame_widget = |
| CreateFrameWidgetAndInitializeCompositing(frame); |
| // Set an initial size for subframes. |
| frame_widget->Resize(gfx::Size()); |
| return frame; |
| } |
| |
| WebLocalFrameImpl* WebViewHelper::CreateProvisional( |
| WebFrame& old_frame, |
| TestWebFrameClient* client) { |
| CheckFrameIsAssociatedWithWebView(&old_frame); |
| std::unique_ptr<TestWebFrameClient> owned_client; |
| client = CreateDefaultClientIfNeeded(client, owned_client); |
| auto* frame = To<WebLocalFrameImpl>(WebLocalFrame::CreateProvisional( |
| client, nullptr, LocalFrameToken(), &old_frame, FramePolicy(), |
| WebFrame::ToCoreFrame(old_frame)->Tree().GetName())); |
| client->Bind(frame, std::move(owned_client)); |
| |
| // Create a widget, if necessary. |
| if (!frame->Parent() || frame->Parent()->IsWebRemoteFrame()) { |
| TestWebFrameWidget* frame_widget = |
| CreateFrameWidgetAndInitializeCompositing(frame); |
| // Set an initial size for subframes. |
| if (frame->Parent()) |
| frame_widget->Resize(gfx::Size()); |
| } |
| return frame; |
| } |
| |
| TestWebFrameWidget* WebViewHelper::CreateFrameWidget(WebLocalFrame* frame) { |
| ScopedCreateWebFrameWidget create_hook(&create_widget_callback_wrapper_); |
| mojo::AssociatedRemote<mojom::blink::FrameWidget> frame_widget_remote; |
| mojo::PendingAssociatedReceiver<mojom::blink::FrameWidget> |
| frame_widget_receiver = |
| frame_widget_remote.BindNewEndpointAndPassDedicatedReceiver(); |
| |
| mojo::AssociatedRemote<mojom::blink::FrameWidgetHost> frame_widget_host; |
| mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost> |
| frame_widget_host_receiver = |
| frame_widget_host.BindNewEndpointAndPassDedicatedReceiver(); |
| |
| mojo::AssociatedRemote<mojom::blink::Widget> widget_remote; |
| mojo::PendingAssociatedReceiver<mojom::blink::Widget> widget_receiver = |
| widget_remote.BindNewEndpointAndPassDedicatedReceiver(); |
| |
| mojo::AssociatedRemote<mojom::blink::WidgetHost> widget_host; |
| mojo::PendingAssociatedReceiver<mojom::blink::WidgetHost> |
| widget_host_receiver = |
| widget_host.BindNewEndpointAndPassDedicatedReceiver(); |
| |
| auto* frame_widget = |
| static_cast<TestWebFrameWidget*>(frame->InitializeFrameWidget( |
| frame_widget_host.Unbind(), std::move(frame_widget_receiver), |
| widget_host.Unbind(), std::move(widget_receiver), |
| AllocateFrameSinkId())); |
| frame_widget->BindWidgetChannels(std::move(widget_remote), |
| std::move(widget_host_receiver), |
| std::move(frame_widget_host_receiver)); |
| return frame_widget; |
| } |
| |
| TestWebFrameWidget* WebViewHelper::CreateFrameWidgetAndInitializeCompositing( |
| WebLocalFrame* frame) { |
| TestWebFrameWidget* frame_widget = CreateFrameWidget(frame); |
| // The WebWidget requires the compositor to be set before it is used. |
| cc::LayerTreeSettings layer_tree_settings = |
| GetSynchronousSingleThreadLayerTreeSettings(); |
| frame_widget->InitializeCompositing( |
| frame_widget->GetAgentGroupScheduler(), frame_widget->task_graph_runner(), |
| frame_widget->GetInitialScreenInfo(), |
| std::make_unique<cc::TestUkmRecorderFactory>(), &layer_tree_settings); |
| frame_widget->SetCompositorVisible(true); |
| return frame_widget; |
| } |
| |
| void WebViewHelper::LoadAhem() { |
| LocalFrame* local_frame = |
| To<LocalFrame>(WebFrame::ToCoreFrame(*LocalMainFrame())); |
| DCHECK(local_frame); |
| RenderingTest::LoadAhem(*local_frame); |
| } |
| |
| void WebViewHelper::Reset() { |
| DCHECK_EQ(platform_, Platform::Current()) |
| << "Platform::Current() should be the same for the life of a test, " |
| "including shutdown."; |
| |
| if (test_web_view_client_) |
| test_web_view_client_->DestroyChildViews(); |
| if (web_view_) { |
| DCHECK(!TestWebFrameClient::IsLoading()); |
| web_view_->Close(); |
| web_view_ = nullptr; |
| } |
| test_web_view_client_ = nullptr; |
| } |
| |
| cc::LayerTreeHost* WebViewHelper::GetLayerTreeHost() const { |
| return GetMainFrameWidget()->LayerTreeHostForTesting(); |
| } |
| |
| WebLocalFrameImpl* WebViewHelper::LocalMainFrame() const { |
| return To<WebLocalFrameImpl>(web_view_->MainFrame()); |
| } |
| |
| WebRemoteFrameImpl* WebViewHelper::RemoteMainFrame() const { |
| return To<WebRemoteFrameImpl>(web_view_->MainFrame()); |
| } |
| |
| TestWebFrameWidget* WebViewHelper::GetMainFrameWidget() const { |
| return static_cast<TestWebFrameWidget*>(LocalMainFrame()->FrameWidgetImpl()); |
| } |
| |
| void WebViewHelper::Resize(const gfx::Size& size) { |
| GetWebView()->MainFrameWidget()->Resize(size); |
| } |
| |
| void WebViewHelper::InitializeWebView(TestWebViewClient* web_view_client, |
| class WebView* opener) { |
| test_web_view_client_ = |
| CreateDefaultClientIfNeeded(web_view_client, owned_test_web_view_client_); |
| web_view_ = static_cast<WebViewImpl*>( |
| WebView::Create(test_web_view_client_, |
| /*is_hidden=*/false, |
| /*is_inside_portal=*/false, |
| /*compositing_enabled=*/true, |
| /*opener=*/opener, mojo::NullAssociatedReceiver(), |
| *agent_group_scheduler_)); |
| // This property must be set at initialization time, it is not supported to be |
| // changed afterward, and does nothing. |
| web_view_->GetSettings()->SetViewportEnabled(viewport_enabled_); |
| web_view_->GetSettings()->SetJavaScriptEnabled(true); |
| web_view_->GetSettings()->SetPluginsEnabled(true); |
| // Enable (mocked) network loads of image URLs, as this simplifies |
| // the completion of resource loads upon test shutdown & helps avoid |
| // dormant loads trigger Resource leaks for image loads. |
| // |
| // Consequently, all external image resources must be mocked. |
| web_view_->GetSettings()->SetLoadsImagesAutomatically(true); |
| |
| // If a test turned off this settings, opened WebViews should propagate that. |
| if (opener) { |
| web_view_->GetSettings()->SetAllowUniversalAccessFromFileURLs( |
| static_cast<WebViewImpl*>(opener) |
| ->GetPage() |
| ->GetSettings() |
| .GetAllowUniversalAccessFromFileURLs()); |
| } |
| |
| web_view_->SetDefaultPageScaleLimits(1, 4); |
| } |
| |
| int TestWebFrameClient::loads_in_progress_ = 0; |
| |
| TestWebFrameClient::TestWebFrameClient() |
| : associated_interface_provider_(new AssociatedInterfaceProvider(nullptr)), |
| effective_connection_type_(WebEffectiveConnectionType::kTypeUnknown) {} |
| |
| TestWebFrameClient::~TestWebFrameClient() = default; |
| |
| void TestWebFrameClient::Bind(WebLocalFrame* frame, |
| std::unique_ptr<TestWebFrameClient> self_owned) { |
| DCHECK(!frame_); |
| DCHECK(!self_owned || self_owned.get() == this); |
| frame_ = To<WebLocalFrameImpl>(frame); |
| self_owned_ = std::move(self_owned); |
| } |
| |
| void TestWebFrameClient::FrameDetached() { |
| frame_->Close(); |
| self_owned_.reset(); |
| } |
| |
| WebLocalFrame* TestWebFrameClient::CreateChildFrame( |
| mojom::blink::TreeScopeType scope, |
| const WebString& name, |
| const WebString& fallback_name, |
| const FramePolicy&, |
| const WebFrameOwnerProperties& frame_owner_properties, |
| mojom::blink::FrameOwnerElementType owner_type, |
| WebPolicyContainerBindParams policy_container_bind_params) { |
| return CreateLocalChild(*frame_, scope, /*test_web_frame_client=*/nullptr, |
| std::move(policy_container_bind_params)); |
| } |
| |
| void TestWebFrameClient::InitializeAsChildFrame(WebLocalFrame* parent) {} |
| |
| void TestWebFrameClient::DidStartLoading() { |
| ++loads_in_progress_; |
| } |
| |
| void TestWebFrameClient::DidStopLoading() { |
| DCHECK_GT(loads_in_progress_, 0); |
| --loads_in_progress_; |
| } |
| |
| bool TestWebFrameClient::SwapIn(WebFrame* previous_frame) { |
| bool result = previous_frame->Swap(frame_); |
| |
| if (!frame_->Parent()) |
| frame_->View()->DidAttachLocalMainFrame(); |
| |
| return result; |
| } |
| |
| void TestWebFrameClient::BeginNavigation( |
| std::unique_ptr<WebNavigationInfo> info) { |
| navigation_callback_.Cancel(); |
| if (DocumentLoader::WillLoadUrlAsEmpty(info->url_request.Url()) && |
| !frame_->HasCommittedFirstRealLoad()) { |
| CommitNavigation(std::move(info)); |
| return; |
| } |
| |
| if (!frame_->WillStartNavigation(*info)) |
| return; |
| |
| navigation_callback_.Reset( |
| base::BindOnce(&TestWebFrameClient::CommitNavigation, |
| weak_factory_.GetWeakPtr(), std::move(info))); |
| frame_->GetTaskRunner(blink::TaskType::kInternalLoading) |
| ->PostTask(FROM_HERE, navigation_callback_.callback()); |
| } |
| |
| void TestWebFrameClient::CommitNavigation( |
| std::unique_ptr<WebNavigationInfo> info) { |
| if (!frame_) |
| return; |
| auto params = WebNavigationParams::CreateFromInfo(*info); |
| if (info->archive_status != WebNavigationInfo::ArchiveStatus::Present) |
| FillNavigationParamsResponse(params.get()); |
| frame_->CommitNavigation(std::move(params), nullptr /* extra_data */); |
| } |
| |
| WebEffectiveConnectionType TestWebFrameClient::GetEffectiveConnectionType() { |
| return effective_connection_type_; |
| } |
| |
| void TestWebFrameClient::SetEffectiveConnectionTypeForTesting( |
| WebEffectiveConnectionType effective_connection_type) { |
| effective_connection_type_ = effective_connection_type; |
| } |
| |
| void TestWebFrameClient::DidAddMessageToConsole( |
| const WebConsoleMessage& message, |
| const WebString& source_name, |
| unsigned source_line, |
| const WebString& stack_trace) { |
| console_messages_.push_back(message.text); |
| } |
| |
| WebPlugin* TestWebFrameClient::CreatePlugin(const WebPluginParams& params) { |
| return new FakeWebPlugin(params); |
| } |
| |
| AssociatedInterfaceProvider* |
| TestWebFrameClient::GetRemoteNavigationAssociatedInterfaces() { |
| return associated_interface_provider_.get(); |
| } |
| |
| void TestWebFrameClient::DidMeaningfulLayout( |
| WebMeaningfulLayout meaningful_layout) { |
| switch (meaningful_layout) { |
| case WebMeaningfulLayout::kVisuallyNonEmpty: |
| visually_non_empty_layout_count_++; |
| break; |
| case WebMeaningfulLayout::kFinishedParsing: |
| finished_parsing_layout_count_++; |
| break; |
| case WebMeaningfulLayout::kFinishedLoading: |
| finished_loading_layout_count_++; |
| break; |
| } |
| } |
| |
| TestWebRemoteFrameClient::TestWebRemoteFrameClient() |
| : associated_interface_provider_(new AssociatedInterfaceProvider(nullptr)) { |
| } |
| |
| void TestWebRemoteFrameClient::Bind( |
| WebRemoteFrame* frame, |
| std::unique_ptr<TestWebRemoteFrameClient> self_owned) { |
| DCHECK(!frame_); |
| DCHECK(!self_owned || self_owned.get() == this); |
| frame_ = frame; |
| self_owned_ = std::move(self_owned); |
| } |
| |
| void TestWebRemoteFrameClient::FrameDetached(DetachType type) { |
| frame_->Close(); |
| self_owned_.reset(); |
| } |
| |
| TestWidgetInputHandlerHost* TestWebFrameWidget::GetInputHandlerHost() { |
| if (!widget_input_handler_host_) |
| widget_input_handler_host_ = std::make_unique<TestWidgetInputHandlerHost>(); |
| return widget_input_handler_host_.get(); |
| } |
| |
| ScreenInfo TestWebFrameWidget::GetInitialScreenInfo() { |
| return ScreenInfo(); |
| } |
| |
| cc::FakeLayerTreeFrameSink* TestWebFrameWidget::LastCreatedFrameSink() { |
| DCHECK(LayerTreeHostForTesting()->IsSingleThreaded()); |
| return last_created_frame_sink_; |
| } |
| |
| std::unique_ptr<TestWebFrameWidgetHost> TestWebFrameWidget::CreateWidgetHost() { |
| return std::make_unique<TestWebFrameWidgetHost>(); |
| } |
| |
| void TestWebFrameWidget::BindWidgetChannels( |
| mojo::AssociatedRemote<mojom::blink::Widget> widget_remote, |
| mojo::PendingAssociatedReceiver<mojom::blink::WidgetHost> receiver, |
| mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost> |
| frame_receiver) { |
| widget_host_ = CreateWidgetHost(); |
| widget_host_->BindWidgetHost(std::move(receiver), std::move(frame_receiver)); |
| mojo::Remote<mojom::blink::WidgetInputHandler> input_handler; |
| widget_remote->GetWidgetInputHandler( |
| input_handler.BindNewPipeAndPassReceiver(), |
| GetInputHandlerHost()->BindNewRemote()); |
| } |
| |
| bool TestWebFrameWidget::HaveScrollEventHandlers() const { |
| return LayerTreeHostForTesting()->have_scroll_event_handlers(); |
| } |
| |
| std::unique_ptr<cc::LayerTreeFrameSink> |
| TestWebFrameWidget::AllocateNewLayerTreeFrameSink() { |
| std::unique_ptr<cc::FakeLayerTreeFrameSink> sink = |
| cc::FakeLayerTreeFrameSink::Create3d(); |
| last_created_frame_sink_ = sink.get(); |
| return sink; |
| } |
| |
| void TestWebFrameWidget::WillQueueSyntheticEvent( |
| const WebCoalescedInputEvent& event) { |
| injected_scroll_events_.push_back( |
| std::make_unique<WebCoalescedInputEvent>(event)); |
| } |
| |
| void TestWebFrameWidgetHost::SetCursor(const ui::Cursor& cursor) { |
| cursor_set_count_++; |
| } |
| |
| void TestWebFrameWidgetHost::SetToolTipText( |
| const String& tooltip_text, |
| base::i18n::TextDirection text_direction_hint) {} |
| |
| void TestWebFrameWidgetHost::TextInputStateChanged( |
| ui::mojom::blink::TextInputStatePtr state) { |
| if (state->show_ime_if_needed) |
| ++virtual_keyboard_request_count_; |
| } |
| |
| void TestWebFrameWidgetHost::SelectionBoundsChanged( |
| const gfx::Rect& anchor_rect, |
| base::i18n::TextDirection anchor_dir, |
| const gfx::Rect& focus_rect, |
| base::i18n::TextDirection focus_dir, |
| bool is_anchor_first) {} |
| |
| void TestWebFrameWidgetHost::CreateFrameSink( |
| mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSink> |
| compositor_frame_sink_receiver, |
| mojo::PendingRemote<viz::mojom::blink::CompositorFrameSinkClient> |
| compositor_frame_sink_client) {} |
| |
| void TestWebFrameWidgetHost::RegisterRenderFrameMetadataObserver( |
| mojo::PendingReceiver<cc::mojom::blink::RenderFrameMetadataObserverClient> |
| render_frame_metadata_observer_client_receiver, |
| mojo::PendingRemote<cc::mojom::blink::RenderFrameMetadataObserver> |
| render_frame_metadata_observer) {} |
| |
| void TestWebFrameWidgetHost::AnimateDoubleTapZoomInMainFrame( |
| const gfx::Point& tap_point, |
| const gfx::Rect& rect_to_zoom) {} |
| |
| void TestWebFrameWidgetHost::ZoomToFindInPageRectInMainFrame( |
| const gfx::Rect& rect_to_zoom) {} |
| |
| void TestWebFrameWidgetHost::SetHasTouchEventConsumers( |
| mojom::blink::TouchEventConsumersPtr consumers) {} |
| |
| void TestWebFrameWidgetHost::IntrinsicSizingInfoChanged( |
| mojom::blink::IntrinsicSizingInfoPtr sizing_info) {} |
| |
| void TestWebFrameWidgetHost::AutoscrollStart(const gfx::PointF& position) {} |
| |
| void TestWebFrameWidgetHost::AutoscrollFling(const gfx::Vector2dF& position) {} |
| |
| void TestWebFrameWidgetHost::AutoscrollEnd() {} |
| |
| void TestWebFrameWidgetHost::DidFirstVisuallyNonEmptyPaint() {} |
| |
| void TestWebFrameWidgetHost::StartDragging( |
| const blink::WebDragData& drag_data, |
| blink::DragOperationsMask operations_allowed, |
| const SkBitmap& bitmap, |
| const gfx::Vector2d& bitmap_offset_in_dip, |
| mojom::blink::DragEventSourceInfoPtr event_info) {} |
| |
| void TestWebFrameWidgetHost::BindWidgetHost( |
| mojo::PendingAssociatedReceiver<mojom::blink::WidgetHost> receiver, |
| mojo::PendingAssociatedReceiver<mojom::blink::FrameWidgetHost> |
| frame_receiver) { |
| receiver_.Bind(std::move(receiver)); |
| frame_receiver_.Bind(std::move(frame_receiver)); |
| } |
| |
| void TestWebViewClient::DestroyChildViews() { |
| child_web_views_.clear(); |
| } |
| |
| WebView* TestWebViewClient::CreateView(WebLocalFrame* opener, |
| const WebURLRequest&, |
| const WebWindowFeatures&, |
| const WebString& name, |
| WebNavigationPolicy, |
| network::mojom::blink::WebSandboxFlags, |
| const SessionStorageNamespaceId&, |
| bool& consumed_user_gesture, |
| const base::Optional<WebImpression>&) { |
| auto webview_helper = std::make_unique<WebViewHelper>(); |
| WebView* result = webview_helper->InitializeWithOpener(opener); |
| child_web_views_.push_back(std::move(webview_helper)); |
| return result; |
| } |
| |
| mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> |
| TestWidgetInputHandlerHost::BindNewRemote() { |
| receiver_.reset(); |
| return receiver_.BindNewPipeAndPassRemote(); |
| } |
| |
| void TestWidgetInputHandlerHost::SetTouchActionFromMain( |
| cc::TouchAction touch_action) {} |
| |
| void TestWidgetInputHandlerHost::DidOverscroll( |
| mojom::blink::DidOverscrollParamsPtr params) {} |
| |
| void TestWidgetInputHandlerHost::DidStartScrollingViewport() {} |
| |
| void TestWidgetInputHandlerHost::ImeCancelComposition() {} |
| |
| void TestWidgetInputHandlerHost::ImeCompositionRangeChanged( |
| const gfx::Range& range, |
| const WTF::Vector<gfx::Rect>& bounds) {} |
| |
| void TestWidgetInputHandlerHost::SetMouseCapture(bool capture) {} |
| |
| void TestWidgetInputHandlerHost::RequestMouseLock( |
| bool from_user_gesture, |
| bool unadjusted_movement, |
| RequestMouseLockCallback callback) {} |
| |
| } // namespace frame_test_helpers |
| } // namespace blink |