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/web_contents.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "content/public/common/bindings_policy.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/common/url_constants.h"
19 #include "content/public/test/browser_test_utils.h"
20 #include "content/public/test/content_browser_test.h"
21 #include "content/public/test/content_browser_test_utils.h"
22 #include "content/public/test/test_navigation_observer.h"
23 #include "content/public/test/test_utils.h"
24 #include "content/shell/browser/shell.h"
25 #include "content/test/content_browser_test_utils_internal.h"
26 #include "net/dns/mock_host_resolver.h"
27 #include "net/test/embedded_test_server/embedded_test_server.h"
28 #include "net/test/url_request/url_request_failed_job.h"
32 class NavigationControllerBrowserTest
: public ContentBrowserTest
{
34 void SetUpOnMainThread() override
{
35 host_resolver()->AddRule("*", "127.0.0.1");
36 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
40 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
, LoadDataWithBaseURL
) {
41 const GURL
base_url("http://baseurl");
42 const GURL
history_url("http://historyurl");
43 const std::string data
= "<html><body>foo</body></html>";
45 const NavigationController
& controller
=
46 shell()->web_contents()->GetController();
47 // Load data. Blocks until it is done.
48 content::LoadDataWithBaseURL(shell(), history_url
, data
, base_url
);
50 // We should use history_url instead of the base_url as the original url of
51 // this navigation entry, because base_url is only used for resolving relative
52 // paths in the data, or enforcing same origin policy.
53 EXPECT_EQ(controller
.GetVisibleEntry()->GetOriginalRequestURL(), history_url
);
56 // The renderer uses the position in the history list as a clue to whether a
57 // navigation is stale. In the case where the entry limit is reached and the
58 // history list is pruned, make sure that there is no mismatch that would cause
59 // it to start incorrectly rejecting navigations as stale. See
60 // http://crbug.com/89798.
61 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
62 DontIgnoreBackAfterNavEntryLimit
) {
63 NavigationController
& controller
=
64 shell()->web_contents()->GetController();
66 const int kMaxEntryCount
=
67 static_cast<int>(NavigationControllerImpl::max_entry_count());
69 // Load up to the max count, all entries should be there.
70 for (int url_index
= 0; url_index
< kMaxEntryCount
; ++url_index
) {
71 GURL
url(base::StringPrintf("data:text/html,page%d", url_index
));
72 EXPECT_TRUE(NavigateToURL(shell(), url
));
75 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
77 // Navigate twice more more.
78 for (int url_index
= kMaxEntryCount
;
79 url_index
< kMaxEntryCount
+ 2; ++url_index
) {
80 GURL
url(base::StringPrintf("data:text/html,page%d", url_index
));
81 EXPECT_TRUE(NavigateToURL(shell(), url
));
84 // We expect page0 and page1 to be gone.
85 EXPECT_EQ(kMaxEntryCount
, controller
.GetEntryCount());
86 EXPECT_EQ(GURL("data:text/html,page2"),
87 controller
.GetEntryAtIndex(0)->GetURL());
89 // Now try to go back. This should not hang.
90 ASSERT_TRUE(controller
.CanGoBack());
92 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
94 // This should have successfully gone back.
95 EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount
)),
96 controller
.GetLastCommittedEntry()->GetURL());
101 int RendererHistoryLength(Shell
* shell
) {
103 EXPECT_TRUE(ExecuteScriptAndExtractInt(
104 shell
->web_contents(),
105 "domAutomationController.send(history.length)",
110 // Similar to the ones from content_browser_test_utils.
111 bool NavigateToURLAndReplace(Shell
* shell
, const GURL
& url
) {
112 WebContents
* web_contents
= shell
->web_contents();
113 WaitForLoadStop(web_contents
);
114 TestNavigationObserver
same_tab_observer(web_contents
, 1);
115 NavigationController::LoadURLParams
params(url
);
116 params
.should_replace_current_entry
= true;
117 web_contents
->GetController().LoadURLWithParams(params
);
118 web_contents
->Focus();
119 same_tab_observer
.Wait();
120 if (!IsLastCommittedEntryOfPageType(web_contents
, PAGE_TYPE_NORMAL
))
122 return web_contents
->GetLastCommittedURL() == url
;
127 // When loading a new page to replace an old page in the history list, make sure
128 // that the browser and renderer agree, and that both get it right.
129 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
130 CorrectLengthWithCurrentItemReplacement
) {
131 NavigationController
& controller
=
132 shell()->web_contents()->GetController();
134 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
135 EXPECT_EQ(1, controller
.GetEntryCount());
136 EXPECT_EQ(1, RendererHistoryLength(shell()));
138 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page2")));
139 EXPECT_EQ(1, controller
.GetEntryCount());
140 EXPECT_EQ(1, RendererHistoryLength(shell()));
142 // Note that there's no way to access the renderer's notion of the history
143 // offset via JavaScript. Checking just the history length, though, is enough;
144 // if the replacement failed, there would be a new history entry and thus an
148 // When spawning a new page from a WebUI page, make sure that the browser and
149 // renderer agree about the length of the history list, and that both get it
151 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
152 CorrectLengthWithNewTabNavigatingFromWebUI
) {
153 GURL
web_ui_page(std::string(kChromeUIScheme
) + "://" +
154 std::string(kChromeUIGpuHost
));
155 EXPECT_TRUE(NavigateToURL(shell(), web_ui_page
));
156 EXPECT_EQ(BINDINGS_POLICY_WEB_UI
,
157 shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
159 ShellAddedObserver observer
;
160 std::string page_url
= embedded_test_server()->GetURL(
161 "/navigation_controller/simple_page_1.html").spec();
162 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
163 "window.open('" + page_url
+ "', '_blank')"));
164 Shell
* shell2
= observer
.GetShell();
165 EXPECT_TRUE(WaitForLoadStop(shell2
->web_contents()));
167 EXPECT_EQ(1, shell2
->web_contents()->GetController().GetEntryCount());
168 EXPECT_EQ(1, RendererHistoryLength(shell2
));
170 // Again, as above, there's no way to access the renderer's notion of the
171 // history offset via JavaScript. Checking just the history length, again,
172 // will have to suffice.
177 class NoNavigationsObserver
: public WebContentsObserver
{
179 // Observes navigation for the specified |web_contents|.
180 explicit NoNavigationsObserver(WebContents
* web_contents
)
181 : WebContentsObserver(web_contents
) {}
184 void DidNavigateAnyFrame(RenderFrameHost
* render_frame_host
,
185 const LoadCommittedDetails
& details
,
186 const FrameNavigateParams
& params
) override
{
187 FAIL() << "No navigations should occur";
193 // Some pages create a popup, then write an iframe into it. This causes a
194 // subframe navigation without having any committed entry. Such navigations
195 // just get thrown on the ground, but we shouldn't crash.
197 // This test actually hits NAVIGATION_TYPE_NAV_IGNORE three times. Two of them,
198 // the initial window.open() and the iframe creation, don't try to create
199 // navigation entries, and the third, the new navigation, tries to.
200 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
, SubframeOnEmptyPage
) {
201 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
202 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
204 FrameTreeNode
* root
=
205 static_cast<WebContentsImpl
*>(shell()->web_contents())->
206 GetFrameTree()->root();
208 // Pop open a new window.
209 ShellAddedObserver new_shell_observer
;
210 std::string script
= "window.open()";
211 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
212 Shell
* new_shell
= new_shell_observer
.GetShell();
213 ASSERT_NE(new_shell
->web_contents(), shell()->web_contents());
214 FrameTreeNode
* new_root
=
215 static_cast<WebContentsImpl
*>(new_shell
->web_contents())->
216 GetFrameTree()->root();
218 // Make a new iframe in it.
219 NoNavigationsObserver
observer(new_shell
->web_contents());
220 script
= "var iframe = document.createElement('iframe');"
221 "iframe.src = 'data:text/html,<p>some page</p>';"
222 "document.body.appendChild(iframe);";
223 EXPECT_TRUE(content::ExecuteScript(new_root
->current_frame_host(), script
));
224 // The success check is of the last-committed entry, and there is none.
225 WaitForLoadStopWithoutSuccessCheck(new_shell
->web_contents());
227 ASSERT_EQ(1U, new_root
->child_count());
228 ASSERT_NE(nullptr, new_root
->child_at(0));
231 GURL frame_url
= embedded_test_server()->GetURL(
232 "/navigation_controller/simple_page_2.html");
233 script
= "location.assign('" + frame_url
.spec() + "')";
234 EXPECT_TRUE(content::ExecuteScript(
235 new_root
->child_at(0)->current_frame_host(), script
));
237 // Success is not crashing, and not navigating.
239 new_shell
->web_contents()->GetController().GetLastCommittedEntry());
244 class FrameNavigateParamsCapturer
: public WebContentsObserver
{
246 // Observes navigation for the specified |node|.
247 explicit FrameNavigateParamsCapturer(FrameTreeNode
* node
)
248 : WebContentsObserver(
249 node
->current_frame_host()->delegate()->GetAsWebContents()),
250 frame_tree_node_id_(node
->frame_tree_node_id()),
251 navigations_remaining_(1),
252 message_loop_runner_(new MessageLoopRunner
) {}
254 void set_navigations_remaining(int count
) {
255 navigations_remaining_
= count
;
259 message_loop_runner_
->Run();
262 const FrameNavigateParams
& params() const {
263 EXPECT_EQ(1U, params_
.size());
267 const std::vector
<FrameNavigateParams
>& all_params() const {
271 const LoadCommittedDetails
& details() const {
272 EXPECT_EQ(1U, details_
.size());
276 const std::vector
<LoadCommittedDetails
>& all_details() const {
281 void DidNavigateAnyFrame(RenderFrameHost
* render_frame_host
,
282 const LoadCommittedDetails
& details
,
283 const FrameNavigateParams
& params
) override
{
284 RenderFrameHostImpl
* rfh
=
285 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
286 if (rfh
->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_
)
289 --navigations_remaining_
;
290 params_
.push_back(params
);
291 details_
.push_back(details
);
292 if (!web_contents()->IsLoading() && !navigations_remaining_
)
293 message_loop_runner_
->Quit();
296 void DidStopLoading() override
{
297 if (!navigations_remaining_
)
298 message_loop_runner_
->Quit();
301 // The id of the FrameTreeNode whose navigations to observe.
302 int frame_tree_node_id_
;
304 // How many navigations remain to capture.
305 int navigations_remaining_
;
307 // The params of the navigations.
308 std::vector
<FrameNavigateParams
> params_
;
310 // The details of the navigations.
311 std::vector
<LoadCommittedDetails
> details_
;
313 // The MessageLoopRunner used to spin the message loop.
314 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
317 class LoadCommittedCapturer
: public WebContentsObserver
{
319 // Observes the load commit for the specified |node|.
320 explicit LoadCommittedCapturer(FrameTreeNode
* node
)
321 : WebContentsObserver(
322 node
->current_frame_host()->delegate()->GetAsWebContents()),
323 frame_tree_node_id_(node
->frame_tree_node_id()),
324 message_loop_runner_(new MessageLoopRunner
) {}
326 // Observes the load commit for the next created frame in the specified
328 explicit LoadCommittedCapturer(WebContents
* web_contents
)
329 : WebContentsObserver(web_contents
),
330 frame_tree_node_id_(0),
331 message_loop_runner_(new MessageLoopRunner
) {}
334 message_loop_runner_
->Run();
337 ui::PageTransition
transition_type() const {
338 return transition_type_
;
342 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
343 // If this object was created with a specified tree frame node, there
344 // shouldn't be any frames being created.
345 DCHECK_EQ(0, frame_tree_node_id_
);
346 RenderFrameHostImpl
* rfh
=
347 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
348 frame_tree_node_id_
= rfh
->frame_tree_node()->frame_tree_node_id();
351 void DidCommitProvisionalLoadForFrame(
352 RenderFrameHost
* render_frame_host
,
354 ui::PageTransition transition_type
) override
{
355 DCHECK_NE(0, frame_tree_node_id_
);
356 RenderFrameHostImpl
* rfh
=
357 static_cast<RenderFrameHostImpl
*>(render_frame_host
);
358 if (rfh
->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_
)
361 transition_type_
= transition_type
;
362 if (!web_contents()->IsLoading())
363 message_loop_runner_
->Quit();
366 void DidStopLoading() override
{ message_loop_runner_
->Quit(); }
368 // The id of the FrameTreeNode whose navigations to observe.
369 int frame_tree_node_id_
;
371 // The transition_type of the last navigation.
372 ui::PageTransition transition_type_
;
374 // The MessageLoopRunner used to spin the message loop.
375 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
380 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
381 ErrorPageReplacement
) {
382 NavigationController
& controller
= shell()->web_contents()->GetController();
384 net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET
));
385 net::URLRequestFailedJob::AddUrlHandler();
387 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
388 EXPECT_EQ(1, controller
.GetEntryCount());
390 FrameTreeNode
* root
=
391 static_cast<WebContentsImpl
*>(shell()->web_contents())->
392 GetFrameTree()->root();
394 // Navigate to a page that fails to load. It must result in an error page, the
395 // NEW_PAGE navigation type, and an addition to the history list.
397 FrameNavigateParamsCapturer
capturer(root
);
398 NavigateFrameToURL(root
, error_url
);
400 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
401 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
402 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
403 EXPECT_EQ(2, controller
.GetEntryCount());
406 // Navigate again to the page that fails to load. It must result in an error
407 // page, the EXISTING_PAGE navigation type, and no addition to the history
408 // list. We do not use SAME_PAGE here; that case only differs in that it
409 // clears the pending entry, and there is no pending entry after a load
412 FrameNavigateParamsCapturer
capturer(root
);
413 NavigateFrameToURL(root
, error_url
);
415 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
416 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
417 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
418 EXPECT_EQ(2, controller
.GetEntryCount());
421 // Make a new entry ...
422 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
423 EXPECT_EQ(3, controller
.GetEntryCount());
425 // ... and replace it with a failed load. (Note that when you set the
426 // should_replace_current_entry flag, the navigation is classified as NEW_PAGE
427 // because that is a classification of the renderer's behavior, and the flag
428 // is a browser-side flag.)
430 FrameNavigateParamsCapturer
capturer(root
);
431 NavigateToURLAndReplace(shell(), error_url
);
433 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
434 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
435 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
436 EXPECT_EQ(3, controller
.GetEntryCount());
439 // Make a new web ui page to force a process swap ...
440 GURL
web_ui_page(std::string(kChromeUIScheme
) + "://" +
441 std::string(kChromeUIGpuHost
));
442 NavigateToURL(shell(), web_ui_page
);
443 EXPECT_EQ(4, controller
.GetEntryCount());
445 // ... and replace it with a failed load. (It is NEW_PAGE for the reason noted
448 FrameNavigateParamsCapturer
capturer(root
);
449 NavigateToURLAndReplace(shell(), error_url
);
451 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
452 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
453 EXPECT_EQ(PAGE_TYPE_ERROR
, entry
->GetPageType());
454 EXPECT_EQ(4, controller
.GetEntryCount());
458 // Various tests for navigation type classifications. TODO(avi): It's rather
459 // bogus that the same info is in two different enums; http://crbug.com/453555.
461 // Verify that navigations for NAVIGATION_TYPE_NEW_PAGE are correctly
463 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
464 NavigationTypeClassification_NewPage
) {
465 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
467 FrameTreeNode
* root
=
468 static_cast<WebContentsImpl
*>(shell()->web_contents())->
469 GetFrameTree()->root();
473 FrameNavigateParamsCapturer
capturer(root
);
474 GURL
frame_url(embedded_test_server()->GetURL(
475 "/navigation_controller/page_with_links.html"));
476 NavigateFrameToURL(root
, frame_url
);
478 // TODO(avi,creis): Why is this (and quite a few others below) a "link"
479 // transition? Lots of these transitions should be cleaned up.
480 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
481 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
485 // Load via a fragment link click.
486 FrameNavigateParamsCapturer
capturer(root
);
487 std::string script
= "document.getElementById('fraglink').click()";
488 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
490 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
491 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
495 // Load via link click.
496 FrameNavigateParamsCapturer
capturer(root
);
497 std::string script
= "document.getElementById('thelink').click()";
498 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
500 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
501 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
505 // location.assign().
506 FrameNavigateParamsCapturer
capturer(root
);
507 GURL
frame_url(embedded_test_server()->GetURL(
508 "/navigation_controller/simple_page_2.html"));
509 std::string script
= "location.assign('" + frame_url
.spec() + "')";
510 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
512 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
513 capturer
.params().transition
);
514 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
518 // history.pushState().
519 FrameNavigateParamsCapturer
capturer(root
);
521 "history.pushState({}, 'page 1', 'simple_page_1.html')";
522 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
524 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
525 capturer
.params().transition
);
526 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, capturer
.details().type
);
530 // Verify that navigations for NAVIGATION_TYPE_EXISTING_PAGE are correctly
532 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
533 NavigationTypeClassification_ExistingPage
) {
534 GURL
url1(embedded_test_server()->GetURL(
535 "/navigation_controller/simple_page_1.html"));
536 NavigateToURL(shell(), url1
);
537 GURL
url2(embedded_test_server()->GetURL(
538 "/navigation_controller/simple_page_2.html"));
539 NavigateToURL(shell(), url2
);
541 FrameTreeNode
* root
=
542 static_cast<WebContentsImpl
*>(shell()->web_contents())->
543 GetFrameTree()->root();
546 // Back from the browser side.
547 FrameNavigateParamsCapturer
capturer(root
);
548 shell()->web_contents()->GetController().GoBack();
550 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
551 | ui::PAGE_TRANSITION_FORWARD_BACK
552 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
553 capturer
.params().transition
);
554 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
558 // Forward from the browser side.
559 FrameNavigateParamsCapturer
capturer(root
);
560 shell()->web_contents()->GetController().GoForward();
562 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
563 | ui::PAGE_TRANSITION_FORWARD_BACK
564 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
565 capturer
.params().transition
);
566 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
570 // Back from the renderer side.
571 FrameNavigateParamsCapturer
capturer(root
);
572 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
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 // Forward from the renderer side.
584 FrameNavigateParamsCapturer
capturer(root
);
585 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
586 "history.forward()"));
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 // Back from the renderer side via history.go().
597 FrameNavigateParamsCapturer
capturer(root
);
598 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
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 // Forward 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 // Reload from the browser side.
623 FrameNavigateParamsCapturer
capturer(root
);
624 shell()->web_contents()->GetController().Reload(false);
626 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
, capturer
.params().transition
);
627 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
631 // Reload from the renderer side.
632 FrameNavigateParamsCapturer
capturer(root
);
633 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(),
634 "location.reload()"));
636 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
637 capturer
.params().transition
);
638 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, capturer
.details().type
);
642 // location.replace().
643 FrameNavigateParamsCapturer
capturer(root
);
644 GURL
frame_url(embedded_test_server()->GetURL(
645 "/navigation_controller/simple_page_1.html"));
646 std::string script
= "location.replace('" + frame_url
.spec() + "')";
647 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
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 // Verify that navigations for NAVIGATION_TYPE_SAME_PAGE are correctly
657 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
658 NavigationTypeClassification_SamePage
) {
659 GURL
url1(embedded_test_server()->GetURL(
660 "/navigation_controller/simple_page_1.html"));
661 NavigateToURL(shell(), url1
);
663 FrameTreeNode
* root
=
664 static_cast<WebContentsImpl
*>(shell()->web_contents())->
665 GetFrameTree()->root();
669 FrameNavigateParamsCapturer
capturer(root
);
670 GURL
frame_url(embedded_test_server()->GetURL(
671 "/navigation_controller/simple_page_1.html"));
672 NavigateFrameToURL(root
, frame_url
);
674 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
675 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, capturer
.details().type
);
679 // Verify that navigations for NAVIGATION_TYPE_IN_PAGE are correctly
681 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
682 NavigationTypeClassification_InPage
) {
683 GURL
url1(embedded_test_server()->GetURL(
684 "/navigation_controller/simple_page_1.html"));
685 NavigateToURL(shell(), url1
);
687 FrameTreeNode
* root
=
688 static_cast<WebContentsImpl
*>(shell()->web_contents())->
689 GetFrameTree()->root();
692 // history.replaceState().
693 FrameNavigateParamsCapturer
capturer(root
);
695 "history.replaceState({}, 'page 1', 'simple_page_2.html')";
696 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
698 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, capturer
.params().transition
);
699 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
702 // Back and forward across a fragment navigation.
704 GURL
url2(embedded_test_server()->GetURL(
705 "/navigation_controller/page_with_links.html"));
706 NavigateToURL(shell(), url2
);
707 std::string script
= "document.getElementById('fraglink').click()";
708 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
709 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
713 FrameNavigateParamsCapturer
capturer(root
);
714 shell()->web_contents()->GetController().GoBack();
716 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
717 | ui::PAGE_TRANSITION_FORWARD_BACK
718 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
719 capturer
.params().transition
);
720 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
725 FrameNavigateParamsCapturer
capturer(root
);
726 shell()->web_contents()->GetController().GoForward();
728 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_FORWARD_BACK
,
729 capturer
.params().transition
);
730 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
733 // Back and forward across a pushState-created navigation.
735 NavigateToURL(shell(), url1
);
736 script
= "history.pushState({}, 'page 2', 'simple_page_2.html')";
737 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
738 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
742 FrameNavigateParamsCapturer
capturer(root
);
743 shell()->web_contents()->GetController().GoBack();
745 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
746 | ui::PAGE_TRANSITION_FORWARD_BACK
747 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
,
748 capturer
.params().transition
);
749 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
754 FrameNavigateParamsCapturer
capturer(root
);
755 shell()->web_contents()->GetController().GoForward();
757 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_FORWARD_BACK
,
758 capturer
.params().transition
);
759 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, capturer
.details().type
);
763 // Verify that navigations for NAVIGATION_TYPE_NEW_SUBFRAME and
764 // NAVIGATION_TYPE_AUTO_SUBFRAME are properly classified.
765 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
766 NavigationTypeClassification_NewAndAutoSubframe
) {
767 GURL
main_url(embedded_test_server()->GetURL(
768 "/navigation_controller/page_with_iframe.html"));
769 NavigateToURL(shell(), main_url
);
771 // It is safe to obtain the root frame tree node here, as it doesn't change.
772 FrameTreeNode
* root
=
773 static_cast<WebContentsImpl
*>(shell()->web_contents())->
774 GetFrameTree()->root();
776 ASSERT_EQ(1U, root
->child_count());
777 ASSERT_NE(nullptr, root
->child_at(0));
781 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
782 GURL
frame_url(embedded_test_server()->GetURL(
783 "/navigation_controller/simple_page_1.html"));
784 NavigateFrameToURL(root
->child_at(0), frame_url
);
786 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
787 capturer
.params().transition
);
788 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
793 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
794 shell()->web_contents()->GetController().GoBack();
796 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.params().transition
);
797 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME
, capturer
.details().type
);
802 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
803 shell()->web_contents()->GetController().GoForward();
805 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.params().transition
);
806 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME
, capturer
.details().type
);
811 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
812 GURL
frame_url(embedded_test_server()->GetURL(
813 "/navigation_controller/page_with_links.html"));
814 NavigateFrameToURL(root
->child_at(0), frame_url
);
816 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
817 capturer
.params().transition
);
818 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
822 // Load via a fragment link click.
823 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
824 std::string script
= "document.getElementById('fraglink').click()";
825 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
828 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
829 capturer
.params().transition
);
830 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
834 // location.assign().
835 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
836 GURL
frame_url(embedded_test_server()->GetURL(
837 "/navigation_controller/simple_page_1.html"));
838 std::string script
= "location.assign('" + frame_url
.spec() + "')";
839 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
842 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
843 capturer
.params().transition
);
844 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
848 // location.replace().
849 LoadCommittedCapturer
capturer(root
->child_at(0));
850 GURL
frame_url(embedded_test_server()->GetURL(
851 "/navigation_controller/simple_page_2.html"));
852 std::string script
= "location.replace('" + frame_url
.spec() + "')";
853 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
856 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
860 // history.pushState().
861 FrameNavigateParamsCapturer
capturer(root
->child_at(0));
863 "history.pushState({}, 'page 1', 'simple_page_1.html')";
864 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
867 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME
,
868 capturer
.params().transition
);
869 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME
, capturer
.details().type
);
873 // history.replaceState().
874 LoadCommittedCapturer
capturer(root
->child_at(0));
876 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
877 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
880 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
885 LoadCommittedCapturer
capturer(root
->child_at(0));
886 EXPECT_TRUE(content::ExecuteScript(root
->child_at(0)->current_frame_host(),
887 "location.reload()"));
889 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
894 LoadCommittedCapturer
capturer(shell()->web_contents());
895 GURL
frame_url(embedded_test_server()->GetURL(
896 "/navigation_controller/simple_page_1.html"));
897 std::string script
= "var iframe = document.createElement('iframe');"
898 "iframe.src = '" + frame_url
.spec() + "';"
899 "document.body.appendChild(iframe);";
900 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
902 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
906 // Verify that navigations caused by client-side redirects are correctly
908 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
909 NavigationTypeClassification_ClientSideRedirect
) {
910 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
911 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
913 FrameTreeNode
* root
=
914 static_cast<WebContentsImpl
*>(shell()->web_contents())->
915 GetFrameTree()->root();
918 // Load the redirecting page.
919 FrameNavigateParamsCapturer
capturer(root
);
920 capturer
.set_navigations_remaining(2);
921 GURL
frame_url(embedded_test_server()->GetURL(
922 "/navigation_controller/client_redirect.html"));
923 NavigateFrameToURL(root
, frame_url
);
926 std::vector
<FrameNavigateParams
> params
= capturer
.all_params();
927 std::vector
<LoadCommittedDetails
> details
= capturer
.all_details();
928 ASSERT_EQ(2U, params
.size());
929 ASSERT_EQ(2U, details
.size());
930 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
, params
[0].transition
);
931 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
[0].type
);
932 EXPECT_EQ(ui::PAGE_TRANSITION_LINK
| ui::PAGE_TRANSITION_CLIENT_REDIRECT
,
933 params
[1].transition
);
934 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
[1].type
);
939 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
941 // TODO(creis): Test cross-site and nested iframes.
942 // TODO(creis): Test updating entries for history auto subframe navigations.
943 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest
,
944 FrameNavigationEntry_AutoSubframe
) {
945 GURL
main_url(embedded_test_server()->GetURL(
946 "/navigation_controller/simple_page_1.html"));
947 NavigateToURL(shell(), main_url
);
948 const NavigationControllerImpl
& controller
=
949 static_cast<const NavigationControllerImpl
&>(
950 shell()->web_contents()->GetController());
951 FrameTreeNode
* root
=
952 static_cast<WebContentsImpl
*>(shell()->web_contents())->
953 GetFrameTree()->root();
956 GURL
frame_url(embedded_test_server()->GetURL(
957 "/navigation_controller/simple_page_2.html"));
959 LoadCommittedCapturer
capturer(shell()->web_contents());
960 std::string script
= "var iframe = document.createElement('iframe');"
961 "iframe.src = '" + frame_url
.spec() + "';"
962 "document.body.appendChild(iframe);";
963 EXPECT_TRUE(content::ExecuteScript(root
->current_frame_host(), script
));
965 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME
, capturer
.transition_type());
968 // Check last committed NavigationEntry.
969 EXPECT_EQ(1, controller
.GetEntryCount());
970 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
971 EXPECT_EQ(main_url
, entry
->GetURL());
972 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
973 EXPECT_EQ(main_url
, root_entry
->url());
975 // Verify subframe entries if we're in --site-per-process mode.
976 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
977 switches::kSitePerProcess
)) {
978 // The entry should now have a subframe FrameNavigationEntry.
979 ASSERT_EQ(1U, entry
->root_node()->children
.size());
980 FrameNavigationEntry
* frame_entry
=
981 entry
->root_node()->children
[0]->frame_entry
.get();
982 EXPECT_EQ(frame_url
, frame_entry
->url());
984 // There are no subframe FrameNavigationEntries by default.
985 EXPECT_EQ(0U, entry
->root_node()->children
.size());
989 } // namespace content