1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/test/test_web_contents.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/no_destructor.h"
12 #include "content/browser/browser_url_handler_impl.h"
13 #include "content/browser/portal/portal.h"
14 #include "content/browser/renderer_host/cross_process_frame_connector.h"
15 #include "content/browser/renderer_host/debug_urls.h"
16 #include "content/browser/renderer_host/navigation_entry_impl.h"
17 #include "content/browser/renderer_host/navigation_request.h"
18 #include "content/browser/renderer_host/navigator.h"
19 #include "content/browser/renderer_host/render_process_host_impl.h"
20 #include "content/browser/renderer_host/render_view_host_impl.h"
21 #include "content/browser/site_instance_impl.h"
22 #include "content/common/frame_messages.h"
23 #include "content/common/render_message_filter.mojom.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/common/url_utils.h"
28 #include "content/public/test/mock_render_process_host.h"
29 #include "content/public/test/navigation_simulator.h"
30 #include "content/test/test_render_view_host.h"
31 #include "third_party/blink/public/common/page_state/page_state.h"
32 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
33 #include "ui/base/page_transition_types.h"
34
35 namespace content {
36
37 namespace {
38
GetMockProcessFactory()39 RenderProcessHostFactory* GetMockProcessFactory() {
40 static base::NoDestructor<MockRenderProcessHostFactory> factory;
41 return factory.get();
42 }
43
44 } // namespace
45
TestWebContents(BrowserContext * browser_context)46 TestWebContents::TestWebContents(BrowserContext* browser_context)
47 : WebContentsImpl(browser_context),
48 delegate_view_override_(nullptr),
49 web_preferences_changed_counter_(nullptr),
50 expect_set_history_offset_and_length_(false),
51 expect_set_history_offset_and_length_history_length_(0),
52 pause_subresource_loading_called_(false),
53 audio_group_id_(base::UnguessableToken::Create()) {
54 if (!RenderProcessHostImpl::get_render_process_host_factory_for_testing()) {
55 // Most unit tests should prefer to create a generic MockRenderProcessHost
56 // (instead of a real RenderProcessHostImpl). Tests that need to use a
57 // specific, custom RenderProcessHostFactory should set it before creating
58 // the first TestWebContents.
59 RenderProcessHostImpl::set_render_process_host_factory_for_testing(
60 GetMockProcessFactory());
61 }
62 }
63
Create(BrowserContext * browser_context,scoped_refptr<SiteInstance> instance)64 std::unique_ptr<TestWebContents> TestWebContents::Create(
65 BrowserContext* browser_context,
66 scoped_refptr<SiteInstance> instance) {
67 std::unique_ptr<TestWebContents> test_web_contents(
68 new TestWebContents(browser_context));
69 test_web_contents->Init(CreateParams(browser_context, std::move(instance)));
70 return test_web_contents;
71 }
72
Create(const CreateParams & params)73 TestWebContents* TestWebContents::Create(const CreateParams& params) {
74 TestWebContents* test_web_contents =
75 new TestWebContents(params.browser_context);
76 test_web_contents->Init(params);
77 return test_web_contents;
78 }
79
~TestWebContents()80 TestWebContents::~TestWebContents() {
81 EXPECT_FALSE(expect_set_history_offset_and_length_);
82 }
83
GetMainFrame()84 TestRenderFrameHost* TestWebContents::GetMainFrame() {
85 auto* instance = WebContentsImpl::GetMainFrame();
86 DCHECK(instance->IsTestRenderFrameHost())
87 << "You may want to instantiate RenderViewHostTestEnabler.";
88 return static_cast<TestRenderFrameHost*>(instance);
89 }
90
GetRenderViewHost()91 TestRenderViewHost* TestWebContents::GetRenderViewHost() {
92 auto* instance = WebContentsImpl::GetRenderViewHost();
93 DCHECK(instance->IsTestRenderViewHost())
94 << "You may want to instantiate RenderViewHostTestEnabler.";
95 return static_cast<TestRenderViewHost*>(instance);
96 }
97
GetPendingMainFrame()98 TestRenderFrameHost* TestWebContents::GetPendingMainFrame() {
99 return static_cast<TestRenderFrameHost*>(
100 WebContentsImpl::GetPendingMainFrame());
101 }
102
DownloadImage(const GURL & url,bool is_favicon,uint32_t preferred_size,uint32_t max_bitmap_size,bool bypass_cache,ImageDownloadCallback callback)103 int TestWebContents::DownloadImage(const GURL& url,
104 bool is_favicon,
105 uint32_t preferred_size,
106 uint32_t max_bitmap_size,
107 bool bypass_cache,
108 ImageDownloadCallback callback) {
109 static int g_next_image_download_id = 0;
110 ++g_next_image_download_id;
111 pending_image_downloads_[url].emplace_back(g_next_image_download_id,
112 std::move(callback));
113 return g_next_image_download_id;
114 }
115
GetLastCommittedURL()116 const GURL& TestWebContents::GetLastCommittedURL() {
117 if (last_committed_url_.is_valid()) {
118 return last_committed_url_;
119 }
120 return WebContentsImpl::GetLastCommittedURL();
121 }
122
GetTitle()123 const base::string16& TestWebContents::GetTitle() {
124 if (title_)
125 return title_.value();
126
127 return WebContentsImpl::GetTitle();
128 }
129
TestDidNavigate(RenderFrameHost * render_frame_host,int nav_entry_id,bool did_create_new_entry,const GURL & url,ui::PageTransition transition)130 void TestWebContents::TestDidNavigate(RenderFrameHost* render_frame_host,
131 int nav_entry_id,
132 bool did_create_new_entry,
133 const GURL& url,
134 ui::PageTransition transition) {
135 TestDidNavigateWithSequenceNumber(render_frame_host, nav_entry_id,
136 did_create_new_entry, url, Referrer(),
137 transition, false, -1, -1);
138 }
139
TestDidNavigateWithSequenceNumber(RenderFrameHost * render_frame_host,int nav_entry_id,bool did_create_new_entry,const GURL & url,const Referrer & referrer,ui::PageTransition transition,bool was_within_same_document,int item_sequence_number,int document_sequence_number)140 void TestWebContents::TestDidNavigateWithSequenceNumber(
141 RenderFrameHost* render_frame_host,
142 int nav_entry_id,
143 bool did_create_new_entry,
144 const GURL& url,
145 const Referrer& referrer,
146 ui::PageTransition transition,
147 bool was_within_same_document,
148 int item_sequence_number,
149 int document_sequence_number) {
150 TestRenderFrameHost* rfh =
151 static_cast<TestRenderFrameHost*>(render_frame_host);
152 rfh->InitializeRenderFrameIfNeeded();
153
154 if (!rfh->is_loading())
155 rfh->SimulateNavigationStart(url);
156
157 FrameHostMsg_DidCommitProvisionalLoad_Params params;
158
159 params.nav_entry_id = nav_entry_id;
160 params.item_sequence_number = item_sequence_number;
161 params.document_sequence_number = document_sequence_number;
162 params.url = url;
163 params.base_url = GURL();
164 params.referrer = referrer;
165 params.transition = transition;
166 params.redirects = std::vector<GURL>();
167 params.should_update_history = true;
168 params.contents_mime_type = std::string("text/html");
169 params.intended_as_new_entry = did_create_new_entry;
170 params.did_create_new_entry = did_create_new_entry;
171 params.should_replace_current_entry = false;
172 params.gesture = NavigationGestureUser;
173 params.method = "GET";
174 params.post_id = 0;
175 params.http_status_code = 200;
176 params.url_is_unreachable = false;
177 if (item_sequence_number != -1 && document_sequence_number != -1) {
178 params.page_state = blink::PageState::CreateForTestingWithSequenceNumbers(
179 url, item_sequence_number, document_sequence_number);
180 } else {
181 params.page_state = blink::PageState::CreateFromURL(url);
182 }
183 params.original_request_url = GURL();
184 params.is_overriding_user_agent = false;
185 params.history_list_was_cleared = false;
186 params.origin = url::Origin::Create(url);
187 params.insecure_request_policy =
188 blink::mojom::InsecureRequestPolicy::kLeaveInsecureRequestsAlone;
189 params.has_potentially_trustworthy_unique_origin = false;
190
191 rfh->SendNavigateWithParams(¶ms, was_within_same_document);
192 }
193
GetSaveFrameHeaders()194 const std::string& TestWebContents::GetSaveFrameHeaders() {
195 return save_frame_headers_;
196 }
197
GetSuggestedFileName()198 const base::string16& TestWebContents::GetSuggestedFileName() {
199 return suggested_filename_;
200 }
201
HasPendingDownloadImage(const GURL & url)202 bool TestWebContents::HasPendingDownloadImage(const GURL& url) {
203 return !pending_image_downloads_[url].empty();
204 }
205
OnWebPreferencesChanged()206 void TestWebContents::OnWebPreferencesChanged() {
207 WebContentsImpl::OnWebPreferencesChanged();
208 if (web_preferences_changed_counter_)
209 ++*web_preferences_changed_counter_;
210 }
211
TestDidDownloadImage(const GURL & url,int http_status_code,const std::vector<SkBitmap> & bitmaps,const std::vector<gfx::Size> & original_bitmap_sizes)212 bool TestWebContents::TestDidDownloadImage(
213 const GURL& url,
214 int http_status_code,
215 const std::vector<SkBitmap>& bitmaps,
216 const std::vector<gfx::Size>& original_bitmap_sizes) {
217 if (!HasPendingDownloadImage(url))
218 return false;
219 int id = pending_image_downloads_[url].front().first;
220 ImageDownloadCallback callback =
221 std::move(pending_image_downloads_[url].front().second);
222 pending_image_downloads_[url].pop_front();
223 WebContentsImpl::OnDidDownloadImage(std::move(callback), id, url,
224 http_status_code, bitmaps,
225 original_bitmap_sizes);
226 return true;
227 }
228
SetLastCommittedURL(const GURL & url)229 void TestWebContents::SetLastCommittedURL(const GURL& url) {
230 last_committed_url_ = url;
231 }
232
SetTitle(const base::string16 & title)233 void TestWebContents::SetTitle(const base::string16& title) {
234 title_ = title;
235 }
236
SetMainFrameMimeType(const std::string & mime_type)237 void TestWebContents::SetMainFrameMimeType(const std::string& mime_type) {
238 static_cast<RenderViewHostImpl*>(GetRenderViewHost())
239 ->SetContentsMimeType(mime_type);
240 }
241
GetContentsMimeType()242 const std::string& TestWebContents::GetContentsMimeType() {
243 return static_cast<RenderViewHostImpl*>(GetRenderViewHost())
244 ->contents_mime_type();
245 }
246
SetIsCurrentlyAudible(bool audible)247 void TestWebContents::SetIsCurrentlyAudible(bool audible) {
248 audio_stream_monitor()->set_is_currently_audible_for_testing(audible);
249 OnAudioStateChanged();
250 }
251
TestDidReceiveMouseDownEvent()252 void TestWebContents::TestDidReceiveMouseDownEvent() {
253 blink::WebMouseEvent event;
254 event.SetType(blink::WebInputEvent::Type::kMouseDown);
255 // Use the first RenderWidgetHost from the frame tree to make sure that the
256 // interaction doesn't get ignored.
257 DCHECK(frame_tree_.Nodes().begin() != frame_tree_.Nodes().end());
258 RenderWidgetHostImpl* render_widget_host = (*frame_tree_.Nodes().begin())
259 ->current_frame_host()
260 ->GetRenderWidgetHost();
261 DidReceiveInputEvent(render_widget_host, event);
262 }
263
TestDidFinishLoad(const GURL & url)264 void TestWebContents::TestDidFinishLoad(const GURL& url) {
265 OnDidFinishLoad(frame_tree_.root()->current_frame_host(), url);
266 }
267
TestDidFailLoadWithError(const GURL & url,int error_code)268 void TestWebContents::TestDidFailLoadWithError(const GURL& url,
269 int error_code) {
270 GetMainFrame()->DidFailLoadWithError(url, error_code);
271 }
272
CrossProcessNavigationPending()273 bool TestWebContents::CrossProcessNavigationPending() {
274 return GetRenderManager()->speculative_render_frame_host_ != nullptr;
275 }
276
CreateRenderViewForRenderManager(RenderViewHost * render_view_host,const base::Optional<base::UnguessableToken> & opener_frame_token,int proxy_routing_id)277 bool TestWebContents::CreateRenderViewForRenderManager(
278 RenderViewHost* render_view_host,
279 const base::Optional<base::UnguessableToken>& opener_frame_token,
280 int proxy_routing_id) {
281 // This will go to a TestRenderViewHost.
282 static_cast<RenderViewHostImpl*>(render_view_host)
283 ->CreateRenderView(opener_frame_token, proxy_routing_id, false);
284 return true;
285 }
286
Clone()287 std::unique_ptr<WebContents> TestWebContents::Clone() {
288 std::unique_ptr<WebContentsImpl> contents =
289 Create(GetBrowserContext(), SiteInstance::Create(GetBrowserContext()));
290 contents->GetController().CopyStateFrom(&controller_, true);
291 return contents;
292 }
293
NavigateAndCommit(const GURL & url,ui::PageTransition transition)294 void TestWebContents::NavigateAndCommit(const GURL& url,
295 ui::PageTransition transition) {
296 std::unique_ptr<NavigationSimulator> navigation =
297 NavigationSimulator::CreateBrowserInitiated(url, this);
298 // TODO(clamy): Browser-initiated navigations should not have a transition of
299 // type ui::PAGE_TRANSITION_LINK however several tests expect this. They
300 // should be rewritten to simulate renderer-initiated navigations in these
301 // cases. Once that's done, the transtion can be set to
302 // ui::PAGE_TRANSITION_TYPED which makes more sense in this context.
303 // ui::PAGE_TRANSITION_TYPED is the default value for transition
304 navigation->SetTransition(transition);
305 navigation->Commit();
306 }
307
NavigateAndFail(const GURL & url,int error_code)308 void TestWebContents::NavigateAndFail(const GURL& url, int error_code) {
309 std::unique_ptr<NavigationSimulator> navigation =
310 NavigationSimulator::CreateBrowserInitiated(url, this);
311 navigation->Fail(error_code);
312 }
313
TestSetIsLoading(bool value)314 void TestWebContents::TestSetIsLoading(bool value) {
315 if (value) {
316 DidStartLoading(GetMainFrame()->frame_tree_node(), true);
317 } else {
318 for (FrameTreeNode* node : frame_tree_.Nodes()) {
319 RenderFrameHostImpl* current_frame_host =
320 node->render_manager()->current_frame_host();
321 DCHECK(current_frame_host);
322 current_frame_host->ResetLoadingState();
323
324 RenderFrameHostImpl* speculative_frame_host =
325 node->render_manager()->speculative_frame_host();
326 if (speculative_frame_host)
327 speculative_frame_host->ResetLoadingState();
328 node->ResetNavigationRequest(false);
329 }
330 }
331 }
332
CommitPendingNavigation()333 void TestWebContents::CommitPendingNavigation() {
334 NavigationEntry* entry = GetController().GetPendingEntry();
335 DCHECK(entry);
336
337 auto navigation = NavigationSimulator::CreateFromPending(this);
338 navigation->Commit();
339 }
340
GetDelegateView()341 RenderViewHostDelegateView* TestWebContents::GetDelegateView() {
342 if (delegate_view_override_)
343 return delegate_view_override_;
344 return WebContentsImpl::GetDelegateView();
345 }
346
SetOpener(WebContents * opener)347 void TestWebContents::SetOpener(WebContents* opener) {
348 frame_tree_.root()->SetOpener(
349 static_cast<WebContentsImpl*>(opener)->GetFrameTree()->root());
350 }
351
AddPendingContents(std::unique_ptr<WebContentsImpl> contents,const GURL & target_url)352 void TestWebContents::AddPendingContents(
353 std::unique_ptr<WebContentsImpl> contents,
354 const GURL& target_url) {
355 // This is normally only done in WebContentsImpl::CreateNewWindow.
356 GlobalRoutingID key(
357 contents->GetRenderViewHost()->GetProcess()->GetID(),
358 contents->GetRenderViewHost()->GetWidget()->GetRoutingID());
359 AddDestructionObserver(contents.get());
360 pending_contents_[key] = CreatedWindow(std::move(contents), target_url);
361 }
362
ExpectSetHistoryOffsetAndLength(int history_offset,int history_length)363 void TestWebContents::ExpectSetHistoryOffsetAndLength(int history_offset,
364 int history_length) {
365 expect_set_history_offset_and_length_ = true;
366 expect_set_history_offset_and_length_history_offset_ = history_offset;
367 expect_set_history_offset_and_length_history_length_ = history_length;
368 }
369
SetHistoryOffsetAndLength(int history_offset,int history_length)370 void TestWebContents::SetHistoryOffsetAndLength(int history_offset,
371 int history_length) {
372 EXPECT_TRUE(expect_set_history_offset_and_length_);
373 expect_set_history_offset_and_length_ = false;
374 EXPECT_EQ(expect_set_history_offset_and_length_history_offset_,
375 history_offset);
376 EXPECT_EQ(expect_set_history_offset_and_length_history_length_,
377 history_length);
378 }
379
CreateNewWindow(RenderFrameHost * opener,const mojom::CreateNewWindowParams & params,bool is_new_browsing_instance,bool has_user_gesture,SessionStorageNamespace * session_storage_namespace)380 RenderFrameHostDelegate* TestWebContents::CreateNewWindow(
381 RenderFrameHost* opener,
382 const mojom::CreateNewWindowParams& params,
383 bool is_new_browsing_instance,
384 bool has_user_gesture,
385 SessionStorageNamespace* session_storage_namespace) {
386 return nullptr;
387 }
388
CreateNewPopupWidget(AgentSchedulingGroupHost & agent_scheduling_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)389 RenderWidgetHostImpl* TestWebContents::CreateNewPopupWidget(
390 AgentSchedulingGroupHost& agent_scheduling_group,
391 int32_t route_id,
392 mojo::PendingAssociatedReceiver<blink::mojom::PopupWidgetHost>
393 blink_popup_widget_host,
394 mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
395 mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget) {
396 return nullptr;
397 }
398
ShowCreatedWindow(RenderFrameHost * opener,int route_id,WindowOpenDisposition disposition,const gfx::Rect & initial_rect,bool user_gesture)399 void TestWebContents::ShowCreatedWindow(RenderFrameHost* opener,
400 int route_id,
401 WindowOpenDisposition disposition,
402 const gfx::Rect& initial_rect,
403 bool user_gesture) {}
404
ShowCreatedWidget(int process_id,int route_id,const gfx::Rect & initial_rect)405 void TestWebContents::ShowCreatedWidget(int process_id,
406 int route_id,
407 const gfx::Rect& initial_rect) {}
408
SaveFrameWithHeaders(const GURL & url,const Referrer & referrer,const std::string & headers,const base::string16 & suggested_filename)409 void TestWebContents::SaveFrameWithHeaders(
410 const GURL& url,
411 const Referrer& referrer,
412 const std::string& headers,
413 const base::string16& suggested_filename) {
414 save_frame_headers_ = headers;
415 suggested_filename_ = suggested_filename;
416 }
417
GetPauseSubresourceLoadingCalled()418 bool TestWebContents::GetPauseSubresourceLoadingCalled() {
419 return pause_subresource_loading_called_;
420 }
421
ResetPauseSubresourceLoadingCalled()422 void TestWebContents::ResetPauseSubresourceLoadingCalled() {
423 pause_subresource_loading_called_ = false;
424 }
425
SetLastActiveTime(base::TimeTicks last_active_time)426 void TestWebContents::SetLastActiveTime(base::TimeTicks last_active_time) {
427 last_active_time_ = last_active_time;
428 }
429
TestIncrementBluetoothConnectedDeviceCount()430 void TestWebContents::TestIncrementBluetoothConnectedDeviceCount() {
431 IncrementBluetoothConnectedDeviceCount();
432 }
433
TestDecrementBluetoothConnectedDeviceCount()434 void TestWebContents::TestDecrementBluetoothConnectedDeviceCount() {
435 DecrementBluetoothConnectedDeviceCount();
436 }
437
GetAudioGroupId()438 base::UnguessableToken TestWebContents::GetAudioGroupId() {
439 return audio_group_id_;
440 }
441
CreatePortal(std::unique_ptr<WebContents> web_contents)442 const blink::PortalToken& TestWebContents::CreatePortal(
443 std::unique_ptr<WebContents> web_contents) {
444 auto portal =
445 std::make_unique<Portal>(GetMainFrame(), std::move(web_contents));
446 const blink::PortalToken& token = portal->portal_token();
447 portal->CreateProxyAndAttachPortal();
448 GetMainFrame()->OnPortalCreatedForTesting(std::move(portal));
449 return token;
450 }
451
GetPortalContents(const blink::PortalToken & portal_token)452 WebContents* TestWebContents::GetPortalContents(
453 const blink::PortalToken& portal_token) {
454 Portal* portal = GetMainFrame()->FindPortalByToken(portal_token);
455 if (!portal)
456 return nullptr;
457 return portal->GetPortalContents();
458 }
459
460 } // namespace content
461