blob: 9888ba55f84d77d844a41c15d228d8696c7c3f33 [file] [log] [blame]
// Copyright (c) 2012 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_web_contents.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/no_destructor.h"
#include "content/browser/browser_url_handler_impl.h"
#include "content/browser/portal/portal.h"
#include "content/browser/preloading/prerender/prerender_host.h"
#include "content/browser/preloading/prerender/prerender_host_registry.h"
#include "content/browser/renderer_host/cross_process_frame_connector.h"
#include "content/browser/renderer_host/debug_urls.h"
#include "content/browser/renderer_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/navigator.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/common/render_message_filter.mojom.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/referrer_type_converters.h"
#include "content/public/common/url_utils.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/prerender_test_util.h"
#include "content/test/navigation_simulator_impl.h"
#include "content/test/test_render_view_host.h"
#include "mojo/public/cpp/bindings/clone_traits.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/page_state/page_state.h"
#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
#include "ui/base/page_transition_types.h"
namespace content {
namespace {
RenderProcessHostFactory* GetMockProcessFactory() {
static base::NoDestructor<MockRenderProcessHostFactory> factory;
return factory.get();
}
} // namespace
TestWebContents::TestWebContents(BrowserContext* browser_context)
: WebContentsImpl(browser_context),
delegate_view_override_(nullptr),
web_preferences_changed_counter_(nullptr),
pause_subresource_loading_called_(false),
audio_group_id_(base::UnguessableToken::Create()),
is_page_frozen_(false) {
if (!RenderProcessHostImpl::get_render_process_host_factory_for_testing()) {
// Most unit tests should prefer to create a generic MockRenderProcessHost
// (instead of a real RenderProcessHostImpl). Tests that need to use a
// specific, custom RenderProcessHostFactory should set it before creating
// the first TestWebContents.
RenderProcessHostImpl::set_render_process_host_factory_for_testing(
GetMockProcessFactory());
}
}
std::unique_ptr<TestWebContents> TestWebContents::Create(
BrowserContext* browser_context,
scoped_refptr<SiteInstance> instance) {
std::unique_ptr<TestWebContents> test_web_contents(
new TestWebContents(browser_context));
test_web_contents->Init(CreateParams(browser_context, std::move(instance)),
blink::FramePolicy());
return test_web_contents;
}
TestWebContents* TestWebContents::Create(const CreateParams& params) {
TestWebContents* test_web_contents =
new TestWebContents(params.browser_context);
test_web_contents->Init(params, blink::FramePolicy());
return test_web_contents;
}
TestWebContents::~TestWebContents() = default;
TestRenderFrameHost* TestWebContents::GetPrimaryMainFrame() {
auto* instance = WebContentsImpl::GetPrimaryMainFrame();
DCHECK(instance->IsTestRenderFrameHost())
<< "You may want to instantiate RenderViewHostTestEnabler.";
return static_cast<TestRenderFrameHost*>(instance);
}
TestRenderViewHost* TestWebContents::GetRenderViewHost() {
auto* instance = WebContentsImpl::GetRenderViewHost();
DCHECK(instance->IsTestRenderViewHost())
<< "You may want to instantiate RenderViewHostTestEnabler.";
return static_cast<TestRenderViewHost*>(instance);
}
TestRenderFrameHost* TestWebContents::GetSpeculativePrimaryMainFrame() {
return static_cast<TestRenderFrameHost*>(
GetPrimaryFrameTree().root()->render_manager()->speculative_frame_host());
}
int TestWebContents::DownloadImage(const GURL& url,
bool is_favicon,
const gfx::Size& preferred_size,
uint32_t max_bitmap_size,
bool bypass_cache,
ImageDownloadCallback callback) {
static int g_next_image_download_id = 0;
++g_next_image_download_id;
pending_image_downloads_[url].emplace_back(g_next_image_download_id,
std::move(callback));
return g_next_image_download_id;
}
const GURL& TestWebContents::GetLastCommittedURL() {
if (last_committed_url_.is_valid()) {
return last_committed_url_;
}
return WebContentsImpl::GetLastCommittedURL();
}
const std::u16string& TestWebContents::GetTitle() {
if (title_)
return title_.value();
return WebContentsImpl::GetTitle();
}
void TestWebContents::SetTabSwitchStartTime(base::TimeTicks start_time,
bool destination_is_loaded) {
tab_switch_start_time_ = start_time;
WebContentsImpl::SetTabSwitchStartTime(start_time, destination_is_loaded);
}
const std::string& TestWebContents::GetSaveFrameHeaders() {
return save_frame_headers_;
}
const std::u16string& TestWebContents::GetSuggestedFileName() {
return suggested_filename_;
}
bool TestWebContents::HasPendingDownloadImage(const GURL& url) {
return !pending_image_downloads_[url].empty();
}
void TestWebContents::OnWebPreferencesChanged() {
WebContentsImpl::OnWebPreferencesChanged();
if (web_preferences_changed_counter_)
++*web_preferences_changed_counter_;
}
void TestWebContents::SetBackForwardCacheSupported(bool supported) {
back_forward_cache_supported_ = supported;
}
bool TestWebContents::IsPageFrozen() {
return is_page_frozen_;
}
bool TestWebContents::TestDidDownloadImage(
const GURL& url,
int http_status_code,
const std::vector<SkBitmap>& bitmaps,
const std::vector<gfx::Size>& original_bitmap_sizes) {
if (!HasPendingDownloadImage(url))
return false;
int id = pending_image_downloads_[url].front().first;
ImageDownloadCallback callback =
std::move(pending_image_downloads_[url].front().second);
pending_image_downloads_[url].pop_front();
WebContentsImpl::OnDidDownloadImage(/*rfh=*/nullptr, std::move(callback), id,
url, http_status_code, bitmaps,
original_bitmap_sizes);
return true;
}
void TestWebContents::TestSetFaviconURL(
const std::vector<blink::mojom::FaviconURLPtr>& favicon_urls) {
GetPrimaryPage().set_favicon_urls(mojo::Clone(favicon_urls));
}
void TestWebContents::TestUpdateFaviconURL(
const std::vector<blink::mojom::FaviconURLPtr>& favicon_urls) {
GetPrimaryMainFrame()->UpdateFaviconURL(mojo::Clone(favicon_urls));
}
void TestWebContents::SetLastCommittedURL(const GURL& url) {
last_committed_url_ = url;
}
void TestWebContents::SetTitle(const std::u16string& title) {
title_ = title;
}
void TestWebContents::SetMainFrameMimeType(const std::string& mime_type) {
GetPrimaryPage().SetContentsMimeType(mime_type);
}
const std::string& TestWebContents::GetContentsMimeType() {
return GetPrimaryPage().contents_mime_type();
}
void TestWebContents::SetIsCurrentlyAudible(bool audible) {
audio_stream_monitor()->set_is_currently_audible_for_testing(audible);
OnAudioStateChanged();
}
void TestWebContents::TestDidReceiveMouseDownEvent() {
blink::WebMouseEvent event;
event.SetType(blink::WebInputEvent::Type::kMouseDown);
// Use the first RenderWidgetHost from the frame tree to make sure that the
// interaction doesn't get ignored.
DCHECK(primary_frame_tree_.Nodes().begin() !=
primary_frame_tree_.Nodes().end());
RenderWidgetHostImpl* render_widget_host =
(*primary_frame_tree_.Nodes().begin())
->current_frame_host()
->GetRenderWidgetHost();
DidReceiveInputEvent(render_widget_host, event);
}
void TestWebContents::TestDidFinishLoad(const GURL& url) {
OnDidFinishLoad(primary_frame_tree_.root()->current_frame_host(), url);
}
void TestWebContents::TestDidFailLoadWithError(const GURL& url,
int error_code) {
GetPrimaryMainFrame()->DidFailLoadWithError(url, error_code);
}
bool TestWebContents::CrossProcessNavigationPending() {
// If we don't have a speculative RenderFrameHost then it means we did not
// change SiteInstances so we must be in the same process.
if (GetRenderManager()->speculative_render_frame_host_ == nullptr)
return false;
auto* current_instance =
GetRenderManager()->current_frame_host()->GetSiteInstance();
auto* speculative_instance =
GetRenderManager()->speculative_frame_host()->GetSiteInstance();
if (current_instance == speculative_instance)
return false;
return current_instance->GetProcess() != speculative_instance->GetProcess();
}
bool TestWebContents::CreateRenderViewForRenderManager(
RenderViewHost* render_view_host,
const absl::optional<blink::FrameToken>& opener_frame_token,
RenderFrameProxyHost* proxy_host) {
const auto proxy_routing_id =
proxy_host ? proxy_host->GetRoutingID() : MSG_ROUTING_NONE;
// This will go to a TestRenderViewHost.
static_cast<RenderViewHostImpl*>(render_view_host)
->CreateRenderView(opener_frame_token, proxy_routing_id, false);
return true;
}
std::unique_ptr<WebContents> TestWebContents::Clone() {
std::unique_ptr<WebContentsImpl> contents =
Create(GetBrowserContext(), SiteInstance::Create(GetBrowserContext()));
contents->GetController().CopyStateFrom(&GetController(), true);
return contents;
}
void TestWebContents::NavigateAndCommit(const GURL& url,
ui::PageTransition transition) {
std::unique_ptr<NavigationSimulator> navigation =
NavigationSimulator::CreateBrowserInitiated(url, this);
// TODO(clamy): Browser-initiated navigations should not have a transition of
// type ui::PAGE_TRANSITION_LINK however several tests expect this. They
// should be rewritten to simulate renderer-initiated navigations in these
// cases. Once that's done, the transtion can be set to
// ui::PAGE_TRANSITION_TYPED which makes more sense in this context.
// ui::PAGE_TRANSITION_TYPED is the default value for transition
navigation->SetTransition(transition);
navigation->Commit();
}
void TestWebContents::NavigateAndFail(const GURL& url, int error_code) {
std::unique_ptr<NavigationSimulator> navigation =
NavigationSimulator::CreateBrowserInitiated(url, this);
navigation->Fail(error_code);
}
void TestWebContents::TestSetIsLoading(bool value) {
if (value) {
DidStartLoading(GetPrimaryMainFrame()->frame_tree_node(), true);
} else {
for (FrameTreeNode* node : primary_frame_tree_.Nodes()) {
RenderFrameHostImpl* current_frame_host =
node->render_manager()->current_frame_host();
DCHECK(current_frame_host);
current_frame_host->ResetLoadingState();
RenderFrameHostImpl* speculative_frame_host =
node->render_manager()->speculative_frame_host();
if (speculative_frame_host)
speculative_frame_host->ResetLoadingState();
node->ResetNavigationRequest(false);
}
}
}
void TestWebContents::CommitPendingNavigation() {
NavigationEntry* entry = GetController().GetPendingEntry();
DCHECK(entry);
auto navigation = NavigationSimulator::CreateFromPending(GetController());
navigation->Commit();
}
RenderViewHostDelegateView* TestWebContents::GetDelegateView() {
if (delegate_view_override_)
return delegate_view_override_;
return WebContentsImpl::GetDelegateView();
}
void TestWebContents::SetOpener(WebContents* opener) {
primary_frame_tree_.root()->SetOpener(
static_cast<WebContentsImpl*>(opener)->GetPrimaryFrameTree().root());
}
void TestWebContents::SetIsCrashed(base::TerminationStatus status,
int error_code) {
SetPrimaryMainFrameProcessStatus(status, error_code);
}
void TestWebContents::AddPendingContents(
std::unique_ptr<WebContentsImpl> contents,
const GURL& target_url) {
// This is normally only done in WebContentsImpl::CreateNewWindow.
GlobalRoutingID key(
contents->GetRenderViewHost()->GetProcess()->GetID(),
contents->GetRenderViewHost()->GetWidget()->GetRoutingID());
AddWebContentsDestructionObserver(contents.get());
pending_contents_[key] = CreatedWindow(std::move(contents), target_url);
}
FrameTree* TestWebContents::CreateNewWindow(
RenderFrameHostImpl* opener,
const mojom::CreateNewWindowParams& params,
bool is_new_browsing_instance,
bool has_user_gesture,
SessionStorageNamespace* session_storage_namespace) {
return nullptr;
}
RenderWidgetHostImpl* TestWebContents::CreateNewPopupWidget(
base::SafeRef<SiteInstanceGroup> site_instance_group,
int32_t route_id,
mojo::PendingAssociatedReceiver<blink::mojom::PopupWidgetHost>
blink_popup_widget_host,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget) {
return nullptr;
}
void TestWebContents::ShowCreatedWindow(RenderFrameHostImpl* opener,
int route_id,
WindowOpenDisposition disposition,
const gfx::Rect& initial_rect,
bool user_gesture) {}
void TestWebContents::ShowCreatedWidget(int process_id,
int route_id,
const gfx::Rect& initial_rect,
const gfx::Rect& initial_anchor_rect) {}
void TestWebContents::SaveFrameWithHeaders(
const GURL& url,
const Referrer& referrer,
const std::string& headers,
const std::u16string& suggested_filename,
RenderFrameHost* rfh) {
save_frame_headers_ = headers;
suggested_filename_ = suggested_filename;
}
bool TestWebContents::GetPauseSubresourceLoadingCalled() {
return pause_subresource_loading_called_;
}
void TestWebContents::ResetPauseSubresourceLoadingCalled() {
pause_subresource_loading_called_ = false;
}
void TestWebContents::SetLastActiveTime(base::TimeTicks last_active_time) {
last_active_time_ = last_active_time;
}
void TestWebContents::TestIncrementBluetoothConnectedDeviceCount() {
IncrementBluetoothConnectedDeviceCount();
}
void TestWebContents::TestDecrementBluetoothConnectedDeviceCount() {
DecrementBluetoothConnectedDeviceCount();
}
base::UnguessableToken TestWebContents::GetAudioGroupId() {
return audio_group_id_;
}
const blink::PortalToken& TestWebContents::CreatePortal(
std::unique_ptr<WebContents> web_contents) {
auto portal =
std::make_unique<Portal>(GetPrimaryMainFrame(), std::move(web_contents));
const blink::PortalToken& token = portal->portal_token();
// Create stub RemoteFrameInterfaces.
auto remote_frame_interfaces =
blink::mojom::RemoteFrameInterfacesFromRenderer::New();
remote_frame_interfaces->frame_host_receiver =
mojo::AssociatedRemote<blink::mojom::RemoteFrameHost>()
.BindNewEndpointAndPassDedicatedReceiver();
mojo::AssociatedRemote<blink::mojom::RemoteFrame> frame;
std::ignore = frame.BindNewEndpointAndPassDedicatedReceiver();
remote_frame_interfaces->frame = frame.Unbind();
portal->CreateProxyAndAttachPortal(std::move(remote_frame_interfaces));
GetPrimaryMainFrame()->OnPortalCreatedForTesting(std::move(portal));
return token;
}
WebContents* TestWebContents::GetPortalContents(
const blink::PortalToken& portal_token) {
Portal* portal = GetPrimaryMainFrame()->FindPortalByToken(portal_token);
if (!portal)
return nullptr;
return portal->GetPortalContents();
}
void TestWebContents::SetPageFrozen(bool frozen) {
is_page_frozen_ = frozen;
}
bool TestWebContents::IsBackForwardCacheSupported() {
return back_forward_cache_supported_;
}
int TestWebContents::AddPrerender(const GURL& url) {
DCHECK(!base::FeatureList::IsEnabled(
blink::features::kPrerender2MemoryControls));
TestRenderFrameHost* rfhi = GetPrimaryMainFrame();
return GetPrerenderHostRegistry()->CreateAndStartHost(
PrerenderAttributes(url, PrerenderTriggerType::kSpeculationRule,
/*embedder_histogram_suffix=*/"", Referrer(),
rfhi->GetLastCommittedOrigin(),
rfhi->GetLastCommittedURL(),
rfhi->GetProcess()->GetID(), rfhi->GetFrameToken(),
rfhi->GetFrameTreeNodeId(),
rfhi->GetPageUkmSourceId(), ui::PAGE_TRANSITION_LINK,
/*url_match_predicate=*/absl::nullopt),
*this);
}
TestRenderFrameHost* TestWebContents::AddPrerenderAndCommitNavigation(
const GURL& url) {
int host_id = AddPrerender(url);
DCHECK_NE(RenderFrameHost::kNoFrameTreeNodeId, host_id);
PrerenderHost* host =
GetPrerenderHostRegistry()->FindNonReservedHostById(host_id);
DCHECK(host);
{
std::unique_ptr<NavigationSimulatorImpl> navigation =
NavigationSimulatorImpl::CreateFromPendingInFrame(
FrameTreeNode::GloballyFindByID(host->frame_tree_node_id()));
navigation->Commit();
}
return static_cast<TestRenderFrameHost*>(host->GetPrerenderedMainFrameHost());
}
std::unique_ptr<NavigationSimulator>
TestWebContents::AddPrerenderAndStartNavigation(const GURL& url) {
int host_id = AddPrerender(url);
DCHECK_NE(RenderFrameHost::kNoFrameTreeNodeId, host_id);
PrerenderHost* host =
GetPrerenderHostRegistry()->FindNonReservedHostById(host_id);
DCHECK(host);
return NavigationSimulatorImpl::CreateFromPendingInFrame(
FrameTreeNode::GloballyFindByID(host->frame_tree_node_id()));
}
void TestWebContents::ActivatePrerenderedPage(const GURL& url) {
// Make sure the page for `url` has been prerendered.
PrerenderHostRegistry* registry = GetPrerenderHostRegistry();
PrerenderHost* prerender_host = registry->FindHostByUrlForTesting(url);
DCHECK(prerender_host);
int prerender_host_id = prerender_host->frame_tree_node_id();
// Activate the prerendered page.
test::PrerenderHostObserver prerender_host_observer(*this, prerender_host_id);
std::unique_ptr<NavigationSimulatorImpl> navigation =
NavigationSimulatorImpl::CreateRendererInitiated(url,
GetPrimaryMainFrame());
navigation->SetReferrer(blink::mojom::Referrer::New(
GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
prerender_host_observer.WaitForDestroyed();
DCHECK_EQ(GetPrimaryMainFrame()->GetLastCommittedURL(), url);
DCHECK(prerender_host_observer.was_activated());
DCHECK_EQ(registry->FindReservedHostById(prerender_host_id), nullptr);
}
base::TimeTicks TestWebContents::GetTabSwitchStartTime() {
return tab_switch_start_time_;
}
} // namespace content