1 // Copyright 2014 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.
6 #include "base/command_line.h"
7 #include "base/strings/stringprintf.h"
8 #include "content/browser/frame_host/frame_navigation_entry.h"
9 #include "content/browser/frame_host/frame_tree.h"
10 #include "content/browser/frame_host/navigation_controller_impl.h"
11 #include "content/browser/frame_host/navigation_entry_impl.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/public/browser/render_view_host.h"
14 #include "content/public/browser/resource_controller.h"
15 #include "content/public/browser/resource_dispatcher_host.h"
16 #include "content/public/browser/resource_dispatcher_host_delegate.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_observer.h"
20 #include "content/public/common/bindings_policy.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/public/test/browser_test_utils.h"
24 #include "content/public/test/content_browser_test.h"
25 #include "content/public/test/content_browser_test_utils.h"
26 #include "content/public/test/test_navigation_observer.h"
27 #include "content/public/test/test_utils.h"
28 #include "content/shell/browser/shell.h"
29 #include "content/test/content_browser_test_utils_internal.h"
30 #include "net/dns/mock_host_resolver.h"
31 #include "net/test/embedded_test_server/embedded_test_server.h"
32 #include "net/test/url_request/url_request_failed_job.h"
36 class NavigationControllerBrowserTest
: public ContentBrowserTest
{
38 void SetUpOnMainThread() override
{
39 host_resolver()->AddRule("*", "127.0.0.1");
40 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
44 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
, LoadDataWithBaseURL
) {
45 const GURL
base_url("http://baseurl");
46 const GURL
history_url("http://historyurl");
47 const std::string data
= "<html><body>foo</body></html>";
49 const NavigationController
& controller
=
50 shell()->web_contents()->GetController();
51 // Load data. Blocks until it is done.
52 content::LoadDataWithBaseURL(shell(), history_url
, data
, base_url
);
54 // We should use history_url instead of the base_url as the original url of
55 // this navigation entry, because base_url is only used for resolving relative
56 // paths in the data, or enforcing same origin policy.
57 EXPECT_EQ(controller
.GetVisibleEntry()->GetOriginalRequestURL(), history_url
);
60 // The renderer uses the position in the history list as a clue to whether a
61 // navigation is stale. In the case where the entry limit is reached and the
62 // history list is pruned, make sure that there is no mismatch that would cause
63 // it to start incorrectly rejecting navigations as stale. See
64 // http://crbug.com/89798.
65 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
66 DontIgnoreBackAfterNavEntryLimit
) {
67 NavigationController
& controller
=
68 shell()->web_contents()->GetController();
70 const int kMaxEntryCount
=
71 static_cast<int>(NavigationControllerImpl::max_entry_count());
73 // Load up to the max count, all entries should be there.
74 for (int url_index
= 0; url_index
< kMaxEntryCount
; ++url_index
) {
75 GURL
url(base::StringPrintf("data:text/html,page%d", url_index
));
76 EXPECT_TRUE(NavigateToURL(shell(), url
));
79 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
81 // Navigate twice more more.
82 for (int url_index
= kMaxEntryCount
;
83 url_index
< kMaxEntryCount
+ 2; ++url_index
) {
84 GURL
url(base::StringPrintf("data:text/html,page%d", url_index
));
85 EXPECT_TRUE(NavigateToURL(shell(), url
));
88 // We expect page0 and page1 to be gone.
89 EXPECT_EQ(kMaxEntryCount
, controller
.GetEntryCount());
90 EXPECT_EQ(GURL("data:text/html,page2"),
91 controller
.GetEntryAtIndex(0)->GetURL());
93 // Now try to go back. This should not hang.
94 ASSERT_TRUE(controller
.CanGoBack());
96 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
98 // This should have successfully gone back.
99 EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount
)),
100 controller
.GetLastCommittedEntry()->GetURL());
105 int RendererHistoryLength(Shell
* shell
) {
107 EXPECT_TRUE(ExecuteScriptAndExtractInt(
108 shell
->web_contents(),
109 "domAutomationController.send(history.length)",
114 // Similar to the ones from content_browser_test_utils.
115 bool NavigateToURLAndReplace(Shell
* shell
, const GURL
& url
) {
116 WebContents
* web_contents
= shell
->web_contents();
117 WaitForLoadStop(web_contents
);
118 TestNavigationObserver
same_tab_observer(web_contents
, 1);
119 NavigationController::LoadURLParams
params(url
);
120 params
.should_replace_current_entry
= true;
121 web_contents
->GetController().LoadURLWithParams(params
);
122 web_contents
->Focus();
123 same_tab_observer
.Wait();
124 if (!IsLastCommittedEntryOfPageType(web_contents
, PAGE_TYPE_NORMAL
))
126 return web_contents
->GetLastCommittedURL() == url
;
131 // When loading a new page to replace an old page in the history list, make sure
132 // that the browser and renderer agree, and that both get it right.
133 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
134 CorrectLengthWithCurrentItemReplacement
) {
135 NavigationController
& controller
=
136 shell()->web_contents()->GetController();
138 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
139 EXPECT_EQ(1, controller
.GetEntryCount());
140 EXPECT_EQ(1, RendererHistoryLength(shell()));
142 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page2")));
143 EXPECT_EQ(1, controller
.GetEntryCount());
144 EXPECT_EQ(1, RendererHistoryLength(shell()));
146 // Note that there's no way to access the renderer's notion of the history
147 // offset via JavaScript. Checking just the history length, though, is enough;
148 // if the replacement failed, there would be a new history entry and thus an
152 // When spawning a new page from a WebUI page, make sure that the browser and
153 // renderer agree about the length of the history list, and that both get it
155 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
156 CorrectLengthWithNewTabNavigatingFromWebUI
) {
157 GURL
web_ui_page(std::string(kChromeUIScheme
) + "://" +
158 std::string(kChromeUIGpuHost
));
159 EXPECT_TRUE(NavigateToURL(shell(), web_ui_page
));
160 EXPECT_EQ(BINDINGS_POLICY_WEB_UI
,
161 shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
163 ShellAddedObserver observer
;
164 std::string page_url
= embedded_test_server()->GetURL(
165 "/navigation_controller/simple_page_1.html").spec();
166 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
167 "window.open('" + page_url
+ "', '_blank')"));
168 Shell
* shell2
= observer
.GetShell();
169 EXPECT_TRUE(WaitForLoadStop(shell2
->web_contents()));
171 EXPECT_EQ(1, shell2
->web_contents()->GetController().GetEntryCount());
172 EXPECT_EQ(1, RendererHistoryLength(shell2
));
174 // Again, as above, there's no way to access the renderer's notion of the
175 // history offset via JavaScript. Checking just the history length, again,
176 // will have to suffice.
181 class NoNavigationsObserver
: public WebContentsObserver
{
183 // Observes navigation for the specified |web_contents|.
184 explicit NoNavigationsObserver(WebContents
* web_contents
)
185 : WebContentsObserver(web_contents
) {}
188 void DidNavigateAnyFrame(RenderFrameHost
* render_frame_host
,
189 const LoadCommittedDetails
& details
,
190 const FrameNavigateParams
& params
) override
{
191 FAIL() << "No navigations should occur";
197 // Some pages create a popup, then write an iframe into it. This causes a
198 // subframe navigation without having any committed entry. Such navigations
199 // just get thrown on the ground, but we shouldn't crash.
201 // This test actually hits NAVIGATION_TYPE_NAV_IGNORE three times. Two of them,
202 // the initial window.open() and the iframe creation, don't try to create
203 // navigation entries, and the third, the new navigation, tries to.
204 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
, SubframeOnEmptyPage
) {
205 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
206 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
208 FrameTreeNode
* root
=
209 static_cast<WebContentsImpl
*>(shell()->web_contents())->
210 GetFrameTree()->root();
212 // Pop open a new window.
213 ShellAddedObserver new_shell_observer
;
214 std::string script
= "window.open()";
215 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
216 Shell
* new_shell
= new_shell_observer
.GetShell();
217 ASSERT_NE(new_shell
->web_contents(), shell()->web_contents());
218 FrameTreeNode
* new_root
=
219 static_cast<WebContentsImpl
*>(new_shell
->web_contents())->
220 GetFrameTree()->root();
222 // Make a new iframe in it.
223 NoNavigationsObserver
observer(new_shell
->web_contents());
224 script
= "var iframe = document.createElement('iframe');"
225 "iframe.src = 'data:text/html,<p>some page</p>';"
226 "document.body.appendChild(iframe);";
227 EXPECT_TRUE(content::ExecuteScript(new_root
->current_frame_host(), script
));
228 // The success check is of the last-committed entry, and there is none.
229 WaitForLoadStopWithoutSuccessCheck(new_shell
->web_contents());
231 ASSERT_EQ(1U, new_root
->child_count());
232 ASSERT_NE(nullptr, new_root
->child_at(0));
235 GURL frame_url
= embedded_test_server()->GetURL(
236 "/navigation_controller/simple_page_2.html");
237 script
= "location.assign('" + frame_url
.spec() + "')";
238 EXPECT_TRUE(content::ExecuteScript(
239 new_root
->child_at(0)->current_frame_host(), script
));
241 // Success is not crashing, and not navigating.
243 new_shell
->web_contents()->GetController().GetLastCommittedEntry());
248 class FrameNavigateParamsCapturer
: public WebContentsObserver
{
250 // Observes navigation for the specified |node|.
251 explicit FrameNavigateParamsCapturer(FrameTreeNode
* node
)
252 : WebContentsObserver(
253 node
->current_frame_host()->delegate()->GetAsWebContents()),
254 frame_tree_node_id_(node
->frame_tree_node_id()),
255 navigations_remaining_(1),
256 wait_for_load_(true),
257 message_loop_runner_(new MessageLoopRunner
) {}
259 void set_navigations_remaining(int count
) {
260 navigations_remaining_
= count
;
263 void set_wait_for_load(bool ignore
) {
264 wait_for_load_
= ignore
;
268 message_loop_runner_
->Run();
271 const FrameNavigateParams
& params() const {
272 EXPECT_EQ(1U, params_
.size());
276 const std::vector
<FrameNavigateParams
>& all_params() const {
280 const LoadCommittedDetails
& details() const {
281 EXPECT_EQ(1U, details_
.size());
285 const std::vector
<LoadCommittedDetails
>& all_details() const {
290 void DidNavigateAnyFrame(RenderFrameHost
* render_frame_host
,
291 const LoadCommittedDetails
& details
,
292 const FrameNavigateParams
& params
) override
{
293 RenderFrameHostImpl
* rfh
=
294 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
295 if (rfh
->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_
)
298 --navigations_remaining_
;
299 params_
.push_back(params
);
300 details_
.push_back(details
);
301 if (!navigations_remaining_
&&
302 (!web_contents()->IsLoading() || !wait_for_load_
))
303 message_loop_runner_
->Quit();
306 void DidStopLoading() override
{
307 if (!navigations_remaining_
)
308 message_loop_runner_
->Quit();
311 // The id of the FrameTreeNode whose navigations to observe.
312 int frame_tree_node_id_
;
314 // How many navigations remain to capture.
315 int navigations_remaining_
;
317 // Whether to also wait for the load to complete.
320 // The params of the navigations.
321 std::vector
<FrameNavigateParams
> params_
;
323 // The details of the navigations.
324 std::vector
<LoadCommittedDetails
> details_
;
326 // The MessageLoopRunner used to spin the message loop.
327 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
330 class LoadCommittedCapturer
: public WebContentsObserver
{
332 // Observes the load commit for the specified |node|.
333 explicit LoadCommittedCapturer(FrameTreeNode
* node
)
334 : WebContentsObserver(
335 node
->current_frame_host()->delegate()->GetAsWebContents()),
336 frame_tree_node_id_(node
->frame_tree_node_id()),
337 message_loop_runner_(new MessageLoopRunner
) {}
339 // Observes the load commit for the next created frame in the specified
341 explicit LoadCommittedCapturer(WebContents
* web_contents
)
342 : WebContentsObserver(web_contents
),
343 frame_tree_node_id_(0),
344 message_loop_runner_(new MessageLoopRunner
) {}
347 message_loop_runner_
->Run();
350 ui::PageTransition
transition_type() const {
351 return transition_type_
;
355 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
356 // If this object was created with a specified tree frame node, there
357 // shouldn't be any frames being created.
358 DCHECK_EQ(0, frame_tree_node_id_
);
359 RenderFrameHostImpl
* rfh
=
360 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
361 frame_tree_node_id_
= rfh
->frame_tree_node()->frame_tree_node_id();
364 void DidCommitProvisionalLoadForFrame(
365 RenderFrameHost
* render_frame_host
,
367 ui::PageTransition transition_type
) override
{
368 DCHECK_NE(0, frame_tree_node_id_
);
369 RenderFrameHostImpl
* rfh
=
370 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
371 if (rfh
->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_
)
374 transition_type_
= transition_type
;
375 if (!web_contents()->IsLoading())
376 message_loop_runner_
->Quit();
379 void DidStopLoading() override
{ message_loop_runner_
->Quit(); }
381 // The id of the FrameTreeNode whose navigations to observe.
382 int frame_tree_node_id_
;
384 // The transition_type of the last navigation.
385 ui::PageTransition transition_type_
;
387 // The MessageLoopRunner used to spin the message loop.
388 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
393 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
394 ErrorPageReplacement
) {
395 NavigationController
& controller
= shell()->web_contents()->GetController();
397 net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET
));
398 net::URLRequestFailedJob::AddUrlHandler();
400 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
401 EXPECT_EQ(1, controller
.GetEntryCount());
403 FrameTreeNode
* root
=
404 static_cast<WebContentsImpl
*>(shell()->web_contents())->
405 GetFrameTree()->root();
407 // Navigate to a page that fails to load. It must result in an error page, the
408 // NEW_PAGE navigation type, and an addition to the history list.
410 FrameNavigateParamsCapturer
capturer(root
);
411 NavigateFrameToURL(root
, error_url
);
413 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
414 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
415 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
416 EXPECT_EQ(2, controller
.GetEntryCount());
419 // Navigate again to the page that fails to load. It must result in an error
420 // page, the EXISTING_PAGE navigation type, and no addition to the history
421 // list. We do not use SAME_PAGE here; that case only differs in that it
422 // clears the pending entry, and there is no pending entry after a load
425 FrameNavigateParamsCapturer
capturer(root
);
426 NavigateFrameToURL(root
, error_url
);
428 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
429 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
430 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
431 EXPECT_EQ(2, controller
.GetEntryCount());
434 // Make a new entry ...
435 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
436 EXPECT_EQ(3, controller
.GetEntryCount());
438 // ... and replace it with a failed load. (Note that when you set the
439 // should_replace_current_entry flag, the navigation is classified as NEW_PAGE
440 // because that is a classification of the renderer's behavior, and the flag
441 // is a browser-side flag.)
443 FrameNavigateParamsCapturer
capturer(root
);
444 NavigateToURLAndReplace(shell(), error_url
);
446 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
447 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
448 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
449 EXPECT_EQ(3, controller
.GetEntryCount());
452 // Make a new web ui page to force a process swap ...
453 GURL
web_ui_page(std::string(kChromeUIScheme
) + "://" +
454 std::string(kChromeUIGpuHost
));
455 NavigateToURL(shell(), web_ui_page
);
456 EXPECT_EQ(4, controller
.GetEntryCount());
458 // ... and replace it with a failed load. (It is NEW_PAGE for the reason noted
461 FrameNavigateParamsCapturer
capturer(root
);
462 NavigateToURLAndReplace(shell(), error_url
);
464 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
465 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
466 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
467 EXPECT_EQ(4, controller
.GetEntryCount());
471 // Various tests for navigation type classifications. TODO(avi): It's rather
472 // bogus that the same info is in two different enums; http://crbug.com/453555.
474 // Verify that navigations for NAVIGATION_TYPE_NEW_PAGE are correctly
476 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
477 NavigationTypeClassification_NewPage
) {
478 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
480 FrameTreeNode
* root
=
481 static_cast<WebContentsImpl
*>(shell()->web_contents())->
482 GetFrameTree()->root();
486 FrameNavigateParamsCapturer
capturer(root
);
487 GURL
frame_url(embedded_test_server()->GetURL(
488 "/navigation_controller/page_with_links.html"));
489 NavigateFrameToURL(root
, frame_url
);
491 // TODO(avi,creis): Why is this (and quite a few others below) a "link"
492 // transition? Lots of these transitions should be cleaned up.
493 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
494 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
498 // Load via a fragment link click.
499 FrameNavigateParamsCapturer
capturer(root
);
500 std::string script
= "document.getElementById('fraglink').click()";
501 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
503 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
504 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
508 // Load via link click.
509 FrameNavigateParamsCapturer
capturer(root
);
510 std::string script
= "document.getElementById('thelink').click()";
511 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
513 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
514 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
518 // location.assign().
519 FrameNavigateParamsCapturer
capturer(root
);
520 GURL
frame_url(embedded_test_server()->GetURL(
521 "/navigation_controller/simple_page_2.html"));
522 std::string script
= "location.assign('" + frame_url
.spec() + "')";
523 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
525 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
526 capturer
.params().transition
);
527 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
531 // history.pushState().
532 FrameNavigateParamsCapturer
capturer(root
);
534 "history.pushState({}, 'page 1', 'simple_page_1.html')";
535 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
537 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
538 capturer
.params().transition
);
539 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
543 // Verify that navigations for NAVIGATION_TYPE_EXISTING_PAGE are correctly
545 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
546 NavigationTypeClassification_ExistingPage
) {
547 GURL
url1(embedded_test_server()->GetURL(
548 "/navigation_controller/simple_page_1.html"));
549 NavigateToURL(shell(), url1
);
550 GURL
url2(embedded_test_server()->GetURL(
551 "/navigation_controller/simple_page_2.html"));
552 NavigateToURL(shell(), url2
);
554 FrameTreeNode
* root
=
555 static_cast<WebContentsImpl
*>(shell()->web_contents())->
556 GetFrameTree()->root();
559 // Back from the browser side.
560 FrameNavigateParamsCapturer
capturer(root
);
561 shell()->web_contents()->GetController().GoBack();
563 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
564 | ui::PAGE_TRANSITION_FORWARD_BACK
565 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
566 capturer
.params().transition
);
567 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
571 // Forward from the browser side.
572 FrameNavigateParamsCapturer
capturer(root
);
573 shell()->web_contents()->GetController().GoForward();
575 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
576 | ui::PAGE_TRANSITION_FORWARD_BACK
577 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
578 capturer
.params().transition
);
579 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
583 // Back from the renderer side.
584 FrameNavigateParamsCapturer
capturer(root
);
585 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
588 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
589 | ui::PAGE_TRANSITION_FORWARD_BACK
590 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
591 capturer
.params().transition
);
592 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
596 // Forward from the renderer side.
597 FrameNavigateParamsCapturer
capturer(root
);
598 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
599 "history.forward()"));
601 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
602 | ui::PAGE_TRANSITION_FORWARD_BACK
603 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
604 capturer
.params().transition
);
605 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
609 // Back from the renderer side via history.go().
610 FrameNavigateParamsCapturer
capturer(root
);
611 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
614 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
615 | ui::PAGE_TRANSITION_FORWARD_BACK
616 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
617 capturer
.params().transition
);
618 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
622 // Forward from the renderer side via history.go().
623 FrameNavigateParamsCapturer
capturer(root
);
624 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
627 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
628 | ui::PAGE_TRANSITION_FORWARD_BACK
629 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
630 capturer
.params().transition
);
631 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
635 // Reload from the browser side.
636 FrameNavigateParamsCapturer
capturer(root
);
637 shell()->web_contents()->GetController().Reload(false);
639 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
, capturer
.params().transition
);
640 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
644 // Reload from the renderer side.
645 FrameNavigateParamsCapturer
capturer(root
);
646 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
647 "location.reload()"));
649 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
650 capturer
.params().transition
);
651 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
655 // location.replace().
656 FrameNavigateParamsCapturer
capturer(root
);
657 GURL
frame_url(embedded_test_server()->GetURL(
658 "/navigation_controller/simple_page_1.html"));
659 std::string script
= "location.replace('" + frame_url
.spec() + "')";
660 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
662 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
663 capturer
.params().transition
);
664 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
668 // Verify that navigations for NAVIGATION_TYPE_SAME_PAGE are correctly
670 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
671 NavigationTypeClassification_SamePage
) {
672 GURL
url1(embedded_test_server()->GetURL(
673 "/navigation_controller/simple_page_1.html"));
674 NavigateToURL(shell(), url1
);
676 FrameTreeNode
* root
=
677 static_cast<WebContentsImpl
*>(shell()->web_contents())->
678 GetFrameTree()->root();
682 FrameNavigateParamsCapturer
capturer(root
);
683 GURL
frame_url(embedded_test_server()->GetURL(
684 "/navigation_controller/simple_page_1.html"));
685 NavigateFrameToURL(root
, frame_url
);
687 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
688 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, capturer
.details().type
);
692 // Verify that navigations for NAVIGATION_TYPE_IN_PAGE are correctly
694 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
695 NavigationTypeClassification_InPage
) {
696 GURL
url1(embedded_test_server()->GetURL(
697 "/navigation_controller/simple_page_1.html"));
698 NavigateToURL(shell(), url1
);
700 FrameTreeNode
* root
=
701 static_cast<WebContentsImpl
*>(shell()->web_contents())->
702 GetFrameTree()->root();
705 // history.replaceState().
706 FrameNavigateParamsCapturer
capturer(root
);
708 "history.replaceState({}, 'page 1', 'simple_page_2.html')";
709 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
711 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
712 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
715 // Back and forward across a fragment navigation.
717 GURL
url2(embedded_test_server()->GetURL(
718 "/navigation_controller/page_with_links.html"));
719 NavigateToURL(shell(), url2
);
720 std::string script
= "document.getElementById('fraglink').click()";
721 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
722 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
726 FrameNavigateParamsCapturer
capturer(root
);
727 shell()->web_contents()->GetController().GoBack();
729 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
730 | ui::PAGE_TRANSITION_FORWARD_BACK
731 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
732 capturer
.params().transition
);
733 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
738 FrameNavigateParamsCapturer
capturer(root
);
739 shell()->web_contents()->GetController().GoForward();
741 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_FORWARD_BACK
,
742 capturer
.params().transition
);
743 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
746 // Back and forward across a pushState-created navigation.
748 NavigateToURL(shell(), url1
);
749 script
= "history.pushState({}, 'page 2', 'simple_page_2.html')";
750 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
751 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
755 FrameNavigateParamsCapturer
capturer(root
);
756 shell()->web_contents()->GetController().GoBack();
758 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
759 | ui::PAGE_TRANSITION_FORWARD_BACK
760 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
761 capturer
.params().transition
);
762 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
767 FrameNavigateParamsCapturer
capturer(root
);
768 shell()->web_contents()->GetController().GoForward();
770 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_FORWARD_BACK
,
771 capturer
.params().transition
);
772 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
776 // Verify that navigations for NAVIGATION_TYPE_NEW_SUBFRAME and
777 // NAVIGATION_TYPE_AUTO_SUBFRAME are properly classified.
778 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
779 NavigationTypeClassification_NewAndAutoSubframe
) {
780 GURL
main_url(embedded_test_server()->GetURL(
781 "/navigation_controller/page_with_iframe.html"));
782 NavigateToURL(shell(), main_url
);
784 // It is safe to obtain the root frame tree node here, as it doesn't change.
785 FrameTreeNode
* root
=
786 static_cast<WebContentsImpl
*>(shell()->web_contents())->
787 GetFrameTree()->root();
789 ASSERT_EQ(1U, root
->child_count());
790 ASSERT_NE(nullptr, root
->child_at(0));
794 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
795 GURL
frame_url(embedded_test_server()->GetURL(
796 "/navigation_controller/simple_page_1.html"));
797 NavigateFrameToURL(root
->child_at(0), frame_url
);
799 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
800 capturer
.params().transition
);
801 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
806 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
807 shell()->web_contents()->GetController().GoBack();
809 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.params().transition
);
810 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME
, capturer
.details().type
);
815 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
816 shell()->web_contents()->GetController().GoForward();
818 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.params().transition
);
819 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME
, capturer
.details().type
);
824 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
825 GURL
frame_url(embedded_test_server()->GetURL(
826 "/navigation_controller/page_with_links.html"));
827 NavigateFrameToURL(root
->child_at(0), frame_url
);
829 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
830 capturer
.params().transition
);
831 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
835 // Load via a fragment link click.
836 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
837 std::string script
= "document.getElementById('fraglink').click()";
838 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
841 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
842 capturer
.params().transition
);
843 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
847 // location.assign().
848 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
849 GURL
frame_url(embedded_test_server()->GetURL(
850 "/navigation_controller/simple_page_1.html"));
851 std::string script
= "location.assign('" + frame_url
.spec() + "')";
852 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
855 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
856 capturer
.params().transition
);
857 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
861 // location.replace().
862 LoadCommittedCapturer
capturer(root
->child_at(0));
863 GURL
frame_url(embedded_test_server()->GetURL(
864 "/navigation_controller/simple_page_2.html"));
865 std::string script
= "location.replace('" + frame_url
.spec() + "')";
866 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
869 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
873 // history.pushState().
874 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
876 "history.pushState({}, 'page 1', 'simple_page_1.html')";
877 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
880 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
881 capturer
.params().transition
);
882 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
886 // history.replaceState().
887 LoadCommittedCapturer
capturer(root
->child_at(0));
889 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
890 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
893 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
898 LoadCommittedCapturer
capturer(root
->child_at(0));
899 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
900 "location.reload()"));
902 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
907 LoadCommittedCapturer
capturer(shell()->web_contents());
908 GURL
frame_url(embedded_test_server()->GetURL(
909 "/navigation_controller/simple_page_1.html"));
910 std::string script
= "var iframe = document.createElement('iframe');"
911 "iframe.src = '" + frame_url
.spec() + "';"
912 "document.body.appendChild(iframe);";
913 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
915 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
919 // Verify that navigations caused by client-side redirects are correctly
921 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
922 NavigationTypeClassification_ClientSideRedirect
) {
923 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
924 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
926 FrameTreeNode
* root
=
927 static_cast<WebContentsImpl
*>(shell()->web_contents())->
928 GetFrameTree()->root();
931 // Load the redirecting page.
932 FrameNavigateParamsCapturer
capturer(root
);
933 capturer
.set_navigations_remaining(2);
934 GURL
frame_url(embedded_test_server()->GetURL(
935 "/navigation_controller/client_redirect.html"));
936 NavigateFrameToURL(root
, frame_url
);
939 std::vector
<FrameNavigateParams
> params
= capturer
.all_params();
940 std::vector
<LoadCommittedDetails
> details
= capturer
.all_details();
941 ASSERT_EQ(2U, params
.size());
942 ASSERT_EQ(2U, details
.size());
943 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, params
[0].transition
);
944 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
[0].type
);
945 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
946 params
[1].transition
);
947 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
[1].type
);
951 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
953 // TODO(creis): Test cross-site and nested iframes.
954 // TODO(creis): Test updating entries for history auto subframe navigations.
955 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
956 FrameNavigationEntry_AutoSubframe
) {
957 GURL
main_url(embedded_test_server()->GetURL(
958 "/navigation_controller/simple_page_1.html"));
959 NavigateToURL(shell(), main_url
);
960 const NavigationControllerImpl
& controller
=
961 static_cast<const NavigationControllerImpl
&>(
962 shell()->web_contents()->GetController());
963 FrameTreeNode
* root
=
964 static_cast<WebContentsImpl
*>(shell()->web_contents())->
965 GetFrameTree()->root();
968 GURL
frame_url(embedded_test_server()->GetURL(
969 "/navigation_controller/simple_page_2.html"));
971 LoadCommittedCapturer
capturer(shell()->web_contents());
972 std::string script
= "var iframe = document.createElement('iframe');"
973 "iframe.src = '" + frame_url
.spec() + "';"
974 "document.body.appendChild(iframe);";
975 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
977 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
980 // Check last committed NavigationEntry.
981 EXPECT_EQ(1, controller
.GetEntryCount());
982 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
983 EXPECT_EQ(main_url
, entry
->GetURL());
984 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
985 EXPECT_EQ(main_url
, root_entry
->url());
987 // Verify subframe entries if we're in --site-per-process mode.
988 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
989 switches::kSitePerProcess
)) {
990 // The entry should now have a subframe FrameNavigationEntry.
991 ASSERT_EQ(1U, entry
->root_node()->children
.size());
992 FrameNavigationEntry
* frame_entry
=
993 entry
->root_node()->children
[0]->frame_entry
.get();
994 EXPECT_EQ(frame_url
, frame_entry
->url());
996 // There are no subframe FrameNavigationEntries by default.
997 EXPECT_EQ(0U, entry
->root_node()->children
.size());
1003 class HttpThrottle
: public ResourceThrottle
{
1006 void WillStartRequest(bool* defer
) override
{
1010 const char* GetNameForLogging() const override
{
1011 return "HttpThrottle";
1015 class StallDelegate
: public ResourceDispatcherHostDelegate
{
1016 // ResourceDispatcherHostDelegate
1017 void RequestBeginning(
1018 net::URLRequest
* request
,
1019 content::ResourceContext
* resource_context
,
1020 content::AppCacheService
* appcache_service
,
1021 ResourceType resource_type
,
1022 ScopedVector
<content::ResourceThrottle
>* throttles
) override
{
1023 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
1024 throttles
->push_back(new HttpThrottle
);
1030 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
1031 NavigationTypeClassification_InPageWhilePending
) {
1032 NavigationControllerImpl
& controller
=
1033 static_cast<NavigationControllerImpl
&>(
1034 shell()->web_contents()->GetController());
1036 FrameTreeNode
* root
=
1037 static_cast<WebContentsImpl
*>(shell()->web_contents())->
1038 GetFrameTree()->root();
1040 // Start with a normal page.
1041 GURL
url1(embedded_test_server()->GetURL(
1042 "/navigation_controller/simple_page_1.html"));
1043 EXPECT_TRUE(NavigateToURL(shell(), url1
));
1045 // Have the user decide to go to a different page which is very slow.
1046 StallDelegate stall_delegate
;
1047 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate
);
1048 GURL
url2(embedded_test_server()->GetURL(
1049 "/navigation_controller/simple_page_2.html"));
1050 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
1052 // That should be the pending entry.
1053 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
1054 ASSERT_NE(nullptr, entry
);
1055 EXPECT_EQ(url2
, entry
->GetURL());
1058 // Now the existing page uses history.replaceState().
1059 FrameNavigateParamsCapturer
capturer(root
);
1060 capturer
.set_wait_for_load(false);
1061 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
1062 "history.replaceState({}, '', 'x')"));
1065 // The fact that there was a pending entry shouldn't interfere with the
1067 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
1070 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
1073 } // namespace content