| /* |
| * 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 "core/frame/FrameTestHelpers.h" |
| |
| #include <utility> |
| |
| #include "core/exported/WebRemoteFrameImpl.h" |
| #include "core/frame/WebLocalFrameImpl.h" |
| #include "core/layout/LayoutTestHelper.h" |
| #include "platform/testing/URLTestHelpers.h" |
| #include "platform/testing/UnitTestHelpers.h" |
| #include "platform/wtf/Functional.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "platform/wtf/StdLibExtras.h" |
| #include "platform/wtf/text/StringBuilder.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebData.h" |
| #include "public/platform/WebString.h" |
| #include "public/platform/WebThread.h" |
| #include "public/platform/WebURLLoaderMockFactory.h" |
| #include "public/platform/WebURLRequest.h" |
| #include "public/platform/WebURLResponse.h" |
| #include "public/web/WebFrameWidget.h" |
| #include "public/web/WebSettings.h" |
| #include "public/web/WebTreeScopeType.h" |
| #include "public/web/WebViewClient.h" |
| #include "third_party/WebKit/common/page/page_visibility_state.mojom-blink.h" |
| |
| namespace blink { |
| namespace FrameTestHelpers { |
| |
| 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() { |
| // TODO(kinuko,toyoshim): Create a mock factory and use it instead of |
| // getting the platform's one. (crbug.com/751425) |
| Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); |
| if (TestWebFrameClient::IsLoading()) { |
| Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostTask( |
| FROM_HERE, WTF::Bind(&RunServeAsyncRequestsTask)); |
| } else { |
| testing::ExitRunLoop(); |
| } |
| } |
| |
| // Helper to create a default test client if the supplied client pointer is |
| // null. |
| template <typename T> |
| std::unique_ptr<T> CreateDefaultClientIfNeeded(T*& client) { |
| if (client) |
| return nullptr; |
| auto owned_client = std::make_unique<T>(); |
| client = owned_client.get(); |
| return owned_client; |
| } |
| |
| } // namespace |
| |
| void LoadFrame(WebLocalFrame* frame, const std::string& url) { |
| WebURLRequest url_request(URLTestHelpers::ToKURL(url)); |
| if (url_request.Url().ProtocolIs("javascript")) { |
| frame->LoadJavaScriptURL(url_request.Url()); |
| } else { |
| frame->LoadRequest(url_request); |
| } |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void LoadHTMLString(WebLocalFrame* frame, |
| const std::string& html, |
| const WebURL& base_url) { |
| frame->LoadHTMLString(WebData(html.data(), html.size()), base_url); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void LoadHistoryItem(WebLocalFrame* frame, |
| const WebHistoryItem& item, |
| WebHistoryLoadType load_type, |
| mojom::FetchCacheMode cache_mode) { |
| WebURLRequest request = frame->RequestFromHistoryItem(item, cache_mode); |
| frame->Load(request, WebFrameLoadType::kBackForward, item, |
| kWebHistoryDifferentDocumentLoad, /*is_client_redirect=*/false, |
| base::UnguessableToken::Create()); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void ReloadFrame(WebLocalFrame* frame) { |
| frame->Reload(WebFrameLoadType::kReload); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void ReloadFrameBypassingCache(WebLocalFrame* frame) { |
| frame->Reload(WebFrameLoadType::kReloadBypassingCache); |
| PumpPendingRequestsForFrameToLoad(frame); |
| } |
| |
| void PumpPendingRequestsForFrameToLoad(WebFrame* frame) { |
| Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostTask( |
| FROM_HERE, WTF::Bind(&RunServeAsyncRequestsTask)); |
| testing::EnterRunLoop(); |
| } |
| |
| WebMouseEvent CreateMouseEvent(WebInputEvent::Type type, |
| WebMouseEvent::Button button, |
| const IntPoint& point, |
| int modifiers) { |
| WebMouseEvent result(type, modifiers, WebInputEvent::kTimeStampForTesting); |
| 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, |
| WebTreeScopeType scope, |
| TestWebFrameClient* client) { |
| auto owned_client = CreateDefaultClientIfNeeded(client); |
| WebLocalFrameImpl* frame = |
| ToWebLocalFrameImpl(parent.CreateLocalChild(scope, client, nullptr)); |
| client->Bind(frame, std::move(owned_client)); |
| return frame; |
| } |
| |
| WebLocalFrameImpl* CreateLocalChild( |
| WebLocalFrame& parent, |
| WebTreeScopeType scope, |
| std::unique_ptr<TestWebFrameClient> self_owned) { |
| DCHECK(self_owned); |
| TestWebFrameClient* client = self_owned.get(); |
| WebLocalFrameImpl* frame = |
| ToWebLocalFrameImpl(parent.CreateLocalChild(scope, client, nullptr)); |
| client->Bind(frame, std::move(self_owned)); |
| return frame; |
| } |
| |
| WebLocalFrameImpl* CreateProvisional(WebRemoteFrame& old_frame, |
| TestWebFrameClient* client) { |
| auto owned_client = CreateDefaultClientIfNeeded(client); |
| WebLocalFrameImpl* frame = |
| ToWebLocalFrameImpl(WebLocalFrame::CreateProvisional( |
| client, nullptr, &old_frame, WebSandboxFlags::kNone, |
| ParsedFeaturePolicy())); |
| client->Bind(frame, std::move(owned_client)); |
| // Create a local root, if necessary. |
| std::unique_ptr<TestWebWidgetClient> owned_widget_client; |
| if (!frame->Parent()) { |
| // TODO(dcheng): The main frame widget currently has a special case. |
| // Eliminate this once WebView is no longer a WebWidget. |
| owned_widget_client = std::make_unique<TestWebViewWidgetClient>( |
| *static_cast<TestWebViewClient*>(frame->ViewImpl()->Client())); |
| } else if (frame->Parent()->IsWebRemoteFrame()) { |
| owned_widget_client = std::make_unique<TestWebWidgetClient>(); |
| } |
| if (owned_widget_client) { |
| WebFrameWidget::Create(owned_widget_client.get(), frame); |
| client->BindWidgetClient(std::move(owned_widget_client)); |
| } |
| return frame; |
| } |
| |
| WebRemoteFrameImpl* CreateRemote(TestWebRemoteFrameClient* client) { |
| auto owned_client = CreateDefaultClientIfNeeded(client); |
| auto* frame = WebRemoteFrameImpl::Create(WebTreeScopeType::kDocument, client); |
| client->Bind(frame, std::move(owned_client)); |
| return frame; |
| } |
| |
| WebLocalFrameImpl* CreateLocalChild(WebRemoteFrame& parent, |
| const WebString& name, |
| const WebFrameOwnerProperties& properties, |
| WebFrame* previous_sibling, |
| TestWebFrameClient* client, |
| TestWebWidgetClient* widget_client) { |
| auto owned_client = CreateDefaultClientIfNeeded(client); |
| auto* frame = ToWebLocalFrameImpl(parent.CreateLocalChild( |
| WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone, client, |
| nullptr, previous_sibling, ParsedFeaturePolicy(), properties, nullptr)); |
| client->Bind(frame, std::move(owned_client)); |
| |
| auto owned_widget_client = CreateDefaultClientIfNeeded(widget_client); |
| WebFrameWidget::Create(widget_client, frame); |
| client->BindWidgetClient(std::move(owned_widget_client)); |
| return frame; |
| } |
| |
| WebRemoteFrameImpl* CreateRemoteChild( |
| WebRemoteFrame& parent, |
| const WebString& name, |
| scoped_refptr<SecurityOrigin> security_origin, |
| TestWebRemoteFrameClient* client) { |
| auto owned_client = CreateDefaultClientIfNeeded(client); |
| auto* frame = ToWebRemoteFrameImpl(parent.CreateRemoteChild( |
| WebTreeScopeType::kDocument, name, WebSandboxFlags::kNone, |
| ParsedFeaturePolicy(), client, nullptr)); |
| client->Bind(frame, std::move(owned_client)); |
| if (!security_origin) |
| security_origin = SecurityOrigin::CreateUnique(); |
| frame->GetFrame()->GetSecurityContext()->SetReplicatedOrigin( |
| std::move(security_origin)); |
| return frame; |
| } |
| |
| WebViewHelper::WebViewHelper() : web_view_(nullptr) {} |
| |
| WebViewHelper::~WebViewHelper() { |
| Reset(); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeWithOpener( |
| WebFrame* opener, |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| TestWebWidgetClient* web_widget_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()); |
| |
| auto owned_web_frame_client = CreateDefaultClientIfNeeded(web_frame_client); |
| WebLocalFrame* frame = WebLocalFrame::CreateMainFrame( |
| web_view_, web_frame_client, nullptr, opener); |
| web_frame_client->Bind(frame, std::move(owned_web_frame_client)); |
| |
| // TODO(dcheng): The main frame widget currently has a special case. |
| // Eliminate this once WebView is no longer a WebWidget. |
| std::unique_ptr<TestWebWidgetClient> owned_web_widget_client; |
| if (!web_widget_client) { |
| owned_web_widget_client = |
| std::make_unique<TestWebViewWidgetClient>(*test_web_view_client_); |
| web_widget_client = owned_web_widget_client.get(); |
| } |
| blink::WebFrameWidget::Create(web_widget_client, frame); |
| web_frame_client->BindWidgetClient(std::move(owned_web_widget_client)); |
| |
| return web_view_; |
| } |
| |
| WebViewImpl* WebViewHelper::Initialize( |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| TestWebWidgetClient* web_widget_client, |
| void (*update_settings_func)(WebSettings*)) { |
| return InitializeWithOpener(nullptr, web_frame_client, web_view_client, |
| web_widget_client, update_settings_func); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeAndLoad( |
| const std::string& url, |
| TestWebFrameClient* web_frame_client, |
| TestWebViewClient* web_view_client, |
| TestWebWidgetClient* web_widget_client, |
| void (*update_settings_func)(WebSettings*)) { |
| Initialize(web_frame_client, web_view_client, web_widget_client, |
| update_settings_func); |
| |
| LoadFrame(GetWebView()->MainFrameImpl(), url); |
| |
| return GetWebView(); |
| } |
| |
| WebViewImpl* WebViewHelper::InitializeRemote( |
| TestWebRemoteFrameClient* web_remote_frame_client, |
| scoped_refptr<SecurityOrigin> security_origin, |
| TestWebViewClient* web_view_client) { |
| Reset(); |
| |
| InitializeWebView(web_view_client, nullptr); |
| |
| auto owned_web_remote_frame_client = |
| CreateDefaultClientIfNeeded(web_remote_frame_client); |
| WebRemoteFrameImpl* frame = WebRemoteFrameImpl::CreateMainFrame( |
| web_view_, web_remote_frame_client, nullptr); |
| web_remote_frame_client->Bind(frame, |
| std::move(owned_web_remote_frame_client)); |
| if (!security_origin) |
| security_origin = SecurityOrigin::CreateUnique(); |
| frame->GetFrame()->GetSecurityContext()->SetReplicatedOrigin( |
| std::move(security_origin)); |
| return web_view_; |
| } |
| |
| void WebViewHelper::LoadAhem() { |
| LocalFrame* local_frame = |
| ToLocalFrame(WebFrame::ToCoreFrame(*LocalMainFrame())); |
| DCHECK(local_frame); |
| RenderingTest::LoadAhem(*local_frame); |
| } |
| |
| void WebViewHelper::Reset() { |
| if (web_view_) { |
| DCHECK(!TestWebFrameClient::IsLoading()); |
| web_view_->Close(); |
| web_view_ = nullptr; |
| } |
| } |
| |
| WebLocalFrameImpl* WebViewHelper::LocalMainFrame() const { |
| return ToWebLocalFrameImpl(web_view_->MainFrame()); |
| } |
| |
| WebRemoteFrameImpl* WebViewHelper::RemoteMainFrame() const { |
| return ToWebRemoteFrameImpl(web_view_->MainFrame()); |
| } |
| |
| void WebViewHelper::SetViewportSize(const WebSize& viewport_size) { |
| test_web_view_client_->GetLayerTreeViewForTesting()->SetViewportSize( |
| viewport_size); |
| } |
| |
| void WebViewHelper::Resize(WebSize size) { |
| test_web_view_client_->ClearAnimationScheduled(); |
| GetWebView()->Resize(size); |
| EXPECT_FALSE(test_web_view_client_->AnimationScheduled()); |
| test_web_view_client_->ClearAnimationScheduled(); |
| } |
| |
| void WebViewHelper::InitializeWebView(TestWebViewClient* web_view_client, |
| class WebView* opener) { |
| owned_test_web_view_client_ = CreateDefaultClientIfNeeded(web_view_client); |
| web_view_ = static_cast<WebViewImpl*>(WebView::Create( |
| web_view_client, mojom::PageVisibilityState::kVisible, opener)); |
| 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); |
| web_view_->SetDeviceScaleFactor( |
| web_view_client->GetScreenInfo().device_scale_factor); |
| web_view_->SetDefaultPageScaleLimits(1, 4); |
| |
| test_web_view_client_ = web_view_client; |
| } |
| |
| int TestWebFrameClient::loads_in_progress_ = 0; |
| |
| TestWebFrameClient::TestWebFrameClient() |
| : interface_provider_(new service_manager::InterfaceProvider()) {} |
| |
| void TestWebFrameClient::Bind(WebLocalFrame* frame, |
| std::unique_ptr<TestWebFrameClient> self_owned) { |
| DCHECK(!frame_); |
| DCHECK(!self_owned || self_owned.get() == this); |
| frame_ = frame; |
| self_owned_ = std::move(self_owned); |
| } |
| |
| void TestWebFrameClient::BindWidgetClient( |
| std::unique_ptr<TestWebWidgetClient> client) { |
| DCHECK(!owned_widget_client_); |
| owned_widget_client_ = std::move(client); |
| } |
| |
| void TestWebFrameClient::FrameDetached(DetachType type) { |
| if (frame_->FrameWidget()) { |
| frame_->FrameWidget()->WillCloseLayerTreeView(); |
| frame_->FrameWidget()->Close(); |
| } |
| |
| owned_widget_client_.reset(); |
| frame_->Close(); |
| self_owned_.reset(); |
| } |
| |
| WebLocalFrame* TestWebFrameClient::CreateChildFrame( |
| WebLocalFrame* parent, |
| WebTreeScopeType scope, |
| const WebString& name, |
| const WebString& fallback_name, |
| WebSandboxFlags sandbox_flags, |
| const ParsedFeaturePolicy& container_policy, |
| const WebFrameOwnerProperties& frame_owner_properties) { |
| return CreateLocalChild(*parent, scope); |
| } |
| |
| void TestWebFrameClient::DidStartLoading(bool) { |
| ++loads_in_progress_; |
| } |
| |
| void TestWebFrameClient::DidStopLoading() { |
| DCHECK_GT(loads_in_progress_, 0); |
| --loads_in_progress_; |
| } |
| |
| TestWebRemoteFrameClient::TestWebRemoteFrameClient() = default; |
| |
| 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(); |
| } |
| |
| WebLayerTreeViewImplForTesting* |
| TestWebViewClient::GetLayerTreeViewForTesting() { |
| return layer_tree_view_.get(); |
| } |
| |
| WebLayerTreeView* TestWebViewClient::InitializeLayerTreeView() { |
| layer_tree_view_ = std::make_unique<WebLayerTreeViewImplForTesting>(); |
| return layer_tree_view_.get(); |
| } |
| |
| WebLayerTreeView* TestWebViewWidgetClient::InitializeLayerTreeView() { |
| return test_web_view_client_.InitializeLayerTreeView(); |
| } |
| |
| WebLayerTreeView* TestWebWidgetClient::InitializeLayerTreeView() { |
| layer_tree_view_ = std::make_unique<WebLayerTreeViewImplForTesting>(); |
| return layer_tree_view_.get(); |
| } |
| |
| void TestWebViewWidgetClient::ScheduleAnimation() { |
| test_web_view_client_.ScheduleAnimation(); |
| } |
| |
| void TestWebViewWidgetClient::DidMeaningfulLayout( |
| WebMeaningfulLayout layout_type) { |
| test_web_view_client_.DidMeaningfulLayout(layout_type); |
| } |
| |
| } // namespace FrameTestHelpers |
| } // namespace blink |