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(&params, 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