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
,
50 int32 widget_routing_id
,
53 : RenderFrameHostImpl(site_instance
,
63 child_creation_observer_(delegate
? delegate
->GetAsWebContents() : NULL
),
64 contents_mime_type_("text/html"),
65 simulate_history_list_was_cleared_(false) {}
67 TestRenderFrameHost::~TestRenderFrameHost() {
70 TestRenderViewHost
* TestRenderFrameHost::GetRenderViewHost() {
71 return static_cast<TestRenderViewHost
*>(
72 RenderFrameHostImpl::GetRenderViewHost());
75 MockRenderProcessHost
* TestRenderFrameHost::GetProcess() {
76 return static_cast<MockRenderProcessHost
*>(RenderFrameHostImpl::GetProcess());
79 void TestRenderFrameHost::InitializeRenderFrameIfNeeded() {
80 if (!render_view_host()->IsRenderViewLive()) {
81 RenderViewHostTester::For(render_view_host())->CreateTestRenderView(
82 base::string16(), MSG_ROUTING_NONE
, MSG_ROUTING_NONE
, -1, false);
86 TestRenderFrameHost
* TestRenderFrameHost::AppendChild(
87 const std::string
& frame_name
) {
88 OnCreateChildFrame(GetProcess()->GetNextRoutingID(),
89 blink::WebTreeScopeType::Document
, frame_name
,
90 blink::WebSandboxFlags::None
);
91 return static_cast<TestRenderFrameHost
*>(
92 child_creation_observer_
.last_created_frame());
95 void TestRenderFrameHost::SimulateNavigationStart(const GURL
& url
) {
96 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
97 switches::kEnableBrowserSideNavigation
)) {
98 SendRendererInitiatedNavigationRequest(url
, false);
102 OnDidStartLoading(true);
103 OnDidStartProvisionalLoadForFrame(url
);
106 void TestRenderFrameHost::SimulateRedirect(const GURL
& new_url
) {
107 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
108 switches::kEnableBrowserSideNavigation
)) {
109 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
110 TestNavigationURLLoader
* url_loader
=
111 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
113 url_loader
->SimulateServerRedirect(new_url
);
117 // Note that this does not simulate
118 // WebContentsImpl::DidGetRedirectForResourceRequest due to the difficulty in
119 // creating fake ResourceRequestDetails on the UI thread.
120 navigation_handle()->DidRedirectNavigation(new_url
);
123 void TestRenderFrameHost::SimulateNavigationCommit(const GURL
& url
) {
124 if (frame_tree_node()->navigation_request())
127 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
128 params
.page_id
= ComputeNextPageID();
129 params
.nav_entry_id
= 0;
131 params
.transition
= GetParent() ? ui::PAGE_TRANSITION_MANUAL_SUBFRAME
132 : ui::PAGE_TRANSITION_LINK
;
133 params
.should_update_history
= true;
134 params
.did_create_new_entry
= true;
135 params
.gesture
= NavigationGestureUser
;
136 params
.contents_mime_type
= contents_mime_type_
;
137 params
.is_post
= false;
138 params
.http_status_code
= 200;
139 params
.socket_address
.set_host("2001:db8::1");
140 params
.socket_address
.set_port(80);
141 params
.history_list_was_cleared
= simulate_history_list_was_cleared_
;
142 params
.original_request_url
= url
;
144 url::Replacements
<char> replacements
;
145 replacements
.ClearRef();
146 params
.was_within_same_page
=
147 url
.ReplaceComponents(replacements
) ==
148 GetLastCommittedURL().ReplaceComponents(replacements
);
150 params
.page_state
= PageState::CreateForTesting(url
, false, nullptr, nullptr);
152 SendNavigateWithParams(¶ms
);
155 void TestRenderFrameHost::SimulateNavigationError(const GURL
& url
,
157 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
158 switches::kEnableBrowserSideNavigation
)) {
159 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
160 TestNavigationURLLoader
* url_loader
=
161 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
163 url_loader
->SimulateError(error_code
);
167 FrameHostMsg_DidFailProvisionalLoadWithError_Params error_params
;
168 error_params
.error_code
= error_code
;
169 error_params
.url
= url
;
170 OnDidFailProvisionalLoadWithError(error_params
);
173 void TestRenderFrameHost::SimulateNavigationErrorPageCommit() {
174 CHECK(navigation_handle());
175 GURL error_url
= GURL(kUnreachableWebDataURL
);
176 OnDidStartProvisionalLoadForFrame(error_url
);
177 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
178 params
.page_id
= ComputeNextPageID();
179 params
.nav_entry_id
= 0;
180 params
.did_create_new_entry
= true;
181 params
.url
= navigation_handle()->GetURL();
182 params
.transition
= GetParent() ? ui::PAGE_TRANSITION_MANUAL_SUBFRAME
183 : ui::PAGE_TRANSITION_LINK
;
184 params
.was_within_same_page
= false;
185 params
.url_is_unreachable
= true;
186 params
.page_state
= PageState::CreateForTesting(navigation_handle()->GetURL(),
187 false, nullptr, nullptr);
188 SendNavigateWithParams(¶ms
);
191 void TestRenderFrameHost::SimulateNavigationStop() {
194 } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
195 switches::kEnableBrowserSideNavigation
)) {
196 // Even if the RenderFrameHost is not loading, there may still be an
197 // ongoing navigation in the FrameTreeNode. Cancel this one as well.
198 frame_tree_node()->ResetNavigationRequest(false);
202 void TestRenderFrameHost::SetContentsMimeType(const std::string
& mime_type
) {
203 contents_mime_type_
= mime_type
;
206 void TestRenderFrameHost::SendBeforeUnloadACK(bool proceed
) {
207 base::TimeTicks now
= base::TimeTicks::Now();
208 OnBeforeUnloadACK(proceed
, now
, now
);
211 void TestRenderFrameHost::SimulateSwapOutACK() {
215 void TestRenderFrameHost::SendNavigate(int page_id
,
217 bool did_create_new_entry
,
219 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
220 ui::PAGE_TRANSITION_LINK
, 200,
221 ModificationCallback());
224 void TestRenderFrameHost::SendFailedNavigate(int page_id
,
226 bool did_create_new_entry
,
228 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
229 ui::PAGE_TRANSITION_RELOAD
, 500,
230 ModificationCallback());
233 void TestRenderFrameHost::SendNavigateWithTransition(
236 bool did_create_new_entry
,
238 ui::PageTransition transition
) {
239 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
240 transition
, 200, ModificationCallback());
243 void TestRenderFrameHost::SendNavigateWithModificationCallback(
246 bool did_create_new_entry
,
248 const ModificationCallback
& callback
) {
249 SendNavigateWithParameters(page_id
, nav_entry_id
, did_create_new_entry
, url
,
250 ui::PAGE_TRANSITION_LINK
, 200, callback
);
253 void TestRenderFrameHost::SendNavigateWithParameters(
256 bool did_create_new_entry
,
258 ui::PageTransition transition
,
260 const ModificationCallback
& callback
) {
261 // DidStartProvisionalLoad may delete the pending entry that holds |url|,
262 // so we keep a copy of it to use below.
264 OnDidStartLoading(true);
265 OnDidStartProvisionalLoadForFrame(url_copy
);
267 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
268 params
.page_id
= page_id
;
269 params
.nav_entry_id
= nav_entry_id
;
270 params
.url
= url_copy
;
271 params
.transition
= transition
;
272 params
.should_update_history
= true;
273 params
.did_create_new_entry
= did_create_new_entry
;
274 params
.gesture
= NavigationGestureUser
;
275 params
.contents_mime_type
= contents_mime_type_
;
276 params
.is_post
= false;
277 params
.http_status_code
= response_code
;
278 params
.socket_address
.set_host("2001:db8::1");
279 params
.socket_address
.set_port(80);
280 params
.history_list_was_cleared
= simulate_history_list_was_cleared_
;
281 params
.original_request_url
= url_copy
;
283 url::Replacements
<char> replacements
;
284 replacements
.ClearRef();
285 params
.was_within_same_page
=
286 transition
!= ui::PAGE_TRANSITION_RELOAD
&&
287 transition
!= ui::PAGE_TRANSITION_TYPED
&&
288 url_copy
.ReplaceComponents(replacements
) ==
289 GetLastCommittedURL().ReplaceComponents(replacements
);
292 PageState::CreateForTesting(url_copy
, false, nullptr, nullptr);
294 if (!callback
.is_null())
295 callback
.Run(¶ms
);
297 SendNavigateWithParams(¶ms
);
300 void TestRenderFrameHost::SendNavigateWithParams(
301 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
302 FrameHostMsg_DidCommitProvisionalLoad
msg(GetRoutingID(), *params
);
303 OnDidCommitProvisionalLoad(msg
);
306 void TestRenderFrameHost::NavigateAndCommitRendererInitiated(
308 bool did_create_new_entry
,
310 SendRendererInitiatedNavigationRequest(url
, false);
311 // PlzNavigate: If no network request is needed by the navigation, then there
312 // will be no NavigationRequest, nor is it necessary to simulate the network
314 if (frame_tree_node()->navigation_request())
316 bool browser_side_navigation
=
317 base::CommandLine::ForCurrentProcess()->HasSwitch(
318 switches::kEnableBrowserSideNavigation
);
319 CHECK_IMPLIES(browser_side_navigation
, is_loading());
320 CHECK_IMPLIES(browser_side_navigation
,
321 !frame_tree_node()->navigation_request());
322 SendNavigate(page_id
, 0, did_create_new_entry
, url
);
325 void TestRenderFrameHost::SendRendererInitiatedNavigationRequest(
327 bool has_user_gesture
) {
328 // Since this is renderer-initiated navigation, the RenderFrame must be
329 // initialized. Do it if it hasn't happened yet.
330 InitializeRenderFrameIfNeeded();
332 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
333 switches::kEnableBrowserSideNavigation
)) {
334 BeginNavigationParams
begin_params("GET", std::string(), net::LOAD_NORMAL
,
336 CommonNavigationParams common_params
;
337 common_params
.url
= url
;
338 common_params
.referrer
= Referrer(GURL(), blink::WebReferrerPolicyDefault
);
339 common_params
.transition
= ui::PAGE_TRANSITION_LINK
;
340 OnBeginNavigation(common_params
, begin_params
,
341 scoped_refptr
<ResourceRequestBody
>());
345 void TestRenderFrameHost::DidChangeOpener(int opener_routing_id
) {
346 OnDidChangeOpener(opener_routing_id
);
349 void TestRenderFrameHost::PrepareForCommit() {
350 PrepareForCommitWithServerRedirect(GURL());
353 void TestRenderFrameHost::PrepareForCommitWithServerRedirect(
354 const GURL
& redirect_url
) {
355 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
356 switches::kEnableBrowserSideNavigation
)) {
358 if (is_waiting_for_beforeunload_ack())
359 SendBeforeUnloadACK(true);
364 NavigationRequest
* request
= frame_tree_node_
->navigation_request();
367 // Simulate a beforeUnload ACK from the renderer if the browser is waiting for
368 // it. If it runs it will update the request state.
369 if (request
->state() == NavigationRequest::WAITING_FOR_RENDERER_RESPONSE
)
370 SendBeforeUnloadACK(true);
372 CHECK(request
->state() == NavigationRequest::STARTED
);
374 TestNavigationURLLoader
* url_loader
=
375 static_cast<TestNavigationURLLoader
*>(request
->loader_for_testing());
378 // If a non-empty |redirect_url| was provided, simulate a server redirect.
379 if (!redirect_url
.is_empty())
380 url_loader
->SimulateServerRedirect(redirect_url
);
382 // Simulate the network stack commit.
383 scoped_refptr
<ResourceResponse
> response(new ResourceResponse
);
384 // TODO(carlosk): ideally with PlzNavigate it should be possible someday to
385 // fully commit the navigation at this call to CallOnResponseStarted.
386 url_loader
->CallOnResponseStarted(response
, MakeEmptyStream());
389 int32
TestRenderFrameHost::ComputeNextPageID() {
390 const NavigationEntryImpl
* entry
= static_cast<NavigationEntryImpl
*>(
391 frame_tree_node()->navigator()->GetController()->GetPendingEntry());
392 DCHECK_IMPLIES(entry
&& entry
->site_instance(),
393 entry
->site_instance() == GetSiteInstance());
394 // Entry can be null when committing an error page (the pending entry was
395 // cleared during DidFailProvisionalLoad).
396 int page_id
= entry
? entry
->GetPageID() : -1;
398 WebContentsImpl
* web_contents
= static_cast<WebContentsImpl
*>(delegate());
399 page_id
= web_contents
->GetMaxPageIDForSiteInstance(GetSiteInstance()) + 1;
404 } // namespace content