[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_browsertest.cc
blobe1d17b033920387a7f12692448ec2a275b9f98c5
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/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.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/common/site_isolation_policy.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/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 // Ensure that tests can navigate subframes cross-site in both default mode and
45 // --site-per-process, but that they only go cross-process in the latter.
46 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadCrossSiteSubframe) {
47 // Load a main frame with a subframe.
48 GURL main_url(embedded_test_server()->GetURL(
49 "/navigation_controller/page_with_iframe.html"));
50 NavigateToURL(shell(), main_url);
51 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
52 ->GetFrameTree()
53 ->root();
54 ASSERT_EQ(1U, root->child_count());
55 ASSERT_NE(nullptr, root->child_at(0));
57 // Use NavigateFrameToURL to go cross-site in the subframe.
58 GURL foo_url(embedded_test_server()->GetURL(
59 "foo.com", "/navigation_controller/simple_page_1.html"));
60 NavigateFrameToURL(root->child_at(0), foo_url);
61 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
63 // We should only have swapped processes in --site-per-process.
64 bool cross_process = root->current_frame_host()->GetProcess() !=
65 root->child_at(0)->current_frame_host()->GetProcess();
66 EXPECT_EQ(AreAllSitesIsolatedForTesting(), cross_process);
69 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, LoadDataWithBaseURL) {
70 const GURL base_url("http://baseurl");
71 const GURL history_url("http://historyurl");
72 const std::string data = "<html><body>foo</body></html>";
74 const NavigationController& controller =
75 shell()->web_contents()->GetController();
76 // Load data. Blocks until it is done.
77 content::LoadDataWithBaseURL(shell(), history_url, data, base_url);
79 // We should use history_url instead of the base_url as the original url of
80 // this navigation entry, because base_url is only used for resolving relative
81 // paths in the data, or enforcing same origin policy.
82 EXPECT_EQ(controller.GetVisibleEntry()->GetOriginalRequestURL(), history_url);
85 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, UniqueIDs) {
86 const NavigationControllerImpl& controller =
87 static_cast<const NavigationControllerImpl&>(
88 shell()->web_contents()->GetController());
90 GURL main_url(embedded_test_server()->GetURL(
91 "/navigation_controller/page_with_link_to_load_iframe.html"));
92 NavigateToURL(shell(), main_url);
93 ASSERT_EQ(1, controller.GetEntryCount());
95 // Use JavaScript to click the link and load the iframe.
96 std::string script = "document.getElementById('link').click()";
97 EXPECT_TRUE(content::ExecuteScript(shell()->web_contents(), script));
98 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
99 ASSERT_EQ(2, controller.GetEntryCount());
101 // Unique IDs should... um... be unique.
102 ASSERT_NE(controller.GetEntryAtIndex(0)->GetUniqueID(),
103 controller.GetEntryAtIndex(1)->GetUniqueID());
106 // This test used to make sure that a scheme used to prevent spoofs didn't ever
107 // interfere with navigations. We switched to a different scheme, so now this is
108 // just a test to make sure we can still navigate once we prune the history
109 // list.
110 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
111 DontIgnoreBackAfterNavEntryLimit) {
112 NavigationController& controller =
113 shell()->web_contents()->GetController();
115 const int kMaxEntryCount =
116 static_cast<int>(NavigationControllerImpl::max_entry_count());
118 // Load up to the max count, all entries should be there.
119 for (int url_index = 0; url_index < kMaxEntryCount; ++url_index) {
120 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
121 EXPECT_TRUE(NavigateToURL(shell(), url));
124 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
126 // Navigate twice more more.
127 for (int url_index = kMaxEntryCount;
128 url_index < kMaxEntryCount + 2; ++url_index) {
129 GURL url(base::StringPrintf("data:text/html,page%d", url_index));
130 EXPECT_TRUE(NavigateToURL(shell(), url));
133 // We expect page0 and page1 to be gone.
134 EXPECT_EQ(kMaxEntryCount, controller.GetEntryCount());
135 EXPECT_EQ(GURL("data:text/html,page2"),
136 controller.GetEntryAtIndex(0)->GetURL());
138 // Now try to go back. This should not hang.
139 ASSERT_TRUE(controller.CanGoBack());
140 controller.GoBack();
141 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
143 // This should have successfully gone back.
144 EXPECT_EQ(GURL(base::StringPrintf("data:text/html,page%d", kMaxEntryCount)),
145 controller.GetLastCommittedEntry()->GetURL());
148 namespace {
150 int RendererHistoryLength(Shell* shell) {
151 int value = 0;
152 EXPECT_TRUE(ExecuteScriptAndExtractInt(
153 shell->web_contents(),
154 "domAutomationController.send(history.length)",
155 &value));
156 return value;
159 // Similar to the ones from content_browser_test_utils.
160 bool NavigateToURLAndReplace(Shell* shell, const GURL& url) {
161 WebContents* web_contents = shell->web_contents();
162 WaitForLoadStop(web_contents);
163 TestNavigationObserver same_tab_observer(web_contents, 1);
164 NavigationController::LoadURLParams params(url);
165 params.should_replace_current_entry = true;
166 web_contents->GetController().LoadURLWithParams(params);
167 web_contents->Focus();
168 same_tab_observer.Wait();
169 if (!IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL))
170 return false;
171 return web_contents->GetLastCommittedURL() == url;
174 } // namespace
176 // When loading a new page to replace an old page in the history list, make sure
177 // that the browser and renderer agree, and that both get it right.
178 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
179 CorrectLengthWithCurrentItemReplacement) {
180 NavigationController& controller =
181 shell()->web_contents()->GetController();
183 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page1")));
184 EXPECT_EQ(1, controller.GetEntryCount());
185 EXPECT_EQ(1, RendererHistoryLength(shell()));
187 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page1a")));
188 EXPECT_EQ(1, controller.GetEntryCount());
189 EXPECT_EQ(1, RendererHistoryLength(shell()));
191 // Now create two more entries and go back, to test replacing an entry without
192 // pruning the forward history.
193 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page2")));
194 EXPECT_EQ(2, controller.GetEntryCount());
195 EXPECT_EQ(2, RendererHistoryLength(shell()));
197 EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,page3")));
198 EXPECT_EQ(3, controller.GetEntryCount());
199 EXPECT_EQ(3, RendererHistoryLength(shell()));
201 controller.GoBack();
202 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
203 controller.GoBack();
204 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
205 EXPECT_TRUE(controller.CanGoForward());
207 EXPECT_TRUE(NavigateToURLAndReplace(shell(), GURL("data:text/html,page1b")));
208 EXPECT_EQ(3, controller.GetEntryCount());
209 EXPECT_EQ(3, RendererHistoryLength(shell()));
210 EXPECT_TRUE(controller.CanGoForward());
212 // Note that there's no way to access the renderer's notion of the history
213 // offset via JavaScript. Checking just the history length, though, is enough;
214 // if the replacement failed, there would be a new history entry and thus an
215 // incorrect length.
218 // When spawning a new page from a WebUI page, make sure that the browser and
219 // renderer agree about the length of the history list, and that both get it
220 // right.
221 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
222 CorrectLengthWithNewTabNavigatingFromWebUI) {
223 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
224 std::string(kChromeUIGpuHost));
225 EXPECT_TRUE(NavigateToURL(shell(), web_ui_page));
226 EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
227 shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
229 ShellAddedObserver observer;
230 std::string page_url = embedded_test_server()->GetURL(
231 "/navigation_controller/simple_page_1.html").spec();
232 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
233 "window.open('" + page_url + "', '_blank')"));
234 Shell* shell2 = observer.GetShell();
235 EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
237 EXPECT_EQ(1, shell2->web_contents()->GetController().GetEntryCount());
238 EXPECT_EQ(1, RendererHistoryLength(shell2));
240 // Again, as above, there's no way to access the renderer's notion of the
241 // history offset via JavaScript. Checking just the history length, again,
242 // will have to suffice.
245 namespace {
247 class NoNavigationsObserver : public WebContentsObserver {
248 public:
249 // Observes navigation for the specified |web_contents|.
250 explicit NoNavigationsObserver(WebContents* web_contents)
251 : WebContentsObserver(web_contents) {}
253 private:
254 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
255 const LoadCommittedDetails& details,
256 const FrameNavigateParams& params) override {
257 FAIL() << "No navigations should occur";
261 } // namespace
263 // Some pages create a popup, then write an iframe into it. This causes a
264 // subframe navigation without having any committed entry. Such navigations
265 // just get thrown on the ground, but we shouldn't crash.
267 // This test actually hits NAVIGATION_TYPE_NAV_IGNORE three times. Two of them,
268 // the initial window.open() and the iframe creation, don't try to create
269 // navigation entries, and the third, the new navigation, tries to.
270 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, SubframeOnEmptyPage) {
271 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
272 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
274 FrameTreeNode* root =
275 static_cast<WebContentsImpl*>(shell()->web_contents())->
276 GetFrameTree()->root();
278 // Pop open a new window.
279 ShellAddedObserver new_shell_observer;
280 std::string script = "window.open()";
281 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
282 Shell* new_shell = new_shell_observer.GetShell();
283 ASSERT_NE(new_shell->web_contents(), shell()->web_contents());
284 FrameTreeNode* new_root =
285 static_cast<WebContentsImpl*>(new_shell->web_contents())->
286 GetFrameTree()->root();
288 // Make a new iframe in it.
289 NoNavigationsObserver observer(new_shell->web_contents());
290 script = "var iframe = document.createElement('iframe');"
291 "iframe.src = 'data:text/html,<p>some page</p>';"
292 "document.body.appendChild(iframe);";
293 EXPECT_TRUE(content::ExecuteScript(new_root->current_frame_host(), script));
294 // The success check is of the last-committed entry, and there is none.
295 WaitForLoadStopWithoutSuccessCheck(new_shell->web_contents());
297 ASSERT_EQ(1U, new_root->child_count());
298 ASSERT_NE(nullptr, new_root->child_at(0));
300 // Navigate it.
301 GURL frame_url = embedded_test_server()->GetURL(
302 "/navigation_controller/simple_page_2.html");
303 script = "location.assign('" + frame_url.spec() + "')";
304 EXPECT_TRUE(content::ExecuteScript(
305 new_root->child_at(0)->current_frame_host(), script));
307 // Success is not crashing, and not navigating.
308 EXPECT_EQ(nullptr,
309 new_shell->web_contents()->GetController().GetLastCommittedEntry());
312 namespace {
314 class FrameNavigateParamsCapturer : public WebContentsObserver {
315 public:
316 // Observes navigation for the specified |node|.
317 explicit FrameNavigateParamsCapturer(FrameTreeNode* node)
318 : WebContentsObserver(
319 node->current_frame_host()->delegate()->GetAsWebContents()),
320 frame_tree_node_id_(node->frame_tree_node_id()),
321 navigations_remaining_(1),
322 wait_for_load_(true),
323 message_loop_runner_(new MessageLoopRunner) {}
325 void set_navigations_remaining(int count) {
326 navigations_remaining_ = count;
329 void set_wait_for_load(bool ignore) {
330 wait_for_load_ = ignore;
333 void Wait() {
334 message_loop_runner_->Run();
337 const FrameNavigateParams& params() const {
338 EXPECT_EQ(1U, params_.size());
339 return params_[0];
342 const std::vector<FrameNavigateParams>& all_params() const {
343 return params_;
346 const LoadCommittedDetails& details() const {
347 EXPECT_EQ(1U, details_.size());
348 return details_[0];
351 const std::vector<LoadCommittedDetails>& all_details() const {
352 return details_;
355 private:
356 void DidNavigateAnyFrame(RenderFrameHost* render_frame_host,
357 const LoadCommittedDetails& details,
358 const FrameNavigateParams& params) override {
359 RenderFrameHostImpl* rfh =
360 static_cast<RenderFrameHostImpl*>(render_frame_host);
361 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
362 return;
364 --navigations_remaining_;
365 params_.push_back(params);
366 details_.push_back(details);
367 if (!navigations_remaining_ &&
368 (!web_contents()->IsLoading() || !wait_for_load_))
369 message_loop_runner_->Quit();
372 void DidStopLoading() override {
373 if (!navigations_remaining_)
374 message_loop_runner_->Quit();
377 // The id of the FrameTreeNode whose navigations to observe.
378 int frame_tree_node_id_;
380 // How many navigations remain to capture.
381 int navigations_remaining_;
383 // Whether to also wait for the load to complete.
384 bool wait_for_load_;
386 // The params of the navigations.
387 std::vector<FrameNavigateParams> params_;
389 // The details of the navigations.
390 std::vector<LoadCommittedDetails> details_;
392 // The MessageLoopRunner used to spin the message loop.
393 scoped_refptr<MessageLoopRunner> message_loop_runner_;
396 class LoadCommittedCapturer : public WebContentsObserver {
397 public:
398 // Observes the load commit for the specified |node|.
399 explicit LoadCommittedCapturer(FrameTreeNode* node)
400 : WebContentsObserver(
401 node->current_frame_host()->delegate()->GetAsWebContents()),
402 frame_tree_node_id_(node->frame_tree_node_id()),
403 message_loop_runner_(new MessageLoopRunner) {}
405 // Observes the load commit for the next created frame in the specified
406 // |web_contents|.
407 explicit LoadCommittedCapturer(WebContents* web_contents)
408 : WebContentsObserver(web_contents),
409 frame_tree_node_id_(0),
410 message_loop_runner_(new MessageLoopRunner) {}
412 void Wait() {
413 message_loop_runner_->Run();
416 ui::PageTransition transition_type() const {
417 return transition_type_;
420 private:
421 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
422 RenderFrameHostImpl* rfh =
423 static_cast<RenderFrameHostImpl*>(render_frame_host);
425 // Don't pay attention to swapped out RenderFrameHosts in the main frame.
426 // TODO(nasko): Remove once swappedout:// is gone.
427 // See https://crbug.com/357747.
428 if (!RenderFrameHostImpl::IsRFHStateActive(rfh->rfh_state())) {
429 DLOG(INFO) << "Skipping swapped out RFH: "
430 << rfh->GetSiteInstance()->GetSiteURL();
431 return;
434 // If this object was not created with a specified frame tree node, then use
435 // the first created active RenderFrameHost. Once a node is selected, there
436 // shouldn't be any other frames being created.
437 int frame_tree_node_id = rfh->frame_tree_node()->frame_tree_node_id();
438 DCHECK(frame_tree_node_id_ == 0 ||
439 frame_tree_node_id_ == frame_tree_node_id);
440 frame_tree_node_id_ = frame_tree_node_id;
443 void DidCommitProvisionalLoadForFrame(
444 RenderFrameHost* render_frame_host,
445 const GURL& url,
446 ui::PageTransition transition_type) override {
447 DCHECK_NE(0, frame_tree_node_id_);
448 RenderFrameHostImpl* rfh =
449 static_cast<RenderFrameHostImpl*>(render_frame_host);
450 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
451 return;
453 transition_type_ = transition_type;
454 if (!web_contents()->IsLoading())
455 message_loop_runner_->Quit();
458 void DidStopLoading() override { message_loop_runner_->Quit(); }
460 // The id of the FrameTreeNode whose navigations to observe.
461 int frame_tree_node_id_;
463 // The transition_type of the last navigation.
464 ui::PageTransition transition_type_;
466 // The MessageLoopRunner used to spin the message loop.
467 scoped_refptr<MessageLoopRunner> message_loop_runner_;
470 } // namespace
472 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
473 ErrorPageReplacement) {
474 NavigationController& controller = shell()->web_contents()->GetController();
475 GURL error_url(
476 net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_RESET));
477 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
478 base::Bind(&net::URLRequestFailedJob::AddUrlHandler));
480 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
481 EXPECT_EQ(1, controller.GetEntryCount());
483 FrameTreeNode* root =
484 static_cast<WebContentsImpl*>(shell()->web_contents())->
485 GetFrameTree()->root();
487 // Navigate to a page that fails to load. It must result in an error page, the
488 // NEW_PAGE navigation type, and an addition to the history list.
490 FrameNavigateParamsCapturer capturer(root);
491 NavigateFrameToURL(root, error_url);
492 capturer.Wait();
493 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
494 NavigationEntry* entry = controller.GetLastCommittedEntry();
495 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
496 EXPECT_EQ(2, controller.GetEntryCount());
499 // Navigate again to the page that fails to load. It must result in an error
500 // page, the EXISTING_PAGE navigation type, and no addition to the history
501 // list. We do not use SAME_PAGE here; that case only differs in that it
502 // clears the pending entry, and there is no pending entry after a load
503 // failure.
505 FrameNavigateParamsCapturer capturer(root);
506 NavigateFrameToURL(root, error_url);
507 capturer.Wait();
508 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
509 NavigationEntry* entry = controller.GetLastCommittedEntry();
510 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
511 EXPECT_EQ(2, controller.GetEntryCount());
514 // Make a new entry ...
515 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
516 EXPECT_EQ(3, controller.GetEntryCount());
518 // ... and replace it with a failed load. (Note that when you set the
519 // should_replace_current_entry flag, the navigation is classified as NEW_PAGE
520 // because that is a classification of the renderer's behavior, and the flag
521 // is a browser-side flag.)
523 FrameNavigateParamsCapturer capturer(root);
524 NavigateToURLAndReplace(shell(), error_url);
525 capturer.Wait();
526 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
527 NavigationEntry* entry = controller.GetLastCommittedEntry();
528 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
529 EXPECT_EQ(3, controller.GetEntryCount());
532 // Make a new web ui page to force a process swap ...
533 GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
534 std::string(kChromeUIGpuHost));
535 NavigateToURL(shell(), web_ui_page);
536 EXPECT_EQ(4, controller.GetEntryCount());
538 // ... and replace it with a failed load. (It is NEW_PAGE for the reason noted
539 // above.)
541 FrameNavigateParamsCapturer capturer(root);
542 NavigateToURLAndReplace(shell(), error_url);
543 capturer.Wait();
544 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
545 NavigationEntry* entry = controller.GetLastCommittedEntry();
546 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
547 EXPECT_EQ(4, controller.GetEntryCount());
551 // Various tests for navigation type classifications. TODO(avi): It's rather
552 // bogus that the same info is in two different enums; http://crbug.com/453555.
554 // Verify that navigations for NAVIGATION_TYPE_NEW_PAGE are correctly
555 // classified.
556 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
557 NavigationTypeClassification_NewPage) {
558 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
560 FrameTreeNode* root =
561 static_cast<WebContentsImpl*>(shell()->web_contents())->
562 GetFrameTree()->root();
565 // Simple load.
566 FrameNavigateParamsCapturer capturer(root);
567 GURL frame_url(embedded_test_server()->GetURL(
568 "/navigation_controller/page_with_links.html"));
569 NavigateFrameToURL(root, frame_url);
570 capturer.Wait();
571 // TODO(avi,creis): Why is this (and quite a few others below) a "link"
572 // transition? Lots of these transitions should be cleaned up.
573 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
574 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
575 EXPECT_FALSE(capturer.details().is_in_page);
579 // Load via a fragment link click.
580 FrameNavigateParamsCapturer capturer(root);
581 std::string script = "document.getElementById('fraglink').click()";
582 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
583 capturer.Wait();
584 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
585 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
586 EXPECT_TRUE(capturer.details().is_in_page);
590 // Load via link click.
591 FrameNavigateParamsCapturer capturer(root);
592 std::string script = "document.getElementById('thelink').click()";
593 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
594 capturer.Wait();
595 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
596 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
597 EXPECT_FALSE(capturer.details().is_in_page);
601 // location.assign().
602 FrameNavigateParamsCapturer capturer(root);
603 GURL frame_url(embedded_test_server()->GetURL(
604 "/navigation_controller/simple_page_2.html"));
605 std::string script = "location.assign('" + frame_url.spec() + "')";
606 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
607 capturer.Wait();
608 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
609 capturer.params().transition);
610 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
611 EXPECT_FALSE(capturer.details().is_in_page);
615 // history.pushState().
616 FrameNavigateParamsCapturer capturer(root);
617 std::string script =
618 "history.pushState({}, 'page 1', 'simple_page_1.html')";
619 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
620 capturer.Wait();
621 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
622 capturer.params().transition);
623 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
624 EXPECT_TRUE(capturer.details().is_in_page);
628 // Verify that navigations for NAVIGATION_TYPE_EXISTING_PAGE are correctly
629 // classified.
630 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
631 NavigationTypeClassification_ExistingPage) {
632 GURL url1(embedded_test_server()->GetURL(
633 "/navigation_controller/simple_page_1.html"));
634 NavigateToURL(shell(), url1);
635 GURL url2(embedded_test_server()->GetURL(
636 "/navigation_controller/simple_page_2.html"));
637 NavigateToURL(shell(), url2);
639 FrameTreeNode* root =
640 static_cast<WebContentsImpl*>(shell()->web_contents())->
641 GetFrameTree()->root();
644 // Back from the browser side.
645 FrameNavigateParamsCapturer capturer(root);
646 shell()->web_contents()->GetController().GoBack();
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);
653 EXPECT_FALSE(capturer.details().is_in_page);
657 // Forward from the browser side.
658 FrameNavigateParamsCapturer capturer(root);
659 shell()->web_contents()->GetController().GoForward();
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);
666 EXPECT_FALSE(capturer.details().is_in_page);
670 // Back from the renderer side.
671 FrameNavigateParamsCapturer capturer(root);
672 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
673 "history.back()"));
674 capturer.Wait();
675 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
676 | ui::PAGE_TRANSITION_FORWARD_BACK
677 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
678 capturer.params().transition);
679 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
680 EXPECT_FALSE(capturer.details().is_in_page);
684 // Forward from the renderer side.
685 FrameNavigateParamsCapturer capturer(root);
686 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
687 "history.forward()"));
688 capturer.Wait();
689 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
690 | ui::PAGE_TRANSITION_FORWARD_BACK
691 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
692 capturer.params().transition);
693 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
694 EXPECT_FALSE(capturer.details().is_in_page);
698 // Back from the renderer side via history.go().
699 FrameNavigateParamsCapturer capturer(root);
700 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
701 "history.go(-1)"));
702 capturer.Wait();
703 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
704 | ui::PAGE_TRANSITION_FORWARD_BACK
705 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
706 capturer.params().transition);
707 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
708 EXPECT_FALSE(capturer.details().is_in_page);
712 // Forward from the renderer side via history.go().
713 FrameNavigateParamsCapturer capturer(root);
714 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
715 "history.go(1)"));
716 capturer.Wait();
717 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
718 | ui::PAGE_TRANSITION_FORWARD_BACK
719 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
720 capturer.params().transition);
721 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
722 EXPECT_FALSE(capturer.details().is_in_page);
726 // Reload from the browser side.
727 FrameNavigateParamsCapturer capturer(root);
728 shell()->web_contents()->GetController().Reload(false);
729 capturer.Wait();
730 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD, capturer.params().transition);
731 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
732 EXPECT_FALSE(capturer.details().is_in_page);
736 // Reload from the renderer side.
737 FrameNavigateParamsCapturer capturer(root);
738 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(),
739 "location.reload()"));
740 capturer.Wait();
741 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
742 capturer.params().transition);
743 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
744 EXPECT_FALSE(capturer.details().is_in_page);
748 // location.replace().
749 FrameNavigateParamsCapturer capturer(root);
750 GURL frame_url(embedded_test_server()->GetURL(
751 "/navigation_controller/simple_page_1.html"));
752 std::string script = "location.replace('" + frame_url.spec() + "')";
753 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
754 capturer.Wait();
755 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
756 capturer.params().transition);
757 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
758 EXPECT_FALSE(capturer.details().is_in_page);
761 // Now, various in-page navigations.
764 // history.replaceState().
765 FrameNavigateParamsCapturer capturer(root);
766 std::string script =
767 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
768 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
769 capturer.Wait();
770 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
771 capturer.params().transition);
772 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
773 EXPECT_TRUE(capturer.details().is_in_page);
776 // Back and forward across a fragment navigation.
778 GURL url_links(embedded_test_server()->GetURL(
779 "/navigation_controller/page_with_links.html"));
780 NavigateToURL(shell(), url_links);
781 std::string script = "document.getElementById('fraglink').click()";
782 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
783 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
786 // Back.
787 FrameNavigateParamsCapturer capturer(root);
788 shell()->web_contents()->GetController().GoBack();
789 capturer.Wait();
790 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
791 | ui::PAGE_TRANSITION_FORWARD_BACK
792 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
793 capturer.params().transition);
794 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
795 EXPECT_TRUE(capturer.details().is_in_page);
799 // Forward.
800 FrameNavigateParamsCapturer capturer(root);
801 shell()->web_contents()->GetController().GoForward();
802 capturer.Wait();
803 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
804 capturer.params().transition);
805 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
806 EXPECT_TRUE(capturer.details().is_in_page);
809 // Back and forward across a pushState-created navigation.
811 NavigateToURL(shell(), url1);
812 script = "history.pushState({}, 'page 2', 'simple_page_2.html')";
813 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
814 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
817 // Back.
818 FrameNavigateParamsCapturer capturer(root);
819 shell()->web_contents()->GetController().GoBack();
820 capturer.Wait();
821 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
822 | ui::PAGE_TRANSITION_FORWARD_BACK
823 | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR,
824 capturer.params().transition);
825 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
826 EXPECT_TRUE(capturer.details().is_in_page);
830 // Forward.
831 FrameNavigateParamsCapturer capturer(root);
832 shell()->web_contents()->GetController().GoForward();
833 capturer.Wait();
834 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_FORWARD_BACK,
835 capturer.params().transition);
836 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
837 EXPECT_TRUE(capturer.details().is_in_page);
841 // Verify that navigations for NAVIGATION_TYPE_SAME_PAGE are correctly
842 // classified.
843 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
844 NavigationTypeClassification_SamePage) {
845 GURL url1(embedded_test_server()->GetURL(
846 "/navigation_controller/simple_page_1.html"));
847 NavigateToURL(shell(), url1);
849 FrameTreeNode* root =
850 static_cast<WebContentsImpl*>(shell()->web_contents())->
851 GetFrameTree()->root();
854 // Simple load.
855 FrameNavigateParamsCapturer capturer(root);
856 GURL frame_url(embedded_test_server()->GetURL(
857 "/navigation_controller/simple_page_1.html"));
858 NavigateFrameToURL(root, frame_url);
859 capturer.Wait();
860 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
861 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, capturer.details().type);
865 // Verify that navigations for NAVIGATION_TYPE_NEW_SUBFRAME and
866 // NAVIGATION_TYPE_AUTO_SUBFRAME are properly classified.
867 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
868 NavigationTypeClassification_NewAndAutoSubframe) {
869 GURL main_url(embedded_test_server()->GetURL(
870 "/navigation_controller/page_with_iframe.html"));
871 NavigateToURL(shell(), main_url);
873 // It is safe to obtain the root frame tree node here, as it doesn't change.
874 FrameTreeNode* root =
875 static_cast<WebContentsImpl*>(shell()->web_contents())->
876 GetFrameTree()->root();
878 ASSERT_EQ(1U, root->child_count());
879 ASSERT_NE(nullptr, root->child_at(0));
882 // Initial load.
883 LoadCommittedCapturer capturer(root->child_at(0));
884 GURL frame_url(embedded_test_server()->GetURL(
885 "/navigation_controller/simple_page_1.html"));
886 NavigateFrameToURL(root->child_at(0), frame_url);
887 capturer.Wait();
888 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
892 // Simple load.
893 FrameNavigateParamsCapturer capturer(root->child_at(0));
894 GURL frame_url(embedded_test_server()->GetURL(
895 "/navigation_controller/simple_page_2.html"));
896 NavigateFrameToURL(root->child_at(0), frame_url);
897 capturer.Wait();
898 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
899 capturer.params().transition);
900 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
904 // Back.
905 FrameNavigateParamsCapturer capturer(root->child_at(0));
906 shell()->web_contents()->GetController().GoBack();
907 capturer.Wait();
908 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
909 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
913 // Forward.
914 FrameNavigateParamsCapturer capturer(root->child_at(0));
915 shell()->web_contents()->GetController().GoForward();
916 capturer.Wait();
917 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
918 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
922 // Simple load.
923 FrameNavigateParamsCapturer capturer(root->child_at(0));
924 GURL frame_url(embedded_test_server()->GetURL(
925 "/navigation_controller/page_with_links.html"));
926 NavigateFrameToURL(root->child_at(0), frame_url);
927 capturer.Wait();
928 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
929 capturer.params().transition);
930 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
934 // Load via a fragment link click.
935 FrameNavigateParamsCapturer capturer(root->child_at(0));
936 std::string script = "document.getElementById('fraglink').click()";
937 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
938 script));
939 capturer.Wait();
940 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
941 capturer.params().transition);
942 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
946 // location.assign().
947 FrameNavigateParamsCapturer capturer(root->child_at(0));
948 GURL frame_url(embedded_test_server()->GetURL(
949 "/navigation_controller/simple_page_1.html"));
950 std::string script = "location.assign('" + frame_url.spec() + "')";
951 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
952 script));
953 capturer.Wait();
954 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
955 capturer.params().transition);
956 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
960 // location.replace().
961 LoadCommittedCapturer capturer(root->child_at(0));
962 GURL frame_url(embedded_test_server()->GetURL(
963 "/navigation_controller/simple_page_2.html"));
964 std::string script = "location.replace('" + frame_url.spec() + "')";
965 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
966 script));
967 capturer.Wait();
968 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
972 // history.pushState().
973 FrameNavigateParamsCapturer capturer(root->child_at(0));
974 std::string script =
975 "history.pushState({}, 'page 1', 'simple_page_1.html')";
976 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
977 script));
978 capturer.Wait();
979 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
980 capturer.params().transition);
981 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
985 // history.replaceState().
986 LoadCommittedCapturer capturer(root->child_at(0));
987 std::string script =
988 "history.replaceState({}, 'page 2', 'simple_page_2.html')";
989 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
990 script));
991 capturer.Wait();
992 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
996 // Reload.
997 LoadCommittedCapturer capturer(root->child_at(0));
998 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
999 "location.reload()"));
1000 capturer.Wait();
1001 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1005 // Create an iframe.
1006 LoadCommittedCapturer capturer(shell()->web_contents());
1007 GURL frame_url(embedded_test_server()->GetURL(
1008 "/navigation_controller/simple_page_1.html"));
1009 std::string script = "var iframe = document.createElement('iframe');"
1010 "iframe.src = '" + frame_url.spec() + "';"
1011 "document.body.appendChild(iframe);";
1012 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1013 capturer.Wait();
1014 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1018 // Verify that navigations caused by client-side redirects are correctly
1019 // classified.
1020 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1021 NavigationTypeClassification_ClientSideRedirect) {
1022 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
1023 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1025 FrameTreeNode* root =
1026 static_cast<WebContentsImpl*>(shell()->web_contents())->
1027 GetFrameTree()->root();
1030 // Load the redirecting page.
1031 FrameNavigateParamsCapturer capturer(root);
1032 capturer.set_navigations_remaining(2);
1033 GURL frame_url(embedded_test_server()->GetURL(
1034 "/navigation_controller/client_redirect.html"));
1035 NavigateFrameToURL(root, frame_url);
1036 capturer.Wait();
1038 std::vector<FrameNavigateParams> params = capturer.all_params();
1039 std::vector<LoadCommittedDetails> details = capturer.all_details();
1040 ASSERT_EQ(2U, params.size());
1041 ASSERT_EQ(2U, details.size());
1042 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, params[0].transition);
1043 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details[0].type);
1044 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
1045 params[1].transition);
1046 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details[1].type);
1050 // Verify that the LoadCommittedDetails::is_in_page value is properly set for
1051 // non-IN_PAGE navigations. (It's tested for IN_PAGE navigations with the
1052 // NavigationTypeClassification_InPage test.)
1053 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1054 LoadCommittedDetails_IsInPage) {
1055 GURL links_url(embedded_test_server()->GetURL(
1056 "/navigation_controller/page_with_links.html"));
1057 NavigateToURL(shell(), links_url);
1058 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1060 FrameTreeNode* root =
1061 static_cast<WebContentsImpl*>(shell()->web_contents())->
1062 GetFrameTree()->root();
1065 // Do a fragment link click.
1066 FrameNavigateParamsCapturer capturer(root);
1067 std::string script = "document.getElementById('fraglink').click()";
1068 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1069 capturer.Wait();
1070 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1071 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1072 EXPECT_TRUE(capturer.details().is_in_page);
1076 // Do a non-fragment link click.
1077 FrameNavigateParamsCapturer capturer(root);
1078 std::string script = "document.getElementById('thelink').click()";
1079 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1080 capturer.Wait();
1081 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, capturer.params().transition);
1082 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, capturer.details().type);
1083 EXPECT_FALSE(capturer.details().is_in_page);
1086 // Second verse, same as the first. (But in a subframe.)
1088 GURL iframe_url(embedded_test_server()->GetURL(
1089 "/navigation_controller/page_with_iframe.html"));
1090 NavigateToURL(shell(), iframe_url);
1092 root = static_cast<WebContentsImpl*>(shell()->web_contents())->
1093 GetFrameTree()->root();
1095 ASSERT_EQ(1U, root->child_count());
1096 ASSERT_NE(nullptr, root->child_at(0));
1098 NavigateFrameToURL(root->child_at(0), links_url);
1099 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1102 // Do a fragment link click.
1103 FrameNavigateParamsCapturer capturer(root->child_at(0));
1104 std::string script = "document.getElementById('fraglink').click()";
1105 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1106 script));
1107 capturer.Wait();
1108 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1109 capturer.params().transition);
1110 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1111 EXPECT_TRUE(capturer.details().is_in_page);
1115 // Do a non-fragment link click.
1116 FrameNavigateParamsCapturer capturer(root->child_at(0));
1117 std::string script = "document.getElementById('thelink').click()";
1118 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1119 script));
1120 capturer.Wait();
1121 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1122 capturer.params().transition);
1123 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1124 EXPECT_FALSE(capturer.details().is_in_page);
1128 // Verify the tree of FrameNavigationEntries after initial about:blank commits
1129 // in subframes, which should not count as real committed loads.
1130 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1131 FrameNavigationEntry_BlankAutoSubframe) {
1132 GURL about_blank_url(url::kAboutBlankURL);
1133 GURL main_url(embedded_test_server()->GetURL(
1134 "/navigation_controller/simple_page_1.html"));
1135 NavigateToURL(shell(), main_url);
1136 const NavigationControllerImpl& controller =
1137 static_cast<const NavigationControllerImpl&>(
1138 shell()->web_contents()->GetController());
1139 FrameTreeNode* root =
1140 static_cast<WebContentsImpl*>(shell()->web_contents())->
1141 GetFrameTree()->root();
1143 // 1. Create a iframe with no URL.
1145 LoadCommittedCapturer capturer(shell()->web_contents());
1146 std::string script = "var iframe = document.createElement('iframe');"
1147 "document.body.appendChild(iframe);";
1148 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1149 capturer.Wait();
1150 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1153 // Check last committed NavigationEntry.
1154 EXPECT_EQ(1, controller.GetEntryCount());
1155 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1156 EXPECT_EQ(main_url, entry->GetURL());
1157 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1158 EXPECT_EQ(main_url, root_entry->url());
1160 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1161 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1162 // The entry should now have one blank subframe FrameNavigationEntry, but
1163 // this does not count as committing a real load.
1164 ASSERT_EQ(1U, entry->root_node()->children.size());
1165 FrameNavigationEntry* frame_entry =
1166 entry->root_node()->children[0]->frame_entry.get();
1167 EXPECT_EQ(about_blank_url, frame_entry->url());
1168 } else {
1169 // There are no subframe FrameNavigationEntries by default.
1170 EXPECT_EQ(0U, entry->root_node()->children.size());
1172 EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
1174 // 1a. A nested iframe with no URL should also create a subframe entry but not
1175 // count as a real load.
1177 LoadCommittedCapturer capturer(shell()->web_contents());
1178 std::string script = "var iframe = document.createElement('iframe');"
1179 "document.body.appendChild(iframe);";
1180 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1181 script));
1182 capturer.Wait();
1183 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1186 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1187 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1188 // The nested entry should have one blank subframe FrameNavigationEntry, but
1189 // this does not count as committing a real load.
1190 ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
1191 FrameNavigationEntry* frame_entry =
1192 entry->root_node()->children[0]->children[0]->frame_entry.get();
1193 EXPECT_EQ(about_blank_url, frame_entry->url());
1194 } else {
1195 // There are no subframe FrameNavigationEntries by default.
1196 EXPECT_EQ(0U, entry->root_node()->children.size());
1198 EXPECT_FALSE(root->child_at(0)->child_at(0)->has_committed_real_load());
1200 // 2. Create another iframe with an explicit about:blank URL.
1202 LoadCommittedCapturer capturer(shell()->web_contents());
1203 std::string script = "var iframe = document.createElement('iframe');"
1204 "iframe.src = 'about:blank';"
1205 "document.body.appendChild(iframe);";
1206 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1207 capturer.Wait();
1208 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1211 // Check last committed NavigationEntry.
1212 EXPECT_EQ(1, controller.GetEntryCount());
1213 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1215 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1216 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1217 // The new entry should have one blank subframe FrameNavigationEntry, but
1218 // this does not count as committing a real load.
1219 ASSERT_EQ(2U, entry->root_node()->children.size());
1220 FrameNavigationEntry* frame_entry =
1221 entry->root_node()->children[1]->frame_entry.get();
1222 EXPECT_EQ(about_blank_url, frame_entry->url());
1223 } else {
1224 // There are no subframe FrameNavigationEntries by default.
1225 EXPECT_EQ(0U, entry->root_node()->children.size());
1227 EXPECT_FALSE(root->child_at(1)->has_committed_real_load());
1229 // 3. A real same-site navigation in the nested iframe should be AUTO.
1230 GURL frame_url(embedded_test_server()->GetURL(
1231 "/navigation_controller/simple_page_1.html"));
1233 LoadCommittedCapturer capturer(root->child_at(0)->child_at(0));
1234 std::string script = "var frames = document.getElementsByTagName('iframe');"
1235 "frames[0].src = '" + frame_url.spec() + "';";
1236 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1237 script));
1238 capturer.Wait();
1239 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1242 // Check last committed NavigationEntry. It should have replaced the previous
1243 // frame entry in the original NavigationEntry.
1244 EXPECT_EQ(1, controller.GetEntryCount());
1245 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1247 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1248 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1249 // The entry should still have one nested subframe FrameNavigationEntry.
1250 ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
1251 FrameNavigationEntry* frame_entry =
1252 entry->root_node()->children[0]->children[0]->frame_entry.get();
1253 EXPECT_EQ(frame_url, frame_entry->url());
1254 } else {
1255 // There are no subframe FrameNavigationEntries by default.
1256 EXPECT_EQ(0U, entry->root_node()->children.size());
1258 EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
1259 EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
1260 EXPECT_FALSE(root->child_at(1)->has_committed_real_load());
1262 // 4. A real cross-site navigation in the second iframe should be AUTO.
1263 GURL foo_url(embedded_test_server()->GetURL(
1264 "foo.com", "/navigation_controller/simple_page_2.html"));
1266 LoadCommittedCapturer capturer(root->child_at(1));
1267 std::string script = "var frames = document.getElementsByTagName('iframe');"
1268 "frames[1].src = '" + foo_url.spec() + "';";
1269 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1270 capturer.Wait();
1271 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1274 // Check last committed NavigationEntry.
1275 EXPECT_EQ(1, controller.GetEntryCount());
1276 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
1278 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1279 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1280 // The entry should still have two subframe FrameNavigationEntries.
1281 ASSERT_EQ(2U, entry->root_node()->children.size());
1282 FrameNavigationEntry* frame_entry =
1283 entry->root_node()->children[1]->frame_entry.get();
1284 EXPECT_EQ(foo_url, frame_entry->url());
1285 } else {
1286 // There are no subframe FrameNavigationEntries by default.
1287 EXPECT_EQ(0U, entry->root_node()->children.size());
1289 EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
1290 EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
1291 EXPECT_TRUE(root->child_at(1)->has_committed_real_load());
1293 // 5. A new navigation to about:blank in the nested frame should count as a
1294 // real load, since that frame has already committed a real load and this is
1295 // not the initial blank page.
1297 LoadCommittedCapturer capturer(root->child_at(0)->child_at(0));
1298 std::string script = "var frames = document.getElementsByTagName('iframe');"
1299 "frames[0].src = 'about:blank';";
1300 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1301 script));
1302 capturer.Wait();
1303 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME, capturer.transition_type());
1306 // This should have created a new NavigationEntry.
1307 EXPECT_EQ(2, controller.GetEntryCount());
1308 EXPECT_NE(entry, controller.GetLastCommittedEntry());
1309 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
1311 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1312 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1313 ASSERT_EQ(2U, entry->root_node()->children.size());
1314 FrameNavigationEntry* frame_entry =
1315 entry2->root_node()->children[0]->children[0]->frame_entry.get();
1316 EXPECT_EQ(about_blank_url, frame_entry->url());
1317 } else {
1318 // There are no subframe FrameNavigationEntries by default.
1319 EXPECT_EQ(0U, entry->root_node()->children.size());
1321 EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
1322 EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
1323 EXPECT_TRUE(root->child_at(1)->has_committed_real_load());
1325 // Check the end result of the frame tree.
1326 if (AreAllSitesIsolatedForTesting()) {
1327 FrameTreeVisualizer visualizer;
1328 EXPECT_EQ(
1329 " Site A ------------ proxies for B\n"
1330 " |--Site A ------- proxies for B\n"
1331 " | +--Site A -- proxies for B\n"
1332 " +--Site B ------- proxies for A\n"
1333 "Where A = http://127.0.0.1/\n"
1334 " B = http://foo.com/",
1335 visualizer.DepictFrameTree(root));
1339 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_AUTO_SUBFRAME
1340 // commits.
1341 // TODO(creis): Test updating entries for history auto subframe navigations.
1342 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1343 FrameNavigationEntry_AutoSubframe) {
1344 GURL main_url(embedded_test_server()->GetURL(
1345 "/navigation_controller/simple_page_1.html"));
1346 NavigateToURL(shell(), main_url);
1347 const NavigationControllerImpl& controller =
1348 static_cast<const NavigationControllerImpl&>(
1349 shell()->web_contents()->GetController());
1350 FrameTreeNode* root =
1351 static_cast<WebContentsImpl*>(shell()->web_contents())->
1352 GetFrameTree()->root();
1354 // 1. Create a same-site iframe.
1355 GURL frame_url(embedded_test_server()->GetURL(
1356 "/navigation_controller/simple_page_2.html"));
1358 LoadCommittedCapturer capturer(shell()->web_contents());
1359 std::string script = "var iframe = document.createElement('iframe');"
1360 "iframe.src = '" + frame_url.spec() + "';"
1361 "document.body.appendChild(iframe);";
1362 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1363 capturer.Wait();
1364 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1367 // Check last committed NavigationEntry.
1368 EXPECT_EQ(1, controller.GetEntryCount());
1369 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1370 EXPECT_EQ(main_url, entry->GetURL());
1371 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
1372 EXPECT_EQ(main_url, root_entry->url());
1373 EXPECT_FALSE(controller.GetPendingEntry());
1375 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1376 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1377 // The entry should now have a subframe FrameNavigationEntry.
1378 ASSERT_EQ(1U, entry->root_node()->children.size());
1379 FrameNavigationEntry* frame_entry =
1380 entry->root_node()->children[0]->frame_entry.get();
1381 EXPECT_EQ(frame_url, frame_entry->url());
1382 EXPECT_TRUE(root->child_at(0)->has_committed_real_load());
1383 } else {
1384 // There are no subframe FrameNavigationEntries by default.
1385 EXPECT_EQ(0U, entry->root_node()->children.size());
1388 // 2. Create a second, initially cross-site iframe.
1389 GURL foo_url(embedded_test_server()->GetURL(
1390 "foo.com", "/navigation_controller/simple_page_1.html"));
1392 LoadCommittedCapturer capturer(shell()->web_contents());
1393 std::string script = "var iframe = document.createElement('iframe');"
1394 "iframe.src = '" + foo_url.spec() + "';"
1395 "document.body.appendChild(iframe);";
1396 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1397 capturer.Wait();
1398 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1401 // The last committed NavigationEntry shouldn't have changed.
1402 EXPECT_EQ(1, controller.GetEntryCount());
1403 entry = controller.GetLastCommittedEntry();
1404 EXPECT_EQ(main_url, entry->GetURL());
1405 root_entry = entry->root_node()->frame_entry.get();
1406 EXPECT_EQ(main_url, root_entry->url());
1407 EXPECT_FALSE(controller.GetPendingEntry());
1409 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1410 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1411 // The entry should now have 2 subframe FrameNavigationEntries.
1412 ASSERT_EQ(2U, entry->root_node()->children.size());
1413 FrameNavigationEntry* frame_entry =
1414 entry->root_node()->children[1]->frame_entry.get();
1415 EXPECT_EQ(foo_url, frame_entry->url());
1416 EXPECT_TRUE(root->child_at(1)->has_committed_real_load());
1417 } else {
1418 // There are no subframe FrameNavigationEntries by default.
1419 EXPECT_EQ(0U, entry->root_node()->children.size());
1422 // 3. Create a nested iframe in the second subframe.
1424 LoadCommittedCapturer capturer(shell()->web_contents());
1425 std::string script = "var iframe = document.createElement('iframe');"
1426 "iframe.src = '" + foo_url.spec() + "';"
1427 "document.body.appendChild(iframe);";
1428 EXPECT_TRUE(content::ExecuteScript(root->child_at(1)->current_frame_host(),
1429 script));
1430 capturer.Wait();
1431 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1434 // The last committed NavigationEntry shouldn't have changed.
1435 EXPECT_EQ(1, controller.GetEntryCount());
1436 entry = controller.GetLastCommittedEntry();
1437 EXPECT_EQ(main_url, entry->GetURL());
1438 root_entry = entry->root_node()->frame_entry.get();
1439 EXPECT_EQ(main_url, root_entry->url());
1441 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1442 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1443 // The entry should now have 2 subframe FrameNavigationEntries.
1444 ASSERT_EQ(2U, entry->root_node()->children.size());
1445 ASSERT_EQ(1U, entry->root_node()->children[1]->children.size());
1446 FrameNavigationEntry* frame_entry =
1447 entry->root_node()->children[1]->children[0]->frame_entry.get();
1448 EXPECT_EQ(foo_url, frame_entry->url());
1449 } else {
1450 // There are no subframe FrameNavigationEntries by default.
1451 EXPECT_EQ(0U, entry->root_node()->children.size());
1454 // 4. Create a third iframe on the same site as the second. This ensures that
1455 // the commit type is correct even when the subframe process already exists.
1457 LoadCommittedCapturer capturer(shell()->web_contents());
1458 std::string script = "var iframe = document.createElement('iframe');"
1459 "iframe.src = '" + foo_url.spec() + "';"
1460 "document.body.appendChild(iframe);";
1461 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1462 capturer.Wait();
1463 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1466 // The last committed NavigationEntry shouldn't have changed.
1467 EXPECT_EQ(1, controller.GetEntryCount());
1468 entry = controller.GetLastCommittedEntry();
1469 EXPECT_EQ(main_url, entry->GetURL());
1470 root_entry = entry->root_node()->frame_entry.get();
1471 EXPECT_EQ(main_url, root_entry->url());
1473 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1474 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1475 // The entry should now have 3 subframe FrameNavigationEntries.
1476 ASSERT_EQ(3U, entry->root_node()->children.size());
1477 FrameNavigationEntry* frame_entry =
1478 entry->root_node()->children[2]->frame_entry.get();
1479 EXPECT_EQ(foo_url, frame_entry->url());
1480 } else {
1481 // There are no subframe FrameNavigationEntries by default.
1482 EXPECT_EQ(0U, entry->root_node()->children.size());
1485 // 5. Create a nested iframe on the original site (A-B-A).
1487 LoadCommittedCapturer capturer(shell()->web_contents());
1488 std::string script = "var iframe = document.createElement('iframe');"
1489 "iframe.src = '" + frame_url.spec() + "';"
1490 "document.body.appendChild(iframe);";
1491 FrameTreeNode* child = root->child_at(2);
1492 EXPECT_TRUE(content::ExecuteScript(child->current_frame_host(), script));
1493 capturer.Wait();
1494 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1497 // The last committed NavigationEntry shouldn't have changed.
1498 EXPECT_EQ(1, controller.GetEntryCount());
1499 entry = controller.GetLastCommittedEntry();
1500 EXPECT_EQ(main_url, entry->GetURL());
1501 root_entry = entry->root_node()->frame_entry.get();
1502 EXPECT_EQ(main_url, root_entry->url());
1504 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1505 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1506 // There should be a corresponding FrameNavigationEntry.
1507 ASSERT_EQ(1U, entry->root_node()->children[2]->children.size());
1508 FrameNavigationEntry* frame_entry =
1509 entry->root_node()->children[2]->children[0]->frame_entry.get();
1510 EXPECT_EQ(frame_url, frame_entry->url());
1511 } else {
1512 // There are no subframe FrameNavigationEntries by default.
1513 EXPECT_EQ(0U, entry->root_node()->children.size());
1516 // Check the end result of the frame tree.
1517 if (AreAllSitesIsolatedForTesting()) {
1518 FrameTreeVisualizer visualizer;
1519 EXPECT_EQ(
1520 " Site A ------------ proxies for B\n"
1521 " |--Site A ------- proxies for B\n"
1522 " |--Site B ------- proxies for A\n"
1523 " | +--Site B -- proxies for A\n"
1524 " +--Site B ------- proxies for A\n"
1525 " +--Site A -- proxies for B\n"
1526 "Where A = http://127.0.0.1/\n"
1527 " B = http://foo.com/",
1528 visualizer.DepictFrameTree(root));
1532 // Verify the tree of FrameNavigationEntries after NAVIGATION_TYPE_NEW_SUBFRAME
1533 // commits.
1534 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1535 FrameNavigationEntry_NewSubframe) {
1536 GURL main_url(embedded_test_server()->GetURL(
1537 "/navigation_controller/simple_page_1.html"));
1538 NavigateToURL(shell(), main_url);
1539 const NavigationControllerImpl& controller =
1540 static_cast<const NavigationControllerImpl&>(
1541 shell()->web_contents()->GetController());
1542 FrameTreeNode* root =
1543 static_cast<WebContentsImpl*>(shell()->web_contents())->
1544 GetFrameTree()->root();
1546 // 1. Create a same-site iframe.
1547 GURL frame_url(embedded_test_server()->GetURL(
1548 "/navigation_controller/simple_page_2.html"));
1550 LoadCommittedCapturer capturer(shell()->web_contents());
1551 std::string script = "var iframe = document.createElement('iframe');"
1552 "iframe.src = '" + frame_url.spec() + "';"
1553 "document.body.appendChild(iframe);";
1554 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1555 capturer.Wait();
1557 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
1559 // 2. Navigate in the subframe same-site.
1560 GURL frame_url2(embedded_test_server()->GetURL(
1561 "/navigation_controller/page_with_links.html"));
1563 FrameNavigateParamsCapturer capturer(root->child_at(0));
1564 NavigateFrameToURL(root->child_at(0), frame_url2);
1565 capturer.Wait();
1566 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1567 capturer.params().transition);
1568 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1571 // We should have created a new NavigationEntry with the same main frame URL.
1572 EXPECT_EQ(2, controller.GetEntryCount());
1573 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1574 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
1575 EXPECT_NE(entry, entry2);
1576 EXPECT_EQ(main_url, entry2->GetURL());
1577 FrameNavigationEntry* root_entry2 = entry2->root_node()->frame_entry.get();
1578 EXPECT_EQ(main_url, root_entry2->url());
1580 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1581 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1582 // The entry should have a new FrameNavigationEntries for the subframe.
1583 ASSERT_EQ(1U, entry2->root_node()->children.size());
1584 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1585 } else {
1586 // There are no subframe FrameNavigationEntries by default.
1587 EXPECT_EQ(0U, entry2->root_node()->children.size());
1590 // 3. Create a second, initially cross-site iframe.
1591 GURL foo_url(embedded_test_server()->GetURL(
1592 "foo.com", "/navigation_controller/simple_page_1.html"));
1594 LoadCommittedCapturer capturer(shell()->web_contents());
1595 std::string script = "var iframe = document.createElement('iframe');"
1596 "iframe.src = '" + foo_url.spec() + "';"
1597 "document.body.appendChild(iframe);";
1598 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1599 capturer.Wait();
1602 // 4. Create a nested same-site iframe in the second subframe, wait for it to
1603 // commit, then navigate it again.
1605 LoadCommittedCapturer capturer(shell()->web_contents());
1606 std::string script = "var iframe = document.createElement('iframe');"
1607 "iframe.src = '" + foo_url.spec() + "';"
1608 "document.body.appendChild(iframe);";
1609 EXPECT_TRUE(content::ExecuteScript(root->child_at(1)->current_frame_host(),
1610 script));
1611 capturer.Wait();
1613 GURL bar_url(embedded_test_server()->GetURL(
1614 "bar.com", "/navigation_controller/simple_page_1.html"));
1616 FrameNavigateParamsCapturer capturer(root->child_at(1)->child_at(0));
1617 NavigateFrameToURL(root->child_at(1)->child_at(0), bar_url);
1618 capturer.Wait();
1619 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1620 capturer.params().transition);
1621 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1624 // We should have created a new NavigationEntry with the same main frame URL.
1625 EXPECT_EQ(3, controller.GetEntryCount());
1626 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1627 NavigationEntryImpl* entry3 = controller.GetLastCommittedEntry();
1628 EXPECT_NE(entry, entry3);
1629 EXPECT_EQ(main_url, entry3->GetURL());
1630 FrameNavigationEntry* root_entry3 = entry3->root_node()->frame_entry.get();
1631 EXPECT_EQ(main_url, root_entry3->url());
1633 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1634 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1635 // The entry should still have FrameNavigationEntries for all 3 subframes.
1636 ASSERT_EQ(2U, entry3->root_node()->children.size());
1637 EXPECT_EQ(frame_url2, entry3->root_node()->children[0]->frame_entry->url());
1638 EXPECT_EQ(foo_url, entry3->root_node()->children[1]->frame_entry->url());
1639 ASSERT_EQ(1U, entry3->root_node()->children[1]->children.size());
1640 EXPECT_EQ(
1641 bar_url,
1642 entry3->root_node()->children[1]->children[0]->frame_entry->url());
1643 } else {
1644 // There are no subframe FrameNavigationEntries by default.
1645 EXPECT_EQ(0U, entry3->root_node()->children.size());
1648 // 6. Navigate the second subframe cross-site, clearing its existing subtree.
1649 GURL baz_url(embedded_test_server()->GetURL(
1650 "baz.com", "/navigation_controller/simple_page_1.html"));
1652 FrameNavigateParamsCapturer capturer(root->child_at(1));
1653 std::string script = "var frames = document.getElementsByTagName('iframe');"
1654 "frames[1].src = '" + baz_url.spec() + "';";
1655 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1656 capturer.Wait();
1657 EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
1658 capturer.params().transition);
1659 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
1662 // We should have created a new NavigationEntry with the same main frame URL.
1663 EXPECT_EQ(4, controller.GetEntryCount());
1664 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
1665 NavigationEntryImpl* entry4 = controller.GetLastCommittedEntry();
1666 EXPECT_NE(entry, entry4);
1667 EXPECT_EQ(main_url, entry4->GetURL());
1668 FrameNavigationEntry* root_entry4 = entry4->root_node()->frame_entry.get();
1669 EXPECT_EQ(main_url, root_entry4->url());
1671 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1672 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1673 // The entry should still have FrameNavigationEntries for all 3 subframes.
1674 ASSERT_EQ(2U, entry4->root_node()->children.size());
1675 EXPECT_EQ(frame_url2, entry4->root_node()->children[0]->frame_entry->url());
1676 EXPECT_EQ(baz_url, entry4->root_node()->children[1]->frame_entry->url());
1677 ASSERT_EQ(0U, entry4->root_node()->children[1]->children.size());
1678 } else {
1679 // There are no subframe FrameNavigationEntries by default.
1680 EXPECT_EQ(0U, entry4->root_node()->children.size());
1683 // Check the end result of the frame tree.
1684 if (AreAllSitesIsolatedForTesting()) {
1685 FrameTreeVisualizer visualizer;
1686 EXPECT_EQ(
1687 " Site A ------------ proxies for B\n"
1688 " |--Site A ------- proxies for B\n"
1689 " +--Site B ------- proxies for A\n"
1690 "Where A = http://127.0.0.1/\n"
1691 " B = http://baz.com/",
1692 visualizer.DepictFrameTree(root));
1696 // Ensure that we don't crash when navigating subframes after in-page
1697 // navigations. See https://crbug.com/522193.
1698 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1699 FrameNavigationEntry_SubframeAfterInPage) {
1700 // 1. Start on a page with a subframe.
1701 GURL main_url(embedded_test_server()->GetURL(
1702 "/navigation_controller/page_with_iframe.html"));
1703 NavigateToURL(shell(), main_url);
1704 FrameTreeNode* root =
1705 static_cast<WebContentsImpl*>(shell()->web_contents())->
1706 GetFrameTree()->root();
1708 ASSERT_EQ(1U, root->child_count());
1709 ASSERT_NE(nullptr, root->child_at(0));
1711 // Navigate to a real page in the subframe, so that the next navigation will
1712 // be MANUAL_SUBFRAME.
1713 GURL subframe_url(embedded_test_server()->GetURL(
1714 "/navigation_controller/simple_page_1.html"));
1716 LoadCommittedCapturer capturer(root->child_at(0));
1717 NavigateFrameToURL(root->child_at(0), subframe_url);
1718 capturer.Wait();
1719 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1722 // 2. In-page navigation in the main frame.
1723 std::string push_script = "history.pushState({}, 'page 2', 'page_2.html')";
1724 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), push_script));
1725 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1727 // TODO(creis): Verify subframe entries. https://crbug.com/522193.
1729 // 3. Add a nested subframe.
1731 LoadCommittedCapturer capturer(shell()->web_contents());
1732 std::string script = "var iframe = document.createElement('iframe');"
1733 "iframe.src = '" + subframe_url.spec() + "';"
1734 "document.body.appendChild(iframe);";
1735 EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
1736 script));
1737 capturer.Wait();
1738 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1741 // TODO(creis): Verify subframe entries. https://crbug.com/522193.
1744 // Verify the tree of FrameNavigationEntries after back/forward navigations in a
1745 // cross-site subframe.
1746 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1747 FrameNavigationEntry_SubframeBackForward) {
1748 GURL main_url(embedded_test_server()->GetURL(
1749 "/navigation_controller/simple_page_1.html"));
1750 NavigateToURL(shell(), main_url);
1751 const NavigationControllerImpl& controller =
1752 static_cast<const NavigationControllerImpl&>(
1753 shell()->web_contents()->GetController());
1754 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1755 ->GetFrameTree()
1756 ->root();
1758 // 1. Create a same-site iframe.
1759 GURL frame_url(embedded_test_server()->GetURL(
1760 "/navigation_controller/simple_page_2.html"));
1762 LoadCommittedCapturer capturer(shell()->web_contents());
1763 std::string script = "var iframe = document.createElement('iframe');"
1764 "iframe.src = '" + frame_url.spec() + "';"
1765 "document.body.appendChild(iframe);";
1766 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1767 capturer.Wait();
1769 NavigationEntryImpl* entry1 = controller.GetLastCommittedEntry();
1771 // 2. Navigate in the subframe cross-site.
1772 GURL frame_url2(embedded_test_server()->GetURL(
1773 "foo.com", "/navigation_controller/page_with_links.html"));
1775 FrameNavigateParamsCapturer capturer(root->child_at(0));
1776 NavigateFrameToURL(root->child_at(0), frame_url2);
1777 capturer.Wait();
1779 EXPECT_EQ(2, controller.GetEntryCount());
1780 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1781 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
1783 // 3. Navigate in the subframe cross-site again.
1784 GURL frame_url3(embedded_test_server()->GetURL(
1785 "bar.com", "/navigation_controller/page_with_links.html"));
1787 FrameNavigateParamsCapturer capturer(root->child_at(0));
1788 NavigateFrameToURL(root->child_at(0), frame_url3);
1789 capturer.Wait();
1791 EXPECT_EQ(3, controller.GetEntryCount());
1792 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1793 NavigationEntryImpl* entry3 = controller.GetLastCommittedEntry();
1795 // 4. Go back in the subframe.
1797 FrameNavigateParamsCapturer capturer(root->child_at(0));
1798 shell()->web_contents()->GetController().GoBack();
1799 capturer.Wait();
1800 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1801 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1803 EXPECT_EQ(3, controller.GetEntryCount());
1804 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1805 EXPECT_EQ(entry2, controller.GetLastCommittedEntry());
1807 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1808 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1809 // The entry should have a new FrameNavigationEntries for the subframe.
1810 ASSERT_EQ(1U, entry2->root_node()->children.size());
1811 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1812 } else {
1813 // There are no subframe FrameNavigationEntries by default.
1814 EXPECT_EQ(0U, entry2->root_node()->children.size());
1817 // 5. Go back in the subframe again to the parent page's site.
1819 FrameNavigateParamsCapturer capturer(root->child_at(0));
1820 shell()->web_contents()->GetController().GoBack();
1821 capturer.Wait();
1822 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1823 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1825 EXPECT_EQ(3, controller.GetEntryCount());
1826 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1827 EXPECT_EQ(entry1, controller.GetLastCommittedEntry());
1829 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1830 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1831 // The entry should have a new FrameNavigationEntries for the subframe.
1832 ASSERT_EQ(1U, entry1->root_node()->children.size());
1833 EXPECT_EQ(frame_url, entry1->root_node()->children[0]->frame_entry->url());
1834 } else {
1835 // There are no subframe FrameNavigationEntries by default.
1836 EXPECT_EQ(0U, entry1->root_node()->children.size());
1839 // 6. Go forward in the subframe cross-site.
1841 FrameNavigateParamsCapturer capturer(root->child_at(0));
1842 shell()->web_contents()->GetController().GoForward();
1843 capturer.Wait();
1844 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1845 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1847 EXPECT_EQ(3, controller.GetEntryCount());
1848 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1849 EXPECT_EQ(entry2, controller.GetLastCommittedEntry());
1851 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1852 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1853 // The entry should have a new FrameNavigationEntries for the subframe.
1854 ASSERT_EQ(1U, entry2->root_node()->children.size());
1855 EXPECT_EQ(frame_url2, entry2->root_node()->children[0]->frame_entry->url());
1856 } else {
1857 // There are no subframe FrameNavigationEntries by default.
1858 EXPECT_EQ(0U, entry2->root_node()->children.size());
1861 // 7. Go forward in the subframe again, cross-site.
1863 FrameNavigateParamsCapturer capturer(root->child_at(0));
1864 shell()->web_contents()->GetController().GoForward();
1865 capturer.Wait();
1866 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.params().transition);
1867 EXPECT_EQ(NAVIGATION_TYPE_AUTO_SUBFRAME, capturer.details().type);
1869 EXPECT_EQ(3, controller.GetEntryCount());
1870 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1871 EXPECT_EQ(entry3, controller.GetLastCommittedEntry());
1873 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
1874 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
1875 // The entry should have a new FrameNavigationEntries for the subframe.
1876 ASSERT_EQ(1U, entry3->root_node()->children.size());
1877 EXPECT_EQ(frame_url3, entry3->root_node()->children[0]->frame_entry->url());
1878 } else {
1879 // There are no subframe FrameNavigationEntries by default.
1880 EXPECT_EQ(0U, entry3->root_node()->children.size());
1884 // Verifies that item sequence numbers and document sequence numbers update
1885 // properly for main frames and subframes.
1886 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
1887 FrameNavigationEntry_SequenceNumbers) {
1888 const NavigationControllerImpl& controller =
1889 static_cast<const NavigationControllerImpl&>(
1890 shell()->web_contents()->GetController());
1892 // 1. Navigate the main frame.
1893 GURL url(embedded_test_server()->GetURL(
1894 "/navigation_controller/page_with_links.html"));
1895 NavigateToURL(shell(), url);
1896 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1897 ->GetFrameTree()
1898 ->root();
1900 FrameNavigationEntry* frame_entry =
1901 controller.GetLastCommittedEntry()->GetFrameEntry(root);
1902 int64 isn_1 = frame_entry->item_sequence_number();
1903 int64 dsn_1 = frame_entry->document_sequence_number();
1904 EXPECT_NE(-1, isn_1);
1905 EXPECT_NE(-1, dsn_1);
1907 // 2. Do an in-page fragment navigation.
1908 std::string script = "document.getElementById('fraglink').click()";
1909 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1910 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1912 frame_entry = controller.GetLastCommittedEntry()->GetFrameEntry(root);
1913 int64 isn_2 = frame_entry->item_sequence_number();
1914 int64 dsn_2 = frame_entry->document_sequence_number();
1915 EXPECT_NE(-1, isn_2);
1916 EXPECT_NE(isn_1, isn_2);
1917 EXPECT_EQ(dsn_1, dsn_2);
1919 // Test subframe sequence numbers only if enabled, e.g. in --site-per-process.
1920 if (!SiteIsolationPolicy::UseSubframeNavigationEntries())
1921 return;
1923 // 3. Add a subframe, which does an AUTO_SUBFRAME navigation.
1925 LoadCommittedCapturer capturer(shell()->web_contents());
1926 std::string script = "var iframe = document.createElement('iframe');"
1927 "iframe.src = '" + url.spec() + "';"
1928 "document.body.appendChild(iframe);";
1929 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
1930 capturer.Wait();
1931 EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
1934 // The root FrameNavigationEntry hasn't changed.
1935 EXPECT_EQ(frame_entry,
1936 controller.GetLastCommittedEntry()->GetFrameEntry(root));
1938 // We should have a unique ISN and DSN for the subframe entry.
1939 FrameTreeNode* subframe = root->child_at(0);
1940 FrameNavigationEntry* subframe_entry =
1941 controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
1942 int64 isn_3 = subframe_entry->item_sequence_number();
1943 int64 dsn_3 = subframe_entry->document_sequence_number();
1944 EXPECT_NE(-1, isn_2);
1945 EXPECT_NE(isn_2, isn_3);
1946 EXPECT_NE(dsn_2, dsn_3);
1948 // 4. Do an in-page fragment navigation in the subframe.
1949 EXPECT_TRUE(content::ExecuteScript(subframe->current_frame_host(), script));
1950 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1952 subframe_entry = controller.GetLastCommittedEntry()->GetFrameEntry(subframe);
1953 int64 isn_4 = subframe_entry->item_sequence_number();
1954 int64 dsn_4 = subframe_entry->document_sequence_number();
1955 EXPECT_NE(-1, isn_4);
1956 EXPECT_NE(isn_3, isn_4);
1957 EXPECT_EQ(dsn_3, dsn_4);
1960 namespace {
1962 class HttpThrottle : public ResourceThrottle {
1963 public:
1964 // ResourceThrottle
1965 void WillStartRequest(bool* defer) override {
1966 *defer = true;
1969 const char* GetNameForLogging() const override {
1970 return "HttpThrottle";
1974 class StallDelegate : public ResourceDispatcherHostDelegate {
1975 // ResourceDispatcherHostDelegate
1976 void RequestBeginning(
1977 net::URLRequest* request,
1978 content::ResourceContext* resource_context,
1979 content::AppCacheService* appcache_service,
1980 ResourceType resource_type,
1981 ScopedVector<content::ResourceThrottle>* throttles) override {
1982 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
1983 throttles->push_back(new HttpThrottle);
1987 // Loads |start_url|, then loads |stalled_url| which stalls. While the page is
1988 // stalled, an in-page navigation happens. Make sure that all the navigations
1989 // are properly classified.
1990 void DoReplaceStateWhilePending(Shell* shell,
1991 const GURL& start_url,
1992 const GURL& stalled_url,
1993 const std::string& replace_state_filename) {
1994 NavigationControllerImpl& controller =
1995 static_cast<NavigationControllerImpl&>(
1996 shell->web_contents()->GetController());
1998 FrameTreeNode* root =
1999 static_cast<WebContentsImpl*>(shell->web_contents())->
2000 GetFrameTree()->root();
2002 // Start with one page.
2003 EXPECT_TRUE(NavigateToURL(shell, start_url));
2005 // Have the user decide to go to a different page which is very slow.
2006 StallDelegate stall_delegate;
2007 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
2008 controller.LoadURL(
2009 stalled_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2011 // That should be the pending entry.
2012 NavigationEntryImpl* entry = controller.GetPendingEntry();
2013 ASSERT_NE(nullptr, entry);
2014 EXPECT_EQ(stalled_url, entry->GetURL());
2017 // Now the existing page uses history.replaceState().
2018 FrameNavigateParamsCapturer capturer(root);
2019 capturer.set_wait_for_load(false);
2020 std::string script =
2021 "history.replaceState({}, '', '" + replace_state_filename + "')";
2022 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
2023 capturer.Wait();
2025 // The fact that there was a pending entry shouldn't interfere with the
2026 // classification.
2027 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
2028 EXPECT_TRUE(capturer.details().is_in_page);
2031 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
2034 } // namespace
2036 IN_PROC_BROWSER_TEST_F(
2037 NavigationControllerBrowserTest,
2038 NavigationTypeClassification_On1InPageToXWhile2Pending) {
2039 GURL url1(embedded_test_server()->GetURL(
2040 "/navigation_controller/simple_page_1.html"));
2041 GURL url2(embedded_test_server()->GetURL(
2042 "/navigation_controller/simple_page_2.html"));
2043 DoReplaceStateWhilePending(shell(), url1, url2, "x");
2046 IN_PROC_BROWSER_TEST_F(
2047 NavigationControllerBrowserTest,
2048 NavigationTypeClassification_On1InPageTo2While2Pending) {
2049 GURL url1(embedded_test_server()->GetURL(
2050 "/navigation_controller/simple_page_1.html"));
2051 GURL url2(embedded_test_server()->GetURL(
2052 "/navigation_controller/simple_page_2.html"));
2053 DoReplaceStateWhilePending(shell(), url1, url2, "simple_page_2.html");
2056 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2057 NavigationTypeClassification_On1InPageToXWhile1Pending) {
2058 GURL url(embedded_test_server()->GetURL(
2059 "/navigation_controller/simple_page_1.html"));
2060 DoReplaceStateWhilePending(shell(), url, url, "x");
2063 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2064 NavigationTypeClassification_On1InPageTo1While1Pending) {
2065 GURL url(embedded_test_server()->GetURL(
2066 "/navigation_controller/simple_page_1.html"));
2067 DoReplaceStateWhilePending(shell(), url, url, "simple_page_1.html");
2070 // Ensure the renderer process does not get confused about the current entry
2071 // due to subframes and replaced entries. See https://crbug.com/480201.
2072 // TODO(creis): Re-enable for Site Isolation FYI bots: https://crbug.com/502317.
2073 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2074 PreventSpoofFromSubframeAndReplace) {
2075 // Start at an initial URL.
2076 GURL url1(embedded_test_server()->GetURL(
2077 "/navigation_controller/simple_page_1.html"));
2078 NavigateToURL(shell(), url1);
2080 // Now go to a page with a real iframe.
2081 GURL url2(embedded_test_server()->GetURL(
2082 "/navigation_controller/page_with_data_iframe.html"));
2083 NavigateToURL(shell(), url2);
2085 // It is safe to obtain the root frame tree node here, as it doesn't change.
2086 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2087 ->GetFrameTree()
2088 ->root();
2089 ASSERT_EQ(1U, root->child_count());
2090 ASSERT_NE(nullptr, root->child_at(0));
2093 // Navigate in the iframe.
2094 FrameNavigateParamsCapturer capturer(root->child_at(0));
2095 GURL frame_url(embedded_test_server()->GetURL(
2096 "/navigation_controller/simple_page_2.html"));
2097 NavigateFrameToURL(root->child_at(0), frame_url);
2098 capturer.Wait();
2099 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
2103 // Go back in the iframe.
2104 TestNavigationObserver back_load_observer(shell()->web_contents());
2105 shell()->web_contents()->GetController().GoBack();
2106 back_load_observer.Wait();
2110 // Go forward in the iframe.
2111 TestNavigationObserver forward_load_observer(shell()->web_contents());
2112 shell()->web_contents()->GetController().GoForward();
2113 forward_load_observer.Wait();
2116 GURL url3(embedded_test_server()->GetURL(
2117 "/navigation_controller/page_with_iframe.html"));
2119 // location.replace() to cause an inert commit.
2120 TestNavigationObserver replace_load_observer(shell()->web_contents());
2121 std::string script = "location.replace('" + url3.spec() + "')";
2122 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
2123 replace_load_observer.Wait();
2127 // Go back to url2.
2128 TestNavigationObserver back_load_observer(shell()->web_contents());
2129 shell()->web_contents()->GetController().GoBack();
2130 back_load_observer.Wait();
2132 // Make sure the URL is correct for both the entry and the main frame, and
2133 // that the process hasn't been killed for showing a spoof.
2134 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
2135 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
2136 EXPECT_EQ(url2, root->current_url());
2140 // Go back to reset main frame entirely.
2141 TestNavigationObserver back_load_observer(shell()->web_contents());
2142 shell()->web_contents()->GetController().GoBack();
2143 back_load_observer.Wait();
2144 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
2145 EXPECT_EQ(url1, root->current_url());
2149 // Go forward.
2150 TestNavigationObserver back_load_observer(shell()->web_contents());
2151 shell()->web_contents()->GetController().GoForward();
2152 back_load_observer.Wait();
2153 EXPECT_EQ(url2, shell()->web_contents()->GetLastCommittedURL());
2154 EXPECT_EQ(url2, root->current_url());
2158 // Go forward to the replaced URL.
2159 TestNavigationObserver forward_load_observer(shell()->web_contents());
2160 shell()->web_contents()->GetController().GoForward();
2161 forward_load_observer.Wait();
2163 // Make sure the URL is correct for both the entry and the main frame, and
2164 // that the process hasn't been killed for showing a spoof.
2165 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
2166 EXPECT_EQ(url3, shell()->web_contents()->GetLastCommittedURL());
2167 EXPECT_EQ(url3, root->current_url());
2171 // Ensure the renderer process does not get killed if the main frame URL's path
2172 // changes when going back in a subframe, since this is currently possible after
2173 // a replaceState in the main frame (thanks to https://crbug.com/373041).
2174 // See https:///crbug.com/486916.
2175 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2176 SubframeBackFromReplaceState) {
2177 // Start at a page with a real iframe.
2178 GURL url1(embedded_test_server()->GetURL(
2179 "/navigation_controller/page_with_data_iframe.html"));
2180 NavigateToURL(shell(), url1);
2182 // It is safe to obtain the root frame tree node here, as it doesn't change.
2183 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2184 ->GetFrameTree()
2185 ->root();
2186 ASSERT_EQ(1U, root->child_count());
2187 ASSERT_NE(nullptr, root->child_at(0));
2190 // Navigate in the iframe.
2191 FrameNavigateParamsCapturer capturer(root->child_at(0));
2192 GURL frame_url(embedded_test_server()->GetURL(
2193 "/navigation_controller/simple_page_2.html"));
2194 NavigateFrameToURL(root->child_at(0), frame_url);
2195 capturer.Wait();
2196 EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
2200 // history.replaceState().
2201 FrameNavigateParamsCapturer capturer(root);
2202 std::string script =
2203 "history.replaceState({}, 'replaced', 'replaced')";
2204 EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
2205 capturer.Wait();
2209 // Go back in the iframe.
2210 TestNavigationObserver back_load_observer(shell()->web_contents());
2211 shell()->web_contents()->GetController().GoBack();
2212 back_load_observer.Wait();
2215 // For now, we expect the main frame's URL to revert. This won't happen once
2216 // https://crbug.com/373041 is fixed.
2217 EXPECT_EQ(url1, shell()->web_contents()->GetLastCommittedURL());
2219 // Make sure the renderer process has not been killed.
2220 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
2223 namespace {
2225 class FailureWatcher : public WebContentsObserver {
2226 public:
2227 // Observes failure for the specified |node|.
2228 explicit FailureWatcher(FrameTreeNode* node)
2229 : WebContentsObserver(
2230 node->current_frame_host()->delegate()->GetAsWebContents()),
2231 frame_tree_node_id_(node->frame_tree_node_id()),
2232 message_loop_runner_(new MessageLoopRunner) {}
2234 void Wait() {
2235 message_loop_runner_->Run();
2238 private:
2239 void DidFailLoad(RenderFrameHost* render_frame_host,
2240 const GURL& validated_url,
2241 int error_code,
2242 const base::string16& error_description,
2243 bool was_ignored_by_handler) override {
2244 RenderFrameHostImpl* rfh =
2245 static_cast<RenderFrameHostImpl*>(render_frame_host);
2246 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
2247 return;
2249 message_loop_runner_->Quit();
2252 void DidFailProvisionalLoad(
2253 RenderFrameHost* render_frame_host,
2254 const GURL& validated_url,
2255 int error_code,
2256 const base::string16& error_description,
2257 bool was_ignored_by_handler) override {
2258 RenderFrameHostImpl* rfh =
2259 static_cast<RenderFrameHostImpl*>(render_frame_host);
2260 if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
2261 return;
2263 message_loop_runner_->Quit();
2266 // The id of the FrameTreeNode whose navigations to observe.
2267 int frame_tree_node_id_;
2269 // The MessageLoopRunner used to spin the message loop.
2270 scoped_refptr<MessageLoopRunner> message_loop_runner_;
2273 } // namespace
2275 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
2276 StopCausesFailureDespiteJavaScriptURL) {
2277 NavigationControllerImpl& controller =
2278 static_cast<NavigationControllerImpl&>(
2279 shell()->web_contents()->GetController());
2281 FrameTreeNode* root =
2282 static_cast<WebContentsImpl*>(shell()->web_contents())->
2283 GetFrameTree()->root();
2285 // Start with a normal page.
2286 GURL url1(embedded_test_server()->GetURL(
2287 "/navigation_controller/simple_page_1.html"));
2288 EXPECT_TRUE(NavigateToURL(shell(), url1));
2290 // Have the user decide to go to a different page which is very slow.
2291 StallDelegate stall_delegate;
2292 ResourceDispatcherHost::Get()->SetDelegate(&stall_delegate);
2293 GURL url2(embedded_test_server()->GetURL(
2294 "/navigation_controller/simple_page_2.html"));
2295 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2297 // That should be the pending entry.
2298 NavigationEntryImpl* entry = controller.GetPendingEntry();
2299 ASSERT_NE(nullptr, entry);
2300 EXPECT_EQ(url2, entry->GetURL());
2302 // Loading a JavaScript URL shouldn't affect the ability to stop.
2304 FailureWatcher watcher(root);
2305 GURL js("javascript:(function(){})()");
2306 controller.LoadURL(js, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
2307 // This LoadURL ends up purging the pending entry, which is why this is
2308 // tricky.
2309 EXPECT_EQ(nullptr, controller.GetPendingEntry());
2310 shell()->web_contents()->Stop();
2311 watcher.Wait();
2314 ResourceDispatcherHost::Get()->SetDelegate(nullptr);
2317 namespace {
2318 class RenderProcessKilledObserver : public WebContentsObserver {
2319 public:
2320 RenderProcessKilledObserver(WebContents* web_contents)
2321 : WebContentsObserver(web_contents) {}
2322 ~RenderProcessKilledObserver() override {}
2324 void RenderProcessGone(base::TerminationStatus status) override {
2325 CHECK_NE(status,
2326 base::TerminationStatus::TERMINATION_STATUS_PROCESS_WAS_KILLED);
2331 // This tests a race in ReloadOriginalRequest, where a cross-origin reload was
2332 // causing an in-flight replaceState to look like a cross-origin navigation,
2333 // even though it's in-page. (The reload should not modify the underlying last
2334 // committed entry.) Not crashing means that the test is successful.
2335 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, ReloadOriginalRequest) {
2336 GURL original_url(embedded_test_server()->GetURL(
2337 "/navigation_controller/simple_page_1.html"));
2338 NavigateToURL(shell(), original_url);
2339 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2340 ->GetFrameTree()
2341 ->root();
2342 RenderProcessKilledObserver kill_observer(shell()->web_contents());
2344 // Redirect so that we can use ReloadOriginalRequest.
2345 GURL redirect_url(embedded_test_server()->GetURL(
2346 "foo.com", "/navigation_controller/simple_page_1.html"));
2348 std::string script = "location.replace('" + redirect_url.spec() + "');";
2349 FrameNavigateParamsCapturer capturer(root);
2350 EXPECT_TRUE(ExecuteScript(shell()->web_contents(), script));
2351 capturer.Wait();
2352 EXPECT_EQ(ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT,
2353 capturer.params().transition);
2354 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, capturer.details().type);
2357 // Modify an entry in the session history and reload the original request.
2359 // We first send a replaceState() to the renderer, which will cause the
2360 // renderer to send back a DidCommitProvisionalLoad. Immediately after,
2361 // we send a ReloadOriginalRequest (which in this case is a different
2362 // origin) and will also cause the renderer to commit the frame. In the
2363 // end we verify that both navigations committed and that the URLs are
2364 // correct.
2365 std::string script = "history.replaceState({}, '', 'foo');";
2366 root->render_manager()
2367 ->current_frame_host()
2368 ->ExecuteJavaScriptWithUserGestureForTests(base::UTF8ToUTF16(script));
2369 EXPECT_FALSE(shell()->web_contents()->IsLoading());
2370 shell()->web_contents()->GetController().ReloadOriginalRequestURL(false);
2371 EXPECT_TRUE(shell()->web_contents()->IsLoading());
2372 EXPECT_EQ(redirect_url, shell()->web_contents()->GetLastCommittedURL());
2374 // Wait until there's no more navigations.
2375 GURL modified_url(embedded_test_server()->GetURL(
2376 "foo.com", "/navigation_controller/foo"));
2377 FrameNavigateParamsCapturer capturer(root);
2378 capturer.set_wait_for_load(false);
2379 capturer.set_navigations_remaining(2);
2380 capturer.Wait();
2381 EXPECT_EQ(2U, capturer.all_details().size());
2382 EXPECT_EQ(modified_url, capturer.all_params()[0].url);
2383 EXPECT_EQ(original_url, capturer.all_params()[1].url);
2384 EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
2387 // Make sure the renderer is still alive.
2388 EXPECT_TRUE(
2389 ExecuteScript(shell()->web_contents(), "console.log('Success');"));
2392 } // namespace content