1 // Copyright 2013 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.
5 #include "content/test/test_render_frame_host.h"
7 #include "base/command_line.h"
8 #include "content/browser/frame_host/frame_tree.h"
9 #include "content/browser/frame_host/navigation_handle_impl.h"
10 #include "content/browser/frame_host/navigation_request.h"
11 #include "content/browser/frame_host/navigator.h"
12 #include "content/browser/frame_host/navigator_impl.h"
13 #include "content/browser/frame_host/render_frame_host_delegate.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/common/frame_messages.h"
16 #include "content/public/browser/stream_handle.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/common/url_constants.h"
19 #include "content/test/browser_side_navigation_test_utils.h"
20 #include "content/test/test_navigation_url_loader.h"
21 #include "content/test/test_render_view_host.h"
22 #include "net/base/load_flags.h"
23 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
24 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
25 #include "third_party/WebKit/public/web/WebTreeScopeType.h"
26 #include "ui/base/page_transition_types.h"
30 TestRenderFrameHostCreationObserver::TestRenderFrameHostCreationObserver(
31 WebContents
* web_contents
)
32 : WebContentsObserver(web_contents
), last_created_frame_(NULL
) {
35 TestRenderFrameHostCreationObserver::~TestRenderFrameHostCreationObserver() {
38 void TestRenderFrameHostCreationObserver::RenderFrameCreated(
39 RenderFrameHost
* render_frame_host
) {
40 last_created_frame_
= render_frame_host
;
43 TestRenderFrameHost::TestRenderFrameHost(SiteInstance
* site_instance
,
44 RenderViewHostImpl
* render_view_host
,
45 RenderFrameHostDelegate
* delegate
,
46 RenderWidgetHostDelegate
* rwh_delegate
,
47 FrameTree
* frame_tree
,
48 FrameTreeNode
* frame_tree_node
,
51 : RenderFrameHostImpl(site_instance
,
59 child_creation_observer_(delegate
? delegate
->GetAsWebContents() : NULL
),
60 contents_mime_type_("text/html"),
61 simulate_history_list_was_cleared_(false) {
64 TestRenderFrameHost::~TestRenderFrameHost() {
67 TestRenderViewHost
* TestRenderFrameHost::GetRenderViewHost() {
68 return static_cast<TestRenderViewHost
*>(
69 RenderFrameHostImpl::GetRenderViewHost());
72 MockRenderProcessHost
* TestRenderFrameHost::GetProcess() {
73 return static_cast<MockRenderProcessHost
*>(RenderFrameHostImpl::GetProcess());
76 void TestRenderFrameHost::InitializeRenderFrameIfNeeded() {
77 if (!render_view_host()->IsRenderViewLive()) {
78 RenderViewHostTester::For(render_view_host())->CreateTestRenderView(
79 base::string16(), MSG_ROUTING_NONE
, MSG_ROUTING_NONE
, -1, false);
83 TestRenderFrameHost
* TestRenderFrameHost::AppendChild(
84 const std::string
& frame_name
) {
85 OnCreateChildFrame(GetProcess()->GetNextRoutingID(),
86 blink::WebTreeScopeType::Document
, frame_name
,
87 blink::WebSandboxFlags::None
);
88 return static_cast<TestRenderFrameHost
*>(
89 child_creation_observer_
.last_created_frame());
92 void TestRenderFrameHost::SimulateNavigationStart(const GURL
& url
) {
93 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
94 switches::kEnableBrowserSideNavigation
)) {
95 SendRendererInitiatedNavigationRequest(url
, false);
99 OnDidStartLoading(true);
100 OnDidStartProvisionalLoadForFrame(url
);
103 void TestRenderFrameHost::SimulateRedirect(const GURL
& new_url
) {
104 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
105 switches::kEnableBrowserSideNavigation
)) {
106 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
107 TestNavigationURLLoader
* url_loader
=
108 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
110 url_loader
->SimulateServerRedirect(new_url
);
114 // Note that this does not simulate
115 // WebContentsImpl::DidGetRedirectForResourceRequest due to the difficulty in
116 // creating fake ResourceRequestDetails on the UI thread.
117 navigation_handle()->DidRedirectNavigation(new_url
);
120 void TestRenderFrameHost::SimulateNavigationCommit(const GURL
& url
) {
121 if (frame_tree_node()->navigation_request())
124 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
125 params
.page_id
= ComputeNextPageID();
126 params
.nav_entry_id
= 0;
128 params
.transition
= GetParent() ? ui::PAGE_TRANSITION_MANUAL_SUBFRAME
129 : ui::PAGE_TRANSITION_LINK
;
130 params
.should_update_history
= true;
131 params
.did_create_new_entry
= true;
132 params
.gesture
= NavigationGestureUser
;
133 params
.contents_mime_type
= contents_mime_type_
;
134 params
.is_post
= false;
135 params
.http_status_code
= 200;
136 params
.socket_address
.set_host("2001:db8::1");
137 params
.socket_address
.set_port(80);
138 params
.history_list_was_cleared
= simulate_history_list_was_cleared_
;
139 params
.original_request_url
= url
;
141 url::Replacements
<char> replacements
;
142 replacements
.ClearRef();
143 params
.was_within_same_page
=
144 url
.ReplaceComponents(replacements
) ==
145 GetLastCommittedURL().ReplaceComponents(replacements
);
147 params
.page_state
= PageState::CreateForTesting(url
, false, nullptr, nullptr);
149 SendNavigateWithParams(¶ms
);
152 void TestRenderFrameHost::SimulateNavigationError(const GURL
& url
,
154 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
155 switches::kEnableBrowserSideNavigation
)) {
156 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
157 TestNavigationURLLoader
* url_loader
=
158 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
160 url_loader
->SimulateError(error_code
);
164 FrameHostMsg_DidFailProvisionalLoadWithError_Params error_params
;
165 error_params
.error_code
= error_code
;
166 error_params
.url
= url
;
167 OnDidFailProvisionalLoadWithError(error_params
);
170 void TestRenderFrameHost::SimulateNavigationErrorPageCommit() {
171 CHECK(navigation_handle());
172 GURL error_url
= GURL(kUnreachableWebDataURL
);
173 OnDidStartProvisionalLoadForFrame(error_url
);
174 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
175 params
.page_id
= ComputeNextPageID();
176 params
.nav_entry_id
= 0;
177 params
.did_create_new_entry
= true;
178 params
.url
= navigation_handle()->GetURL();
179 params
.transition
= GetParent() ? ui::PAGE_TRANSITION_MANUAL_SUBFRAME
180 : ui::PAGE_TRANSITION_LINK
;
181 params
.was_within_same_page
= false;
182 params
.url_is_unreachable
= true;
183 params
.page_state
= PageState::CreateForTesting(navigation_handle()->GetURL(),
184 false, nullptr, nullptr);
185 SendNavigateWithParams(¶ms
);
188 void TestRenderFrameHost::SimulateNavigationStop() {
191 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
192 switches::kEnableBrowserSideNavigation
)) {
193 // Even if the RenderFrameHost is not loading, there may still be an
194 // ongoing navigation in the FrameTreeNode. Cancel this one as well.
195 frame_tree_node()->ResetNavigationRequest(false);
199 void TestRenderFrameHost::SetContentsMimeType(const std::string
& mime_type
) {
200 contents_mime_type_
= mime_type
;
203 void TestRenderFrameHost::SendBeforeUnloadACK(bool proceed
) {
204 base::TimeTicks now
= base::TimeTicks::Now();
205 OnBeforeUnloadACK(proceed
, now
, now
);
208 void TestRenderFrameHost::SimulateSwapOutACK() {
212 void TestRenderFrameHost::SendNavigate(int page_id
,
214 bool did_create_new_entry
,
216 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
217 ui::PAGE_TRANSITION_LINK
, 200,
218 ModificationCallback());
221 void TestRenderFrameHost::SendFailedNavigate(int page_id
,
223 bool did_create_new_entry
,
225 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
226 ui::PAGE_TRANSITION_RELOAD
, 500,
227 ModificationCallback());
230 void TestRenderFrameHost::SendNavigateWithTransition(
233 bool did_create_new_entry
,
235 ui::PageTransition transition
) {
236 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
237 transition
, 200, ModificationCallback());
240 void TestRenderFrameHost::SendNavigateWithModificationCallback(
243 bool did_create_new_entry
,
245 const ModificationCallback
& callback
) {
246 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
247 ui::PAGE_TRANSITION_LINK
, 200, callback
);
250 void TestRenderFrameHost::SendNavigateWithParameters(
253 bool did_create_new_entry
,
255 ui::PageTransition transition
,
257 const ModificationCallback
& callback
) {
258 // DidStartProvisionalLoad may delete the pending entry that holds |url|,
259 // so we keep a copy of it to use below.
261 OnDidStartLoading(true);
262 OnDidStartProvisionalLoadForFrame(url_copy
);
264 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
265 params
.page_id
= page_id
;
266 params
.nav_entry_id
= nav_entry_id
;
267 params
.url
= url_copy
;
268 params
.transition
= transition
;
269 params
.should_update_history
= true;
270 params
.did_create_new_entry
= did_create_new_entry
;
271 params
.gesture
= NavigationGestureUser
;
272 params
.contents_mime_type
= contents_mime_type_
;
273 params
.is_post
= false;
274 params
.http_status_code
= response_code
;
275 params
.socket_address
.set_host("2001:db8::1");
276 params
.socket_address
.set_port(80);
277 params
.history_list_was_cleared
= simulate_history_list_was_cleared_
;
278 params
.original_request_url
= url_copy
;
280 url::Replacements
<char> replacements
;
281 replacements
.ClearRef();
282 params
.was_within_same_page
=
283 transition
!= ui::PAGE_TRANSITION_RELOAD
&&
284 transition
!= ui::PAGE_TRANSITION_TYPED
&&
285 url_copy
.ReplaceComponents(replacements
) ==
286 GetLastCommittedURL().ReplaceComponents(replacements
);
289 PageState::CreateForTesting(url_copy
, false, nullptr, nullptr);
291 if (!callback
.is_null())
292 callback
.Run(¶ms
);
294 SendNavigateWithParams(¶ms
);
297 void TestRenderFrameHost::SendNavigateWithParams(
298 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
299 FrameHostMsg_DidCommitProvisionalLoad
msg(GetRoutingID(), *params
);
300 OnDidCommitProvisionalLoad(msg
);
303 void TestRenderFrameHost::NavigateAndCommitRendererInitiated(
305 bool did_create_new_entry
,
307 SendRendererInitiatedNavigationRequest(url
, false);
308 // PlzNavigate: If no network request is needed by the navigation, then there
309 // will be no NavigationRequest, nor is it necessary to simulate the network
311 if (frame_tree_node()->navigation_request())
313 bool browser_side_navigation
=
314 base::CommandLine::ForCurrentProcess()->HasSwitch(
315 switches::kEnableBrowserSideNavigation
);
316 CHECK_IMPLIES(browser_side_navigation
, is_loading());
317 CHECK_IMPLIES(browser_side_navigation
,
318 !frame_tree_node()->navigation_request());
319 SendNavigate(page_id
, 0, did_create_new_entry
, url
);
322 void TestRenderFrameHost::SendRendererInitiatedNavigationRequest(
324 bool has_user_gesture
) {
325 // Since this is renderer-initiated navigation, the RenderFrame must be
326 // initialized. Do it if it hasn't happened yet.
327 InitializeRenderFrameIfNeeded();
329 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
330 switches::kEnableBrowserSideNavigation
)) {
331 BeginNavigationParams
begin_params("GET", std::string(), net::LOAD_NORMAL
,
333 CommonNavigationParams common_params
;
334 common_params
.url
= url
;
335 common_params
.referrer
= Referrer(GURL(), blink::WebReferrerPolicyDefault
);
336 common_params
.transition
= ui::PAGE_TRANSITION_LINK
;
337 OnBeginNavigation(common_params
, begin_params
,
338 scoped_refptr
<ResourceRequestBody
>());
342 void TestRenderFrameHost::DidDisownOpener() {
346 void TestRenderFrameHost::PrepareForCommit() {
347 PrepareForCommitWithServerRedirect(GURL());
350 void TestRenderFrameHost::PrepareForCommitWithServerRedirect(
351 const GURL
& redirect_url
) {
352 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
353 switches::kEnableBrowserSideNavigation
)) {
355 if (is_waiting_for_beforeunload_ack())
356 SendBeforeUnloadACK(true);
361 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
364 // Simulate a beforeUnload ACK from the renderer if the browser is waiting for
365 // it. If it runs it will update the request state.
366 if (request
->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE
)
367 SendBeforeUnloadACK(true);
369 CHECK(request
->state() == NavigationRequest::STARTED
);
371 TestNavigationURLLoader
* url_loader
=
372 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
375 // If a non-empty |redirect_url| was provided, simulate a server redirect.
376 if (!redirect_url
.is_empty())
377 url_loader
->SimulateServerRedirect(redirect_url
);
379 // Simulate the network stack commit.
380 scoped_refptr
<ResourceResponse
> response(new ResourceResponse
);
381 // TODO(carlosk): ideally with PlzNavigate it should be possible someday to
382 // fully commit the navigation at this call to CallOnResponseStarted.
383 url_loader
->CallOnResponseStarted(response
, MakeEmptyStream());
386 int32
TestRenderFrameHost::ComputeNextPageID() {
387 const NavigationEntryImpl
* entry
= static_cast<NavigationEntryImpl
*>(
388 frame_tree_node()->navigator()->GetController()->GetPendingEntry());
389 DCHECK_IMPLIES(entry
&& entry
->site_instance(),
390 entry
->site_instance() == GetSiteInstance());
391 // Entry can be null when committing an error page (the pending entry was
392 // cleared during DidFailProvisionalLoad).
393 int page_id
= entry
? entry
->GetPageID() : -1;
395 WebContentsImpl
* web_contents
= static_cast<WebContentsImpl
*>(delegate());
396 page_id
= web_contents
->GetMaxPageIDForSiteInstance(GetSiteInstance()) + 1;
401 } // namespace content