Don't clear the forward history when replacing entries.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_browsertest.cc
blob5cd628898f60a50d9dc838872df503e3c2d95865
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 "base/strings/utf_string_conversions.h"
9 #include "content/browser/frame_host/frame_navigation_entry.h"
10 #include "content/browser/frame_host/frame_tree.h"
11 #include "content/browser/frame_host/navigation_controller_impl.h"
12 #include "content/browser/frame_host/navigation_entry_impl.h"
13 #include "content/browser/web_contents/web_contents_impl.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/resource_controller.h"
16 #include "content/public/browser/resource_dispatcher_host.h"
17 #include "content/public/browser/resource_dispatcher_host_delegate.h"
18 #include "content/public/browser/resource_throttle.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_observer.h"
21 #include "content/public/common/bindings_policy.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/common/url_constants.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "content/public/test/content_browser_test.h"
26 #include "content/public/test/content_browser_test_utils.h"
27 #include "content/public/test/test_navigation_observer.h"
28 #include "content/public/test/test_utils.h"
29 #include "content/shell/browser/shell.h"
30 #include "content/test/content_browser_test_utils_internal.h"
31 #include "net/dns/mock_host_resolver.h"
32 #include "net/test/embedded_test_server/embedded_test_server.h"
33 #include "net/test/url_request/url_request_failed_job.h"
35 namespace content {
37 class NavigationControllerBrowserTest : public ContentBrowserTest {
38 protected:
39 void SetUpOnMainThread() override {
40 host_resolver()->AddRule("*", "127.0.0.1");
41 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
45 // Ensure that tests can navigate subframes cross-site in both default mode and
46 // --site-per-process, but that they only go cross-process in the latter.
47 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadCrossSiteSubframe) {
48 // Load a main frame with a subframe.
49 GURL main_url(embedded_test_server()->GetURL(
50 "/navigation_controller/page_with_iframe.html"));
51 NavigateToURL(shell(), main_url);
52 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
53 ->GetFrameTree()
54 ->root();
55 ASSERT_EQ(1U, root->child_count());
56 ASSERT_NE(nullptr, root->child_at(0));
58 // Use NavigateFrameToURL to go cross-site in the subframe.
59 GURL foo_url(embedded_test_server()->GetURL(
60 "foo.com", "/navigation_controller/simple_page_1.html"));
61 NavigateFrameToURL(root->child_at(0), foo_url);
62 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
64 // We should only have swapped processes in --site-per-process.
65 bool cross_process = root->current_frame_host()->GetProcess() !=
66 root->child_at(0)->current_frame_host()->GetProcess();
67 EXPECT_EQ(base::CommandLine::ForCurrentProcess()->HasSwitch(
68 switches::kSitePerProcess),
69 cross_process);
72 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadDataWithBaseURL) {
73 const GURL base_url("http://baseurl");
74 const GURL history_url("http://historyurl");
75 const std::string data = "<html><body>foo</body></html>";
77 const NavigationController& controller =
78 shell()->web_contents()->GetController();
79 // Load data. Blocks until it is done.
80 content::LoadDataWithBaseURL(shell(), history_url, data, base_url);
82 // We should use history_url instead of the base_url as the original url of
83 // this navigation entry, because base_url is only used for resolving relative
84 // paths in the data, or enforcing same origin policy.
85 EXPECT_EQ(controller.GetVisibleEntry()->GetOriginalRequestURL(), history_url);
88 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, UniqueIDs) {
89 const NavigationControllerImpl& controller =
90 static_cast<const NavigationControllerImpl&>(
91 shell()->web_contents()->GetController());
93 GURL main_url(embedded_test_server()->GetURL(
94 "/navigation_controller/page_with_link_to_load_iframe.html"));
95 NavigateToURL(shell(), main_url);
96 ASSERT_EQ(1, controller.GetEntryCount());
98 // Use JavaScript to click the link and load the iframe.
99 std::string script = "document.getElementById('link').click()";
100 EXPECT_TRUE(content::ExecuteScript(shell()->web_contents(), script));
101 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
102 ASSERT_EQ(2, controller.GetEntryCount());
104 // Unique IDs should... um... be unique.
105 ASSERT_NE(controller.GetEntryAtIndex(0)->GetUniqueID(),
106 controller.GetEntryAtIndex(1)->GetUniqueID());
109 // This test used to make sure that a scheme used to prevent spoofs didn't ever
110 // interfere with navigations. We switched to a different scheme, so now this is
111 // just a test to make sure we can still navigate once we prune the history
112 // list.
113 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
114 DontIgnoreBackAfterNavEntryLimit) {
115 NavigationController& controller =
116 shell()->web_contents()->GetController();
118 const int kMaxEntryCount =
119 static_cast<int>(NavigationControllerImpl::max_entry_count());
121 // Load up to the max count, all entries should be there.
122 for (int url_index = 0; url_index < kMaxEntryCount; ++url_index) {
123 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
124 EXPECT_TRUE(NavigateToURL(shell(), url));
127 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
129 // Navigate twice more more.
130 for (int url_index = kMaxEntryCount;
131 url_index < kMaxEntryCount + 2; ++url_index) {
132 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
133 EXPECT_TRUE(NavigateToURL(shell(), url));
136 // We expect page0 and page1 to be gone.
137 EXPECT_EQ(kMaxEntryCount, controller.GetEntryCount());
138 EXPECT_EQ(GURL("data:text/html,page2"),
139 controller.GetEntryAtIndex(0)->GetURL());
141 // Now try to go back. This should not hang.
142 ASSERT_TRUE(controller.CanGoBack());
143 controller.GoBack();
144 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
146 // This should have successfully gone back.
147 EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount)),
148 controller.GetLastCommittedEntry()->GetURL());
151 namespace {
153 int RendererHistoryLength(Shell* shell) {
154 int value = 0;
155 EXPECT_TRUE(ExecuteScriptAndExtractInt(
156 shell->web_contents(),
157 "domAutomationController.send(history.length)",
158 &value));
159 return value;
162 // Similar to the ones from content_browser_test_utils.
163 bool NavigateToURLAndReplace(Shell* shell, const GURL& url) {
164 WebContents* web_contents = shell->web_contents();
165 WaitForLoadStop(web_contents);
166 TestNavigationObserver same_tab_observer(web_contents, 1);
167 NavigationController::LoadURLParams params(url);
168 params.should_replace_current_entry = true;
169 web_contents->GetController().LoadURLWithParams(params);
170 web_contents->Focus();
171 same_tab_observer.Wait();
172 if (!IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL))
173 return false;
174 return web_contents->GetLastCommittedURL() == url;
177 } // namespace
179 // When loading a new page to replace an old page in the history list, make sure
180 // that the browser and renderer agree, and that both get it right.
181 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
182 CorrectLengthWithCurrentItemReplacement) {
183 NavigationController& controller =
184 shell()->web_contents()->GetController();
186 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
187 EXPECT_EQ(1, controller.GetEntryCount());
188 EXPECT_EQ(1, RendererHistoryLength(shell()));
190 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page1a")));
191 EXPECT_EQ(1, controller.GetEntryCount());
192 EXPECT_EQ(1, RendererHistoryLength(shell()));
194 // Now create two more entries and go back, to test replacing an entry without
195 // pruning the forward history.
196 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page2")));
197 EXPECT_EQ(2, controller.GetEntryCount());
198 EXPECT_EQ(2, RendererHistoryLength(shell()));
200 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page3")));
201 EXPECT_EQ(3, controller.GetEntryCount());
202 EXPECT_EQ(3, RendererHistoryLength(shell()));
204 controller.GoBack();
205 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
206 controller.GoBack();
207 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
208 EXPECT_TRUE(controller.CanGoForward());
210 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page1b")));
211 EXPECT_EQ(3, controller.GetEntryCount());
212 EXPECT_EQ(3, RendererHistoryLength(shell()));
213 EXPECT_TRUE(controller.CanGoForward());
215 // Note that there's no way to access the renderer's notion of the history
216 // offset via JavaScript. Checking just the history length, though, is enough;
217 // if the replacement failed, there would be a new history entry and thus an
218 // incorrect length.
221 // When spawning a new page from a WebUI page, make sure that the browser and
222 // renderer agree about the length of the history list, and that both get it
223 // right.
224 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
225 CorrectLengthWithNewTabNavigatingFromWebUI) {
226 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
227 std::string(kChromeUIGpuHost));
228 EXPECT_TRUE(NavigateToURL(shell(), web_ui_page));
229 EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
230 shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
232 ShellAddedObserver observer;
233 std::string page_url = embedded_test_server()->GetURL(
234 "/navigation_controller/simple_page_1.html").spec();
235 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
236 "window.open('" + page_url + "', '_blank')"));
237 Shell* shell2 = observer.GetShell();
238 EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
240 EXPECT_EQ(1, shell2->web_contents()->GetController().GetEntryCount());
241 EXPECT_EQ(1, RendererHistoryLength(shell2));
243 // Again, as above, there's no way to access the renderer's notion of the
244 // history offset via JavaScript. Checking just the history length, again,
245 // will have to suffice.
248 namespace {
250 class NoNavigationsObserver : public WebContentsObserver {
251 public:
252 // Observes navigation for the specified |web_contents|.
253 explicit NoNavigationsObserver(WebContents* web_contents)
254 : WebContentsObserver(web_contents) {}
256 private:
257 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
258 const LoadCommittedDetails& details,
259 const FrameNavigateParams& params) override {
260 FAIL() << "No navigations should occur";
264 } // namespace
266 // Some pages create a popup, then write an iframe into it. This causes a
267 // subframe navigation without having any committed entry. Such navigations
268 // just get thrown on the ground, but we shouldn't crash.
270 // This test actually hits NAVIGATION_TYPE_NAV_IGNORE three times. Two of them,
271 // the initial window.open() and the iframe creation, don't try to create
272 // navigation entries, and the third, the new navigation, tries to.
273 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, SubframeOnEmptyPage) {
274 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
275 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
277 FrameTreeNode* root =
278 static_cast<WebContentsImpl*>(shell()->web_contents())->
279 GetFrameTree()->root();
281 // Pop open a new window.
282 ShellAddedObserver new_shell_observer;
283 std::string script = "window.open()";
284 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
285 Shell* new_shell = new_shell_observer.GetShell();
286 ASSERT_NE(new_shell->web_contents(), shell()->web_contents());
287 FrameTreeNode* new_root =
288 static_cast<WebContentsImpl*>(new_shell->web_contents())->
289 GetFrameTree()->root();
291 // Make a new iframe in it.
292 NoNavigationsObserver observer(new_shell->web_contents());
293 script = "var iframe = document.createElement('iframe');"
294 "iframe.src = 'data:text/html,<p>some page</p>';"
295 "document.body.appendChild(iframe);";
296 EXPECT_TRUE(content::ExecuteScript(new_root->current_frame_host(), script));
297 // The success check is of the last-committed entry, and there is none.
298 WaitForLoadStopWithoutSuccessCheck(new_shell->web_contents());
300 ASSERT_EQ(1U, new_root->child_count());
301 ASSERT_NE(nullptr, new_root->child_at(0));
303 // Navigate it.
304 GURL frame_url = embedded_test_server()->GetURL(
305 "/navigation_controller/simple_page_2.html");
306 script = "location.assign('" + frame_url.spec() + "')";
307 EXPECT_TRUE(content::ExecuteScript(
308 new_root->child_at(0)->current_frame_host(), script));
310 // Success is not crashing, and not navigating.
311 EXPECT_EQ(nullptr,
312 new_shell->web_contents()->GetController().GetLastCommittedEntry());
315 namespace {
317 class FrameNavigateParamsCapturer : public WebContentsObserver {
318 public:
319 // Observes navigation for the specified |node|.
320 explicit FrameNavigateParamsCapturer(FrameTreeNode* node)
321 : WebContentsObserver(
322 node->current_frame_host()->delegate()->GetAsWebContents()),
323 frame_tree_node_id_(node->frame_tree_node_id()),
324 navigations_remaining_(1),
325 wait_for_load_(true),
326 message_loop_runner_(new MessageLoopRunner) {}
328 void set_navigations_remaining(int count) {
329 navigations_remaining_ = count;
332 void set_wait_for_load(bool ignore) {
333 wait_for_load_ = ignore;
336 void Wait() {
337 message_loop_runner_->Run();
340 const FrameNavigateParams& params() const {
341 EXPECT_EQ(1U, params_.size());
342 return params_[0];
345 const std::vector<FrameNavigateParams>& all_params() const {
346 return params_;
349 const LoadCommittedDetails& details() const {
350 EXPECT_EQ(1U, details_.size());
351 return details_[0];
354 const std::vector<LoadCommittedDetails>& all_details() const {
355 return details_;
358 private:
359 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
360 const LoadCommittedDetails& details,
361 const FrameNavigateParams& params) override {
362 RenderFrameHostImpl* rfh =
363 static_cast<RenderFrameHostImpl*>(render_frame_host);
364 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
365 return;
367 --navigations_remaining_;
368 params_.push_back(params);
369 details_.push_back(details);
370 if (!navigations_remaining_ &&
371 (!web_contents()->IsLoading() || !wait_for_load_))
372 message_loop_runner_->Quit();
375 void DidStopLoading() override {
376 if (!navigations_remaining_)
377 message_loop_runner_->Quit();
380 // The id of the FrameTreeNode whose navigations to observe.
381 int frame_tree_node_id_;
383 // How many navigations remain to capture.
384 int navigations_remaining_;
386 // Whether to also wait for the load to complete.
387 bool wait_for_load_;
389 // The params of the navigations.
390 std::vector<FrameNavigateParams> params_;
392 // The details of the navigations.
393 std::vector<LoadCommittedDetails> details_;
395 // The MessageLoopRunner used to spin the message loop.
396 scoped_refptr<MessageLoopRunner> message_loop_runner_;
399 class LoadCommittedCapturer : public WebContentsObserver {
400 public:
401 // Observes the load commit for the specified |node|.
402 explicit LoadCommittedCapturer(FrameTreeNode* node)
403 : WebContentsObserver(
404 node->current_frame_host()->delegate()->GetAsWebContents()),
405 frame_tree_node_id_(node->frame_tree_node_id()),
406 message_loop_runner_(new MessageLoopRunner) {}
408 // Observes the load commit for the next created frame in the specified
409 // |web_contents|.
410 explicit LoadCommittedCapturer(WebContents* web_contents)
411 : WebContentsObserver(web_contents),
412 frame_tree_node_id_(0),
413 message_loop_runner_(new MessageLoopRunner) {}
415 void Wait() {
416 message_loop_runner_->Run();
419 ui::PageTransition transition_type() const {
420 return transition_type_;
423 private:
424 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
425 RenderFrameHostImpl* rfh =
426 static_cast<RenderFrameHostImpl*>(render_frame_host);
428 // Don't pay attention to swapped out RenderFrameHosts in the main frame.
429 // TODO(nasko): Remove once swappedout:// is gone.
430 // See https://crbug.com/357747.
431 if (!RenderFrameHostImpl::IsRFHStateActive(rfh->rfh_state())) {
432 DLOG(INFO) << "Skipping swapped out RFH: "
433 << rfh->GetSiteInstance()->GetSiteURL();
434 return;
437 // If this object was not created with a specified frame tree node, then use
438 // the first created active RenderFrameHost. Once a node is selected, there
439 // shouldn't be any other frames being created.
440 int frame_tree_node_id = rfh->frame_tree_node()->frame_tree_node_id();
441 DCHECK(frame_tree_node_id_ == 0 ||
442 frame_tree_node_id_ == frame_tree_node_id);
443 frame_tree_node_id_ = frame_tree_node_id;
446 void DidCommitProvisionalLoadForFrame(
447 RenderFrameHost* render_frame_host,
448 const GURL& url,
449 ui::PageTransition transition_type) override {
450 DCHECK_NE(0, frame_tree_node_id_);
451 RenderFrameHostImpl* rfh =
452 static_cast<RenderFrameHostImpl*>(render_frame_host);
453 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
454 return;
456 transition_type_ = transition_type;
457 if (!web_contents()->IsLoading())
458 message_loop_runner_->Quit();
461 void DidStopLoading() override { message_loop_runner_->Quit(); }
463 // The id of the FrameTreeNode whose navigations to observe.
464 int frame_tree_node_id_;
466 // The transition_type of the last navigation.
467 ui::PageTransition transition_type_;
469 // The MessageLoopRunner used to spin the message loop.
470 scoped_refptr<MessageLoopRunner> message_loop_runner_;
473 } // namespace
475 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
476 ErrorPageReplacement) {
477 NavigationController& controller = shell()->web_contents()->GetController();
478 GURL error_url(
479 net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET));
480 net::URLRequestFailedJob::AddUrlHandler();
482 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
483 EXPECT_EQ(1, controller.GetEntryCount());
485 FrameTreeNode* root =
486 static_cast<WebContentsImpl*>(shell()->web_contents())->
487 GetFrameTree()->root();
489 // Navigate to a page that fails to load. It must result in an error page, the
490 // NEW_PAGE navigation type, and an addition to the history list.
492 FrameNavigateParamsCapturer capturer(root);
493 NavigateFrameToURL(root, error_url);
494 capturer.Wait();
495 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
496 NavigationEntry* entry = controller.GetLastCommittedEntry();
497 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
498 EXPECT_EQ(2, controller.GetEntryCount());
501 // Navigate again to the page that fails to load. It must result in an error
502 // page, the EXISTING_PAGE navigation type, and no addition to the history
503 // list. We do not use SAME_PAGE here; that case only differs in that it
504 // clears the pending entry, and there is no pending entry after a load
505 // failure.
507 FrameNavigateParamsCapturer capturer(root);
508 NavigateFrameToURL(root, error_url);
509 capturer.Wait();
510 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
511 NavigationEntry* entry = controller.GetLastCommittedEntry();
512 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
513 EXPECT_EQ(2, controller.GetEntryCount());
516 // Make a new entry ...
517 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
518 EXPECT_EQ(3, controller.GetEntryCount());
520 // ... and replace it with a failed load. (Note that when you set the
521 // should_replace_current_entry flag, the navigation is classified as NEW_PAGE
522 // because that is a classification of the renderer's behavior, and the flag
523 // is a browser-side flag.)
525 FrameNavigateParamsCapturer capturer(root);
526 NavigateToURLAndReplace(shell(), error_url);
527 capturer.Wait();
528 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
529 NavigationEntry* entry = controller.GetLastCommittedEntry();
530 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
531 EXPECT_EQ(3, controller.GetEntryCount());
534 // Make a new web ui page to force a process swap ...
535 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
536 std::string(kChromeUIGpuHost));
537 NavigateToURL(shell(), web_ui_page);
538 EXPECT_EQ(4, controller.GetEntryCount());
540 // ... and replace it with a failed load. (It is NEW_PAGE for the reason noted
541 // above.)
543 FrameNavigateParamsCapturer capturer(root);
544 NavigateToURLAndReplace(shell(), error_url);
545 capturer.Wait();
546 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
547 NavigationEntry* entry = controller.GetLastCommittedEntry();
548 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
549 EXPECT_EQ(4, controller.GetEntryCount());
553 // Various tests for navigation type classifications. TODO(avi): It's rather
554 // bogus that the same info is in two different enums; http://crbug.com/453555.
556 // Verify that navigations for NAVIGATION_TYPE_NEW_PAGE are correctly
557 // classified.
558 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
559 NavigationTypeClassification_NewPage) {
560 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
562 FrameTreeNode* root =
563 static_cast<WebContentsImpl*>(shell()->web_contents())->
564 GetFrameTree()->root();
567 // Simple load.
568 FrameNavigateParamsCapturer capturer(root);
569 GURL frame_url(embedded_test_server()->GetURL(
570 "/navigation_controller/page_with_links.html"));
571 NavigateFrameToURL(root, frame_url);
572 capturer.Wait();
573 // TODO(avi,creis): Why is this (and quite a few others below) a "link"
574 // transition? Lots of these transitions should be cleaned up.
575 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
576 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
577 EXPECT_FALSE(capturer.details().is_in_page);
581 // Load via a fragment link click.
582 FrameNavigateParamsCapturer capturer(root);
583 std::string script = "document.getElementById('fraglink').click()";
584 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
585 capturer.Wait();
586 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
587 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
588 EXPECT_TRUE(capturer.details().is_in_page);
592 // Load via link click.
593 FrameNavigateParamsCapturer capturer(root);
594 std::string script = "document.getElementById('thelink').click()";
595 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
596 capturer.Wait();
597 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
598 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
599 EXPECT_FALSE(capturer.details().is_in_page);
603 // location.assign().
604 FrameNavigateParamsCapturer capturer(root);
605 GURL frame_url(embedded_test_server()->GetURL(
606 "/navigation_controller/simple_page_2.html"));
607 std::string script = "location.assign('" + frame_url.spec() + "')";
608 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
609 capturer.Wait();
610 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
611 capturer.params().transition);
612 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
613 EXPECT_FALSE(capturer.details().is_in_page);
617 // history.pushState().
618 FrameNavigateParamsCapturer capturer(root);
619 std::string script =
620 "history.pushState({}, 'page 1', 'simple_page_1.html')";
621 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
622 capturer.Wait();
623 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
624 capturer.params().transition);
625 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
626 EXPECT_TRUE(capturer.details().is_in_page);
630 // Verify that navigations for NAVIGATION_TYPE_EXISTING_PAGE are correctly
631 // classified.
632 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
633 NavigationTypeClassification_ExistingPage) {
634 GURL url1(embedded_test_server()->GetURL(
635 "/navigation_controller/simple_page_1.html"));
636 NavigateToURL(shell(), url1);
637 GURL url2(embedded_test_server()->GetURL(
638 "/navigation_controller/simple_page_2.html"));
639 NavigateToURL(shell(), url2);
641 FrameTreeNode* root =
642 static_cast<WebContentsImpl*>(shell()->web_contents())->
643 GetFrameTree()->root();
646 // Back from the browser side.
647 FrameNavigateParamsCapturer capturer(root);
648 shell()->web_contents()->GetController().GoBack();
649 capturer.Wait();
650 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
651 | ui::PAGE_TRANSITION_FORWARD_BACK
652 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
653 capturer.params().transition);
654 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
655 EXPECT_FALSE(capturer.details().is_in_page);
659 // Forward from the browser side.
660 FrameNavigateParamsCapturer capturer(root);
661 shell()->web_contents()->GetController().GoForward();
662 capturer.Wait();
663 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
664 | ui::PAGE_TRANSITION_FORWARD_BACK
665 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
666 capturer.params().transition);
667 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
668 EXPECT_FALSE(capturer.details().is_in_page);
672 // Back from the renderer side.
673 FrameNavigateParamsCapturer capturer(root);
674 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
675 "history.back()"));
676 capturer.Wait();
677 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
678 | ui::PAGE_TRANSITION_FORWARD_BACK
679 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
680 capturer.params().transition);
681 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
682 EXPECT_FALSE(capturer.details().is_in_page);
686 // Forward from the renderer side.
687 FrameNavigateParamsCapturer capturer(root);
688 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
689 "history.forward()"));
690 capturer.Wait();
691 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
692 | ui::PAGE_TRANSITION_FORWARD_BACK
693 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
694 capturer.params().transition);
695 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
696 EXPECT_FALSE(capturer.details().is_in_page);
700 // Back from the renderer side via history.go().
701 FrameNavigateParamsCapturer capturer(root);
702 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
703 "history.go(-1)"));
704 capturer.Wait();
705 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
706 | ui::PAGE_TRANSITION_FORWARD_BACK
707 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
708 capturer.params().transition);
709 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
710 EXPECT_FALSE(capturer.details().is_in_page);
714 // Forward from the renderer side via history.go().
715 FrameNavigateParamsCapturer capturer(root);
716 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
717 "history.go(1)"));
718 capturer.Wait();
719 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
720 | ui::PAGE_TRANSITION_FORWARD_BACK
721 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
722 capturer.params().transition);
723 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
724 EXPECT_FALSE(capturer.details().is_in_page);
728 // Reload from the browser side.
729 FrameNavigateParamsCapturer capturer(root);
730 shell()->web_contents()->GetController().Reload(false);
731 capturer.Wait();
732 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD, capturer.params().transition);
733 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
734 EXPECT_FALSE(capturer.details().is_in_page);
738 // Reload from the renderer side.
739 FrameNavigateParamsCapturer capturer(root);
740 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
741 "location.reload()"));
742 capturer.Wait();
743 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
744 capturer.params().transition);
745 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
746 EXPECT_FALSE(capturer.details().is_in_page);
750 // location.replace().
751 FrameNavigateParamsCapturer capturer(root);
752 GURL frame_url(embedded_test_server()->GetURL(
753 "/navigation_controller/simple_page_1.html"));
754 std::string script = "location.replace('" + frame_url.spec() + "')";
755 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
756 capturer.Wait();
757 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
758 capturer.params().transition);
759 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
760 EXPECT_FALSE(capturer.details().is_in_page);
763 // Now, various in-page navigations.
766 // history.replaceState().
767 FrameNavigateParamsCapturer capturer(root);
768 std::string script =
769 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
770 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
771 capturer.Wait();
772 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
773 capturer.params().transition);
774 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
775 EXPECT_TRUE(capturer.details().is_in_page);
778 // Back and forward across a fragment navigation.
780 GURL url_links(embedded_test_server()->GetURL(
781 "/navigation_controller/page_with_links.html"));
782 NavigateToURL(shell(), url_links);
783 std::string script = "document.getElementById('fraglink').click()";
784 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
785 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
788 // Back.
789 FrameNavigateParamsCapturer capturer(root);
790 shell()->web_contents()->GetController().GoBack();
791 capturer.Wait();
792 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
793 | ui::PAGE_TRANSITION_FORWARD_BACK
794 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
795 capturer.params().transition);
796 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
797 EXPECT_TRUE(capturer.details().is_in_page);
801 // Forward.
802 FrameNavigateParamsCapturer capturer(root);
803 shell()->web_contents()->GetController().GoForward();
804 capturer.Wait();
805 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
806 capturer.params().transition);
807 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
808 EXPECT_TRUE(capturer.details().is_in_page);
811 // Back and forward across a pushState-created navigation.
813 NavigateToURL(shell(), url1);
814 script = "history.pushState({}, 'page 2', 'simple_page_2.html')";
815 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
816 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
819 // Back.
820 FrameNavigateParamsCapturer capturer(root);
821 shell()->web_contents()->GetController().GoBack();
822 capturer.Wait();
823 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
824 | ui::PAGE_TRANSITION_FORWARD_BACK
825 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
826 capturer.params().transition);
827 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
828 EXPECT_TRUE(capturer.details().is_in_page);
832 // Forward.
833 FrameNavigateParamsCapturer capturer(root);
834 shell()->web_contents()->GetController().GoForward();
835 capturer.Wait();
836 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
837 capturer.params().transition);
838 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
839 EXPECT_TRUE(capturer.details().is_in_page);
843 // Verify that navigations for NAVIGATION_TYPE_SAME_PAGE are correctly
844 // classified.
845 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
846 NavigationTypeClassification_SamePage) {
847 GURL url1(embedded_test_server()->GetURL(
848 "/navigation_controller/simple_page_1.html"));
849 NavigateToURL(shell(), url1);
851 FrameTreeNode* root =
852 static_cast<WebContentsImpl*>(shell()->web_contents())->
853 GetFrameTree()->root();
856 // Simple load.
857 FrameNavigateParamsCapturer capturer(root);
858 GURL frame_url(embedded_test_server()->GetURL(
859 "/navigation_controller/simple_page_1.html"));
860 NavigateFrameToURL(root, frame_url);
861 capturer.Wait();
862 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
863 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, capturer.details().type);
867 // Verify that navigations for NAVIGATION_TYPE_NEW_SUBFRAME and
868 // NAVIGATION_TYPE_AUTO_SUBFRAME are properly classified.
869 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
870 NavigationTypeClassification_NewAndAutoSubframe) {
871 GURL main_url(embedded_test_server()->GetURL(
872 "/navigation_controller/page_with_iframe.html"));
873 NavigateToURL(shell(), main_url);
875 // It is safe to obtain the root frame tree node here, as it doesn't change.
876 FrameTreeNode* root =
877 static_cast<WebContentsImpl*>(shell()->web_contents())->
878 GetFrameTree()->root();
880 ASSERT_EQ(1U, root->child_count());
881 ASSERT_NE(nullptr, root->child_at(0));
884 // Initial load.
885 LoadCommittedCapturer capturer(root->child_at(0));
886 GURL frame_url(embedded_test_server()->GetURL(
887 "/navigation_controller/simple_page_1.html"));
888 NavigateFrameToURL(root->child_at(0), frame_url);
889 capturer.Wait();
890 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
894 // Simple load.
895 FrameNavigateParamsCapturer capturer(root->child_at(0));
896 GURL frame_url(embedded_test_server()->GetURL(
897 "/navigation_controller/simple_page_2.html"));
898 NavigateFrameToURL(root->child_at(0), frame_url);
899 capturer.Wait();
900 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
901 capturer.params().transition);
902 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
906 // Back.
907 FrameNavigateParamsCapturer capturer(root->child_at(0));
908 shell()->web_contents()->GetController().GoBack();
909 capturer.Wait();
910 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
911 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
915 // Forward.
916 FrameNavigateParamsCapturer capturer(root->child_at(0));
917 shell()->web_contents()->GetController().GoForward();
918 capturer.Wait();
919 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
920 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
924 // Simple load.
925 FrameNavigateParamsCapturer capturer(root->child_at(0));
926 GURL frame_url(embedded_test_server()->GetURL(
927 "/navigation_controller/page_with_links.html"));
928 NavigateFrameToURL(root->child_at(0), frame_url);
929 capturer.Wait();
930 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
931 capturer.params().transition);
932 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
936 // Load via a fragment link click.
937 FrameNavigateParamsCapturer capturer(root->child_at(0));
938 std::string script = "document.getElementById('fraglink').click()";
939 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
940 script));
941 capturer.Wait();
942 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
943 capturer.params().transition);
944 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
948 // location.assign().
949 FrameNavigateParamsCapturer capturer(root->child_at(0));
950 GURL frame_url(embedded_test_server()->GetURL(
951 "/navigation_controller/simple_page_1.html"));
952 std::string script = "location.assign('" + frame_url.spec() + "')";
953 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
954 script));
955 capturer.Wait();
956 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
957 capturer.params().transition);
958 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
962 // location.replace().
963 LoadCommittedCapturer capturer(root->child_at(0));
964 GURL frame_url(embedded_test_server()->GetURL(
965 "/navigation_controller/simple_page_2.html"));
966 std::string script = "location.replace('" + frame_url.spec() + "')";
967 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
968 script));
969 capturer.Wait();
970 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
974 // history.pushState().
975 FrameNavigateParamsCapturer capturer(root->child_at(0));
976 std::string script =
977 "history.pushState({}, 'page 1', 'simple_page_1.html')";
978 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
979 script));
980 capturer.Wait();
981 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
982 capturer.params().transition);
983 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
987 // history.replaceState().
988 LoadCommittedCapturer capturer(root->child_at(0));
989 std::string script =
990 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
991 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
992 script));
993 capturer.Wait();
994 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
998 // Reload.
999 LoadCommittedCapturer capturer(root->child_at(0));
1000 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1001 "location.reload()"));
1002 capturer.Wait();
1003 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1007 // Create an iframe.
1008 LoadCommittedCapturer capturer(shell()->web_contents());
1009 GURL frame_url(embedded_test_server()->GetURL(
1010 "/navigation_controller/simple_page_1.html"));
1011 std::string script = "var iframe = document.createElement('iframe');"
1012 "iframe.src = '" + frame_url.spec() + "';"
1013 "document.body.appendChild(iframe);";
1014 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1015 capturer.Wait();
1016 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1020 // Verify that navigations caused by client-side redirects are correctly
1021 // classified.
1022 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1023 NavigationTypeClassification_ClientSideRedirect) {
1024 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
1025 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1027 FrameTreeNode* root =
1028 static_cast<WebContentsImpl*>(shell()->web_contents())->
1029 GetFrameTree()->root();
1032 // Load the redirecting page.
1033 FrameNavigateParamsCapturer capturer(root);
1034 capturer.set_navigations_remaining(2);
1035 GURL frame_url(embedded_test_server()->GetURL(
1036 "/navigation_controller/client_redirect.html"));
1037 NavigateFrameToURL(root, frame_url);
1038 capturer.Wait();
1040 std::vector<FrameNavigateParams> params = capturer.all_params();
1041 std::vector<LoadCommittedDetails> details = capturer.all_details();
1042 ASSERT_EQ(2U, params.size());
1043 ASSERT_EQ(2U, details.size());
1044 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, params[0].transition);
1045 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details[0].type);
1046 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
1047 params[1].transition);
1048 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details[1].type);
1052 // Verify that the LoadCommittedDetails::is_in_page value is properly set for
1053 // non-IN_PAGE navigations. (It's tested for IN_PAGE navigations with the
1054 // NavigationTypeClassification_InPage test.)
1055 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1056 LoadCommittedDetails_IsInPage) {
1057 GURL links_url(embedded_test_server()->GetURL(
1058 "/navigation_controller/page_with_links.html"));
1059 NavigateToURL(shell(), links_url);
1060 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1062 FrameTreeNode* root =
1063 static_cast<WebContentsImpl*>(shell()->web_contents())->
1064 GetFrameTree()->root();
1067 // Do a fragment link click.
1068 FrameNavigateParamsCapturer capturer(root);
1069 std::string script = "document.getElementById('fraglink').click()";
1070 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1071 capturer.Wait();
1072 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1073 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1074 EXPECT_TRUE(capturer.details().is_in_page);
1078 // Do a non-fragment link click.
1079 FrameNavigateParamsCapturer capturer(root);
1080 std::string script = "document.getElementById('thelink').click()";
1081 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1082 capturer.Wait();
1083 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1084 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1085 EXPECT_FALSE(capturer.details().is_in_page);
1088 // Second verse, same as the first. (But in a subframe.)
1090 GURL iframe_url(embedded_test_server()->GetURL(
1091 "/navigation_controller/page_with_iframe.html"));
1092 NavigateToURL(shell(), iframe_url);
1094 root = static_cast<WebContentsImpl*>(shell()->web_contents())->
1095 GetFrameTree()->root();
1097 ASSERT_EQ(1U, root->child_count());
1098 ASSERT_NE(nullptr, root->child_at(0));
1100 NavigateFrameToURL(root->child_at(0), links_url);
1101 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1104 // Do a fragment link click.
1105 FrameNavigateParamsCapturer capturer(root->child_at(0));
1106 std::string script = "document.getElementById('fraglink').click()";
1107 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1108 script));
1109 capturer.Wait();
1110 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1111 capturer.params().transition);
1112 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1113 EXPECT_TRUE(capturer.details().is_in_page);
1117 // Do a non-fragment link click.
1118 FrameNavigateParamsCapturer capturer(root->child_at(0));
1119 std::string script = "document.getElementById('thelink').click()";
1120 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1121 script));
1122 capturer.Wait();
1123 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1124 capturer.params().transition);
1125 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1126 EXPECT_FALSE(capturer.details().is_in_page);
1130 // Verify the tree of FrameNavigationEntries after initial about:blank commits
1131 // in subframes, which should not count as real committed loads.
1132 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1133 FrameNavigationEntry_BlankAutoSubframe) {
1134 GURL main_url(embedded_test_server()->GetURL(
1135 "/navigation_controller/simple_page_1.html"));
1136 NavigateToURL(shell(), main_url);
1137 const NavigationControllerImpl& controller =
1138 static_cast<const NavigationControllerImpl&>(
1139 shell()->web_contents()->GetController());
1140 FrameTreeNode* root =
1141 static_cast<WebContentsImpl*>(shell()->web_contents())->
1142 GetFrameTree()->root();
1144 // 1. Create a iframe with no URL.
1146 LoadCommittedCapturer capturer(shell()->web_contents());
1147 std::string script = "var iframe = document.createElement('iframe');"
1148 "document.body.appendChild(iframe);";
1149 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1150 capturer.Wait();
1151 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1154 // Check last committed NavigationEntry.
1155 EXPECT_EQ(1, controller.GetEntryCount());
1156 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1157 EXPECT_EQ(main_url, entry->GetURL());
1158 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1159 EXPECT_EQ(main_url, root_entry->url());
1161 // Verify no subframe entries are created.
1162 EXPECT_EQ(0U, entry->root_node()->children.size());
1163 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(0)));
1165 // 1a. A nested iframe with no URL should also create no subframe entries.
1167 LoadCommittedCapturer capturer(shell()->web_contents());
1168 std::string script = "var iframe = document.createElement('iframe');"
1169 "document.body.appendChild(iframe);";
1170 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1171 script));
1172 capturer.Wait();
1173 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1175 EXPECT_EQ(0U, entry->root_node()->children.size());
1176 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(0)->child_at(0)));
1178 // 2. Create another iframe with an about:blank URL.
1180 LoadCommittedCapturer capturer(shell()->web_contents());
1181 std::string script = "var iframe = document.createElement('iframe');"
1182 "iframe.src = 'about:blank';"
1183 "document.body.appendChild(iframe);";
1184 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1185 capturer.Wait();
1186 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1189 // Check last committed NavigationEntry.
1190 EXPECT_EQ(1, controller.GetEntryCount());
1191 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1193 // Verify no subframe entries are created.
1194 EXPECT_EQ(0U, entry->root_node()->children.size());
1195 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(1)));
1197 // 3. A real same-site navigation in the first iframe should be AUTO.
1198 GURL frame_url(embedded_test_server()->GetURL(
1199 "/navigation_controller/simple_page_1.html"));
1201 LoadCommittedCapturer capturer(root->child_at(0));
1202 std::string script = "var frames = document.getElementsByTagName('iframe');"
1203 "frames[0].src = '" + frame_url.spec() + "';";
1204 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1205 capturer.Wait();
1206 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1209 // Check last committed NavigationEntry.
1210 EXPECT_EQ(1, controller.GetEntryCount());
1211 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1213 // Verify subframe entries if we're in --site-per-process mode.
1214 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1215 switches::kSitePerProcess)) {
1216 // The entry should now have one subframe FrameNavigationEntry.
1217 ASSERT_EQ(1U, entry->root_node()->children.size());
1218 FrameNavigationEntry* frame_entry =
1219 entry->root_node()->children[0]->frame_entry.get();
1220 EXPECT_EQ(frame_url, frame_entry->url());
1221 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(0)));
1222 EXPECT_FALSE(controller.HasCommittedRealLoad(root->child_at(1)));
1223 } else {
1224 // There are no subframe FrameNavigationEntries by default.
1225 EXPECT_EQ(0U, entry->root_node()->children.size());
1228 // 4. A real cross-site navigation in the second iframe should be AUTO.
1229 GURL foo_url(embedded_test_server()->GetURL(
1230 "foo.com", "/navigation_controller/simple_page_2.html"));
1232 LoadCommittedCapturer capturer(root->child_at(1));
1233 std::string script = "var frames = document.getElementsByTagName('iframe');"
1234 "frames[1].src = '" + foo_url.spec() + "';";
1235 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1236 capturer.Wait();
1237 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1240 // Check last committed NavigationEntry.
1241 EXPECT_EQ(1, controller.GetEntryCount());
1242 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1244 // Verify subframe entries if we're in --site-per-process mode.
1245 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1246 switches::kSitePerProcess)) {
1247 // The entry should now have two subframe FrameNavigationEntries.
1248 ASSERT_EQ(2U, entry->root_node()->children.size());
1249 FrameNavigationEntry* frame_entry =
1250 entry->root_node()->children[1]->frame_entry.get();
1251 EXPECT_EQ(foo_url, frame_entry->url());
1252 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(1)));
1253 } else {
1254 // There are no subframe FrameNavigationEntries by default.
1255 EXPECT_EQ(0U, entry->root_node()->children.size());
1258 // Check the end result of the frame tree.
1259 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1260 switches::kSitePerProcess)) {
1261 FrameTreeVisualizer visualizer;
1262 EXPECT_EQ(
1263 " Site A ------------ proxies for B\n"
1264 " |--Site A ------- proxies for B\n"
1265 " +--Site B ------- proxies for A\n"
1266 "Where A = http://127.0.0.1/\n"
1267 " B = http://foo.com/",
1268 visualizer.DepictFrameTree(root));
1272 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
1273 // commits.
1274 // TODO(creis): Test updating entries for history auto subframe navigations.
1275 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1276 FrameNavigationEntry_AutoSubframe) {
1277 GURL main_url(embedded_test_server()->GetURL(
1278 "/navigation_controller/simple_page_1.html"));
1279 NavigateToURL(shell(), main_url);
1280 const NavigationControllerImpl& controller =
1281 static_cast<const NavigationControllerImpl&>(
1282 shell()->web_contents()->GetController());
1283 FrameTreeNode* root =
1284 static_cast<WebContentsImpl*>(shell()->web_contents())->
1285 GetFrameTree()->root();
1287 // 1. Create a same-site iframe.
1288 GURL frame_url(embedded_test_server()->GetURL(
1289 "/navigation_controller/simple_page_2.html"));
1291 LoadCommittedCapturer capturer(shell()->web_contents());
1292 std::string script = "var iframe = document.createElement('iframe');"
1293 "iframe.src = '" + frame_url.spec() + "';"
1294 "document.body.appendChild(iframe);";
1295 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1296 capturer.Wait();
1297 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1300 // Check last committed NavigationEntry.
1301 EXPECT_EQ(1, controller.GetEntryCount());
1302 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1303 EXPECT_EQ(main_url, entry->GetURL());
1304 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1305 EXPECT_EQ(main_url, root_entry->url());
1306 EXPECT_FALSE(controller.GetPendingEntry());
1308 // Verify subframe entries if we're in --site-per-process mode.
1309 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1310 switches::kSitePerProcess)) {
1311 // The entry should now have a subframe FrameNavigationEntry.
1312 ASSERT_EQ(1U, entry->root_node()->children.size());
1313 FrameNavigationEntry* frame_entry =
1314 entry->root_node()->children[0]->frame_entry.get();
1315 EXPECT_EQ(frame_url, frame_entry->url());
1316 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(0)));
1317 } else {
1318 // There are no subframe FrameNavigationEntries by default.
1319 EXPECT_EQ(0U, entry->root_node()->children.size());
1322 // 2. Create a second, initially cross-site iframe.
1323 GURL foo_url(embedded_test_server()->GetURL(
1324 "foo.com", "/navigation_controller/simple_page_1.html"));
1326 LoadCommittedCapturer capturer(shell()->web_contents());
1327 std::string script = "var iframe = document.createElement('iframe');"
1328 "iframe.src = '" + foo_url.spec() + "';"
1329 "document.body.appendChild(iframe);";
1330 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1331 capturer.Wait();
1332 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1335 // The last committed NavigationEntry shouldn't have changed.
1336 EXPECT_EQ(1, controller.GetEntryCount());
1337 entry = controller.GetLastCommittedEntry();
1338 EXPECT_EQ(main_url, entry->GetURL());
1339 root_entry = entry->root_node()->frame_entry.get();
1340 EXPECT_EQ(main_url, root_entry->url());
1341 EXPECT_FALSE(controller.GetPendingEntry());
1343 // Verify subframe entries if we're in --site-per-process mode.
1344 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1345 switches::kSitePerProcess)) {
1346 // The entry should now have 2 subframe FrameNavigationEntries.
1347 ASSERT_EQ(2U, entry->root_node()->children.size());
1348 FrameNavigationEntry* frame_entry =
1349 entry->root_node()->children[1]->frame_entry.get();
1350 EXPECT_EQ(foo_url, frame_entry->url());
1351 EXPECT_TRUE(controller.HasCommittedRealLoad(root->child_at(1)));
1352 } else {
1353 // There are no subframe FrameNavigationEntries by default.
1354 EXPECT_EQ(0U, entry->root_node()->children.size());
1357 // 3. Create a nested iframe in the second subframe.
1359 LoadCommittedCapturer capturer(shell()->web_contents());
1360 std::string script = "var iframe = document.createElement('iframe');"
1361 "iframe.src = '" + foo_url.spec() + "';"
1362 "document.body.appendChild(iframe);";
1363 EXPECT_TRUE(content::ExecuteScript(root->child_at(1)->current_frame_host(),
1364 script));
1365 capturer.Wait();
1366 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1369 // The last committed NavigationEntry shouldn't have changed.
1370 EXPECT_EQ(1, controller.GetEntryCount());
1371 entry = controller.GetLastCommittedEntry();
1372 EXPECT_EQ(main_url, entry->GetURL());
1373 root_entry = entry->root_node()->frame_entry.get();
1374 EXPECT_EQ(main_url, root_entry->url());
1376 // Verify subframe entries if we're in --site-per-process mode.
1377 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1378 switches::kSitePerProcess)) {
1379 // The entry should now have 2 subframe FrameNavigationEntries.
1380 ASSERT_EQ(2U, entry->root_node()->children.size());
1381 ASSERT_EQ(1U, entry->root_node()->children[1]->children.size());
1382 FrameNavigationEntry* frame_entry =
1383 entry->root_node()->children[1]->children[0]->frame_entry.get();
1384 EXPECT_EQ(foo_url, frame_entry->url());
1385 } else {
1386 // There are no subframe FrameNavigationEntries by default.
1387 EXPECT_EQ(0U, entry->root_node()->children.size());
1390 // TODO(creis): Add tests for another subframe on B, and for a subframe on A
1391 // within it. Both are currently broken.
1393 // Check the end result of the frame tree.
1394 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1395 switches::kSitePerProcess)) {
1396 FrameTreeVisualizer visualizer;
1397 EXPECT_EQ(
1398 " Site A ------------ proxies for B\n"
1399 " |--Site A ------- proxies for B\n"
1400 " +--Site B ------- proxies for A\n"
1401 " +--Site B -- proxies for A\n"
1402 "Where A = http://127.0.0.1/\n"
1403 " B = http://foo.com/",
1404 visualizer.DepictFrameTree(root));
1408 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_NEW_SUBFRAME
1409 // commits.
1410 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1411 FrameNavigationEntry_NewSubframe) {
1412 GURL main_url(embedded_test_server()->GetURL(
1413 "/navigation_controller/simple_page_1.html"));
1414 NavigateToURL(shell(), main_url);
1415 const NavigationControllerImpl& controller =
1416 static_cast<const NavigationControllerImpl&>(
1417 shell()->web_contents()->GetController());
1418 FrameTreeNode* root =
1419 static_cast<WebContentsImpl*>(shell()->web_contents())->
1420 GetFrameTree()->root();
1422 // 1. Create a same-site iframe.
1423 GURL frame_url(embedded_test_server()->GetURL(
1424 "/navigation_controller/simple_page_2.html"));
1426 LoadCommittedCapturer capturer(shell()->web_contents());
1427 std::string script = "var iframe = document.createElement('iframe');"
1428 "iframe.src = '" + frame_url.spec() + "';"
1429 "document.body.appendChild(iframe);";
1430 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1431 capturer.Wait();
1433 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1435 // 2. Navigate in the subframe same-site.
1436 GURL frame_url2(embedded_test_server()->GetURL(
1437 "/navigation_controller/page_with_links.html"));
1439 FrameNavigateParamsCapturer capturer(root->child_at(0));
1440 NavigateFrameToURL(root->child_at(0), frame_url2);
1441 capturer.Wait();
1442 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1443 capturer.params().transition);
1444 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1447 // We should have created a new NavigationEntry with the same main frame URL.
1448 EXPECT_EQ(2, controller.GetEntryCount());
1449 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1450 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
1451 EXPECT_NE(entry, entry2);
1452 EXPECT_EQ(main_url, entry2->GetURL());
1453 FrameNavigationEntry* root_entry2 = entry2->root_node()->frame_entry.get();
1454 EXPECT_EQ(main_url, root_entry2->url());
1456 // Verify subframe entries if we're in --site-per-process mode.
1457 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1458 switches::kSitePerProcess)) {
1459 // The entry should have a new FrameNavigationEntries for the subframe.
1460 ASSERT_EQ(1U, entry2->root_node()->children.size());
1461 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1462 } else {
1463 // There are no subframe FrameNavigationEntries by default.
1464 EXPECT_EQ(0U, entry2->root_node()->children.size());
1467 // 3. Create a second, initially cross-site iframe.
1468 GURL foo_url(embedded_test_server()->GetURL(
1469 "foo.com", "/navigation_controller/simple_page_1.html"));
1471 LoadCommittedCapturer capturer(shell()->web_contents());
1472 std::string script = "var iframe = document.createElement('iframe');"
1473 "iframe.src = '" + foo_url.spec() + "';"
1474 "document.body.appendChild(iframe);";
1475 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1476 capturer.Wait();
1479 // 4. Create a nested same-site iframe in the second subframe, wait for it to
1480 // commit, then navigate it again.
1482 LoadCommittedCapturer capturer(shell()->web_contents());
1483 std::string script = "var iframe = document.createElement('iframe');"
1484 "iframe.src = '" + foo_url.spec() + "';"
1485 "document.body.appendChild(iframe);";
1486 EXPECT_TRUE(content::ExecuteScript(root->child_at(1)->current_frame_host(),
1487 script));
1488 capturer.Wait();
1490 GURL bar_url(embedded_test_server()->GetURL(
1491 "bar.com", "/navigation_controller/simple_page_1.html"));
1493 FrameNavigateParamsCapturer capturer(root->child_at(1)->child_at(0));
1494 NavigateFrameToURL(root->child_at(1)->child_at(0), bar_url);
1495 capturer.Wait();
1496 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1497 capturer.params().transition);
1498 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1501 // We should have created a new NavigationEntry with the same main frame URL.
1502 EXPECT_EQ(3, controller.GetEntryCount());
1503 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1504 NavigationEntryImpl* entry3 = controller.GetLastCommittedEntry();
1505 EXPECT_NE(entry, entry3);
1506 EXPECT_EQ(main_url, entry3->GetURL());
1507 FrameNavigationEntry* root_entry3 = entry3->root_node()->frame_entry.get();
1508 EXPECT_EQ(main_url, root_entry3->url());
1510 // Verify subframe entries if we're in --site-per-process mode.
1511 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1512 switches::kSitePerProcess)) {
1513 // The entry should still have FrameNavigationEntries for all 3 subframes.
1514 ASSERT_EQ(2U, entry3->root_node()->children.size());
1515 EXPECT_EQ(frame_url2, entry3->root_node()->children[0]->frame_entry->url());
1516 EXPECT_EQ(foo_url, entry3->root_node()->children[1]->frame_entry->url());
1517 ASSERT_EQ(1U, entry3->root_node()->children[1]->children.size());
1518 EXPECT_EQ(
1519 bar_url,
1520 entry3->root_node()->children[1]->children[0]->frame_entry->url());
1521 } else {
1522 // There are no subframe FrameNavigationEntries by default.
1523 EXPECT_EQ(0U, entry3->root_node()->children.size());
1526 // 6. Navigate the second subframe cross-site, clearing its existing subtree.
1527 GURL baz_url(embedded_test_server()->GetURL(
1528 "baz.com", "/navigation_controller/simple_page_1.html"));
1530 FrameNavigateParamsCapturer capturer(root->child_at(1));
1531 std::string script = "var frames = document.getElementsByTagName('iframe');"
1532 "frames[1].src = '" + baz_url.spec() + "';";
1533 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1534 capturer.Wait();
1535 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1536 capturer.params().transition);
1537 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1540 // We should have created a new NavigationEntry with the same main frame URL.
1541 EXPECT_EQ(4, controller.GetEntryCount());
1542 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
1543 NavigationEntryImpl* entry4 = controller.GetLastCommittedEntry();
1544 EXPECT_NE(entry, entry4);
1545 EXPECT_EQ(main_url, entry4->GetURL());
1546 FrameNavigationEntry* root_entry4 = entry4->root_node()->frame_entry.get();
1547 EXPECT_EQ(main_url, root_entry4->url());
1549 // Verify subframe entries if we're in --site-per-process mode.
1550 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1551 switches::kSitePerProcess)) {
1552 // The entry should still have FrameNavigationEntries for all 3 subframes.
1553 ASSERT_EQ(2U, entry4->root_node()->children.size());
1554 EXPECT_EQ(frame_url2, entry4->root_node()->children[0]->frame_entry->url());
1555 EXPECT_EQ(baz_url, entry4->root_node()->children[1]->frame_entry->url());
1556 ASSERT_EQ(0U, entry4->root_node()->children[1]->children.size());
1557 } else {
1558 // There are no subframe FrameNavigationEntries by default.
1559 EXPECT_EQ(0U, entry4->root_node()->children.size());
1562 // Check the end result of the frame tree.
1563 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1564 switches::kSitePerProcess)) {
1565 FrameTreeVisualizer visualizer;
1566 EXPECT_EQ(
1567 " Site A ------------ proxies for B\n"
1568 " |--Site A ------- proxies for B\n"
1569 " +--Site B ------- proxies for A\n"
1570 "Where A = http://127.0.0.1/\n"
1571 " B = http://baz.com/",
1572 visualizer.DepictFrameTree(root));
1576 // Verify the tree of FrameNavigationEntries after back/forward navigations in a
1577 // cross-site subframe.
1578 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1579 FrameNavigationEntry_SubframeBackForward) {
1580 GURL main_url(embedded_test_server()->GetURL(
1581 "/navigation_controller/simple_page_1.html"));
1582 NavigateToURL(shell(), main_url);
1583 const NavigationControllerImpl& controller =
1584 static_cast<const NavigationControllerImpl&>(
1585 shell()->web_contents()->GetController());
1586 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1587 ->GetFrameTree()
1588 ->root();
1590 // 1. Create a same-site iframe.
1591 GURL frame_url(embedded_test_server()->GetURL(
1592 "/navigation_controller/simple_page_2.html"));
1594 LoadCommittedCapturer capturer(shell()->web_contents());
1595 std::string script = "var iframe = document.createElement('iframe');"
1596 "iframe.src = '" + frame_url.spec() + "';"
1597 "document.body.appendChild(iframe);";
1598 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1599 capturer.Wait();
1601 NavigationEntryImpl* entry1 = controller.GetLastCommittedEntry();
1603 // 2. Navigate in the subframe cross-site.
1604 GURL frame_url2(embedded_test_server()->GetURL(
1605 "foo.com", "/navigation_controller/page_with_links.html"));
1607 FrameNavigateParamsCapturer capturer(root->child_at(0));
1608 NavigateFrameToURL(root->child_at(0), frame_url2);
1609 capturer.Wait();
1611 EXPECT_EQ(2, controller.GetEntryCount());
1612 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1613 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
1615 // 3. Navigate in the subframe cross-site again.
1616 GURL frame_url3(embedded_test_server()->GetURL(
1617 "bar.com", "/navigation_controller/page_with_links.html"));
1619 FrameNavigateParamsCapturer capturer(root->child_at(0));
1620 NavigateFrameToURL(root->child_at(0), frame_url3);
1621 capturer.Wait();
1623 EXPECT_EQ(3, controller.GetEntryCount());
1624 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1625 NavigationEntryImpl* entry3 = controller.GetLastCommittedEntry();
1627 // 4. Go back in the subframe.
1629 FrameNavigateParamsCapturer capturer(root->child_at(0));
1630 shell()->web_contents()->GetController().GoBack();
1631 capturer.Wait();
1632 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1633 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1635 EXPECT_EQ(3, controller.GetEntryCount());
1636 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1637 EXPECT_EQ(entry2, controller.GetLastCommittedEntry());
1639 // Verify subframe entries if we're in --site-per-process mode.
1640 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1641 switches::kSitePerProcess)) {
1642 // The entry should have a new FrameNavigationEntries for the subframe.
1643 ASSERT_EQ(1U, entry2->root_node()->children.size());
1644 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1645 } else {
1646 // There are no subframe FrameNavigationEntries by default.
1647 EXPECT_EQ(0U, entry2->root_node()->children.size());
1650 // 5. Go back in the subframe again to the parent page's site.
1652 FrameNavigateParamsCapturer capturer(root->child_at(0));
1653 shell()->web_contents()->GetController().GoBack();
1654 capturer.Wait();
1655 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1656 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1658 EXPECT_EQ(3, controller.GetEntryCount());
1659 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1660 EXPECT_EQ(entry1, controller.GetLastCommittedEntry());
1662 // Verify subframe entries if we're in --site-per-process mode.
1663 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1664 switches::kSitePerProcess)) {
1665 // The entry should have a new FrameNavigationEntries for the subframe.
1666 ASSERT_EQ(1U, entry1->root_node()->children.size());
1667 EXPECT_EQ(frame_url, entry1->root_node()->children[0]->frame_entry->url());
1668 } else {
1669 // There are no subframe FrameNavigationEntries by default.
1670 EXPECT_EQ(0U, entry1->root_node()->children.size());
1673 // 6. Go forward in the subframe cross-site.
1675 FrameNavigateParamsCapturer capturer(root->child_at(0));
1676 shell()->web_contents()->GetController().GoForward();
1677 capturer.Wait();
1678 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1679 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1681 EXPECT_EQ(3, controller.GetEntryCount());
1682 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1683 EXPECT_EQ(entry2, controller.GetLastCommittedEntry());
1685 // Verify subframe entries if we're in --site-per-process mode.
1686 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1687 switches::kSitePerProcess)) {
1688 // The entry should have a new FrameNavigationEntries for the subframe.
1689 ASSERT_EQ(1U, entry2->root_node()->children.size());
1690 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1691 } else {
1692 // There are no subframe FrameNavigationEntries by default.
1693 EXPECT_EQ(0U, entry2->root_node()->children.size());
1696 // 7. Go forward in the subframe again, cross-site.
1698 FrameNavigateParamsCapturer capturer(root->child_at(0));
1699 shell()->web_contents()->GetController().GoForward();
1700 capturer.Wait();
1701 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1702 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1704 EXPECT_EQ(3, controller.GetEntryCount());
1705 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1706 EXPECT_EQ(entry3, controller.GetLastCommittedEntry());
1708 // Verify subframe entries if we're in --site-per-process mode.
1709 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1710 switches::kSitePerProcess)) {
1711 // The entry should have a new FrameNavigationEntries for the subframe.
1712 ASSERT_EQ(1U, entry3->root_node()->children.size());
1713 EXPECT_EQ(frame_url3, entry3->root_node()->children[0]->frame_entry->url());
1714 } else {
1715 // There are no subframe FrameNavigationEntries by default.
1716 EXPECT_EQ(0U, entry3->root_node()->children.size());
1720 // Verifies that item sequence numbers and document sequence numbers update
1721 // properly for main frames and subframes.
1722 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1723 FrameNavigationEntry_SequenceNumbers) {
1724 const NavigationControllerImpl& controller =
1725 static_cast<const NavigationControllerImpl&>(
1726 shell()->web_contents()->GetController());
1728 // 1. Navigate the main frame.
1729 GURL url(embedded_test_server()->GetURL(
1730 "/navigation_controller/page_with_links.html"));
1731 NavigateToURL(shell(), url);
1732 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1733 ->GetFrameTree()
1734 ->root();
1736 FrameNavigationEntry* frame_entry =
1737 controller.GetLastCommittedEntry()->GetFrameEntry(root);
1738 int64 isn_1 = frame_entry->item_sequence_number();
1739 int64 dsn_1 = frame_entry->document_sequence_number();
1740 EXPECT_NE(-1, isn_1);
1741 EXPECT_NE(-1, dsn_1);
1743 // 2. Do an in-page fragment navigation.
1744 std::string script = "document.getElementById('fraglink').click()";
1745 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1746 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1748 frame_entry = controller.GetLastCommittedEntry()->GetFrameEntry(root);
1749 int64 isn_2 = frame_entry->item_sequence_number();
1750 int64 dsn_2 = frame_entry->document_sequence_number();
1751 EXPECT_NE(-1, isn_2);
1752 EXPECT_NE(isn_1, isn_2);
1753 EXPECT_EQ(dsn_1, dsn_2);
1755 // Also test subframe sequence numbers, but only in --site-per-proces mode.
1756 // (We do not create subframe FrameNavigationEntries in default mode yet.)
1757 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
1758 switches::kSitePerProcess))
1759 return;
1761 // 3. Add a subframe, which does an AUTO_SUBFRAME navigation.
1763 LoadCommittedCapturer capturer(shell()->web_contents());
1764 std::string script = "var iframe = document.createElement('iframe');"
1765 "iframe.src = '" + url.spec() + "';"
1766 "document.body.appendChild(iframe);";
1767 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1768 capturer.Wait();
1769 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1772 // The root FrameNavigationEntry hasn't changed.
1773 EXPECT_EQ(frame_entry,
1774 controller.GetLastCommittedEntry()->GetFrameEntry(root));
1776 // We should have a unique ISN and DSN for the subframe entry.
1777 FrameTreeNode* subframe = root->child_at(0);
1778 FrameNavigationEntry* subframe_entry =
1779 controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
1780 int64 isn_3 = subframe_entry->item_sequence_number();
1781 int64 dsn_3 = subframe_entry->document_sequence_number();
1782 EXPECT_NE(-1, isn_2);
1783 EXPECT_NE(isn_2, isn_3);
1784 EXPECT_NE(dsn_2, dsn_3);
1786 // 4. Do an in-page fragment navigation in the subframe.
1787 EXPECT_TRUE(content::ExecuteScript(subframe->current_frame_host(), script));
1788 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1790 subframe_entry = controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
1791 int64 isn_4 = subframe_entry->item_sequence_number();
1792 int64 dsn_4 = subframe_entry->document_sequence_number();
1793 EXPECT_NE(-1, isn_4);
1794 EXPECT_NE(isn_3, isn_4);
1795 EXPECT_EQ(dsn_3, dsn_4);
1798 namespace {
1800 class HttpThrottle : public ResourceThrottle {
1801 public:
1802 // ResourceThrottle
1803 void WillStartRequest(bool* defer) override {
1804 *defer = true;
1807 const char* GetNameForLogging() const override {
1808 return "HttpThrottle";
1812 class StallDelegate : public ResourceDispatcherHostDelegate {
1813 // ResourceDispatcherHostDelegate
1814 void RequestBeginning(
1815 net::URLRequest* request,
1816 content::ResourceContext* resource_context,
1817 content::AppCacheService* appcache_service,
1818 ResourceType resource_type,
1819 ScopedVector<content::ResourceThrottle>* throttles) override {
1820 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1821 throttles->push_back(new HttpThrottle);
1825 // Loads |start_url|, then loads |stalled_url| which stalls. While the page is
1826 // stalled, an in-page navigation happens. Make sure that all the navigations
1827 // are properly classified.
1828 void DoReplaceStateWhilePending(Shell* shell,
1829 const GURL& start_url,
1830 const GURL& stalled_url,
1831 const std::string& replace_state_filename) {
1832 NavigationControllerImpl& controller =
1833 static_cast<NavigationControllerImpl&>(
1834 shell->web_contents()->GetController());
1836 FrameTreeNode* root =
1837 static_cast<WebContentsImpl*>(shell->web_contents())->
1838 GetFrameTree()->root();
1840 // Start with one page.
1841 EXPECT_TRUE(NavigateToURL(shell, start_url));
1843 // Have the user decide to go to a different page which is very slow.
1844 StallDelegate stall_delegate;
1845 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
1846 controller.LoadURL(
1847 stalled_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1849 // That should be the pending entry.
1850 NavigationEntryImpl* entry = controller.GetPendingEntry();
1851 ASSERT_NE(nullptr, entry);
1852 EXPECT_EQ(stalled_url, entry->GetURL());
1855 // Now the existing page uses history.replaceState().
1856 FrameNavigateParamsCapturer capturer(root);
1857 capturer.set_wait_for_load(false);
1858 std::string script =
1859 "history.replaceState({}, '', '" + replace_state_filename + "')";
1860 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1861 capturer.Wait();
1863 // The fact that there was a pending entry shouldn't interfere with the
1864 // classification.
1865 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
1866 EXPECT_TRUE(capturer.details().is_in_page);
1869 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
1872 } // namespace
1874 IN_PROC_BROWSER_TEST_F(
1875 NavigationControllerBrowserTest,
1876 NavigationTypeClassification_On1InPageToXWhile2Pending) {
1877 GURL url1(embedded_test_server()->GetURL(
1878 "/navigation_controller/simple_page_1.html"));
1879 GURL url2(embedded_test_server()->GetURL(
1880 "/navigation_controller/simple_page_2.html"));
1881 DoReplaceStateWhilePending(shell(), url1, url2, "x");
1884 IN_PROC_BROWSER_TEST_F(
1885 NavigationControllerBrowserTest,
1886 NavigationTypeClassification_On1InPageTo2While2Pending) {
1887 GURL url1(embedded_test_server()->GetURL(
1888 "/navigation_controller/simple_page_1.html"));
1889 GURL url2(embedded_test_server()->GetURL(
1890 "/navigation_controller/simple_page_2.html"));
1891 DoReplaceStateWhilePending(shell(), url1, url2, "simple_page_2.html");
1894 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1895 NavigationTypeClassification_On1InPageToXWhile1Pending) {
1896 GURL url(embedded_test_server()->GetURL(
1897 "/navigation_controller/simple_page_1.html"));
1898 DoReplaceStateWhilePending(shell(), url, url, "x");
1901 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1902 NavigationTypeClassification_On1InPageTo1While1Pending) {
1903 GURL url(embedded_test_server()->GetURL(
1904 "/navigation_controller/simple_page_1.html"));
1905 DoReplaceStateWhilePending(shell(), url, url, "simple_page_1.html");
1908 // Ensure the renderer process does not get confused about the current entry
1909 // due to subframes and replaced entries. See https://crbug.com/480201.
1910 // TODO(creis): Re-enable for Site Isolation FYI bots: https://crbug.com/502317.
1911 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1912 PreventSpoofFromSubframeAndReplace) {
1913 // Start at an initial URL.
1914 GURL url1(embedded_test_server()->GetURL(
1915 "/navigation_controller/simple_page_1.html"));
1916 NavigateToURL(shell(), url1);
1918 // Now go to a page with a real iframe.
1919 GURL url2(embedded_test_server()->GetURL(
1920 "/navigation_controller/page_with_data_iframe.html"));
1921 NavigateToURL(shell(), url2);
1923 // It is safe to obtain the root frame tree node here, as it doesn't change.
1924 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1925 ->GetFrameTree()
1926 ->root();
1927 ASSERT_EQ(1U, root->child_count());
1928 ASSERT_NE(nullptr, root->child_at(0));
1931 // Navigate in the iframe.
1932 FrameNavigateParamsCapturer capturer(root->child_at(0));
1933 GURL frame_url(embedded_test_server()->GetURL(
1934 "/navigation_controller/simple_page_2.html"));
1935 NavigateFrameToURL(root->child_at(0), frame_url);
1936 capturer.Wait();
1937 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1941 // Go back in the iframe.
1942 TestNavigationObserver back_load_observer(shell()->web_contents());
1943 shell()->web_contents()->GetController().GoBack();
1944 back_load_observer.Wait();
1948 // Go forward in the iframe.
1949 TestNavigationObserver forward_load_observer(shell()->web_contents());
1950 shell()->web_contents()->GetController().GoForward();
1951 forward_load_observer.Wait();
1954 GURL url3(embedded_test_server()->GetURL(
1955 "/navigation_controller/page_with_iframe.html"));
1957 // location.replace() to cause an inert commit.
1958 TestNavigationObserver replace_load_observer(shell()->web_contents());
1959 std::string script = "location.replace('" + url3.spec() + "')";
1960 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1961 replace_load_observer.Wait();
1965 // Go back to url2.
1966 TestNavigationObserver back_load_observer(shell()->web_contents());
1967 shell()->web_contents()->GetController().GoBack();
1968 back_load_observer.Wait();
1970 // Make sure the URL is correct for both the entry and the main frame, and
1971 // that the process hasn't been killed for showing a spoof.
1972 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1973 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
1974 EXPECT_EQ(url2, root->current_url());
1978 // Go back to reset main frame entirely.
1979 TestNavigationObserver back_load_observer(shell()->web_contents());
1980 shell()->web_contents()->GetController().GoBack();
1981 back_load_observer.Wait();
1982 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
1983 EXPECT_EQ(url1, root->current_url());
1987 // Go forward.
1988 TestNavigationObserver back_load_observer(shell()->web_contents());
1989 shell()->web_contents()->GetController().GoForward();
1990 back_load_observer.Wait();
1991 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
1992 EXPECT_EQ(url2, root->current_url());
1996 // Go forward to the replaced URL.
1997 TestNavigationObserver forward_load_observer(shell()->web_contents());
1998 shell()->web_contents()->GetController().GoForward();
1999 forward_load_observer.Wait();
2001 // Make sure the URL is correct for both the entry and the main frame, and
2002 // that the process hasn't been killed for showing a spoof.
2003 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
2004 EXPECT_EQ(url3, shell()->web_contents()->GetLastCommittedURL());
2005 EXPECT_EQ(url3, root->current_url());
2009 // Ensure the renderer process does not get killed if the main frame URL's path
2010 // changes when going back in a subframe, since this is currently possible after
2011 // a replaceState in the main frame (thanks to https://crbug.com/373041).
2012 // See https:///crbug.com/486916.
2013 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2014 SubframeBackFromReplaceState) {
2015 // Start at a page with a real iframe.
2016 GURL url1(embedded_test_server()->GetURL(
2017 "/navigation_controller/page_with_data_iframe.html"));
2018 NavigateToURL(shell(), url1);
2020 // It is safe to obtain the root frame tree node here, as it doesn't change.
2021 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2022 ->GetFrameTree()
2023 ->root();
2024 ASSERT_EQ(1U, root->child_count());
2025 ASSERT_NE(nullptr, root->child_at(0));
2028 // Navigate in the iframe.
2029 FrameNavigateParamsCapturer capturer(root->child_at(0));
2030 GURL frame_url(embedded_test_server()->GetURL(
2031 "/navigation_controller/simple_page_2.html"));
2032 NavigateFrameToURL(root->child_at(0), frame_url);
2033 capturer.Wait();
2034 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
2038 // history.replaceState().
2039 FrameNavigateParamsCapturer capturer(root);
2040 std::string script =
2041 "history.replaceState({}, 'replaced', 'replaced')";
2042 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
2043 capturer.Wait();
2047 // Go back in the iframe.
2048 TestNavigationObserver back_load_observer(shell()->web_contents());
2049 shell()->web_contents()->GetController().GoBack();
2050 back_load_observer.Wait();
2053 // For now, we expect the main frame's URL to revert. This won't happen once
2054 // https://crbug.com/373041 is fixed.
2055 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
2057 // Make sure the renderer process has not been killed.
2058 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
2061 namespace {
2063 class FailureWatcher : public WebContentsObserver {
2064 public:
2065 // Observes failure for the specified |node|.
2066 explicit FailureWatcher(FrameTreeNode* node)
2067 : WebContentsObserver(
2068 node->current_frame_host()->delegate()->GetAsWebContents()),
2069 frame_tree_node_id_(node->frame_tree_node_id()),
2070 message_loop_runner_(new MessageLoopRunner) {}
2072 void Wait() {
2073 message_loop_runner_->Run();
2076 private:
2077 void DidFailLoad(RenderFrameHost* render_frame_host,
2078 const GURL& validated_url,
2079 int error_code,
2080 const base::string16& error_description,
2081 bool was_ignored_by_handler) override {
2082 RenderFrameHostImpl* rfh =
2083 static_cast<RenderFrameHostImpl*>(render_frame_host);
2084 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
2085 return;
2087 message_loop_runner_->Quit();
2090 void DidFailProvisionalLoad(
2091 RenderFrameHost* render_frame_host,
2092 const GURL& validated_url,
2093 int error_code,
2094 const base::string16& error_description,
2095 bool was_ignored_by_handler) override {
2096 RenderFrameHostImpl* rfh =
2097 static_cast<RenderFrameHostImpl*>(render_frame_host);
2098 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
2099 return;
2101 message_loop_runner_->Quit();
2104 // The id of the FrameTreeNode whose navigations to observe.
2105 int frame_tree_node_id_;
2107 // The MessageLoopRunner used to spin the message loop.
2108 scoped_refptr<MessageLoopRunner> message_loop_runner_;
2111 } // namespace
2113 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2114 StopCausesFailureDespiteJavaScriptURL) {
2115 NavigationControllerImpl& controller =
2116 static_cast<NavigationControllerImpl&>(
2117 shell()->web_contents()->GetController());
2119 FrameTreeNode* root =
2120 static_cast<WebContentsImpl*>(shell()->web_contents())->
2121 GetFrameTree()->root();
2123 // Start with a normal page.
2124 GURL url1(embedded_test_server()->GetURL(
2125 "/navigation_controller/simple_page_1.html"));
2126 EXPECT_TRUE(NavigateToURL(shell(), url1));
2128 // Have the user decide to go to a different page which is very slow.
2129 StallDelegate stall_delegate;
2130 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
2131 GURL url2(embedded_test_server()->GetURL(
2132 "/navigation_controller/simple_page_2.html"));
2133 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2135 // That should be the pending entry.
2136 NavigationEntryImpl* entry = controller.GetPendingEntry();
2137 ASSERT_NE(nullptr, entry);
2138 EXPECT_EQ(url2, entry->GetURL());
2140 // Loading a JavaScript URL shouldn't affect the ability to stop.
2142 FailureWatcher watcher(root);
2143 GURL js("javascript:(function(){})()");
2144 controller.LoadURL(js, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2145 // This LoadURL ends up purging the pending entry, which is why this is
2146 // tricky.
2147 EXPECT_EQ(nullptr, controller.GetPendingEntry());
2148 shell()->web_contents()->Stop();
2149 watcher.Wait();
2152 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
2155 namespace {
2156 class RenderProcessKilledObserver : public WebContentsObserver {
2157 public:
2158 RenderProcessKilledObserver(WebContents* web_contents)
2159 : WebContentsObserver(web_contents) {}
2160 ~RenderProcessKilledObserver() override {}
2162 void RenderProcessGone(base::TerminationStatus status) override {
2163 CHECK_NE(status,
2164 base::TerminationStatus::TERMINATION_STATUS_PROCESS_WAS_KILLED);
2169 // This tests a race in ReloadOriginalRequest, where a cross-origin reload was
2170 // causing an in-flight replaceState to look like a cross-origin navigation,
2171 // even though it's in-page. (The reload should not modify the underlying last
2172 // committed entry.) Not crashing means that the test is successful.
2173 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, ReloadOriginalRequest) {
2174 GURL original_url(embedded_test_server()->GetURL(
2175 "/navigation_controller/simple_page_1.html"));
2176 NavigateToURL(shell(), original_url);
2177 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2178 ->GetFrameTree()
2179 ->root();
2180 RenderProcessKilledObserver kill_observer(shell()->web_contents());
2182 // Redirect so that we can use ReloadOriginalRequest.
2183 GURL redirect_url(embedded_test_server()->GetURL(
2184 "foo.com", "/navigation_controller/simple_page_1.html"));
2186 std::string script = "location.replace('" + redirect_url.spec() + "');";
2187 FrameNavigateParamsCapturer capturer(root);
2188 EXPECT_TRUE(ExecuteScript(shell()->web_contents(), script));
2189 capturer.Wait();
2190 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
2191 capturer.params().transition);
2192 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
2195 // Modify an entry in the session history and reload the original request.
2197 // We first send a replaceState() to the renderer, which will cause the
2198 // renderer to send back a DidCommitProvisionalLoad. Immediately after,
2199 // we send a ReloadOriginalRequest (which in this case is a different
2200 // origin) and will also cause the renderer to commit the frame. In the
2201 // end we verify that both navigations committed and that the URLs are
2202 // correct.
2203 std::string script = "history.replaceState({}, '', 'foo');";
2204 root->render_manager()
2205 ->current_frame_host()
2206 ->ExecuteJavaScriptWithUserGestureForTests(base::UTF8ToUTF16(script));
2207 EXPECT_FALSE(shell()->web_contents()->IsLoading());
2208 shell()->web_contents()->GetController().ReloadOriginalRequestURL(false);
2209 EXPECT_TRUE(shell()->web_contents()->IsLoading());
2210 EXPECT_EQ(redirect_url, shell()->web_contents()->GetLastCommittedURL());
2212 // Wait until there's no more navigations.
2213 GURL modified_url(embedded_test_server()->GetURL(
2214 "foo.com", "/navigation_controller/foo"));
2215 FrameNavigateParamsCapturer capturer(root);
2216 capturer.set_wait_for_load(false);
2217 capturer.set_navigations_remaining(2);
2218 capturer.Wait();
2219 EXPECT_EQ(2U, capturer.all_details().size());
2220 EXPECT_EQ(modified_url, capturer.all_params()[0].url);
2221 EXPECT_EQ(original_url, capturer.all_params()[1].url);
2222 EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
2225 // Make sure the renderer is still alive.
2226 EXPECT_TRUE(
2227 ExecuteScript(shell()->web_contents(), "console.log('Success');"));
2230 } // namespace content