blob: dd6fac01af925a77d0353e4ece35310d952fe969 [file] [log] [blame]
// Copyright 2015 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/test/test_render_frame.h"
#include <string>
#include <utility>
#include <vector>
#include "base/callback_helpers.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_messages.mojom.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_policy_container_host.h"
#include "content/public/test/mock_render_thread.h"
#include "content/public/test/policy_container_utils.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/data_url.h"
#include "services/network/public/cpp/not_implemented_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_navigation_control.h"
#include "third_party/skia/include/core/SkColor.h"
namespace content {
// static
mojo::PendingAssociatedReceiver<mojom::Frame>
TestRenderFrame::CreateStubFrameReceiver() {
mojo::PendingAssociatedRemote<mojom::Frame> pending_remote;
mojo::PendingAssociatedReceiver<mojom::Frame> pending_receiver =
pending_remote.InitWithNewEndpointAndPassReceiver();
return pending_receiver;
}
// static
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
TestRenderFrame::CreateStubBrowserInterfaceBrokerRemote() {
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> pending_remote;
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> pending_receiver =
pending_remote.InitWithNewPipeAndPassReceiver();
return pending_remote;
}
class MockFrameHost : public mojom::FrameHost {
public:
MockFrameHost() {}
MockFrameHost(const MockFrameHost&) = delete;
MockFrameHost& operator=(const MockFrameHost&) = delete;
~MockFrameHost() override = default;
mojom::DidCommitProvisionalLoadParamsPtr TakeLastCommitParams() {
return std::move(last_commit_params_);
}
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
TakeLastBrowserInterfaceBrokerReceiver() {
return std::move(last_browser_interface_broker_receiver_);
}
// The frame in the renderer sends a BrowserInterfaceBroker Receiver to the
// browser process. The test harness (in MockRenderThread) will stash away
// those pending Receivers. This sets the pending Receiver that was sent for
// the browser process to bind when initially creating the frame.
void SetInitialBrowserInterfaceBrokerReceiver(
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker_receiver) {
last_browser_interface_broker_receiver_ =
std::move(browser_interface_broker_receiver);
}
void DidCommitProvisionalLoad(
mojom::DidCommitProvisionalLoadParamsPtr params,
mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params)
override {
last_commit_params_ = std::move(params);
if (interface_params) {
last_browser_interface_broker_receiver_ =
std::move(interface_params->browser_interface_broker_receiver);
}
}
void set_overlay_routing_token(const base::UnguessableToken& token) {
overlay_routing_token_ = token;
}
size_t request_overlay_routing_token_called() {
return request_overlay_routing_token_called_;
}
bool is_page_state_updated() const { return is_page_state_updated_; }
bool is_url_opened() const { return is_url_opened_; }
protected:
// mojom::FrameHost:
void CreateNewWindow(mojom::CreateNewWindowParamsPtr,
CreateNewWindowCallback) override {
NOTREACHED() << "We should never dispatch to the service side signature.";
}
bool CreateNewWindow(mojom::CreateNewWindowParamsPtr params,
mojom::CreateNewWindowStatus* status,
mojom::CreateNewWindowReplyPtr* reply) override {
*status = mojom::CreateNewWindowStatus::kSuccess;
*reply = mojom::CreateNewWindowReply::New();
MockRenderThread* mock_render_thread =
static_cast<MockRenderThread*>(RenderThread::Get());
mock_render_thread->OnCreateWindow(*params, reply->get());
return true;
}
void CreateChildFrame(
int new_routing_id,
mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker_receiver,
blink::mojom::PolicyContainerBindParamsPtr policy_container_bind_params,
blink::mojom::TreeScopeType scope,
const std::string& frame_name,
const std::string& frame_unique_name,
bool is_created_by_script,
const blink::FramePolicy& frame_policy,
blink::mojom::FrameOwnerPropertiesPtr frame_owner_properties,
blink::FrameOwnerElementType owner_type) override {
MockPolicyContainerHost mock_policy_container_host;
mock_policy_container_host.BindWithNewEndpoint(
std::move(policy_container_bind_params->receiver));
MockRenderThread* mock_render_thread =
static_cast<MockRenderThread*>(RenderThread::Get());
mock_render_thread->OnCreateChildFrame(
new_routing_id, std::move(frame_remote),
std::move(browser_interface_broker_receiver));
}
void CreatePortal(mojo::PendingAssociatedReceiver<blink::mojom::Portal>,
mojo::PendingAssociatedRemote<blink::mojom::PortalClient>,
CreatePortalCallback callback) override {
std::move(callback).Run(MSG_ROUTING_NONE,
blink::mojom::FrameReplicationState::New(),
blink::PortalToken(), blink::RemoteFrameToken(),
base::UnguessableToken());
}
void AdoptPortal(const blink::PortalToken&,
AdoptPortalCallback callback) override {
std::move(callback).Run(
MSG_ROUTING_NONE, blink::mojom::FrameReplicationState::New(),
blink::RemoteFrameToken(), base::UnguessableToken());
}
void CreateFencedFrame(
mojo::PendingAssociatedReceiver<blink::mojom::FencedFrameOwnerHost>,
CreateFencedFrameCallback) override {
NOTREACHED() << "At the moment, content::FencedFrame is not used in any "
"unit tests, so this path should not be hit";
}
void DidCommitSameDocumentNavigation(
mojom::DidCommitProvisionalLoadParamsPtr params,
mojom::DidCommitSameDocumentNavigationParamsPtr same_doc_params)
override {
last_commit_params_ = std::move(params);
}
void DidOpenDocumentInputStream(const GURL& url) override {}
void BeginNavigation(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::BeginNavigationParamsPtr begin_params,
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token,
mojo::PendingAssociatedRemote<mojom::NavigationClient>,
mojo::PendingRemote<blink::mojom::PolicyContainerHostKeepAliveHandle>)
override {}
void SubresourceResponseStarted(const GURL& url,
net::CertStatus cert_status) override {}
void ResourceLoadComplete(
blink::mojom::ResourceLoadInfoPtr resource_load_info) override {}
void DidChangeName(const std::string& name,
const std::string& unique_name) override {}
void CancelInitialHistoryLoad() override {}
void UpdateEncoding(const std::string& encoding_name) override {}
void UpdateState(const blink::PageState& state) override {
is_page_state_updated_ = true;
}
void OpenURL(blink::mojom::OpenURLParamsPtr params) override {
is_url_opened_ = true;
}
void DidStopLoading() override {}
#if defined(OS_ANDROID)
void UpdateUserGestureCarryoverInfo() override {}
#endif
private:
mojom::DidCommitProvisionalLoadParamsPtr last_commit_params_;
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
last_browser_interface_broker_receiver_;
size_t request_overlay_routing_token_called_ = 0;
absl::optional<base::UnguessableToken> overlay_routing_token_;
bool is_page_state_updated_ = false;
bool is_url_opened_ = false;
};
// static
RenderFrameImpl* TestRenderFrame::CreateTestRenderFrame(
RenderFrameImpl::CreateParams params) {
return new TestRenderFrame(std::move(params));
}
TestRenderFrame::TestRenderFrame(RenderFrameImpl::CreateParams params)
: RenderFrameImpl(std::move(params)),
mock_frame_host_(std::make_unique<MockFrameHost>()) {
MockRenderThread* mock_render_thread =
static_cast<MockRenderThread*>(RenderThread::Get());
mock_frame_host_->SetInitialBrowserInterfaceBrokerReceiver(
mock_render_thread->TakeInitialBrowserInterfaceBrokerReceiverForFrame(
GetRoutingID()));
}
TestRenderFrame::~TestRenderFrame() {}
void TestRenderFrame::SetHTMLOverrideForNextNavigation(
const std::string& html) {
next_navigation_html_override_ = html;
}
void TestRenderFrame::Navigate(
network::mojom::URLResponseHeadPtr head,
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params) {
mock_navigation_client_.reset();
BindNavigationClient(
mock_navigation_client_.BindNewEndpointAndPassDedicatedReceiver());
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> pending_factory_bundle =
blink::ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
network::NotImplementedURLLoaderFactory::Create());
MockPolicyContainerHost mock_policy_container_host;
CommitNavigation(
std::move(common_params), std::move(commit_params), std::move(head),
mojo::ScopedDataPipeConsumerHandle(),
network::mojom::URLLoaderClientEndpointsPtr(),
std::move(pending_factory_bundle), absl::nullopt,
blink::mojom::ControllerServiceWorkerInfoPtr(),
blink::mojom::ServiceWorkerContainerInfoForClientPtr(),
mojo::NullRemote() /* prefetch_loader_factory */,
base::UnguessableToken::Create(),
blink::mojom::PolicyContainer::New(
blink::mojom::PolicyContainerPolicies::New(),
mock_policy_container_host.BindNewEndpointAndPassDedicatedRemote()),
mojo::NullRemote() /* code_cache_host */, nullptr, nullptr,
base::BindOnce(&MockFrameHost::DidCommitProvisionalLoad,
base::Unretained(mock_frame_host_.get())));
}
void TestRenderFrame::Navigate(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params) {
Navigate(network::mojom::URLResponseHead::New(), std::move(common_params),
std::move(commit_params));
}
void TestRenderFrame::NavigateWithError(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
int error_code,
const net::ResolveErrorInfo& resolve_error_info,
const absl::optional<std::string>& error_page_content) {
mock_navigation_client_.reset();
BindNavigationClient(
mock_navigation_client_.BindNewEndpointAndPassDedicatedReceiver());
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> pending_factory_bundle =
blink::ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
network::NotImplementedURLLoaderFactory::Create());
mock_navigation_client_->CommitFailedNavigation(
std::move(common_params), std::move(commit_params),
false /* has_stale_copy_in_cache */, error_code,
0 /* extended_error_code */, resolve_error_info, error_page_content,
std::move(pending_factory_bundle), CreateStubPolicyContainer(),
base::BindOnce(&MockFrameHost::DidCommitProvisionalLoad,
base::Unretained(mock_frame_host_.get())));
}
void TestRenderFrame::BeginNavigation(
std::unique_ptr<blink::WebNavigationInfo> info) {
if (next_navigation_html_override_.has_value()) {
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
auto navigation_params =
blink::WebNavigationParams::CreateWithHTMLStringForTesting(
next_navigation_html_override_.value(), info->url_request.Url());
MockPolicyContainerHost mock_policy_container_host;
navigation_params->requestor_origin = info->url_request.RequestorOrigin();
navigation_params->policy_container =
std::make_unique<blink::WebPolicyContainer>(
blink::WebPolicyContainerPolicies(),
mock_policy_container_host.BindNewEndpointAndPassDedicatedRemote());
next_navigation_html_override_ = absl::nullopt;
frame_->CommitNavigation(std::move(navigation_params),
nullptr /* extra_data */);
return;
}
if (info->navigation_policy == blink::kWebNavigationPolicyCurrentTab &&
GetWebFrame()->Parent() && info->form.IsNull()) {
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
// RenderViewTest::LoadHTML immediately commits navigation for the main
// frame. However if the loaded html has an empty or data subframe,
// BeginNavigation will be called from Blink and we should avoid
// going through browser process in this case.
GURL url = info->url_request.Url();
auto navigation_params =
blink::WebNavigationParams::CreateFromInfo(*info.get());
MockPolicyContainerHost mock_policy_container_host;
navigation_params->policy_container =
std::make_unique<blink::WebPolicyContainer>(
blink::WebPolicyContainerPolicies(),
mock_policy_container_host.BindNewEndpointAndPassDedicatedRemote());
if (!url.IsAboutBlank() && !url.IsAboutSrcdoc()) {
std::string mime_type, charset, data;
if (!net::DataURL::Parse(url, &mime_type, &charset, &data)) {
// This case is only here to allow cluster fuzz pass any url,
// to unblock further fuzzing.
mime_type = "text/html";
charset = "UTF-8";
}
blink::WebNavigationParams::FillStaticResponse(
navigation_params.get(), blink::WebString::FromUTF8(mime_type),
blink::WebString::FromUTF8(charset), data);
}
frame_->CommitNavigation(std::move(navigation_params),
nullptr /* extra_data */);
return;
}
RenderFrameImpl::BeginNavigation(std::move(info));
}
mojom::DidCommitProvisionalLoadParamsPtr
TestRenderFrame::TakeLastCommitParams() {
return mock_frame_host_->TakeLastCommitParams();
}
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
TestRenderFrame::TakeLastBrowserInterfaceBrokerReceiver() {
return mock_frame_host_->TakeLastBrowserInterfaceBrokerReceiver();
}
void TestRenderFrame::SimulateBeforeUnload(bool is_reload) {
// This will execute the BeforeUnload event in this frame and all of its
// local descendant frames, including children of remote frames. The browser
// process will send separate IPCs to dispatch beforeunload in any
// out-of-process child frames.
frame_->DispatchBeforeUnloadEvent(is_reload);
}
bool TestRenderFrame::IsPageStateUpdated() const {
return mock_frame_host_->is_page_state_updated();
}
bool TestRenderFrame::IsURLOpened() const {
return mock_frame_host_->is_url_opened();
}
mojom::FrameHost* TestRenderFrame::GetFrameHost() {
// Need to mock this interface directly without going through a binding,
// otherwise calling its sync methods could lead to a deadlock.
//
// Imagine the following sequence of events take place:
//
// 1.) GetFrameHost() called for the first time
// 1.1.) GetRemoteAssociatedInterfaces()->GetInterface(&frame_host_ptr_)
// 1.1.1) ... plumbing ...
// 1.1.2) Task posted to bind the request end to the Mock implementation
// 1.2) The interface pointer end is returned to the caller
// 2.) GetFrameHost()->CreateNewWindow(...) sync method invoked
// 2.1.) Mojo sync request sent
// 2.2.) Waiting for sync response while dispatching incoming sync requests
//
// Normally the sync Mojo request would be processed in 2.2. However, the
// implementation is not yet bound at that point, and will never be, because
// only sync IPCs are dispatched by 2.2, not posted tasks. So the sync request
// is never dispatched, the response never arrives.
//
// Because the first invocation to GetFrameHost() may come while we are inside
// a message loop already, pumping messags before 1.2 would constitute a
// nested message loop and is therefore undesired.
return mock_frame_host_.get();
}
} // namespace content