Roll ANGLE e754fb8..6ffeb74
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_browsertest.cc
blob82fb779d9d519f4f222cd9c8a481eb26fe3ea4a5
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.
5 #include "base/bind.h"
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"
34 namespace content {
36 class NavigationControllerBrowserTest : public ContentBrowserTest {
37 protected:
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 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, UniqueIDs) {
61 const NavigationControllerImpl& controller =
62 static_cast<const NavigationControllerImpl&>(
63 shell()->web_contents()->GetController());
65 GURL main_url(embedded_test_server()->GetURL(
66 "/navigation_controller/page_with_link_to_load_iframe.html"));
67 NavigateToURL(shell(), main_url);
68 ASSERT_EQ(1, controller.GetEntryCount());
70 // Use JavaScript to click the link and load the iframe.
71 std::string script = "document.getElementById('link').click()";
72 EXPECT_TRUE(content::ExecuteScript(shell()->web_contents(), script));
73 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
74 ASSERT_EQ(2, controller.GetEntryCount());
76 // Unique IDs should... um... be unique.
77 ASSERT_NE(controller.GetEntryAtIndex(0)->GetUniqueID(),
78 controller.GetEntryAtIndex(1)->GetUniqueID());
81 // The renderer uses the position in the history list as a clue to whether a
82 // navigation is stale. In the case where the entry limit is reached and the
83 // history list is pruned, make sure that there is no mismatch that would cause
84 // it to start incorrectly rejecting navigations as stale. See
85 // http://crbug.com/89798.
86 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
87 DontIgnoreBackAfterNavEntryLimit) {
88 NavigationController& controller =
89 shell()->web_contents()->GetController();
91 const int kMaxEntryCount =
92 static_cast<int>(NavigationControllerImpl::max_entry_count());
94 // Load up to the max count, all entries should be there.
95 for (int url_index = 0; url_index < kMaxEntryCount; ++url_index) {
96 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
97 EXPECT_TRUE(NavigateToURL(shell(), url));
100 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
102 // Navigate twice more more.
103 for (int url_index = kMaxEntryCount;
104 url_index < kMaxEntryCount + 2; ++url_index) {
105 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
106 EXPECT_TRUE(NavigateToURL(shell(), url));
109 // We expect page0 and page1 to be gone.
110 EXPECT_EQ(kMaxEntryCount, controller.GetEntryCount());
111 EXPECT_EQ(GURL("data:text/html,page2"),
112 controller.GetEntryAtIndex(0)->GetURL());
114 // Now try to go back. This should not hang.
115 ASSERT_TRUE(controller.CanGoBack());
116 controller.GoBack();
117 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
119 // This should have successfully gone back.
120 EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount)),
121 controller.GetLastCommittedEntry()->GetURL());
124 namespace {
126 int RendererHistoryLength(Shell* shell) {
127 int value = 0;
128 EXPECT_TRUE(ExecuteScriptAndExtractInt(
129 shell->web_contents(),
130 "domAutomationController.send(history.length)",
131 &value));
132 return value;
135 // Similar to the ones from content_browser_test_utils.
136 bool NavigateToURLAndReplace(Shell* shell, const GURL& url) {
137 WebContents* web_contents = shell->web_contents();
138 WaitForLoadStop(web_contents);
139 TestNavigationObserver same_tab_observer(web_contents, 1);
140 NavigationController::LoadURLParams params(url);
141 params.should_replace_current_entry = true;
142 web_contents->GetController().LoadURLWithParams(params);
143 web_contents->Focus();
144 same_tab_observer.Wait();
145 if (!IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL))
146 return false;
147 return web_contents->GetLastCommittedURL() == url;
150 } // namespace
152 // When loading a new page to replace an old page in the history list, make sure
153 // that the browser and renderer agree, and that both get it right.
154 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
155 CorrectLengthWithCurrentItemReplacement) {
156 NavigationController& controller =
157 shell()->web_contents()->GetController();
159 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
160 EXPECT_EQ(1, controller.GetEntryCount());
161 EXPECT_EQ(1, RendererHistoryLength(shell()));
163 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page2")));
164 EXPECT_EQ(1, controller.GetEntryCount());
165 EXPECT_EQ(1, RendererHistoryLength(shell()));
167 // Note that there's no way to access the renderer's notion of the history
168 // offset via JavaScript. Checking just the history length, though, is enough;
169 // if the replacement failed, there would be a new history entry and thus an
170 // incorrect length.
173 // When spawning a new page from a WebUI page, make sure that the browser and
174 // renderer agree about the length of the history list, and that both get it
175 // right.
176 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
177 CorrectLengthWithNewTabNavigatingFromWebUI) {
178 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
179 std::string(kChromeUIGpuHost));
180 EXPECT_TRUE(NavigateToURL(shell(), web_ui_page));
181 EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
182 shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
184 ShellAddedObserver observer;
185 std::string page_url = embedded_test_server()->GetURL(
186 "/navigation_controller/simple_page_1.html").spec();
187 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
188 "window.open('" + page_url + "', '_blank')"));
189 Shell* shell2 = observer.GetShell();
190 EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
192 EXPECT_EQ(1, shell2->web_contents()->GetController().GetEntryCount());
193 EXPECT_EQ(1, RendererHistoryLength(shell2));
195 // Again, as above, there's no way to access the renderer's notion of the
196 // history offset via JavaScript. Checking just the history length, again,
197 // will have to suffice.
200 namespace {
202 class NoNavigationsObserver : public WebContentsObserver {
203 public:
204 // Observes navigation for the specified |web_contents|.
205 explicit NoNavigationsObserver(WebContents* web_contents)
206 : WebContentsObserver(web_contents) {}
208 private:
209 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
210 const LoadCommittedDetails& details,
211 const FrameNavigateParams& params) override {
212 FAIL() << "No navigations should occur";
216 } // namespace
218 // Some pages create a popup, then write an iframe into it. This causes a
219 // subframe navigation without having any committed entry. Such navigations
220 // just get thrown on the ground, but we shouldn't crash.
222 // This test actually hits NAVIGATION_TYPE_NAV_IGNORE three times. Two of them,
223 // the initial window.open() and the iframe creation, don't try to create
224 // navigation entries, and the third, the new navigation, tries to.
225 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, SubframeOnEmptyPage) {
226 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
227 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
229 FrameTreeNode* root =
230 static_cast<WebContentsImpl*>(shell()->web_contents())->
231 GetFrameTree()->root();
233 // Pop open a new window.
234 ShellAddedObserver new_shell_observer;
235 std::string script = "window.open()";
236 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
237 Shell* new_shell = new_shell_observer.GetShell();
238 ASSERT_NE(new_shell->web_contents(), shell()->web_contents());
239 FrameTreeNode* new_root =
240 static_cast<WebContentsImpl*>(new_shell->web_contents())->
241 GetFrameTree()->root();
243 // Make a new iframe in it.
244 NoNavigationsObserver observer(new_shell->web_contents());
245 script = "var iframe = document.createElement('iframe');"
246 "iframe.src = 'data:text/html,<p>some page</p>';"
247 "document.body.appendChild(iframe);";
248 EXPECT_TRUE(content::ExecuteScript(new_root->current_frame_host(), script));
249 // The success check is of the last-committed entry, and there is none.
250 WaitForLoadStopWithoutSuccessCheck(new_shell->web_contents());
252 ASSERT_EQ(1U, new_root->child_count());
253 ASSERT_NE(nullptr, new_root->child_at(0));
255 // Navigate it.
256 GURL frame_url = embedded_test_server()->GetURL(
257 "/navigation_controller/simple_page_2.html");
258 script = "location.assign('" + frame_url.spec() + "')";
259 EXPECT_TRUE(content::ExecuteScript(
260 new_root->child_at(0)->current_frame_host(), script));
262 // Success is not crashing, and not navigating.
263 EXPECT_EQ(nullptr,
264 new_shell->web_contents()->GetController().GetLastCommittedEntry());
267 namespace {
269 class FrameNavigateParamsCapturer : public WebContentsObserver {
270 public:
271 // Observes navigation for the specified |node|.
272 explicit FrameNavigateParamsCapturer(FrameTreeNode* node)
273 : WebContentsObserver(
274 node->current_frame_host()->delegate()->GetAsWebContents()),
275 frame_tree_node_id_(node->frame_tree_node_id()),
276 navigations_remaining_(1),
277 wait_for_load_(true),
278 message_loop_runner_(new MessageLoopRunner) {}
280 void set_navigations_remaining(int count) {
281 navigations_remaining_ = count;
284 void set_wait_for_load(bool ignore) {
285 wait_for_load_ = ignore;
288 void Wait() {
289 message_loop_runner_->Run();
292 const FrameNavigateParams& params() const {
293 EXPECT_EQ(1U, params_.size());
294 return params_[0];
297 const std::vector<FrameNavigateParams>& all_params() const {
298 return params_;
301 const LoadCommittedDetails& details() const {
302 EXPECT_EQ(1U, details_.size());
303 return details_[0];
306 const std::vector<LoadCommittedDetails>& all_details() const {
307 return details_;
310 private:
311 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
312 const LoadCommittedDetails& details,
313 const FrameNavigateParams& params) override {
314 RenderFrameHostImpl* rfh =
315 static_cast<RenderFrameHostImpl*>(render_frame_host);
316 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
317 return;
319 --navigations_remaining_;
320 params_.push_back(params);
321 details_.push_back(details);
322 if (!navigations_remaining_ &&
323 (!web_contents()->IsLoading() || !wait_for_load_))
324 message_loop_runner_->Quit();
327 void DidStopLoading() override {
328 if (!navigations_remaining_)
329 message_loop_runner_->Quit();
332 // The id of the FrameTreeNode whose navigations to observe.
333 int frame_tree_node_id_;
335 // How many navigations remain to capture.
336 int navigations_remaining_;
338 // Whether to also wait for the load to complete.
339 bool wait_for_load_;
341 // The params of the navigations.
342 std::vector<FrameNavigateParams> params_;
344 // The details of the navigations.
345 std::vector<LoadCommittedDetails> details_;
347 // The MessageLoopRunner used to spin the message loop.
348 scoped_refptr<MessageLoopRunner> message_loop_runner_;
351 class LoadCommittedCapturer : public WebContentsObserver {
352 public:
353 // Observes the load commit for the specified |node|.
354 explicit LoadCommittedCapturer(FrameTreeNode* node)
355 : WebContentsObserver(
356 node->current_frame_host()->delegate()->GetAsWebContents()),
357 frame_tree_node_id_(node->frame_tree_node_id()),
358 message_loop_runner_(new MessageLoopRunner) {}
360 // Observes the load commit for the next created frame in the specified
361 // |web_contents|.
362 explicit LoadCommittedCapturer(WebContents* web_contents)
363 : WebContentsObserver(web_contents),
364 frame_tree_node_id_(0),
365 message_loop_runner_(new MessageLoopRunner) {}
367 void Wait() {
368 message_loop_runner_->Run();
371 ui::PageTransition transition_type() const {
372 return transition_type_;
375 private:
376 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
377 RenderFrameHostImpl* rfh =
378 static_cast<RenderFrameHostImpl*>(render_frame_host);
380 // Don't pay attention to swapped out RenderFrameHosts in the main frame.
381 // TODO(nasko): Remove once swappedout:// is gone.
382 // See https://crbug.com/357747.
383 if (!RenderFrameHostImpl::IsRFHStateActive(rfh->rfh_state())) {
384 DLOG(INFO) << "Skipping swapped out RFH: "
385 << rfh->GetSiteInstance()->GetSiteURL();
386 return;
389 // If this object was not created with a specified frame tree node, then use
390 // the first created active RenderFrameHost. Once a node is selected, there
391 // shouldn't be any other frames being created.
392 int frame_tree_node_id = rfh->frame_tree_node()->frame_tree_node_id();
393 DCHECK(frame_tree_node_id_ == 0 ||
394 frame_tree_node_id_ == frame_tree_node_id);
395 frame_tree_node_id_ = frame_tree_node_id;
398 void DidCommitProvisionalLoadForFrame(
399 RenderFrameHost* render_frame_host,
400 const GURL& url,
401 ui::PageTransition transition_type) override {
402 DCHECK_NE(0, frame_tree_node_id_);
403 RenderFrameHostImpl* rfh =
404 static_cast<RenderFrameHostImpl*>(render_frame_host);
405 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
406 return;
408 transition_type_ = transition_type;
409 if (!web_contents()->IsLoading())
410 message_loop_runner_->Quit();
413 void DidStopLoading() override { message_loop_runner_->Quit(); }
415 // The id of the FrameTreeNode whose navigations to observe.
416 int frame_tree_node_id_;
418 // The transition_type of the last navigation.
419 ui::PageTransition transition_type_;
421 // The MessageLoopRunner used to spin the message loop.
422 scoped_refptr<MessageLoopRunner> message_loop_runner_;
425 } // namespace
427 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
428 ErrorPageReplacement) {
429 NavigationController& controller = shell()->web_contents()->GetController();
430 GURL error_url(
431 net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET));
432 net::URLRequestFailedJob::AddUrlHandler();
434 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
435 EXPECT_EQ(1, controller.GetEntryCount());
437 FrameTreeNode* root =
438 static_cast<WebContentsImpl*>(shell()->web_contents())->
439 GetFrameTree()->root();
441 // Navigate to a page that fails to load. It must result in an error page, the
442 // NEW_PAGE navigation type, and an addition to the history list.
444 FrameNavigateParamsCapturer capturer(root);
445 NavigateFrameToURL(root, error_url);
446 capturer.Wait();
447 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
448 NavigationEntry* entry = controller.GetLastCommittedEntry();
449 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
450 EXPECT_EQ(2, controller.GetEntryCount());
453 // Navigate again to the page that fails to load. It must result in an error
454 // page, the EXISTING_PAGE navigation type, and no addition to the history
455 // list. We do not use SAME_PAGE here; that case only differs in that it
456 // clears the pending entry, and there is no pending entry after a load
457 // failure.
459 FrameNavigateParamsCapturer capturer(root);
460 NavigateFrameToURL(root, error_url);
461 capturer.Wait();
462 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
463 NavigationEntry* entry = controller.GetLastCommittedEntry();
464 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
465 EXPECT_EQ(2, controller.GetEntryCount());
468 // Make a new entry ...
469 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
470 EXPECT_EQ(3, controller.GetEntryCount());
472 // ... and replace it with a failed load. (Note that when you set the
473 // should_replace_current_entry flag, the navigation is classified as NEW_PAGE
474 // because that is a classification of the renderer's behavior, and the flag
475 // is a browser-side flag.)
477 FrameNavigateParamsCapturer capturer(root);
478 NavigateToURLAndReplace(shell(), error_url);
479 capturer.Wait();
480 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
481 NavigationEntry* entry = controller.GetLastCommittedEntry();
482 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
483 EXPECT_EQ(3, controller.GetEntryCount());
486 // Make a new web ui page to force a process swap ...
487 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
488 std::string(kChromeUIGpuHost));
489 NavigateToURL(shell(), web_ui_page);
490 EXPECT_EQ(4, controller.GetEntryCount());
492 // ... and replace it with a failed load. (It is NEW_PAGE for the reason noted
493 // above.)
495 FrameNavigateParamsCapturer capturer(root);
496 NavigateToURLAndReplace(shell(), error_url);
497 capturer.Wait();
498 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
499 NavigationEntry* entry = controller.GetLastCommittedEntry();
500 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
501 EXPECT_EQ(4, controller.GetEntryCount());
505 // Various tests for navigation type classifications. TODO(avi): It's rather
506 // bogus that the same info is in two different enums; http://crbug.com/453555.
508 // Verify that navigations for NAVIGATION_TYPE_NEW_PAGE are correctly
509 // classified.
510 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
511 NavigationTypeClassification_NewPage) {
512 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
514 FrameTreeNode* root =
515 static_cast<WebContentsImpl*>(shell()->web_contents())->
516 GetFrameTree()->root();
519 // Simple load.
520 FrameNavigateParamsCapturer capturer(root);
521 GURL frame_url(embedded_test_server()->GetURL(
522 "/navigation_controller/page_with_links.html"));
523 NavigateFrameToURL(root, frame_url);
524 capturer.Wait();
525 // TODO(avi,creis): Why is this (and quite a few others below) a "link"
526 // transition? Lots of these transitions should be cleaned up.
527 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
528 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
532 // Load via a fragment link click.
533 FrameNavigateParamsCapturer capturer(root);
534 std::string script = "document.getElementById('fraglink').click()";
535 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
536 capturer.Wait();
537 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
538 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
542 // Load via link click.
543 FrameNavigateParamsCapturer capturer(root);
544 std::string script = "document.getElementById('thelink').click()";
545 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
546 capturer.Wait();
547 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
548 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
552 // location.assign().
553 FrameNavigateParamsCapturer capturer(root);
554 GURL frame_url(embedded_test_server()->GetURL(
555 "/navigation_controller/simple_page_2.html"));
556 std::string script = "location.assign('" + frame_url.spec() + "')";
557 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
558 capturer.Wait();
559 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
560 capturer.params().transition);
561 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
565 // history.pushState().
566 FrameNavigateParamsCapturer capturer(root);
567 std::string script =
568 "history.pushState({}, 'page 1', 'simple_page_1.html')";
569 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
570 capturer.Wait();
571 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
572 capturer.params().transition);
573 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
577 // Verify that navigations for NAVIGATION_TYPE_EXISTING_PAGE are correctly
578 // classified.
579 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
580 NavigationTypeClassification_ExistingPage) {
581 GURL url1(embedded_test_server()->GetURL(
582 "/navigation_controller/simple_page_1.html"));
583 NavigateToURL(shell(), url1);
584 GURL url2(embedded_test_server()->GetURL(
585 "/navigation_controller/simple_page_2.html"));
586 NavigateToURL(shell(), url2);
588 FrameTreeNode* root =
589 static_cast<WebContentsImpl*>(shell()->web_contents())->
590 GetFrameTree()->root();
593 // Back from the browser side.
594 FrameNavigateParamsCapturer capturer(root);
595 shell()->web_contents()->GetController().GoBack();
596 capturer.Wait();
597 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
598 | ui::PAGE_TRANSITION_FORWARD_BACK
599 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
600 capturer.params().transition);
601 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
605 // Forward from the browser side.
606 FrameNavigateParamsCapturer capturer(root);
607 shell()->web_contents()->GetController().GoForward();
608 capturer.Wait();
609 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
610 | ui::PAGE_TRANSITION_FORWARD_BACK
611 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
612 capturer.params().transition);
613 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
617 // Back from the renderer side.
618 FrameNavigateParamsCapturer capturer(root);
619 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
620 "history.back()"));
621 capturer.Wait();
622 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
623 | ui::PAGE_TRANSITION_FORWARD_BACK
624 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
625 capturer.params().transition);
626 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
630 // Forward from the renderer side.
631 FrameNavigateParamsCapturer capturer(root);
632 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
633 "history.forward()"));
634 capturer.Wait();
635 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
636 | ui::PAGE_TRANSITION_FORWARD_BACK
637 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
638 capturer.params().transition);
639 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
643 // Back from the renderer side via history.go().
644 FrameNavigateParamsCapturer capturer(root);
645 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
646 "history.go(-1)"));
647 capturer.Wait();
648 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
649 | ui::PAGE_TRANSITION_FORWARD_BACK
650 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
651 capturer.params().transition);
652 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
656 // Forward from the renderer side via history.go().
657 FrameNavigateParamsCapturer capturer(root);
658 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
659 "history.go(1)"));
660 capturer.Wait();
661 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
662 | ui::PAGE_TRANSITION_FORWARD_BACK
663 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
664 capturer.params().transition);
665 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
669 // Reload from the browser side.
670 FrameNavigateParamsCapturer capturer(root);
671 shell()->web_contents()->GetController().Reload(false);
672 capturer.Wait();
673 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD, capturer.params().transition);
674 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
678 // Reload from the renderer side.
679 FrameNavigateParamsCapturer capturer(root);
680 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
681 "location.reload()"));
682 capturer.Wait();
683 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
684 capturer.params().transition);
685 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
689 // location.replace().
690 FrameNavigateParamsCapturer capturer(root);
691 GURL frame_url(embedded_test_server()->GetURL(
692 "/navigation_controller/simple_page_1.html"));
693 std::string script = "location.replace('" + frame_url.spec() + "')";
694 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
695 capturer.Wait();
696 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
697 capturer.params().transition);
698 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
702 // Verify that navigations for NAVIGATION_TYPE_SAME_PAGE are correctly
703 // classified.
704 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
705 NavigationTypeClassification_SamePage) {
706 GURL url1(embedded_test_server()->GetURL(
707 "/navigation_controller/simple_page_1.html"));
708 NavigateToURL(shell(), url1);
710 FrameTreeNode* root =
711 static_cast<WebContentsImpl*>(shell()->web_contents())->
712 GetFrameTree()->root();
715 // Simple load.
716 FrameNavigateParamsCapturer capturer(root);
717 GURL frame_url(embedded_test_server()->GetURL(
718 "/navigation_controller/simple_page_1.html"));
719 NavigateFrameToURL(root, frame_url);
720 capturer.Wait();
721 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
722 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, capturer.details().type);
726 // Verify that navigations for NAVIGATION_TYPE_IN_PAGE are correctly
727 // classified.
728 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
729 NavigationTypeClassification_InPage) {
730 GURL url1(embedded_test_server()->GetURL(
731 "/navigation_controller/simple_page_1.html"));
732 NavigateToURL(shell(), url1);
734 FrameTreeNode* root =
735 static_cast<WebContentsImpl*>(shell()->web_contents())->
736 GetFrameTree()->root();
739 // history.replaceState().
740 FrameNavigateParamsCapturer capturer(root);
741 std::string script =
742 "history.replaceState({}, 'page 1', 'simple_page_2.html')";
743 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
744 capturer.Wait();
745 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
746 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
747 EXPECT_TRUE(capturer.details().is_in_page);
750 // Back and forward across a fragment navigation.
752 GURL url2(embedded_test_server()->GetURL(
753 "/navigation_controller/page_with_links.html"));
754 NavigateToURL(shell(), url2);
755 std::string script = "document.getElementById('fraglink').click()";
756 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
757 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
760 // Back.
761 FrameNavigateParamsCapturer capturer(root);
762 shell()->web_contents()->GetController().GoBack();
763 capturer.Wait();
764 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
765 | ui::PAGE_TRANSITION_FORWARD_BACK
766 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
767 capturer.params().transition);
768 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
769 EXPECT_TRUE(capturer.details().is_in_page);
773 // Forward.
774 FrameNavigateParamsCapturer capturer(root);
775 shell()->web_contents()->GetController().GoForward();
776 capturer.Wait();
777 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
778 capturer.params().transition);
779 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
780 EXPECT_TRUE(capturer.details().is_in_page);
783 // Back and forward across a pushState-created navigation.
785 NavigateToURL(shell(), url1);
786 script = "history.pushState({}, 'page 2', 'simple_page_2.html')";
787 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
788 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
791 // Back.
792 FrameNavigateParamsCapturer capturer(root);
793 shell()->web_contents()->GetController().GoBack();
794 capturer.Wait();
795 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
796 | ui::PAGE_TRANSITION_FORWARD_BACK
797 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
798 capturer.params().transition);
799 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
800 EXPECT_TRUE(capturer.details().is_in_page);
804 // Forward.
805 FrameNavigateParamsCapturer capturer(root);
806 shell()->web_contents()->GetController().GoForward();
807 capturer.Wait();
808 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
809 capturer.params().transition);
810 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
811 EXPECT_TRUE(capturer.details().is_in_page);
815 // Verify that navigations for NAVIGATION_TYPE_NEW_SUBFRAME and
816 // NAVIGATION_TYPE_AUTO_SUBFRAME are properly classified.
817 // TODO(creis): Re-enable this test when https://crbug.com/498559 is fixed.
818 IN_PROC_BROWSER_TEST_F(
819 NavigationControllerBrowserTest,
820 DISABLED_NavigationTypeClassification_NewAndAutoSubframe) {
821 GURL main_url(embedded_test_server()->GetURL(
822 "/navigation_controller/page_with_iframe.html"));
823 NavigateToURL(shell(), main_url);
825 // It is safe to obtain the root frame tree node here, as it doesn't change.
826 FrameTreeNode* root =
827 static_cast<WebContentsImpl*>(shell()->web_contents())->
828 GetFrameTree()->root();
830 ASSERT_EQ(1U, root->child_count());
831 ASSERT_NE(nullptr, root->child_at(0));
834 // Initial load.
835 LoadCommittedCapturer capturer(root->child_at(0));
836 GURL frame_url(embedded_test_server()->GetURL(
837 "/navigation_controller/simple_page_1.html"));
838 NavigateFrameToURL(root->child_at(0), frame_url);
839 capturer.Wait();
840 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
844 // Simple load.
845 FrameNavigateParamsCapturer capturer(root->child_at(0));
846 GURL frame_url(embedded_test_server()->GetURL(
847 "/navigation_controller/simple_page_2.html"));
848 NavigateFrameToURL(root->child_at(0), frame_url);
849 capturer.Wait();
850 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
851 capturer.params().transition);
852 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
856 // Back.
857 FrameNavigateParamsCapturer capturer(root->child_at(0));
858 shell()->web_contents()->GetController().GoBack();
859 capturer.Wait();
860 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
861 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
865 // Forward.
866 FrameNavigateParamsCapturer capturer(root->child_at(0));
867 shell()->web_contents()->GetController().GoForward();
868 capturer.Wait();
869 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
870 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
874 // Simple load.
875 FrameNavigateParamsCapturer capturer(root->child_at(0));
876 GURL frame_url(embedded_test_server()->GetURL(
877 "/navigation_controller/page_with_links.html"));
878 NavigateFrameToURL(root->child_at(0), frame_url);
879 capturer.Wait();
880 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
881 capturer.params().transition);
882 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
886 // Load via a fragment link click.
887 FrameNavigateParamsCapturer capturer(root->child_at(0));
888 std::string script = "document.getElementById('fraglink').click()";
889 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
890 script));
891 capturer.Wait();
892 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
893 capturer.params().transition);
894 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
898 // location.assign().
899 FrameNavigateParamsCapturer capturer(root->child_at(0));
900 GURL frame_url(embedded_test_server()->GetURL(
901 "/navigation_controller/simple_page_1.html"));
902 std::string script = "location.assign('" + frame_url.spec() + "')";
903 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
904 script));
905 capturer.Wait();
906 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
907 capturer.params().transition);
908 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
912 // location.replace().
913 LoadCommittedCapturer capturer(root->child_at(0));
914 GURL frame_url(embedded_test_server()->GetURL(
915 "/navigation_controller/simple_page_2.html"));
916 std::string script = "location.replace('" + frame_url.spec() + "')";
917 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
918 script));
919 capturer.Wait();
920 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
924 // history.pushState().
925 FrameNavigateParamsCapturer capturer(root->child_at(0));
926 std::string script =
927 "history.pushState({}, 'page 1', 'simple_page_1.html')";
928 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
929 script));
930 capturer.Wait();
931 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
932 capturer.params().transition);
933 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
937 // history.replaceState().
938 LoadCommittedCapturer capturer(root->child_at(0));
939 std::string script =
940 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
941 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
942 script));
943 capturer.Wait();
944 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
948 // Reload.
949 LoadCommittedCapturer capturer(root->child_at(0));
950 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
951 "location.reload()"));
952 capturer.Wait();
953 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
957 // Create an iframe.
958 LoadCommittedCapturer capturer(shell()->web_contents());
959 GURL frame_url(embedded_test_server()->GetURL(
960 "/navigation_controller/simple_page_1.html"));
961 std::string script = "var iframe = document.createElement('iframe');"
962 "iframe.src = '" + frame_url.spec() + "';"
963 "document.body.appendChild(iframe);";
964 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
965 capturer.Wait();
966 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
970 // Verify that navigations caused by client-side redirects are correctly
971 // classified.
972 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
973 NavigationTypeClassification_ClientSideRedirect) {
974 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
975 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
977 FrameTreeNode* root =
978 static_cast<WebContentsImpl*>(shell()->web_contents())->
979 GetFrameTree()->root();
982 // Load the redirecting page.
983 FrameNavigateParamsCapturer capturer(root);
984 capturer.set_navigations_remaining(2);
985 GURL frame_url(embedded_test_server()->GetURL(
986 "/navigation_controller/client_redirect.html"));
987 NavigateFrameToURL(root, frame_url);
988 capturer.Wait();
990 std::vector<FrameNavigateParams> params = capturer.all_params();
991 std::vector<LoadCommittedDetails> details = capturer.all_details();
992 ASSERT_EQ(2U, params.size());
993 ASSERT_EQ(2U, details.size());
994 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, params[0].transition);
995 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details[0].type);
996 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
997 params[1].transition);
998 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details[1].type);
1002 // Verify that the LoadCommittedDetails::is_in_page value is properly set for
1003 // non-IN_PAGE navigations. (It's tested for IN_PAGE navigations with the
1004 // NavigationTypeClassification_InPage test.)
1005 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1006 LoadCommittedDetails_IsInPage) {
1007 GURL links_url(embedded_test_server()->GetURL(
1008 "/navigation_controller/page_with_links.html"));
1009 NavigateToURL(shell(), links_url);
1010 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1012 FrameTreeNode* root =
1013 static_cast<WebContentsImpl*>(shell()->web_contents())->
1014 GetFrameTree()->root();
1017 // Do a fragment link click.
1018 FrameNavigateParamsCapturer capturer(root);
1019 std::string script = "document.getElementById('fraglink').click()";
1020 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1021 capturer.Wait();
1022 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1023 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1024 EXPECT_TRUE(capturer.details().is_in_page);
1028 // Do a non-fragment link click.
1029 FrameNavigateParamsCapturer capturer(root);
1030 std::string script = "document.getElementById('thelink').click()";
1031 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1032 capturer.Wait();
1033 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1034 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1035 EXPECT_FALSE(capturer.details().is_in_page);
1038 // Second verse, same as the first. (But in a subframe.)
1040 GURL iframe_url(embedded_test_server()->GetURL(
1041 "/navigation_controller/page_with_iframe.html"));
1042 NavigateToURL(shell(), iframe_url);
1044 root = static_cast<WebContentsImpl*>(shell()->web_contents())->
1045 GetFrameTree()->root();
1047 ASSERT_EQ(1U, root->child_count());
1048 ASSERT_NE(nullptr, root->child_at(0));
1050 NavigateFrameToURL(root->child_at(0), links_url);
1051 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1054 // Do a fragment link click.
1055 FrameNavigateParamsCapturer capturer(root->child_at(0));
1056 std::string script = "document.getElementById('fraglink').click()";
1057 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1058 script));
1059 capturer.Wait();
1060 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1061 capturer.params().transition);
1062 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1063 EXPECT_TRUE(capturer.details().is_in_page);
1067 // Do a non-fragment link click.
1068 FrameNavigateParamsCapturer capturer(root->child_at(0));
1069 std::string script = "document.getElementById('thelink').click()";
1070 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1071 script));
1072 capturer.Wait();
1073 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1074 capturer.params().transition);
1075 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1076 EXPECT_FALSE(capturer.details().is_in_page);
1080 // Verify the tree of FrameNavigationEntries after initial about:blank commits
1081 // in subframes, which should not count as real committed loads.
1082 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1083 FrameNavigationEntry_BlankAutoSubframe) {
1084 GURL main_url(embedded_test_server()->GetURL(
1085 "/navigation_controller/simple_page_1.html"));
1086 NavigateToURL(shell(), main_url);
1087 const NavigationControllerImpl& controller =
1088 static_cast<const NavigationControllerImpl&>(
1089 shell()->web_contents()->GetController());
1090 FrameTreeNode* root =
1091 static_cast<WebContentsImpl*>(shell()->web_contents())->
1092 GetFrameTree()->root();
1094 // 1. Create a iframe with no URL.
1096 LoadCommittedCapturer capturer(shell()->web_contents());
1097 std::string script = "var iframe = document.createElement('iframe');"
1098 "document.body.appendChild(iframe);";
1099 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1100 capturer.Wait();
1101 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1104 // Check last committed NavigationEntry.
1105 EXPECT_EQ(1, controller.GetEntryCount());
1106 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1107 EXPECT_EQ(main_url, entry->GetURL());
1108 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1109 EXPECT_EQ(main_url, root_entry->url());
1111 // Verify no subframe entries are created.
1112 EXPECT_EQ(0U, entry->root_node()->children.size());
1113 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(0)));
1115 // 2. Create another iframe with an about:blank URL.
1117 LoadCommittedCapturer capturer(shell()->web_contents());
1118 std::string script = "var iframe = document.createElement('iframe');"
1119 "iframe.src = 'about:blank';"
1120 "document.body.appendChild(iframe);";
1121 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1122 capturer.Wait();
1123 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1126 // Check last committed NavigationEntry.
1127 EXPECT_EQ(1, controller.GetEntryCount());
1128 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1130 // Verify no subframe entries are created.
1131 EXPECT_EQ(0U, entry->root_node()->children.size());
1132 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(1)));
1134 // 3. A real same-site navigation in the first iframe should be AUTO.
1135 GURL frame_url(embedded_test_server()->GetURL(
1136 "/navigation_controller/simple_page_1.html"));
1138 LoadCommittedCapturer capturer(root->child_at(0));
1139 std::string script = "var frames = document.getElementsByTagName('iframe');"
1140 "frames[0].src = '" + frame_url.spec() + "';";
1141 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1142 capturer.Wait();
1143 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1146 // Check last committed NavigationEntry.
1147 EXPECT_EQ(1, controller.GetEntryCount());
1148 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1150 // Verify subframe entries if we're in --site-per-process mode.
1151 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1152 switches::kSitePerProcess)) {
1153 // The entry should now have one subframe FrameNavigationEntry.
1154 ASSERT_EQ(1U, entry->root_node()->children.size());
1155 FrameNavigationEntry* frame_entry =
1156 entry->root_node()->children[0]->frame_entry.get();
1157 EXPECT_EQ(frame_url, frame_entry->url());
1158 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(0)));
1159 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(1)));
1160 } else {
1161 // There are no subframe FrameNavigationEntries by default.
1162 EXPECT_EQ(0U, entry->root_node()->children.size());
1165 // 4. A real cross-site navigation in the second iframe should be AUTO.
1166 GURL foo_url(embedded_test_server()->GetURL(
1167 "foo.com", "/navigation_controller/simple_page_2.html"));
1169 LoadCommittedCapturer capturer(root->child_at(1));
1170 std::string script = "var frames = document.getElementsByTagName('iframe');"
1171 "frames[1].src = '" + foo_url.spec() + "';";
1172 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1173 capturer.Wait();
1174 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1177 // Check last committed NavigationEntry.
1178 EXPECT_EQ(1, controller.GetEntryCount());
1179 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1181 // Verify subframe entries if we're in --site-per-process mode.
1182 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1183 switches::kSitePerProcess)) {
1184 // The entry should now have two subframe FrameNavigationEntries.
1185 ASSERT_EQ(2U, entry->root_node()->children.size());
1186 FrameNavigationEntry* frame_entry =
1187 entry->root_node()->children[1]->frame_entry.get();
1188 EXPECT_EQ(foo_url, frame_entry->url());
1189 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(1)));
1190 } else {
1191 // There are no subframe FrameNavigationEntries by default.
1192 EXPECT_EQ(0U, entry->root_node()->children.size());
1195 // Check the end result of the frame tree.
1196 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1197 switches::kSitePerProcess)) {
1198 FrameTreeVisualizer visualizer;
1199 EXPECT_EQ(
1200 " Site A ------------ proxies for B\n"
1201 " |--Site A ------- proxies for B\n"
1202 " +--Site B ------- proxies for A\n"
1203 "Where A = http://127.0.0.1/\n"
1204 " B = http://foo.com/",
1205 visualizer.DepictFrameTree(root));
1209 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
1210 // commits.
1211 // TODO(creis): Test updating entries for history auto subframe navigations.
1212 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1213 FrameNavigationEntry_AutoSubframe) {
1214 GURL main_url(embedded_test_server()->GetURL(
1215 "/navigation_controller/simple_page_1.html"));
1216 NavigateToURL(shell(), main_url);
1217 const NavigationControllerImpl& controller =
1218 static_cast<const NavigationControllerImpl&>(
1219 shell()->web_contents()->GetController());
1220 FrameTreeNode* root =
1221 static_cast<WebContentsImpl*>(shell()->web_contents())->
1222 GetFrameTree()->root();
1224 // 1. Create a same-site iframe.
1225 GURL frame_url(embedded_test_server()->GetURL(
1226 "/navigation_controller/simple_page_2.html"));
1228 LoadCommittedCapturer capturer(shell()->web_contents());
1229 std::string script = "var iframe = document.createElement('iframe');"
1230 "iframe.src = '" + frame_url.spec() + "';"
1231 "document.body.appendChild(iframe);";
1232 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1233 capturer.Wait();
1234 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1237 // Check last committed NavigationEntry.
1238 EXPECT_EQ(1, controller.GetEntryCount());
1239 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1240 EXPECT_EQ(main_url, entry->GetURL());
1241 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1242 EXPECT_EQ(main_url, root_entry->url());
1243 EXPECT_FALSE(controller.GetPendingEntry());
1245 // Verify subframe entries if we're in --site-per-process mode.
1246 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1247 switches::kSitePerProcess)) {
1248 // The entry should now have a subframe FrameNavigationEntry.
1249 ASSERT_EQ(1U, entry->root_node()->children.size());
1250 FrameNavigationEntry* frame_entry =
1251 entry->root_node()->children[0]->frame_entry.get();
1252 EXPECT_EQ(frame_url, frame_entry->url());
1253 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(0)));
1254 } else {
1255 // There are no subframe FrameNavigationEntries by default.
1256 EXPECT_EQ(0U, entry->root_node()->children.size());
1259 // 2. Create a second, initially cross-site iframe.
1260 GURL foo_url(embedded_test_server()->GetURL(
1261 "foo.com", "/navigation_controller/simple_page_1.html"));
1263 LoadCommittedCapturer capturer(shell()->web_contents());
1264 std::string script = "var iframe = document.createElement('iframe');"
1265 "iframe.src = '" + foo_url.spec() + "';"
1266 "document.body.appendChild(iframe);";
1267 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1268 capturer.Wait();
1269 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1272 // The last committed NavigationEntry shouldn't have changed.
1273 EXPECT_EQ(1, controller.GetEntryCount());
1274 entry = controller.GetLastCommittedEntry();
1275 EXPECT_EQ(main_url, entry->GetURL());
1276 root_entry = entry->root_node()->frame_entry.get();
1277 EXPECT_EQ(main_url, root_entry->url());
1278 EXPECT_FALSE(controller.GetPendingEntry());
1280 // Verify subframe entries if we're in --site-per-process mode.
1281 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1282 switches::kSitePerProcess)) {
1283 // The entry should now have 2 subframe FrameNavigationEntries.
1284 ASSERT_EQ(2U, entry->root_node()->children.size());
1285 FrameNavigationEntry* frame_entry =
1286 entry->root_node()->children[1]->frame_entry.get();
1287 EXPECT_EQ(foo_url, frame_entry->url());
1288 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(1)));
1289 } else {
1290 // There are no subframe FrameNavigationEntries by default.
1291 EXPECT_EQ(0U, entry->root_node()->children.size());
1294 // 3. Create a nested iframe in the second subframe.
1296 LoadCommittedCapturer capturer(shell()->web_contents());
1297 std::string script = "var iframe = document.createElement('iframe');"
1298 "iframe.src = '" + foo_url.spec() + "';"
1299 "document.body.appendChild(iframe);";
1300 EXPECT_TRUE(content::ExecuteScript(root->child_at(1)->current_frame_host(),
1301 script));
1302 capturer.Wait();
1303 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1306 // The last committed NavigationEntry shouldn't have changed.
1307 EXPECT_EQ(1, controller.GetEntryCount());
1308 entry = controller.GetLastCommittedEntry();
1309 EXPECT_EQ(main_url, entry->GetURL());
1310 root_entry = entry->root_node()->frame_entry.get();
1311 EXPECT_EQ(main_url, root_entry->url());
1313 // Verify subframe entries if we're in --site-per-process mode.
1314 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1315 switches::kSitePerProcess)) {
1316 // The entry should now have 2 subframe FrameNavigationEntries.
1317 ASSERT_EQ(2U, entry->root_node()->children.size());
1318 ASSERT_EQ(1U, entry->root_node()->children[1]->children.size());
1319 FrameNavigationEntry* frame_entry =
1320 entry->root_node()->children[1]->children[0]->frame_entry.get();
1321 EXPECT_EQ(foo_url, frame_entry->url());
1322 } else {
1323 // There are no subframe FrameNavigationEntries by default.
1324 EXPECT_EQ(0U, entry->root_node()->children.size());
1327 // TODO(creis): Add tests for another subframe on B, and for a subframe on A
1328 // within it. Both are currently broken.
1330 // Check the end result of the frame tree.
1331 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1332 switches::kSitePerProcess)) {
1333 FrameTreeVisualizer visualizer;
1334 EXPECT_EQ(
1335 " Site A ------------ proxies for B\n"
1336 " |--Site A ------- proxies for B\n"
1337 " +--Site B ------- proxies for A\n"
1338 " +--Site B -- proxies for A\n"
1339 "Where A = http://127.0.0.1/\n"
1340 " B = http://foo.com/",
1341 visualizer.DepictFrameTree(root));
1345 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_NEW_SUBFRAME
1346 // commits.
1347 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1348 FrameNavigationEntry_NewSubframe) {
1349 GURL main_url(embedded_test_server()->GetURL(
1350 "/navigation_controller/simple_page_1.html"));
1351 NavigateToURL(shell(), main_url);
1352 FrameTreeNode* root =
1353 static_cast<WebContentsImpl*>(shell()->web_contents())->
1354 GetFrameTree()->root();
1356 // 1. Create a same-site iframe.
1357 GURL frame_url(embedded_test_server()->GetURL(
1358 "/navigation_controller/simple_page_2.html"));
1360 LoadCommittedCapturer capturer(shell()->web_contents());
1361 std::string script = "var iframe = document.createElement('iframe');"
1362 "iframe.src = '" + frame_url.spec() + "';"
1363 "document.body.appendChild(iframe);";
1364 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1365 capturer.Wait();
1368 // 2. Navigate in the first subframe same-site.
1369 GURL frame_url2(embedded_test_server()->GetURL(
1370 "/navigation_controller/page_with_links.html"));
1372 FrameNavigateParamsCapturer capturer(root->child_at(0));
1373 NavigateFrameToURL(root->child_at(0), frame_url2);
1374 capturer.Wait();
1375 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1376 capturer.params().transition);
1377 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1380 // 3. Create a second, initially cross-site iframe.
1381 GURL foo_url(embedded_test_server()->GetURL(
1382 "foo.com", "/navigation_controller/simple_page_1.html"));
1384 LoadCommittedCapturer capturer(shell()->web_contents());
1385 std::string script = "var iframe = document.createElement('iframe');"
1386 "iframe.src = '" + foo_url.spec() + "';"
1387 "document.body.appendChild(iframe);";
1388 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1389 capturer.Wait();
1392 // 4. Navigate in the second subframe cross-site.
1393 GURL bar_url(embedded_test_server()->GetURL(
1394 "bar.com", "/navigation_controller/simple_page_1.html"));
1396 FrameNavigateParamsCapturer capturer(root->child_at(1));
1397 std::string script = "var frames = document.getElementsByTagName('iframe');"
1398 "frames[1].src = '" + bar_url.spec() + "';";
1399 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1400 capturer.Wait();
1401 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1402 capturer.params().transition);
1403 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1406 // TODO(creis): Expand this test once we clone FrameNavigationEntries for
1407 // NEW_SUBFRAME navigations.
1409 // Check the end result of the frame tree.
1410 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1411 switches::kSitePerProcess)) {
1412 FrameTreeVisualizer visualizer;
1413 EXPECT_EQ(
1414 " Site A ------------ proxies for B\n"
1415 " |--Site A ------- proxies for B\n"
1416 " +--Site B ------- proxies for A\n"
1417 "Where A = http://127.0.0.1/\n"
1418 " B = http://bar.com/",
1419 visualizer.DepictFrameTree(root));
1423 namespace {
1425 class HttpThrottle : public ResourceThrottle {
1426 public:
1427 // ResourceThrottle
1428 void WillStartRequest(bool* defer) override {
1429 *defer = true;
1432 const char* GetNameForLogging() const override {
1433 return "HttpThrottle";
1437 class StallDelegate : public ResourceDispatcherHostDelegate {
1438 // ResourceDispatcherHostDelegate
1439 void RequestBeginning(
1440 net::URLRequest* request,
1441 content::ResourceContext* resource_context,
1442 content::AppCacheService* appcache_service,
1443 ResourceType resource_type,
1444 ScopedVector<content::ResourceThrottle>* throttles) override {
1445 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1446 throttles->push_back(new HttpThrottle);
1450 // Loads |start_url|, then loads |stalled_url| which stalls. While the page is
1451 // stalled, an in-page navigation happens. Make sure that all the navigations
1452 // are properly classified.
1453 void DoReplaceStateWhilePending(Shell* shell,
1454 const GURL& start_url,
1455 const GURL& stalled_url,
1456 const std::string& replace_state_filename) {
1457 NavigationControllerImpl& controller =
1458 static_cast<NavigationControllerImpl&>(
1459 shell->web_contents()->GetController());
1461 FrameTreeNode* root =
1462 static_cast<WebContentsImpl*>(shell->web_contents())->
1463 GetFrameTree()->root();
1465 // Start with one page.
1466 EXPECT_TRUE(NavigateToURL(shell, start_url));
1468 // Have the user decide to go to a different page which is very slow.
1469 StallDelegate stall_delegate;
1470 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
1471 controller.LoadURL(
1472 stalled_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1474 // That should be the pending entry.
1475 NavigationEntryImpl* entry = controller.GetPendingEntry();
1476 ASSERT_NE(nullptr, entry);
1477 EXPECT_EQ(stalled_url, entry->GetURL());
1480 // Now the existing page uses history.replaceState().
1481 FrameNavigateParamsCapturer capturer(root);
1482 capturer.set_wait_for_load(false);
1483 std::string script =
1484 "history.replaceState({}, '', '" + replace_state_filename + "')";
1485 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1486 capturer.Wait();
1488 // The fact that there was a pending entry shouldn't interfere with the
1489 // classification.
1490 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, capturer.details().type);
1493 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
1496 } // namespace
1498 IN_PROC_BROWSER_TEST_F(
1499 NavigationControllerBrowserTest,
1500 NavigationTypeClassification_On1InPageToXWhile2Pending) {
1501 GURL url1(embedded_test_server()->GetURL(
1502 "/navigation_controller/simple_page_1.html"));
1503 GURL url2(embedded_test_server()->GetURL(
1504 "/navigation_controller/simple_page_2.html"));
1505 DoReplaceStateWhilePending(shell(), url1, url2, "x");
1508 IN_PROC_BROWSER_TEST_F(
1509 NavigationControllerBrowserTest,
1510 NavigationTypeClassification_On1InPageTo2While2Pending) {
1511 GURL url1(embedded_test_server()->GetURL(
1512 "/navigation_controller/simple_page_1.html"));
1513 GURL url2(embedded_test_server()->GetURL(
1514 "/navigation_controller/simple_page_2.html"));
1515 DoReplaceStateWhilePending(shell(), url1, url2, "simple_page_2.html");
1518 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1519 NavigationTypeClassification_On1InPageToXWhile1Pending) {
1520 GURL url(embedded_test_server()->GetURL(
1521 "/navigation_controller/simple_page_1.html"));
1522 DoReplaceStateWhilePending(shell(), url, url, "x");
1525 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1526 NavigationTypeClassification_On1InPageTo1While1Pending) {
1527 GURL url(embedded_test_server()->GetURL(
1528 "/navigation_controller/simple_page_1.html"));
1529 DoReplaceStateWhilePending(shell(), url, url, "simple_page_1.html");
1532 // Ensure the renderer process does not get confused about the current entry
1533 // due to subframes and replaced entries. See https://crbug.com/480201.
1534 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1535 PreventSpoofFromSubframeAndReplace) {
1536 // Start at an initial URL.
1537 GURL url1(embedded_test_server()->GetURL(
1538 "/navigation_controller/simple_page_1.html"));
1539 NavigateToURL(shell(), url1);
1541 // Now go to a page with a real iframe.
1542 GURL url2(embedded_test_server()->GetURL(
1543 "/navigation_controller/page_with_data_iframe.html"));
1544 NavigateToURL(shell(), url2);
1546 // It is safe to obtain the root frame tree node here, as it doesn't change.
1547 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1548 ->GetFrameTree()
1549 ->root();
1550 ASSERT_EQ(1U, root->child_count());
1551 ASSERT_NE(nullptr, root->child_at(0));
1554 // Navigate in the iframe.
1555 FrameNavigateParamsCapturer capturer(root->child_at(0));
1556 GURL frame_url(embedded_test_server()->GetURL(
1557 "/navigation_controller/simple_page_2.html"));
1558 NavigateFrameToURL(root->child_at(0), frame_url);
1559 capturer.Wait();
1560 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1564 // Go back in the iframe.
1565 TestNavigationObserver back_load_observer(shell()->web_contents());
1566 shell()->web_contents()->GetController().GoBack();
1567 back_load_observer.Wait();
1571 // Go forward in the iframe.
1572 TestNavigationObserver forward_load_observer(shell()->web_contents());
1573 shell()->web_contents()->GetController().GoForward();
1574 forward_load_observer.Wait();
1577 GURL url3(embedded_test_server()->GetURL(
1578 "/navigation_controller/page_with_iframe.html"));
1580 // location.replace() to cause an inert commit.
1581 TestNavigationObserver replace_load_observer(shell()->web_contents());
1582 std::string script = "location.replace('" + url3.spec() + "')";
1583 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1584 replace_load_observer.Wait();
1588 // Go back to url2.
1589 TestNavigationObserver back_load_observer(shell()->web_contents());
1590 shell()->web_contents()->GetController().GoBack();
1591 back_load_observer.Wait();
1593 // Make sure the URL is correct for both the entry and the main frame, and
1594 // that the process hasn't been killed for showing a spoof.
1595 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1596 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
1597 EXPECT_EQ(url2, root->current_url());
1601 // Go back to reset main frame entirely.
1602 TestNavigationObserver back_load_observer(shell()->web_contents());
1603 shell()->web_contents()->GetController().GoBack();
1604 back_load_observer.Wait();
1605 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
1606 EXPECT_EQ(url1, root->current_url());
1610 // Go forward.
1611 TestNavigationObserver back_load_observer(shell()->web_contents());
1612 shell()->web_contents()->GetController().GoForward();
1613 back_load_observer.Wait();
1614 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
1615 EXPECT_EQ(url2, root->current_url());
1619 // Go forward to the replaced URL.
1620 TestNavigationObserver forward_load_observer(shell()->web_contents());
1621 shell()->web_contents()->GetController().GoForward();
1622 forward_load_observer.Wait();
1624 // Make sure the URL is correct for both the entry and the main frame, and
1625 // that the process hasn't been killed for showing a spoof.
1626 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1627 EXPECT_EQ(url3, shell()->web_contents()->GetLastCommittedURL());
1628 EXPECT_EQ(url3, root->current_url());
1632 // Ensure the renderer process does not get killed if the main frame URL's path
1633 // changes when going back in a subframe, since this is currently possible after
1634 // a replaceState in the main frame (thanks to https://crbug.com/373041).
1635 // See https:///crbug.com/486916.
1636 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1637 SubframeBackFromReplaceState) {
1638 // Start at a page with a real iframe.
1639 GURL url1(embedded_test_server()->GetURL(
1640 "/navigation_controller/page_with_data_iframe.html"));
1641 NavigateToURL(shell(), url1);
1643 // It is safe to obtain the root frame tree node here, as it doesn't change.
1644 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1645 ->GetFrameTree()
1646 ->root();
1647 ASSERT_EQ(1U, root->child_count());
1648 ASSERT_NE(nullptr, root->child_at(0));
1651 // Navigate in the iframe.
1652 FrameNavigateParamsCapturer capturer(root->child_at(0));
1653 GURL frame_url(embedded_test_server()->GetURL(
1654 "/navigation_controller/simple_page_2.html"));
1655 NavigateFrameToURL(root->child_at(0), frame_url);
1656 capturer.Wait();
1657 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1661 // history.replaceState().
1662 FrameNavigateParamsCapturer capturer(root);
1663 std::string script =
1664 "history.replaceState({}, 'replaced', 'replaced')";
1665 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1666 capturer.Wait();
1670 // Go back in the iframe.
1671 TestNavigationObserver back_load_observer(shell()->web_contents());
1672 shell()->web_contents()->GetController().GoBack();
1673 back_load_observer.Wait();
1676 // For now, we expect the main frame's URL to revert. This won't happen once
1677 // https://crbug.com/373041 is fixed.
1678 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
1680 // Make sure the renderer process has not been killed.
1681 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1684 namespace {
1686 class FailureWatcher : public WebContentsObserver {
1687 public:
1688 // Observes failure for the specified |node|.
1689 explicit FailureWatcher(FrameTreeNode* node)
1690 : WebContentsObserver(
1691 node->current_frame_host()->delegate()->GetAsWebContents()),
1692 frame_tree_node_id_(node->frame_tree_node_id()),
1693 message_loop_runner_(new MessageLoopRunner) {}
1695 void Wait() {
1696 message_loop_runner_->Run();
1699 private:
1700 void DidFailLoad(RenderFrameHost* render_frame_host,
1701 const GURL& validated_url,
1702 int error_code,
1703 const base::string16& error_description) override {
1704 RenderFrameHostImpl* rfh =
1705 static_cast<RenderFrameHostImpl*>(render_frame_host);
1706 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
1707 return;
1709 message_loop_runner_->Quit();
1712 void DidFailProvisionalLoad(
1713 RenderFrameHost* render_frame_host,
1714 const GURL& validated_url,
1715 int error_code,
1716 const base::string16& error_description) override {
1717 RenderFrameHostImpl* rfh =
1718 static_cast<RenderFrameHostImpl*>(render_frame_host);
1719 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
1720 return;
1722 message_loop_runner_->Quit();
1725 // The id of the FrameTreeNode whose navigations to observe.
1726 int frame_tree_node_id_;
1728 // The MessageLoopRunner used to spin the message loop.
1729 scoped_refptr<MessageLoopRunner> message_loop_runner_;
1732 } // namespace
1734 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1735 StopCausesFailureDespiteJavaScriptURL) {
1736 NavigationControllerImpl& controller =
1737 static_cast<NavigationControllerImpl&>(
1738 shell()->web_contents()->GetController());
1740 FrameTreeNode* root =
1741 static_cast<WebContentsImpl*>(shell()->web_contents())->
1742 GetFrameTree()->root();
1744 // Start with a normal page.
1745 GURL url1(embedded_test_server()->GetURL(
1746 "/navigation_controller/simple_page_1.html"));
1747 EXPECT_TRUE(NavigateToURL(shell(), url1));
1749 // Have the user decide to go to a different page which is very slow.
1750 StallDelegate stall_delegate;
1751 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
1752 GURL url2(embedded_test_server()->GetURL(
1753 "/navigation_controller/simple_page_2.html"));
1754 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1756 // That should be the pending entry.
1757 NavigationEntryImpl* entry = controller.GetPendingEntry();
1758 ASSERT_NE(nullptr, entry);
1759 EXPECT_EQ(url2, entry->GetURL());
1761 // Loading a JavaScript URL shouldn't affect the ability to stop.
1763 FailureWatcher watcher(root);
1764 GURL js("javascript:(function(){})()");
1765 controller.LoadURL(js, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1766 // This LoadURL ends up purging the pending entry, which is why this is
1767 // tricky.
1768 EXPECT_EQ(nullptr, controller.GetPendingEntry());
1769 shell()->web_contents()->Stop();
1770 watcher.Wait();
1773 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
1776 } // namespace content