Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / web_contents / web_contents_impl_unittest.cc
blob32f102dfbdf6e5f0414c9aeaf0030037c9590328
1 // Copyright (c) 2012 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/command_line.h"
6 #include "base/logging.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "content/browser/frame_host/cross_site_transferring_request.h"
9 #include "content/browser/frame_host/interstitial_page_impl.h"
10 #include "content/browser/frame_host/navigation_entry_impl.h"
11 #include "content/browser/frame_host/render_frame_host_impl.h"
12 #include "content/browser/media/audio_state_provider.h"
13 #include "content/browser/renderer_host/render_view_host_impl.h"
14 #include "content/browser/site_instance_impl.h"
15 #include "content/browser/webui/web_ui_controller_factory_registry.h"
16 #include "content/common/frame_messages.h"
17 #include "content/common/input/synthetic_web_input_event_builders.h"
18 #include "content/common/view_messages.h"
19 #include "content/public/browser/global_request_id.h"
20 #include "content/public/browser/interstitial_page_delegate.h"
21 #include "content/public/browser/navigation_details.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/render_widget_host_view.h"
25 #include "content/public/browser/web_contents_delegate.h"
26 #include "content/public/browser/web_contents_observer.h"
27 #include "content/public/browser/web_ui_controller.h"
28 #include "content/public/common/bindings_policy.h"
29 #include "content/public/common/content_constants.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/url_constants.h"
32 #include "content/public/common/url_utils.h"
33 #include "content/public/test/mock_render_process_host.h"
34 #include "content/public/test/test_utils.h"
35 #include "content/test/test_content_browser_client.h"
36 #include "content/test/test_content_client.h"
37 #include "content/test/test_render_frame_host.h"
38 #include "content/test/test_render_view_host.h"
39 #include "content/test/test_web_contents.h"
40 #include "testing/gtest/include/gtest/gtest.h"
42 namespace content {
43 namespace {
45 const char kTestWebUIUrl[] = "chrome://blah";
47 class WebContentsImplTestWebUIControllerFactory
48 : public WebUIControllerFactory {
49 public:
50 WebUIController* CreateWebUIControllerForURL(WebUI* web_ui,
51 const GURL& url) const override {
52 if (!UseWebUI(url))
53 return nullptr;
54 return new WebUIController(web_ui);
57 WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
58 const GURL& url) const override {
59 return WebUI::kNoWebUI;
62 bool UseWebUIForURL(BrowserContext* browser_context,
63 const GURL& url) const override {
64 return UseWebUI(url);
67 bool UseWebUIBindingsForURL(BrowserContext* browser_context,
68 const GURL& url) const override {
69 return UseWebUI(url);
72 private:
73 bool UseWebUI(const GURL& url) const {
74 return url == GURL(kTestWebUIUrl);
78 class TestInterstitialPage;
80 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
81 public:
82 explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
83 : interstitial_page_(interstitial_page) {}
84 void CommandReceived(const std::string& command) override;
85 std::string GetHTMLContents() override { return std::string(); }
86 void OnDontProceed() override;
87 void OnProceed() override;
89 private:
90 TestInterstitialPage* interstitial_page_;
93 class TestInterstitialPage : public InterstitialPageImpl {
94 public:
95 enum InterstitialState {
96 INVALID = 0, // Hasn't yet been initialized.
97 UNDECIDED, // Initialized, but no decision taken yet.
98 OKED, // Proceed was called.
99 CANCELED // DontProceed was called.
102 class Delegate {
103 public:
104 virtual void TestInterstitialPageDeleted(
105 TestInterstitialPage* interstitial) = 0;
107 protected:
108 virtual ~Delegate() {}
111 // IMPORTANT NOTE: if you pass stack allocated values for |state| and
112 // |deleted| (like all interstitial related tests do at this point), make sure
113 // to create an instance of the TestInterstitialPageStateGuard class on the
114 // stack in your test. This will ensure that the TestInterstitialPage states
115 // are cleared when the test finishes.
116 // Not doing so will cause stack trashing if your test does not hide the
117 // interstitial, as in such a case it will be destroyed in the test TearDown
118 // method and will dereference the |deleted| local variable which by then is
119 // out of scope.
120 TestInterstitialPage(WebContentsImpl* contents,
121 bool new_navigation,
122 const GURL& url,
123 InterstitialState* state,
124 bool* deleted)
125 : InterstitialPageImpl(
126 contents,
127 static_cast<RenderWidgetHostDelegate*>(contents),
128 new_navigation, url, new TestInterstitialPageDelegate(this)),
129 state_(state),
130 deleted_(deleted),
131 command_received_count_(0),
132 delegate_(nullptr) {
133 *state_ = UNDECIDED;
134 *deleted_ = false;
137 ~TestInterstitialPage() override {
138 if (deleted_)
139 *deleted_ = true;
140 if (delegate_)
141 delegate_->TestInterstitialPageDeleted(this);
144 void OnDontProceed() {
145 if (state_)
146 *state_ = CANCELED;
148 void OnProceed() {
149 if (state_)
150 *state_ = OKED;
153 int command_received_count() const {
154 return command_received_count_;
157 void TestDomOperationResponse(const std::string& json_string) {
158 if (enabled())
159 CommandReceived();
162 void TestDidNavigate(int page_id, const GURL& url) {
163 FrameHostMsg_DidCommitProvisionalLoad_Params params;
164 InitNavigateParams(&params, page_id, url, ui::PAGE_TRANSITION_TYPED);
165 DidNavigate(GetMainFrame()->GetRenderViewHost(), params);
168 void TestRenderViewTerminated(base::TerminationStatus status,
169 int error_code) {
170 RenderViewTerminated(GetMainFrame()->GetRenderViewHost(), status,
171 error_code);
174 bool is_showing() const {
175 return static_cast<TestRenderWidgetHostView*>(
176 GetMainFrame()->GetRenderViewHost()->GetView())->is_showing();
179 void ClearStates() {
180 state_ = nullptr;
181 deleted_ = nullptr;
182 delegate_ = nullptr;
185 void CommandReceived() {
186 command_received_count_++;
189 void set_delegate(Delegate* delegate) {
190 delegate_ = delegate;
193 protected:
194 WebContentsView* CreateWebContentsView() override { return nullptr; }
196 private:
197 InterstitialState* state_;
198 bool* deleted_;
199 int command_received_count_;
200 Delegate* delegate_;
203 void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
204 interstitial_page_->CommandReceived();
207 void TestInterstitialPageDelegate::OnDontProceed() {
208 interstitial_page_->OnDontProceed();
211 void TestInterstitialPageDelegate::OnProceed() {
212 interstitial_page_->OnProceed();
215 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
216 public:
217 explicit TestInterstitialPageStateGuard(
218 TestInterstitialPage* interstitial_page)
219 : interstitial_page_(interstitial_page) {
220 DCHECK(interstitial_page_);
221 interstitial_page_->set_delegate(this);
223 ~TestInterstitialPageStateGuard() override {
224 if (interstitial_page_)
225 interstitial_page_->ClearStates();
228 void TestInterstitialPageDeleted(
229 TestInterstitialPage* interstitial) override {
230 DCHECK(interstitial_page_ == interstitial);
231 interstitial_page_ = nullptr;
234 private:
235 TestInterstitialPage* interstitial_page_;
238 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
239 public:
240 WebContentsImplTestBrowserClient()
241 : assign_site_for_url_(false) {}
243 ~WebContentsImplTestBrowserClient() override {}
245 bool ShouldAssignSiteForURL(const GURL& url) override {
246 return assign_site_for_url_;
249 void set_assign_site_for_url(bool assign) {
250 assign_site_for_url_ = assign;
253 private:
254 bool assign_site_for_url_;
257 class WebContentsImplTest : public RenderViewHostImplTestHarness {
258 public:
259 void SetUp() override {
260 RenderViewHostImplTestHarness::SetUp();
261 WebUIControllerFactory::RegisterFactory(&factory_);
264 void TearDown() override {
265 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
266 RenderViewHostImplTestHarness::TearDown();
269 private:
270 WebContentsImplTestWebUIControllerFactory factory_;
273 class TestWebContentsObserver : public WebContentsObserver {
274 public:
275 explicit TestWebContentsObserver(WebContents* contents)
276 : WebContentsObserver(contents) {
278 ~TestWebContentsObserver() override {}
280 void DidFinishLoad(RenderFrameHost* render_frame_host,
281 const GURL& validated_url) override {
282 last_url_ = validated_url;
284 void DidFailLoad(RenderFrameHost* render_frame_host,
285 const GURL& validated_url,
286 int error_code,
287 const base::string16& error_description) override {
288 last_url_ = validated_url;
291 const GURL& last_url() const { return last_url_; }
293 private:
294 GURL last_url_;
296 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
299 // Pretends to be a normal browser that receives toggles and transitions to/from
300 // a fullscreened state.
301 class FakeFullscreenDelegate : public WebContentsDelegate {
302 public:
303 FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {}
304 ~FakeFullscreenDelegate() override {}
306 void EnterFullscreenModeForTab(WebContents* web_contents,
307 const GURL& origin) override {
308 fullscreened_contents_ = web_contents;
311 void ExitFullscreenModeForTab(WebContents* web_contents) override {
312 fullscreened_contents_ = nullptr;
315 bool IsFullscreenForTabOrPending(
316 const WebContents* web_contents) const override {
317 return fullscreened_contents_ && web_contents == fullscreened_contents_;
320 private:
321 WebContents* fullscreened_contents_;
323 DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate);
326 class FakeValidationMessageDelegate : public WebContentsDelegate {
327 public:
328 FakeValidationMessageDelegate()
329 : hide_validation_message_was_called_(false) {}
330 ~FakeValidationMessageDelegate() override {}
332 void HideValidationMessage(WebContents* web_contents) override {
333 hide_validation_message_was_called_ = true;
336 bool hide_validation_message_was_called() const {
337 return hide_validation_message_was_called_;
340 private:
341 bool hide_validation_message_was_called_;
343 DISALLOW_COPY_AND_ASSIGN(FakeValidationMessageDelegate);
346 } // namespace
348 // Test to make sure that title updates get stripped of whitespace.
349 TEST_F(WebContentsImplTest, UpdateTitle) {
350 NavigationControllerImpl& cont =
351 static_cast<NavigationControllerImpl&>(controller());
352 FrameHostMsg_DidCommitProvisionalLoad_Params params;
353 InitNavigateParams(
354 &params, 0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED);
356 LoadCommittedDetails details;
357 cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details);
359 contents()->UpdateTitle(contents()->GetMainFrame(), 0,
360 base::ASCIIToUTF16(" Lots O' Whitespace\n"),
361 base::i18n::LEFT_TO_RIGHT);
362 EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
365 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
366 const GURL kGURL("chrome://blah");
367 controller().LoadURL(
368 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
369 EXPECT_EQ(base::string16(), contents()->GetTitle());
372 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
373 const GURL kGURL("chrome://blah");
374 const base::string16 title = base::ASCIIToUTF16("My Title");
375 controller().LoadURL(
376 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
378 NavigationEntry* entry = controller().GetVisibleEntry();
379 ASSERT_EQ(kGURL, entry->GetURL());
380 entry->SetTitle(title);
382 EXPECT_EQ(title, contents()->GetTitle());
385 // Test view source mode for a webui page.
386 TEST_F(WebContentsImplTest, NTPViewSource) {
387 NavigationControllerImpl& cont =
388 static_cast<NavigationControllerImpl&>(controller());
389 const char kUrl[] = "view-source:chrome://blah";
390 const GURL kGURL(kUrl);
392 process()->sink().ClearMessages();
394 cont.LoadURL(
395 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
396 rvh()->GetDelegate()->RenderViewCreated(rvh());
397 // Did we get the expected message?
398 EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
399 ViewMsg_EnableViewSourceMode::ID));
401 FrameHostMsg_DidCommitProvisionalLoad_Params params;
402 InitNavigateParams(&params, 0, kGURL, ui::PAGE_TRANSITION_TYPED);
403 LoadCommittedDetails details;
404 cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details);
405 // Also check title and url.
406 EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
409 // Test to ensure UpdateMaxPageID is working properly.
410 TEST_F(WebContentsImplTest, UpdateMaxPageID) {
411 SiteInstance* instance1 = contents()->GetSiteInstance();
412 scoped_refptr<SiteInstance> instance2(SiteInstance::Create(nullptr));
414 // Starts at -1.
415 EXPECT_EQ(-1, contents()->GetMaxPageID());
416 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
417 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
419 // Make sure max_page_id_ is monotonically increasing per SiteInstance.
420 contents()->UpdateMaxPageID(3);
421 contents()->UpdateMaxPageID(1);
422 EXPECT_EQ(3, contents()->GetMaxPageID());
423 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
424 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
426 contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
427 EXPECT_EQ(3, contents()->GetMaxPageID());
428 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
429 EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
432 // Test simple same-SiteInstance navigation.
433 TEST_F(WebContentsImplTest, SimpleNavigation) {
434 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
435 SiteInstance* instance1 = contents()->GetSiteInstance();
436 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
438 // Navigate to URL
439 const GURL url("http://www.google.com");
440 controller().LoadURL(
441 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
442 EXPECT_FALSE(contents()->cross_navigation_pending());
443 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
444 // Controller's pending entry will have a null site instance until we assign
445 // it in DidNavigate.
446 EXPECT_EQ(
447 nullptr,
448 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
449 site_instance());
451 // DidNavigate from the page
452 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
453 EXPECT_FALSE(contents()->cross_navigation_pending());
454 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
455 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
456 // Controller's entry should now have the SiteInstance, or else we won't be
457 // able to find it later.
458 EXPECT_EQ(
459 instance1,
460 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
461 site_instance());
464 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
465 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
466 // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
467 const GURL url(std::string("http://example.org/").append(
468 GetMaxURLChars() + 1, 'a'));
470 controller().LoadURL(
471 url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
472 EXPECT_EQ(nullptr, controller().GetVisibleEntry());
475 // Test that navigating across a site boundary creates a new RenderViewHost
476 // with a new SiteInstance. Going back should do the same.
477 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
478 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
479 int orig_rvh_delete_count = 0;
480 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
481 SiteInstance* instance1 = contents()->GetSiteInstance();
483 // Navigate to URL. First URL should use first RenderViewHost.
484 const GURL url("http://www.google.com");
485 controller().LoadURL(
486 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
487 orig_rfh->PrepareForCommit();
488 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
490 // Keep the number of active frames in orig_rfh's SiteInstance non-zero so
491 // that orig_rfh doesn't get deleted when it gets swapped out.
492 orig_rfh->GetSiteInstance()->increment_active_frame_count();
494 EXPECT_FALSE(contents()->cross_navigation_pending());
495 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
496 EXPECT_EQ(url, contents()->GetLastCommittedURL());
497 EXPECT_EQ(url, contents()->GetVisibleURL());
499 // Navigate to new site
500 const GURL url2("http://www.yahoo.com");
501 controller().LoadURL(
502 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
503 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
504 switches::kEnableBrowserSideNavigation)) {
505 orig_rfh->PrepareForCommit();
507 EXPECT_TRUE(contents()->cross_navigation_pending());
508 EXPECT_EQ(url, contents()->GetLastCommittedURL());
509 EXPECT_EQ(url2, contents()->GetVisibleURL());
510 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
511 int pending_rvh_delete_count = 0;
512 pending_rfh->GetRenderViewHost()->set_delete_counter(
513 &pending_rvh_delete_count);
515 // Navigations should be suspended in pending_rfh until BeforeUnloadACK.
516 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
517 switches::kEnableBrowserSideNavigation)) {
518 EXPECT_TRUE(pending_rfh->are_navigations_suspended());
519 orig_rfh->SendBeforeUnloadACK(true);
520 EXPECT_FALSE(pending_rfh->are_navigations_suspended());
523 // DidNavigate from the pending page
524 contents()->TestDidNavigate(
525 pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
526 SiteInstance* instance2 = contents()->GetSiteInstance();
528 // Keep the number of active frames in pending_rfh's SiteInstance
529 // non-zero so that orig_rfh doesn't get deleted when it gets
530 // swapped out.
531 pending_rfh->GetSiteInstance()->increment_active_frame_count();
533 EXPECT_FALSE(contents()->cross_navigation_pending());
534 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
535 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
536 EXPECT_EQ(url2, contents()->GetVisibleURL());
537 EXPECT_NE(instance1, instance2);
538 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
539 // We keep the original RFH around, swapped out.
540 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
541 orig_rfh));
542 EXPECT_EQ(orig_rvh_delete_count, 0);
544 // Going back should switch SiteInstances again. The first SiteInstance is
545 // stored in the NavigationEntry, so it should be the same as at the start.
546 // We should use the same RFH as before, swapping it back in.
547 controller().GoBack();
548 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
549 switches::kEnableBrowserSideNavigation)) {
550 contents()->GetMainFrame()->PrepareForCommit();
552 TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame();
553 EXPECT_EQ(orig_rfh, goback_rfh);
554 EXPECT_TRUE(contents()->cross_navigation_pending());
556 // Navigations should be suspended in goback_rfh until BeforeUnloadACK.
557 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
558 switches::kEnableBrowserSideNavigation)) {
559 EXPECT_TRUE(goback_rfh->are_navigations_suspended());
560 pending_rfh->SendBeforeUnloadACK(true);
561 EXPECT_FALSE(goback_rfh->are_navigations_suspended());
564 // DidNavigate from the back action
565 contents()->TestDidNavigate(goback_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
566 EXPECT_FALSE(contents()->cross_navigation_pending());
567 EXPECT_EQ(goback_rfh, contents()->GetMainFrame());
568 EXPECT_EQ(instance1, contents()->GetSiteInstance());
569 // The pending RFH should now be swapped out, not deleted.
570 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
571 IsOnSwappedOutList(pending_rfh));
572 EXPECT_EQ(pending_rvh_delete_count, 0);
573 pending_rfh->OnSwappedOut();
575 // Close contents and ensure RVHs are deleted.
576 DeleteContents();
577 EXPECT_EQ(orig_rvh_delete_count, 1);
578 EXPECT_EQ(pending_rvh_delete_count, 1);
581 // Test that navigating across a site boundary after a crash creates a new
582 // RFH without requiring a cross-site transition (i.e., PENDING state).
583 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
584 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
586 int orig_rvh_delete_count = 0;
587 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
588 SiteInstance* instance1 = contents()->GetSiteInstance();
590 // Navigate to URL. First URL should use first RenderViewHost.
591 const GURL url("http://www.google.com");
592 controller().LoadURL(
593 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
594 contents()->GetMainFrame()->PrepareForCommit();
595 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
597 EXPECT_FALSE(contents()->cross_navigation_pending());
598 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
600 // Simulate a renderer crash.
601 orig_rfh->GetRenderViewHost()->set_render_view_created(false);
602 orig_rfh->SetRenderFrameCreated(false);
604 // Navigate to new site. We should not go into PENDING.
605 const GURL url2("http://www.yahoo.com");
606 controller().LoadURL(
607 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
608 contents()->GetMainFrame()->PrepareForCommit();
609 TestRenderFrameHost* new_rfh = contents()->GetMainFrame();
610 EXPECT_FALSE(contents()->cross_navigation_pending());
611 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
612 EXPECT_NE(orig_rfh, new_rfh);
613 EXPECT_EQ(orig_rvh_delete_count, 1);
615 // DidNavigate from the new page
616 contents()->TestDidNavigate(new_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
617 SiteInstance* instance2 = contents()->GetSiteInstance();
619 EXPECT_FALSE(contents()->cross_navigation_pending());
620 EXPECT_EQ(new_rfh, main_rfh());
621 EXPECT_NE(instance1, instance2);
622 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
624 // Close contents and ensure RVHs are deleted.
625 DeleteContents();
626 EXPECT_EQ(orig_rvh_delete_count, 1);
629 // Test that opening a new contents in the same SiteInstance and then navigating
630 // both contentses to a new site will place both contentses in a single
631 // SiteInstance.
632 TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) {
633 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
634 SiteInstance* instance1 = contents()->GetSiteInstance();
636 // Navigate to URL. First URL should use first RenderViewHost.
637 const GURL url("http://www.google.com");
638 controller().LoadURL(
639 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
640 contents()->GetMainFrame()->PrepareForCommit();
641 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
643 // Open a new contents with the same SiteInstance, navigated to the same site.
644 scoped_ptr<TestWebContents> contents2(
645 TestWebContents::Create(browser_context(), instance1));
646 contents2->GetController().LoadURL(url, Referrer(),
647 ui::PAGE_TRANSITION_TYPED,
648 std::string());
649 contents2->GetMainFrame()->PrepareForCommit();
650 // Need this page id to be 2 since the site instance is the same (which is the
651 // scope of page IDs) and we want to consider this a new page.
652 contents2->TestDidNavigate(
653 contents2->GetMainFrame(), 2, url, ui::PAGE_TRANSITION_TYPED);
655 // Navigate first contents to a new site.
656 const GURL url2a("http://www.yahoo.com");
657 controller().LoadURL(
658 url2a, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
659 orig_rfh->PrepareForCommit();
660 TestRenderFrameHost* pending_rfh_a = contents()->GetPendingMainFrame();
661 contents()->TestDidNavigate(
662 pending_rfh_a, 1, url2a, ui::PAGE_TRANSITION_TYPED);
663 SiteInstance* instance2a = contents()->GetSiteInstance();
664 EXPECT_NE(instance1, instance2a);
666 // Navigate second contents to the same site as the first tab.
667 const GURL url2b("http://mail.yahoo.com");
668 contents2->GetController().LoadURL(url2b, Referrer(),
669 ui::PAGE_TRANSITION_TYPED,
670 std::string());
671 TestRenderFrameHost* rfh2 = contents2->GetMainFrame();
672 rfh2->PrepareForCommit();
673 TestRenderFrameHost* pending_rfh_b = contents2->GetPendingMainFrame();
674 EXPECT_NE(nullptr, pending_rfh_b);
675 EXPECT_TRUE(contents2->cross_navigation_pending());
677 // NOTE(creis): We used to be in danger of showing a crash page here if the
678 // second contents hadn't navigated somewhere first (bug 1145430). That case
679 // is now covered by the CrossSiteBoundariesAfterCrash test.
680 contents2->TestDidNavigate(
681 pending_rfh_b, 2, url2b, ui::PAGE_TRANSITION_TYPED);
682 SiteInstance* instance2b = contents2->GetSiteInstance();
683 EXPECT_NE(instance1, instance2b);
685 // Both contentses should now be in the same SiteInstance.
686 EXPECT_EQ(instance2a, instance2b);
689 // The embedder can request sites for certain urls not be be assigned to the
690 // SiteInstance through ShouldAssignSiteForURL() in content browser client,
691 // allowing to reuse the renderer backing certain chrome urls for subsequent
692 // navigation. The test verifies that the override is honored.
693 TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) {
694 WebContentsImplTestBrowserClient browser_client;
695 SetBrowserClientForTesting(&browser_client);
697 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
698 int orig_rvh_delete_count = 0;
699 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
700 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
702 browser_client.set_assign_site_for_url(false);
703 // Navigate to an URL that will not assign a new SiteInstance.
704 const GURL native_url("non-site-url://stuffandthings");
705 controller().LoadURL(
706 native_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
707 contents()->GetMainFrame()->PrepareForCommit();
708 contents()->TestDidNavigate(
709 orig_rfh, 1, native_url, ui::PAGE_TRANSITION_TYPED);
711 EXPECT_FALSE(contents()->cross_navigation_pending());
712 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
713 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
714 EXPECT_EQ(native_url, contents()->GetVisibleURL());
715 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
716 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
717 EXPECT_FALSE(orig_instance->HasSite());
719 browser_client.set_assign_site_for_url(true);
720 // Navigate to new site (should keep same site instance).
721 const GURL url("http://www.google.com");
722 controller().LoadURL(
723 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
724 contents()->GetMainFrame()->PrepareForCommit();
725 EXPECT_FALSE(contents()->cross_navigation_pending());
726 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
727 EXPECT_EQ(url, contents()->GetVisibleURL());
728 EXPECT_FALSE(contents()->GetPendingMainFrame());
729 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
731 // Keep the number of active frames in orig_rfh's SiteInstance
732 // non-zero so that orig_rfh doesn't get deleted when it gets
733 // swapped out.
734 orig_rfh->GetSiteInstance()->increment_active_frame_count();
736 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
737 EXPECT_TRUE(
738 contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
739 EXPECT_EQ(url, contents()->GetLastCommittedURL());
741 // Navigate to another new site (should create a new site instance).
742 const GURL url2("http://www.yahoo.com");
743 controller().LoadURL(
744 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
745 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
746 switches::kEnableBrowserSideNavigation)) {
747 orig_rfh->PrepareForCommit();
749 EXPECT_TRUE(contents()->cross_navigation_pending());
750 EXPECT_EQ(url, contents()->GetLastCommittedURL());
751 EXPECT_EQ(url2, contents()->GetVisibleURL());
752 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
753 int pending_rvh_delete_count = 0;
754 pending_rfh->GetRenderViewHost()->set_delete_counter(
755 &pending_rvh_delete_count);
757 // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
758 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
759 switches::kEnableBrowserSideNavigation)) {
760 EXPECT_TRUE(pending_rfh->are_navigations_suspended());
761 orig_rfh->SendBeforeUnloadACK(true);
762 EXPECT_FALSE(pending_rfh->are_navigations_suspended());
765 // DidNavigate from the pending page.
766 contents()->TestDidNavigate(
767 pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
768 SiteInstance* new_instance = contents()->GetSiteInstance();
770 EXPECT_FALSE(contents()->cross_navigation_pending());
771 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
772 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
773 EXPECT_EQ(url2, contents()->GetVisibleURL());
774 EXPECT_NE(new_instance, orig_instance);
775 EXPECT_FALSE(contents()->GetPendingMainFrame());
776 // We keep the original RFH around, swapped out.
777 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList(
778 orig_rfh));
779 EXPECT_EQ(orig_rvh_delete_count, 0);
780 orig_rfh->OnSwappedOut();
782 // Close contents and ensure RVHs are deleted.
783 DeleteContents();
784 EXPECT_EQ(orig_rvh_delete_count, 1);
785 EXPECT_EQ(pending_rvh_delete_count, 1);
788 // Regression test for http://crbug.com/386542 - variation of
789 // NavigateFromSitelessUrl in which the original navigation is a session
790 // restore.
791 TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) {
792 WebContentsImplTestBrowserClient browser_client;
793 SetBrowserClientForTesting(&browser_client);
794 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
795 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
797 // Restore a navigation entry for URL that should not assign site to the
798 // SiteInstance.
799 browser_client.set_assign_site_for_url(false);
800 const GURL native_url("non-site-url://stuffandthings");
801 std::vector<NavigationEntry*> entries;
802 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
803 native_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, std::string(),
804 browser_context());
805 entry->SetPageID(0);
806 entries.push_back(entry);
807 controller().Restore(
809 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
810 &entries);
811 ASSERT_EQ(0u, entries.size());
812 ASSERT_EQ(1, controller().GetEntryCount());
813 controller().GoToIndex(0);
814 contents()->TestDidNavigate(
815 orig_rfh, 0, native_url, ui::PAGE_TRANSITION_RELOAD);
816 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
817 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
818 EXPECT_FALSE(orig_instance->HasSite());
820 // Navigate to a regular site and verify that the SiteInstance was kept.
821 browser_client.set_assign_site_for_url(true);
822 const GURL url("http://www.google.com");
823 controller().LoadURL(
824 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
825 contents()->TestDidNavigate(orig_rfh, 2, url, ui::PAGE_TRANSITION_TYPED);
826 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
828 // Cleanup.
829 DeleteContents();
832 // Complement for NavigateFromRestoredSitelessUrl, verifying that when a regular
833 // tab is restored, the SiteInstance will change upon navigation.
834 TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) {
835 WebContentsImplTestBrowserClient browser_client;
836 SetBrowserClientForTesting(&browser_client);
837 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
838 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
840 // Restore a navigation entry for a regular URL ensuring that the embedder
841 // ShouldAssignSiteForUrl override is disabled (i.e. returns true).
842 browser_client.set_assign_site_for_url(true);
843 const GURL regular_url("http://www.yahoo.com");
844 std::vector<NavigationEntry*> entries;
845 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
846 regular_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, std::string(),
847 browser_context());
848 entry->SetPageID(0);
849 entries.push_back(entry);
850 controller().Restore(
852 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
853 &entries);
854 ASSERT_EQ(0u, entries.size());
855 ASSERT_EQ(1, controller().GetEntryCount());
856 controller().GoToIndex(0);
857 orig_rfh->PrepareForCommit();
858 contents()->TestDidNavigate(
859 orig_rfh, 0, regular_url, ui::PAGE_TRANSITION_RELOAD);
860 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
861 EXPECT_TRUE(orig_instance->HasSite());
863 // Navigate to another site and verify that a new SiteInstance was created.
864 const GURL url("http://www.google.com");
865 controller().LoadURL(
866 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
867 orig_rfh->PrepareForCommit();
868 contents()->TestDidNavigate(
869 contents()->GetPendingMainFrame(), 2, url, ui::PAGE_TRANSITION_TYPED);
870 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
872 // Cleanup.
873 DeleteContents();
876 // Test that we can find an opener RVH even if it's pending.
877 // http://crbug.com/176252.
878 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
879 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
881 // Navigate to a URL.
882 const GURL url("http://www.google.com");
883 controller().LoadURL(
884 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
885 orig_rfh->PrepareForCommit();
886 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
888 // Start to navigate first tab to a new site, so that it has a pending RVH.
889 const GURL url2("http://www.yahoo.com");
890 controller().LoadURL(
891 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
892 orig_rfh->PrepareForCommit();
893 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
895 // While it is still pending, simulate opening a new tab with the first tab
896 // as its opener. This will call WebContentsImpl::CreateOpenerRenderViews
897 // on the opener to ensure that an RVH exists.
898 int opener_routing_id =
899 contents()->CreateOpenerRenderViews(pending_rfh->GetSiteInstance());
901 // We should find the pending RVH and not create a new one.
902 EXPECT_EQ(pending_rfh->GetRenderViewHost()->GetRoutingID(),
903 opener_routing_id);
906 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
907 // to determine whether a navigation is cross-site.
908 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
909 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
910 SiteInstance* instance1 = contents()->GetSiteInstance();
912 // Navigate to URL.
913 const GURL url("http://www.google.com");
914 controller().LoadURL(
915 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
916 contents()->GetMainFrame()->PrepareForCommit();
917 contents()->TestDidNavigate(
918 orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
920 // Open a related contents to a second site.
921 scoped_ptr<TestWebContents> contents2(
922 TestWebContents::Create(browser_context(), instance1));
923 const GURL url2("http://www.yahoo.com");
924 contents2->GetController().LoadURL(url2, Referrer(),
925 ui::PAGE_TRANSITION_TYPED,
926 std::string());
927 contents2->GetMainFrame()->PrepareForCommit();
928 // The first RVH in contents2 isn't live yet, so we shortcut the cross site
929 // pending.
930 TestRenderFrameHost* rfh2 = contents2->GetMainFrame();
931 EXPECT_FALSE(contents2->cross_navigation_pending());
932 contents2->TestDidNavigate(rfh2, 2, url2, ui::PAGE_TRANSITION_TYPED);
933 SiteInstance* instance2 = contents2->GetSiteInstance();
934 EXPECT_NE(instance1, instance2);
935 EXPECT_FALSE(contents2->cross_navigation_pending());
937 // Simulate a link click in first contents to second site. Doesn't switch
938 // SiteInstances, because we don't intercept WebKit navigations.
939 contents()->TestDidNavigate(
940 orig_rfh, 2, url2, ui::PAGE_TRANSITION_TYPED);
941 SiteInstance* instance3 = contents()->GetSiteInstance();
942 EXPECT_EQ(instance1, instance3);
943 EXPECT_FALSE(contents()->cross_navigation_pending());
945 // Navigate to the new site. Doesn't switch SiteInstancees, because we
946 // compare against the current URL, not the SiteInstance's site.
947 const GURL url3("http://mail.yahoo.com");
948 controller().LoadURL(
949 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
950 EXPECT_FALSE(contents()->cross_navigation_pending());
951 contents()->GetMainFrame()->PrepareForCommit();
952 contents()->TestDidNavigate(
953 orig_rfh, 3, url3, ui::PAGE_TRANSITION_TYPED);
954 SiteInstance* instance4 = contents()->GetSiteInstance();
955 EXPECT_EQ(instance1, instance4);
958 // Test that the onbeforeunload and onunload handlers run when navigating
959 // across site boundaries.
960 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
961 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
962 SiteInstance* instance1 = contents()->GetSiteInstance();
964 // Navigate to URL. First URL should use first RenderViewHost.
965 const GURL url("http://www.google.com");
966 controller().LoadURL(
967 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
968 contents()->GetMainFrame()->PrepareForCommit();
969 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
970 EXPECT_FALSE(contents()->cross_navigation_pending());
971 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
973 // Navigate to new site, but simulate an onbeforeunload denial.
974 const GURL url2("http://www.yahoo.com");
975 controller().LoadURL(
976 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
977 EXPECT_TRUE(orig_rfh->IsWaitingForBeforeUnloadACK());
978 base::TimeTicks now = base::TimeTicks::Now();
979 orig_rfh->OnMessageReceived(
980 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
981 EXPECT_FALSE(orig_rfh->IsWaitingForBeforeUnloadACK());
982 EXPECT_FALSE(contents()->cross_navigation_pending());
983 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
985 // Navigate again, but simulate an onbeforeunload approval.
986 controller().LoadURL(
987 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
988 EXPECT_TRUE(orig_rfh->IsWaitingForBeforeUnloadACK());
989 now = base::TimeTicks::Now();
990 orig_rfh->PrepareForCommit();
991 EXPECT_FALSE(orig_rfh->IsWaitingForBeforeUnloadACK());
992 EXPECT_TRUE(contents()->cross_navigation_pending());
993 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
995 // We won't hear DidNavigate until the onunload handler has finished running.
997 // DidNavigate from the pending page.
998 contents()->TestDidNavigate(
999 pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
1000 SiteInstance* instance2 = contents()->GetSiteInstance();
1001 EXPECT_FALSE(contents()->cross_navigation_pending());
1002 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
1003 EXPECT_NE(instance1, instance2);
1004 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1007 // Test that during a slow cross-site navigation, the original renderer can
1008 // navigate to a different URL and have it displayed, canceling the slow
1009 // navigation.
1010 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
1011 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1012 SiteInstance* instance1 = contents()->GetSiteInstance();
1014 // Navigate to URL. First URL should use first RenderFrameHost.
1015 const GURL url("http://www.google.com");
1016 controller().LoadURL(
1017 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1018 contents()->GetMainFrame()->PrepareForCommit();
1019 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1020 EXPECT_FALSE(contents()->cross_navigation_pending());
1021 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1023 // Navigate to new site, simulating an onbeforeunload approval.
1024 const GURL url2("http://www.yahoo.com");
1025 controller().LoadURL(
1026 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1027 EXPECT_TRUE(orig_rfh->IsWaitingForBeforeUnloadACK());
1028 orig_rfh->PrepareForCommit();
1029 EXPECT_TRUE(contents()->cross_navigation_pending());
1031 // Suppose the original renderer navigates before the new one is ready.
1032 orig_rfh->SendNavigate(2, GURL("http://www.google.com/foo"));
1034 // Verify that the pending navigation is cancelled.
1035 EXPECT_FALSE(orig_rfh->IsWaitingForBeforeUnloadACK());
1036 SiteInstance* instance2 = contents()->GetSiteInstance();
1037 EXPECT_FALSE(contents()->cross_navigation_pending());
1038 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1039 EXPECT_EQ(instance1, instance2);
1040 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1043 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
1044 // Start with a web ui page, which gets a new RVH with WebUI bindings.
1045 const GURL url1("chrome://blah");
1046 controller().LoadURL(
1047 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1048 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
1049 ntp_rfh->PrepareForCommit();
1050 contents()->TestDidNavigate(ntp_rfh, 1, url1, ui::PAGE_TRANSITION_TYPED);
1051 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1052 SiteInstance* instance1 = contents()->GetSiteInstance();
1054 EXPECT_FALSE(contents()->cross_navigation_pending());
1055 EXPECT_EQ(ntp_rfh, contents()->GetMainFrame());
1056 EXPECT_EQ(url1, entry1->GetURL());
1057 EXPECT_EQ(instance1,
1058 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1059 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->GetEnabledBindings() &
1060 BINDINGS_POLICY_WEB_UI);
1062 // Navigate to new site.
1063 const GURL url2("http://www.google.com");
1064 controller().LoadURL(
1065 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1066 EXPECT_TRUE(contents()->cross_navigation_pending());
1067 TestRenderFrameHost* google_rfh = contents()->GetPendingMainFrame();
1069 // Simulate beforeunload approval.
1070 EXPECT_TRUE(ntp_rfh->IsWaitingForBeforeUnloadACK());
1071 base::TimeTicks now = base::TimeTicks::Now();
1072 ntp_rfh->PrepareForCommit();
1074 // DidNavigate from the pending page.
1075 contents()->TestDidNavigate(
1076 google_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
1077 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1078 SiteInstance* instance2 = contents()->GetSiteInstance();
1080 EXPECT_FALSE(contents()->cross_navigation_pending());
1081 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1082 EXPECT_NE(instance1, instance2);
1083 EXPECT_FALSE(contents()->GetPendingMainFrame());
1084 EXPECT_EQ(url2, entry2->GetURL());
1085 EXPECT_EQ(instance2,
1086 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1087 EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() &
1088 BINDINGS_POLICY_WEB_UI);
1090 // Navigate to third page on same site.
1091 const GURL url3("http://news.google.com");
1092 controller().LoadURL(
1093 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1094 EXPECT_FALSE(contents()->cross_navigation_pending());
1095 contents()->GetMainFrame()->PrepareForCommit();
1096 contents()->TestDidNavigate(
1097 google_rfh, 2, url3, ui::PAGE_TRANSITION_TYPED);
1098 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1099 SiteInstance* instance3 = contents()->GetSiteInstance();
1101 EXPECT_FALSE(contents()->cross_navigation_pending());
1102 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1103 EXPECT_EQ(instance2, instance3);
1104 EXPECT_FALSE(contents()->GetPendingMainFrame());
1105 EXPECT_EQ(url3, entry3->GetURL());
1106 EXPECT_EQ(instance3,
1107 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1109 // Go back within the site.
1110 controller().GoBack();
1111 EXPECT_FALSE(contents()->cross_navigation_pending());
1112 EXPECT_EQ(entry2, controller().GetPendingEntry());
1114 // Before that commits, go back again.
1115 controller().GoBack();
1116 EXPECT_TRUE(contents()->cross_navigation_pending());
1117 EXPECT_TRUE(contents()->GetPendingMainFrame());
1118 EXPECT_EQ(entry1, controller().GetPendingEntry());
1120 // Simulate beforeunload approval.
1121 EXPECT_TRUE(google_rfh->IsWaitingForBeforeUnloadACK());
1122 now = base::TimeTicks::Now();
1123 google_rfh->PrepareForCommit();
1124 google_rfh->OnMessageReceived(
1125 FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1127 // DidNavigate from the first back. This aborts the second back's pending RFH.
1128 contents()->TestDidNavigate(google_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
1130 // We should commit this page and forget about the second back.
1131 EXPECT_FALSE(contents()->cross_navigation_pending());
1132 EXPECT_FALSE(controller().GetPendingEntry());
1133 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1134 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
1136 // We should not have corrupted the NTP entry.
1137 EXPECT_EQ(instance3,
1138 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1139 EXPECT_EQ(instance2,
1140 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1141 EXPECT_EQ(instance1,
1142 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1143 EXPECT_EQ(url1, entry1->GetURL());
1146 // Test that during a slow cross-site navigation, a sub-frame navigation in the
1147 // original renderer will not cancel the slow navigation (bug 42029).
1148 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
1149 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1151 // Navigate to URL. First URL should use the original RenderFrameHost.
1152 const GURL url("http://www.google.com");
1153 controller().LoadURL(
1154 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1155 contents()->GetMainFrame()->PrepareForCommit();
1156 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1157 EXPECT_FALSE(contents()->cross_navigation_pending());
1158 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1160 // Start navigating to new site.
1161 const GURL url2("http://www.yahoo.com");
1162 controller().LoadURL(
1163 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1165 // Simulate a sub-frame navigation arriving and ensure the RVH is still
1166 // waiting for a before unload response.
1167 TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe");
1168 child_rfh->SendNavigateWithTransition(
1169 1, GURL("http://google.com/frame"), ui::PAGE_TRANSITION_AUTO_SUBFRAME);
1170 EXPECT_TRUE(orig_rfh->IsWaitingForBeforeUnloadACK());
1172 // Now simulate the onbeforeunload approval and verify the navigation is
1173 // not canceled.
1174 orig_rfh->PrepareForCommit();
1175 EXPECT_FALSE(orig_rfh->IsWaitingForBeforeUnloadACK());
1176 EXPECT_TRUE(contents()->cross_navigation_pending());
1179 // Test that a cross-site navigation is not preempted if the previous
1180 // renderer sends a FrameNavigate message just before being told to stop.
1181 // We should only preempt the cross-site navigation if the previous renderer
1182 // has started a new navigation. See http://crbug.com/79176.
1183 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1184 // Navigate to NTP URL.
1185 const GURL url("chrome://blah");
1186 controller().LoadURL(
1187 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1188 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1189 EXPECT_FALSE(contents()->cross_navigation_pending());
1191 // Navigate to new site, with the beforeunload request in flight.
1192 const GURL url2("http://www.yahoo.com");
1193 controller().LoadURL(
1194 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1195 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1196 EXPECT_TRUE(contents()->cross_navigation_pending());
1197 EXPECT_TRUE(orig_rfh->IsWaitingForBeforeUnloadACK());
1199 // Suppose the first navigation tries to commit now, with a
1200 // FrameMsg_Stop in flight. This should not cancel the pending navigation,
1201 // but it should act as if the beforeunload ack arrived.
1202 orig_rfh->SendNavigate(1, GURL("chrome://blah"));
1203 EXPECT_TRUE(contents()->cross_navigation_pending());
1204 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1205 EXPECT_FALSE(orig_rfh->IsWaitingForBeforeUnloadACK());
1207 // The pending navigation should be able to commit successfully.
1208 contents()->TestDidNavigate(pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
1209 EXPECT_FALSE(contents()->cross_navigation_pending());
1210 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
1213 // Test that NavigationEntries have the correct page state after going
1214 // forward and back. Prevents regression for bug 1116137.
1215 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1216 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1218 // Navigate to URL. There should be no committed entry yet.
1219 const GURL url("http://www.google.com");
1220 controller().LoadURL(
1221 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1222 NavigationEntry* entry = controller().GetLastCommittedEntry();
1223 EXPECT_EQ(nullptr, entry);
1225 // Committed entry should have page state after DidNavigate.
1226 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1227 entry = controller().GetLastCommittedEntry();
1228 EXPECT_TRUE(entry->GetPageState().IsValid());
1230 // Navigate to same site.
1231 const GURL url2("http://images.google.com");
1232 controller().LoadURL(
1233 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1234 entry = controller().GetLastCommittedEntry();
1235 EXPECT_TRUE(entry->GetPageState().IsValid());
1237 // Committed entry should have page state after DidNavigate.
1238 contents()->TestDidNavigate(orig_rfh, 2, url2, ui::PAGE_TRANSITION_TYPED);
1239 entry = controller().GetLastCommittedEntry();
1240 EXPECT_TRUE(entry->GetPageState().IsValid());
1242 // Now go back. Committed entry should still have page state.
1243 controller().GoBack();
1244 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1245 entry = controller().GetLastCommittedEntry();
1246 EXPECT_TRUE(entry->GetPageState().IsValid());
1249 // Test that NavigationEntries have the correct page state and SiteInstance
1250 // state after opening a new window to about:blank. Prevents regression for
1251 // bugs b/1116137 and http://crbug.com/111975.
1252 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1253 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1255 // When opening a new window, it is navigated to about:blank internally.
1256 // Currently, this results in two DidNavigate events.
1257 const GURL url(url::kAboutBlankURL);
1258 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1259 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1261 // Should have a page state here.
1262 NavigationEntry* entry = controller().GetLastCommittedEntry();
1263 EXPECT_TRUE(entry->GetPageState().IsValid());
1265 // The SiteInstance should be available for other navigations to use.
1266 NavigationEntryImpl* entry_impl =
1267 NavigationEntryImpl::FromNavigationEntry(entry);
1268 EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1269 int32 site_instance_id = entry_impl->site_instance()->GetId();
1271 // Navigating to a normal page should not cause a process swap.
1272 const GURL new_url("http://www.google.com");
1273 controller().LoadURL(new_url, Referrer(),
1274 ui::PAGE_TRANSITION_TYPED, std::string());
1275 EXPECT_FALSE(contents()->cross_navigation_pending());
1276 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1277 contents()->TestDidNavigate(orig_rfh, 1, new_url, ui::PAGE_TRANSITION_TYPED);
1278 NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1279 controller().GetLastCommittedEntry());
1280 EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1281 EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1284 // Tests that fullscreen is exited throughout the object hierarchy when
1285 // navigating to a new page.
1286 TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
1287 FakeFullscreenDelegate fake_delegate;
1288 contents()->SetDelegate(&fake_delegate);
1289 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1290 TestRenderViewHost* orig_rvh = orig_rfh->GetRenderViewHost();
1292 // Navigate to a site.
1293 const GURL url("http://www.google.com");
1294 controller().LoadURL(
1295 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1296 contents()->GetMainFrame()->PrepareForCommit();
1297 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1298 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1300 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1301 EXPECT_FALSE(orig_rvh->IsFullscreen());
1302 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1303 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1304 orig_rfh->OnMessageReceived(
1305 FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
1306 EXPECT_TRUE(orig_rvh->IsFullscreen());
1307 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1308 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1310 // Navigate to a new site.
1311 const GURL url2("http://www.yahoo.com");
1312 controller().LoadURL(
1313 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1314 contents()->GetMainFrame()->PrepareForCommit();
1315 TestRenderFrameHost* const pending_rfh = contents()->GetPendingMainFrame();
1316 contents()->TestDidNavigate(
1317 pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
1319 // Confirm fullscreen has exited.
1320 EXPECT_FALSE(orig_rvh->IsFullscreen());
1321 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1322 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1324 contents()->SetDelegate(nullptr);
1327 // Tests that fullscreen is exited throughout the object hierarchy when
1328 // instructing NavigationController to GoBack() or GoForward().
1329 TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
1330 FakeFullscreenDelegate fake_delegate;
1331 contents()->SetDelegate(&fake_delegate);
1332 TestRenderFrameHost* const orig_rfh = contents()->GetMainFrame();
1333 TestRenderViewHost* const orig_rvh = orig_rfh->GetRenderViewHost();
1335 // Navigate to a site.
1336 const GURL url("http://www.google.com");
1337 controller().LoadURL(
1338 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1339 contents()->TestDidNavigate(orig_rfh, 1, url, ui::PAGE_TRANSITION_TYPED);
1340 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1342 // Now, navigate to another page on the same site.
1343 const GURL url2("http://www.google.com/search?q=kittens");
1344 controller().LoadURL(
1345 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1346 EXPECT_FALSE(contents()->cross_navigation_pending());
1347 contents()->TestDidNavigate(orig_rfh, 2, url2, ui::PAGE_TRANSITION_TYPED);
1348 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1350 // Sanity-check: Confirm we're not starting out in fullscreen mode.
1351 EXPECT_FALSE(orig_rvh->IsFullscreen());
1352 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1353 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1355 for (int i = 0; i < 2; ++i) {
1356 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1357 orig_rfh->OnMessageReceived(
1358 FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
1359 EXPECT_TRUE(orig_rvh->IsFullscreen());
1360 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1361 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1363 // Navigate backward (or forward).
1364 if (i == 0)
1365 controller().GoBack();
1366 else
1367 controller().GoForward();
1368 EXPECT_FALSE(contents()->cross_navigation_pending());
1369 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1370 contents()->TestDidNavigate(
1371 orig_rfh, i + 1, url, ui::PAGE_TRANSITION_FORWARD_BACK);
1373 // Confirm fullscreen has exited.
1374 EXPECT_FALSE(orig_rvh->IsFullscreen());
1375 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1376 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1379 contents()->SetDelegate(nullptr);
1382 TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) {
1383 FakeValidationMessageDelegate fake_delegate;
1384 contents()->SetDelegate(&fake_delegate);
1385 EXPECT_FALSE(fake_delegate.hide_validation_message_was_called());
1387 // Crash the renderer.
1388 contents()->GetMainFrame()->OnMessageReceived(
1389 FrameHostMsg_RenderProcessGone(
1390 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1392 // Confirm HideValidationMessage was called.
1393 EXPECT_TRUE(fake_delegate.hide_validation_message_was_called());
1395 contents()->SetDelegate(nullptr);
1398 // Tests that fullscreen is exited throughout the object hierarchy on a renderer
1399 // crash.
1400 TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
1401 FakeFullscreenDelegate fake_delegate;
1402 contents()->SetDelegate(&fake_delegate);
1404 // Navigate to a site.
1405 const GURL url("http://www.google.com");
1406 controller().LoadURL(
1407 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1408 contents()->TestDidNavigate(
1409 contents()->GetMainFrame(), 1, url, ui::PAGE_TRANSITION_TYPED);
1411 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1412 EXPECT_FALSE(test_rvh()->IsFullscreen());
1413 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1414 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1415 contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_ToggleFullscreen(
1416 contents()->GetMainFrame()->GetRoutingID(), true));
1417 EXPECT_TRUE(test_rvh()->IsFullscreen());
1418 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1419 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1421 // Crash the renderer.
1422 main_rfh()->OnMessageReceived(
1423 FrameHostMsg_RenderProcessGone(
1424 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1426 // Confirm fullscreen has exited.
1427 EXPECT_FALSE(test_rvh()->IsFullscreen());
1428 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1429 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1431 contents()->SetDelegate(nullptr);
1434 ////////////////////////////////////////////////////////////////////////////////
1435 // Interstitial Tests
1436 ////////////////////////////////////////////////////////////////////////////////
1438 // Test navigating to a page (with the navigation initiated from the browser,
1439 // as when a URL is typed in the location bar) that shows an interstitial and
1440 // creates a new navigation entry, then hiding it without proceeding.
1441 TEST_F(WebContentsImplTest,
1442 ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1443 // Navigate to a page.
1444 GURL url1("http://www.google.com");
1445 contents()->GetMainFrame()->SendNavigate(1, url1);
1446 EXPECT_EQ(1, controller().GetEntryCount());
1448 // Initiate a browser navigation that will trigger the interstitial
1449 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1450 ui::PAGE_TRANSITION_TYPED, std::string());
1452 // Show an interstitial.
1453 TestInterstitialPage::InterstitialState state =
1454 TestInterstitialPage::INVALID;
1455 bool deleted = false;
1456 GURL url2("http://interstitial");
1457 TestInterstitialPage* interstitial =
1458 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1459 TestInterstitialPageStateGuard state_guard(interstitial);
1460 interstitial->Show();
1461 // The interstitial should not show until its navigation has committed.
1462 EXPECT_FALSE(interstitial->is_showing());
1463 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1464 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1465 // Let's commit the interstitial navigation.
1466 interstitial->TestDidNavigate(1, url2);
1467 EXPECT_TRUE(interstitial->is_showing());
1468 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1469 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1470 NavigationEntry* entry = controller().GetVisibleEntry();
1471 ASSERT_NE(nullptr, entry);
1472 EXPECT_TRUE(entry->GetURL() == url2);
1474 // Now don't proceed.
1475 interstitial->DontProceed();
1476 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1477 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1478 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1479 entry = controller().GetVisibleEntry();
1480 ASSERT_NE(nullptr, entry);
1481 EXPECT_TRUE(entry->GetURL() == url1);
1482 EXPECT_EQ(1, controller().GetEntryCount());
1484 RunAllPendingInMessageLoop();
1485 EXPECT_TRUE(deleted);
1488 // Test navigating to a page (with the navigation initiated from the renderer,
1489 // as when clicking on a link in the page) that shows an interstitial and
1490 // creates a new navigation entry, then hiding it without proceeding.
1491 TEST_F(WebContentsImplTest,
1492 ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
1493 // Navigate to a page.
1494 GURL url1("http://www.google.com");
1495 contents()->GetMainFrame()->SendNavigate(1, url1);
1496 EXPECT_EQ(1, controller().GetEntryCount());
1498 // Show an interstitial (no pending entry, the interstitial would have been
1499 // triggered by clicking on a link).
1500 TestInterstitialPage::InterstitialState state =
1501 TestInterstitialPage::INVALID;
1502 bool deleted = false;
1503 GURL url2("http://interstitial");
1504 TestInterstitialPage* interstitial =
1505 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1506 TestInterstitialPageStateGuard state_guard(interstitial);
1507 interstitial->Show();
1508 // The interstitial should not show until its navigation has committed.
1509 EXPECT_FALSE(interstitial->is_showing());
1510 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1511 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1512 // Let's commit the interstitial navigation.
1513 interstitial->TestDidNavigate(1, url2);
1514 EXPECT_TRUE(interstitial->is_showing());
1515 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1516 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1517 NavigationEntry* entry = controller().GetVisibleEntry();
1518 ASSERT_NE(nullptr, entry);
1519 EXPECT_TRUE(entry->GetURL() == url2);
1521 // Now don't proceed.
1522 interstitial->DontProceed();
1523 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1524 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1525 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1526 entry = controller().GetVisibleEntry();
1527 ASSERT_NE(nullptr, entry);
1528 EXPECT_TRUE(entry->GetURL() == url1);
1529 EXPECT_EQ(1, controller().GetEntryCount());
1531 RunAllPendingInMessageLoop();
1532 EXPECT_TRUE(deleted);
1535 // Test navigating to a page that shows an interstitial without creating a new
1536 // navigation entry (this happens when the interstitial is triggered by a
1537 // sub-resource in the page), then hiding it without proceeding.
1538 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1539 // Navigate to a page.
1540 GURL url1("http://www.google.com");
1541 contents()->GetMainFrame()->SendNavigate(1, url1);
1542 EXPECT_EQ(1, controller().GetEntryCount());
1544 // Show an interstitial.
1545 TestInterstitialPage::InterstitialState state =
1546 TestInterstitialPage::INVALID;
1547 bool deleted = false;
1548 GURL url2("http://interstitial");
1549 TestInterstitialPage* interstitial =
1550 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1551 TestInterstitialPageStateGuard state_guard(interstitial);
1552 interstitial->Show();
1553 // The interstitial should not show until its navigation has committed.
1554 EXPECT_FALSE(interstitial->is_showing());
1555 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1556 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1557 // Let's commit the interstitial navigation.
1558 interstitial->TestDidNavigate(1, url2);
1559 EXPECT_TRUE(interstitial->is_showing());
1560 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1561 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1562 NavigationEntry* entry = controller().GetVisibleEntry();
1563 ASSERT_NE(nullptr, entry);
1564 // The URL specified to the interstitial should have been ignored.
1565 EXPECT_TRUE(entry->GetURL() == url1);
1567 // Now don't proceed.
1568 interstitial->DontProceed();
1569 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1570 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1571 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1572 entry = controller().GetVisibleEntry();
1573 ASSERT_NE(nullptr, entry);
1574 EXPECT_TRUE(entry->GetURL() == url1);
1575 EXPECT_EQ(1, controller().GetEntryCount());
1577 RunAllPendingInMessageLoop();
1578 EXPECT_TRUE(deleted);
1581 // Test navigating to a page (with the navigation initiated from the browser,
1582 // as when a URL is typed in the location bar) that shows an interstitial and
1583 // creates a new navigation entry, then proceeding.
1584 TEST_F(WebContentsImplTest,
1585 ShowInterstitialFromBrowserNewNavigationProceed) {
1586 // Navigate to a page.
1587 GURL url1("http://www.google.com");
1588 contents()->GetMainFrame()->SendNavigate(1, url1);
1589 EXPECT_EQ(1, controller().GetEntryCount());
1591 // Initiate a browser navigation that will trigger the interstitial
1592 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1593 ui::PAGE_TRANSITION_TYPED, std::string());
1595 // Show an interstitial.
1596 TestInterstitialPage::InterstitialState state =
1597 TestInterstitialPage::INVALID;
1598 bool deleted = false;
1599 GURL url2("http://interstitial");
1600 TestInterstitialPage* interstitial =
1601 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1602 TestInterstitialPageStateGuard state_guard(interstitial);
1603 interstitial->Show();
1604 // The interstitial should not show until its navigation has committed.
1605 EXPECT_FALSE(interstitial->is_showing());
1606 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1607 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1608 // Let's commit the interstitial navigation.
1609 interstitial->TestDidNavigate(1, url2);
1610 EXPECT_TRUE(interstitial->is_showing());
1611 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1612 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1613 NavigationEntry* entry = controller().GetVisibleEntry();
1614 ASSERT_NE(nullptr, entry);
1615 EXPECT_TRUE(entry->GetURL() == url2);
1617 // Then proceed.
1618 interstitial->Proceed();
1619 // The interstitial should show until the new navigation commits.
1620 RunAllPendingInMessageLoop();
1621 ASSERT_FALSE(deleted);
1622 EXPECT_EQ(TestInterstitialPage::OKED, state);
1623 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1624 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1626 // Simulate the navigation to the page, that's when the interstitial gets
1627 // hidden.
1628 GURL url3("http://www.thepage.com");
1629 contents()->GetMainFrame()->SendNavigate(2, url3);
1631 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1632 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1633 entry = controller().GetVisibleEntry();
1634 ASSERT_NE(nullptr, entry);
1635 EXPECT_TRUE(entry->GetURL() == url3);
1637 EXPECT_EQ(2, controller().GetEntryCount());
1639 RunAllPendingInMessageLoop();
1640 EXPECT_TRUE(deleted);
1643 // Test navigating to a page (with the navigation initiated from the renderer,
1644 // as when clicking on a link in the page) that shows an interstitial and
1645 // creates a new navigation entry, then proceeding.
1646 TEST_F(WebContentsImplTest,
1647 ShowInterstitialFromRendererNewNavigationProceed) {
1648 // Navigate to a page.
1649 GURL url1("http://www.google.com");
1650 contents()->GetMainFrame()->SendNavigate(1, url1);
1651 EXPECT_EQ(1, controller().GetEntryCount());
1653 // Show an interstitial.
1654 TestInterstitialPage::InterstitialState state =
1655 TestInterstitialPage::INVALID;
1656 bool deleted = false;
1657 GURL url2("http://interstitial");
1658 TestInterstitialPage* interstitial =
1659 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1660 TestInterstitialPageStateGuard state_guard(interstitial);
1661 interstitial->Show();
1662 // The interstitial should not show until its navigation has committed.
1663 EXPECT_FALSE(interstitial->is_showing());
1664 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1665 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1666 // Let's commit the interstitial navigation.
1667 interstitial->TestDidNavigate(1, url2);
1668 EXPECT_TRUE(interstitial->is_showing());
1669 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1670 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1671 NavigationEntry* entry = controller().GetVisibleEntry();
1672 ASSERT_NE(nullptr, entry);
1673 EXPECT_TRUE(entry->GetURL() == url2);
1675 // Then proceed.
1676 interstitial->Proceed();
1677 // The interstitial should show until the new navigation commits.
1678 RunAllPendingInMessageLoop();
1679 ASSERT_FALSE(deleted);
1680 EXPECT_EQ(TestInterstitialPage::OKED, state);
1681 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1682 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1684 // Simulate the navigation to the page, that's when the interstitial gets
1685 // hidden.
1686 GURL url3("http://www.thepage.com");
1687 contents()->GetMainFrame()->SendNavigate(2, url3);
1689 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1690 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1691 entry = controller().GetVisibleEntry();
1692 ASSERT_NE(nullptr, entry);
1693 EXPECT_TRUE(entry->GetURL() == url3);
1695 EXPECT_EQ(2, controller().GetEntryCount());
1697 RunAllPendingInMessageLoop();
1698 EXPECT_TRUE(deleted);
1701 // Test navigating to a page that shows an interstitial without creating a new
1702 // navigation entry (this happens when the interstitial is triggered by a
1703 // sub-resource in the page), then proceeding.
1704 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1705 // Navigate to a page so we have a navigation entry in the controller.
1706 GURL url1("http://www.google.com");
1707 contents()->GetMainFrame()->SendNavigate(1, url1);
1708 EXPECT_EQ(1, controller().GetEntryCount());
1710 // Show an interstitial.
1711 TestInterstitialPage::InterstitialState state =
1712 TestInterstitialPage::INVALID;
1713 bool deleted = false;
1714 GURL url2("http://interstitial");
1715 TestInterstitialPage* interstitial =
1716 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1717 TestInterstitialPageStateGuard state_guard(interstitial);
1718 interstitial->Show();
1719 // The interstitial should not show until its navigation has committed.
1720 EXPECT_FALSE(interstitial->is_showing());
1721 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1722 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1723 // Let's commit the interstitial navigation.
1724 interstitial->TestDidNavigate(1, url2);
1725 EXPECT_TRUE(interstitial->is_showing());
1726 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1727 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1728 NavigationEntry* entry = controller().GetVisibleEntry();
1729 ASSERT_NE(nullptr, entry);
1730 // The URL specified to the interstitial should have been ignored.
1731 EXPECT_TRUE(entry->GetURL() == url1);
1733 // Then proceed.
1734 interstitial->Proceed();
1735 // Since this is not a new navigation, the previous page is dismissed right
1736 // away and shows the original page.
1737 EXPECT_EQ(TestInterstitialPage::OKED, state);
1738 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1739 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1740 entry = controller().GetVisibleEntry();
1741 ASSERT_NE(nullptr, entry);
1742 EXPECT_TRUE(entry->GetURL() == url1);
1744 EXPECT_EQ(1, controller().GetEntryCount());
1746 RunAllPendingInMessageLoop();
1747 EXPECT_TRUE(deleted);
1750 // Test navigating to a page that shows an interstitial, then navigating away.
1751 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1752 // Show interstitial.
1753 TestInterstitialPage::InterstitialState state =
1754 TestInterstitialPage::INVALID;
1755 bool deleted = false;
1756 GURL url("http://interstitial");
1757 TestInterstitialPage* interstitial =
1758 new TestInterstitialPage(contents(), true, url, &state, &deleted);
1759 TestInterstitialPageStateGuard state_guard(interstitial);
1760 interstitial->Show();
1761 interstitial->TestDidNavigate(1, url);
1763 // While interstitial showing, navigate to a new URL.
1764 const GURL url2("http://www.yahoo.com");
1765 contents()->GetMainFrame()->SendNavigate(1, url2);
1767 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1769 RunAllPendingInMessageLoop();
1770 EXPECT_TRUE(deleted);
1773 // Test navigating to a page that shows an interstitial, then going back.
1774 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1775 // Navigate to a page so we have a navigation entry in the controller.
1776 GURL url1("http://www.google.com");
1777 contents()->GetMainFrame()->SendNavigate(1, url1);
1778 EXPECT_EQ(1, controller().GetEntryCount());
1780 // Show interstitial.
1781 TestInterstitialPage::InterstitialState state =
1782 TestInterstitialPage::INVALID;
1783 bool deleted = false;
1784 GURL interstitial_url("http://interstitial");
1785 TestInterstitialPage* interstitial =
1786 new TestInterstitialPage(contents(), true, interstitial_url,
1787 &state, &deleted);
1788 TestInterstitialPageStateGuard state_guard(interstitial);
1789 interstitial->Show();
1790 interstitial->TestDidNavigate(2, interstitial_url);
1792 // While the interstitial is showing, go back.
1793 controller().GoBack();
1794 contents()->GetMainFrame()->SendNavigate(1, url1);
1796 // Make sure we are back to the original page and that the interstitial is
1797 // gone.
1798 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1799 NavigationEntry* entry = controller().GetVisibleEntry();
1800 ASSERT_TRUE(entry);
1801 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1803 RunAllPendingInMessageLoop();
1804 EXPECT_TRUE(deleted);
1807 // Test navigating to a page that shows an interstitial, has a renderer crash,
1808 // and then goes back.
1809 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1810 // Navigate to a page so we have a navigation entry in the controller.
1811 GURL url1("http://www.google.com");
1812 contents()->GetMainFrame()->SendNavigate(1, url1);
1813 EXPECT_EQ(1, controller().GetEntryCount());
1815 // Show interstitial.
1816 TestInterstitialPage::InterstitialState state =
1817 TestInterstitialPage::INVALID;
1818 bool deleted = false;
1819 GURL interstitial_url("http://interstitial");
1820 TestInterstitialPage* interstitial =
1821 new TestInterstitialPage(contents(), true, interstitial_url,
1822 &state, &deleted);
1823 TestInterstitialPageStateGuard state_guard(interstitial);
1824 interstitial->Show();
1825 interstitial->TestDidNavigate(2, interstitial_url);
1827 // Crash the renderer
1828 main_rfh()->OnMessageReceived(
1829 FrameHostMsg_RenderProcessGone(
1830 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1832 // While the interstitial is showing, go back.
1833 controller().GoBack();
1834 contents()->GetMainFrame()->SendNavigate(1, url1);
1836 // Make sure we are back to the original page and that the interstitial is
1837 // gone.
1838 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1839 NavigationEntry* entry = controller().GetVisibleEntry();
1840 ASSERT_TRUE(entry);
1841 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1843 RunAllPendingInMessageLoop();
1844 EXPECT_TRUE(deleted);
1847 // Test navigating to a page that shows an interstitial, has the renderer crash,
1848 // and then navigates to the interstitial.
1849 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
1850 // Navigate to a page so we have a navigation entry in the controller.
1851 GURL url1("http://www.google.com");
1852 contents()->GetMainFrame()->SendNavigate(1, url1);
1853 EXPECT_EQ(1, controller().GetEntryCount());
1855 // Show interstitial.
1856 TestInterstitialPage::InterstitialState state =
1857 TestInterstitialPage::INVALID;
1858 bool deleted = false;
1859 GURL interstitial_url("http://interstitial");
1860 TestInterstitialPage* interstitial =
1861 new TestInterstitialPage(contents(), true, interstitial_url,
1862 &state, &deleted);
1863 TestInterstitialPageStateGuard state_guard(interstitial);
1864 interstitial->Show();
1866 // Crash the renderer
1867 main_rfh()->OnMessageReceived(
1868 FrameHostMsg_RenderProcessGone(
1869 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
1871 interstitial->TestDidNavigate(2, interstitial_url);
1874 // Test navigating to a page that shows an interstitial, then close the
1875 // contents.
1876 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
1877 // Show interstitial.
1878 TestInterstitialPage::InterstitialState state =
1879 TestInterstitialPage::INVALID;
1880 bool deleted = false;
1881 GURL url("http://interstitial");
1882 TestInterstitialPage* interstitial =
1883 new TestInterstitialPage(contents(), true, url, &state, &deleted);
1884 TestInterstitialPageStateGuard state_guard(interstitial);
1885 interstitial->Show();
1886 interstitial->TestDidNavigate(1, url);
1888 // Now close the contents.
1889 DeleteContents();
1890 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1892 RunAllPendingInMessageLoop();
1893 EXPECT_TRUE(deleted);
1896 // Test navigating to a page that shows an interstitial, then close the
1897 // contents.
1898 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
1899 // Show interstitial.
1900 TestInterstitialPage::InterstitialState state =
1901 TestInterstitialPage::INVALID;
1902 bool deleted = false;
1903 GURL url("http://interstitial");
1904 TestInterstitialPage* interstitial =
1905 new TestInterstitialPage(contents(), true, url, &state, &deleted);
1906 TestInterstitialPageStateGuard state_guard(interstitial);
1907 interstitial->Show();
1908 interstitial->TestDidNavigate(1, url);
1909 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
1910 interstitial->GetMainFrame()->GetRenderViewHost());
1912 // Now close the contents.
1913 DeleteContents();
1914 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1916 // Before the interstitial has a chance to process its shutdown task,
1917 // simulate quitting the browser. This goes through all processes and
1918 // tells them to destruct.
1919 rvh->GetMainFrame()->OnMessageReceived(
1920 FrameHostMsg_RenderProcessGone(0, 0, 0));
1922 RunAllPendingInMessageLoop();
1923 EXPECT_TRUE(deleted);
1926 // Test that after Proceed is called and an interstitial is still shown, no more
1927 // commands get executed.
1928 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
1929 // Navigate to a page so we have a navigation entry in the controller.
1930 GURL url1("http://www.google.com");
1931 contents()->GetMainFrame()->SendNavigate(1, url1);
1932 EXPECT_EQ(1, controller().GetEntryCount());
1934 // Show an interstitial.
1935 TestInterstitialPage::InterstitialState state =
1936 TestInterstitialPage::INVALID;
1937 bool deleted = false;
1938 GURL url2("http://interstitial");
1939 TestInterstitialPage* interstitial =
1940 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1941 TestInterstitialPageStateGuard state_guard(interstitial);
1942 interstitial->Show();
1943 interstitial->TestDidNavigate(1, url2);
1945 // Run a command.
1946 EXPECT_EQ(0, interstitial->command_received_count());
1947 interstitial->TestDomOperationResponse("toto");
1948 EXPECT_EQ(1, interstitial->command_received_count());
1950 // Then proceed.
1951 interstitial->Proceed();
1952 RunAllPendingInMessageLoop();
1953 ASSERT_FALSE(deleted);
1955 // While the navigation to the new page is pending, send other commands, they
1956 // should be ignored.
1957 interstitial->TestDomOperationResponse("hello");
1958 interstitial->TestDomOperationResponse("hi");
1959 EXPECT_EQ(1, interstitial->command_received_count());
1962 // Test showing an interstitial while another interstitial is already showing.
1963 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
1964 // Navigate to a page so we have a navigation entry in the controller.
1965 GURL start_url("http://www.google.com");
1966 contents()->GetMainFrame()->SendNavigate(1, start_url);
1967 EXPECT_EQ(1, controller().GetEntryCount());
1969 // Show an interstitial.
1970 TestInterstitialPage::InterstitialState state1 =
1971 TestInterstitialPage::INVALID;
1972 bool deleted1 = false;
1973 GURL url1("http://interstitial1");
1974 TestInterstitialPage* interstitial1 =
1975 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
1976 TestInterstitialPageStateGuard state_guard1(interstitial1);
1977 interstitial1->Show();
1978 interstitial1->TestDidNavigate(1, url1);
1980 // Now show another interstitial.
1981 TestInterstitialPage::InterstitialState state2 =
1982 TestInterstitialPage::INVALID;
1983 bool deleted2 = false;
1984 GURL url2("http://interstitial2");
1985 TestInterstitialPage* interstitial2 =
1986 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
1987 TestInterstitialPageStateGuard state_guard2(interstitial2);
1988 interstitial2->Show();
1989 interstitial2->TestDidNavigate(1, url2);
1991 // Showing interstitial2 should have caused interstitial1 to go away.
1992 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
1993 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
1995 RunAllPendingInMessageLoop();
1996 EXPECT_TRUE(deleted1);
1997 ASSERT_FALSE(deleted2);
1999 // Let's make sure interstitial2 is working as intended.
2000 interstitial2->Proceed();
2001 GURL landing_url("http://www.thepage.com");
2002 contents()->GetMainFrame()->SendNavigate(2, landing_url);
2004 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2005 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2006 NavigationEntry* entry = controller().GetVisibleEntry();
2007 ASSERT_NE(nullptr, entry);
2008 EXPECT_TRUE(entry->GetURL() == landing_url);
2009 EXPECT_EQ(2, controller().GetEntryCount());
2010 RunAllPendingInMessageLoop();
2011 EXPECT_TRUE(deleted2);
2014 // Test showing an interstitial, proceeding and then navigating to another
2015 // interstitial.
2016 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
2017 // Navigate to a page so we have a navigation entry in the controller.
2018 GURL start_url("http://www.google.com");
2019 contents()->GetMainFrame()->SendNavigate(1, start_url);
2020 EXPECT_EQ(1, controller().GetEntryCount());
2022 // Show an interstitial.
2023 TestInterstitialPage::InterstitialState state1 =
2024 TestInterstitialPage::INVALID;
2025 bool deleted1 = false;
2026 GURL url1("http://interstitial1");
2027 TestInterstitialPage* interstitial1 =
2028 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2029 TestInterstitialPageStateGuard state_guard1(interstitial1);
2030 interstitial1->Show();
2031 interstitial1->TestDidNavigate(1, url1);
2033 // Take action. The interstitial won't be hidden until the navigation is
2034 // committed.
2035 interstitial1->Proceed();
2036 EXPECT_EQ(TestInterstitialPage::OKED, state1);
2038 // Now show another interstitial (simulating the navigation causing another
2039 // interstitial).
2040 TestInterstitialPage::InterstitialState state2 =
2041 TestInterstitialPage::INVALID;
2042 bool deleted2 = false;
2043 GURL url2("http://interstitial2");
2044 TestInterstitialPage* interstitial2 =
2045 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2046 TestInterstitialPageStateGuard state_guard2(interstitial2);
2047 interstitial2->Show();
2048 interstitial2->TestDidNavigate(1, url2);
2050 // Showing interstitial2 should have caused interstitial1 to go away.
2051 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2052 RunAllPendingInMessageLoop();
2053 EXPECT_TRUE(deleted1);
2054 ASSERT_FALSE(deleted2);
2056 // Let's make sure interstitial2 is working as intended.
2057 interstitial2->Proceed();
2058 GURL landing_url("http://www.thepage.com");
2059 contents()->GetMainFrame()->SendNavigate(2, landing_url);
2061 RunAllPendingInMessageLoop();
2062 EXPECT_TRUE(deleted2);
2063 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2064 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2065 NavigationEntry* entry = controller().GetVisibleEntry();
2066 ASSERT_NE(nullptr, entry);
2067 EXPECT_TRUE(entry->GetURL() == landing_url);
2068 EXPECT_EQ(2, controller().GetEntryCount());
2071 // Test that navigating away from an interstitial while it's loading cause it
2072 // not to show.
2073 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
2074 // Show an interstitial.
2075 TestInterstitialPage::InterstitialState state =
2076 TestInterstitialPage::INVALID;
2077 bool deleted = false;
2078 GURL interstitial_url("http://interstitial");
2079 TestInterstitialPage* interstitial =
2080 new TestInterstitialPage(contents(), true, interstitial_url,
2081 &state, &deleted);
2082 TestInterstitialPageStateGuard state_guard(interstitial);
2083 interstitial->Show();
2085 // Let's simulate a navigation initiated from the browser before the
2086 // interstitial finishes loading.
2087 const GURL url("http://www.google.com");
2088 controller().LoadURL(
2089 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2090 EXPECT_FALSE(interstitial->is_showing());
2091 RunAllPendingInMessageLoop();
2092 ASSERT_FALSE(deleted);
2094 // Now let's make the interstitial navigation commit.
2095 interstitial->TestDidNavigate(1, interstitial_url);
2097 // After it loaded the interstitial should be gone.
2098 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2100 RunAllPendingInMessageLoop();
2101 EXPECT_TRUE(deleted);
2104 // Test that a new request to show an interstitial while an interstitial is
2105 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
2106 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
2107 GURL interstitial_url("http://interstitial");
2109 // Show a first interstitial.
2110 TestInterstitialPage::InterstitialState state1 =
2111 TestInterstitialPage::INVALID;
2112 bool deleted1 = false;
2113 TestInterstitialPage* interstitial1 =
2114 new TestInterstitialPage(contents(), true, interstitial_url,
2115 &state1, &deleted1);
2116 TestInterstitialPageStateGuard state_guard1(interstitial1);
2117 interstitial1->Show();
2119 // Show another interstitial on that same contents before the first one had
2120 // time to load.
2121 TestInterstitialPage::InterstitialState state2 =
2122 TestInterstitialPage::INVALID;
2123 bool deleted2 = false;
2124 TestInterstitialPage* interstitial2 =
2125 new TestInterstitialPage(contents(), true, interstitial_url,
2126 &state2, &deleted2);
2127 TestInterstitialPageStateGuard state_guard2(interstitial2);
2128 interstitial2->Show();
2130 // The first interstitial should have been closed and deleted.
2131 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2132 // The 2nd one should still be OK.
2133 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2135 RunAllPendingInMessageLoop();
2136 EXPECT_TRUE(deleted1);
2137 ASSERT_FALSE(deleted2);
2139 // Make the interstitial navigation commit it should be showing.
2140 interstitial2->TestDidNavigate(1, interstitial_url);
2141 EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
2144 // Test showing an interstitial and have its renderer crash.
2145 TEST_F(WebContentsImplTest, InterstitialCrasher) {
2146 // Show an interstitial.
2147 TestInterstitialPage::InterstitialState state =
2148 TestInterstitialPage::INVALID;
2149 bool deleted = false;
2150 GURL url("http://interstitial");
2151 TestInterstitialPage* interstitial =
2152 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2153 TestInterstitialPageStateGuard state_guard(interstitial);
2154 interstitial->Show();
2155 // Simulate a renderer crash before the interstitial is shown.
2156 interstitial->TestRenderViewTerminated(
2157 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2158 // The interstitial should have been dismissed.
2159 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2160 RunAllPendingInMessageLoop();
2161 EXPECT_TRUE(deleted);
2163 // Now try again but this time crash the intersitial after it was shown.
2164 interstitial =
2165 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2166 interstitial->Show();
2167 interstitial->TestDidNavigate(1, url);
2168 // Simulate a renderer crash.
2169 interstitial->TestRenderViewTerminated(
2170 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2171 // The interstitial should have been dismissed.
2172 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2173 RunAllPendingInMessageLoop();
2174 EXPECT_TRUE(deleted);
2177 // Tests that showing an interstitial as a result of a browser initiated
2178 // navigation while an interstitial is showing does not remove the pending
2179 // entry (see http://crbug.com/9791).
2180 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
2181 const char kUrl[] = "http://www.badguys.com/";
2182 const GURL kGURL(kUrl);
2184 // Start a navigation to a page
2185 contents()->GetController().LoadURL(
2186 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2188 // Simulate that navigation triggering an interstitial.
2189 TestInterstitialPage::InterstitialState state =
2190 TestInterstitialPage::INVALID;
2191 bool deleted = false;
2192 TestInterstitialPage* interstitial =
2193 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2194 TestInterstitialPageStateGuard state_guard(interstitial);
2195 interstitial->Show();
2196 interstitial->TestDidNavigate(1, kGURL);
2198 // Initiate a new navigation from the browser that also triggers an
2199 // interstitial.
2200 contents()->GetController().LoadURL(
2201 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2202 TestInterstitialPage::InterstitialState state2 =
2203 TestInterstitialPage::INVALID;
2204 bool deleted2 = false;
2205 TestInterstitialPage* interstitial2 =
2206 new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
2207 TestInterstitialPageStateGuard state_guard2(interstitial2);
2208 interstitial2->Show();
2209 interstitial2->TestDidNavigate(1, kGURL);
2211 // Make sure we still have an entry.
2212 NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2213 ASSERT_TRUE(entry);
2214 EXPECT_EQ(kUrl, entry->GetURL().spec());
2216 // And that the first interstitial is gone, but not the second.
2217 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2218 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2219 RunAllPendingInMessageLoop();
2220 EXPECT_TRUE(deleted);
2221 EXPECT_FALSE(deleted2);
2224 // Tests that Javascript messages are not shown while an interstitial is
2225 // showing.
2226 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2227 const char kUrl[] = "http://www.badguys.com/";
2228 const GURL kGURL(kUrl);
2230 // Start a navigation to a page
2231 contents()->GetController().LoadURL(
2232 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2233 // DidNavigate from the page
2234 contents()->TestDidNavigate(
2235 contents()->GetMainFrame(), 1, kGURL, ui::PAGE_TRANSITION_TYPED);
2237 // Simulate showing an interstitial while the page is showing.
2238 TestInterstitialPage::InterstitialState state =
2239 TestInterstitialPage::INVALID;
2240 bool deleted = false;
2241 TestInterstitialPage* interstitial =
2242 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2243 TestInterstitialPageStateGuard state_guard(interstitial);
2244 interstitial->Show();
2245 interstitial->TestDidNavigate(1, kGURL);
2247 // While the interstitial is showing, let's simulate the hidden page
2248 // attempting to show a JS message.
2249 IPC::Message* dummy_message = new IPC::Message;
2250 contents()->RunJavaScriptMessage(contents()->GetMainFrame(),
2251 base::ASCIIToUTF16("This is an informative message"),
2252 base::ASCIIToUTF16("OK"),
2253 kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message);
2254 EXPECT_TRUE(contents()->last_dialog_suppressed_);
2257 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2258 // interstitial it isn't copied over to the destination.
2259 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2260 // Navigate to a page.
2261 GURL url1("http://www.google.com");
2262 contents()->GetMainFrame()->SendNavigate(1, url1);
2263 EXPECT_EQ(1, controller().GetEntryCount());
2265 // Initiate a browser navigation that will trigger the interstitial
2266 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2267 ui::PAGE_TRANSITION_TYPED, std::string());
2269 // Show an interstitial.
2270 TestInterstitialPage::InterstitialState state =
2271 TestInterstitialPage::INVALID;
2272 bool deleted = false;
2273 GURL url2("http://interstitial");
2274 TestInterstitialPage* interstitial =
2275 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2276 TestInterstitialPageStateGuard state_guard(interstitial);
2277 interstitial->Show();
2278 interstitial->TestDidNavigate(1, url2);
2279 EXPECT_TRUE(interstitial->is_showing());
2280 EXPECT_EQ(2, controller().GetEntryCount());
2282 // Create another NavigationController.
2283 GURL url3("http://foo2");
2284 scoped_ptr<TestWebContents> other_contents(
2285 static_cast<TestWebContents*>(CreateTestWebContents()));
2286 NavigationControllerImpl& other_controller = other_contents->GetController();
2287 other_contents->NavigateAndCommit(url3);
2288 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
2289 other_controller.CopyStateFromAndPrune(&controller(), false);
2291 // The merged controller should only have two entries: url1 and url2.
2292 ASSERT_EQ(2, other_controller.GetEntryCount());
2293 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2294 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2295 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2297 // And the merged controller shouldn't be showing an interstitial.
2298 EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2301 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2302 // showing an interstitial.
2303 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2304 // Navigate to a page.
2305 GURL url1("http://www.google.com");
2306 contents()->NavigateAndCommit(url1);
2308 // Create another NavigationController.
2309 scoped_ptr<TestWebContents> other_contents(
2310 static_cast<TestWebContents*>(CreateTestWebContents()));
2311 NavigationControllerImpl& other_controller = other_contents->GetController();
2313 // Navigate it to url2.
2314 GURL url2("http://foo2");
2315 other_contents->NavigateAndCommit(url2);
2317 // Show an interstitial.
2318 TestInterstitialPage::InterstitialState state =
2319 TestInterstitialPage::INVALID;
2320 bool deleted = false;
2321 GURL url3("http://interstitial");
2322 TestInterstitialPage* interstitial =
2323 new TestInterstitialPage(other_contents.get(), true, url3, &state,
2324 &deleted);
2325 TestInterstitialPageStateGuard state_guard(interstitial);
2326 interstitial->Show();
2327 interstitial->TestDidNavigate(1, url3);
2328 EXPECT_TRUE(interstitial->is_showing());
2329 EXPECT_EQ(2, other_controller.GetEntryCount());
2331 // Ensure that we do not allow calling CopyStateFromAndPrune when an
2332 // interstitial is showing in the target.
2333 EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2336 // Regression test for http://crbug.com/168611 - the URLs passed by the
2337 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
2338 TEST_F(WebContentsImplTest, FilterURLs) {
2339 TestWebContentsObserver observer(contents());
2341 // A navigation to about:whatever should always look like a navigation to
2342 // about:blank
2343 GURL url_normalized(url::kAboutBlankURL);
2344 GURL url_from_ipc("about:whatever");
2346 // We navigate the test WebContents to about:blank, since NavigateAndCommit
2347 // will use the given URL to create the NavigationEntry as well, and that
2348 // entry should contain the filtered URL.
2349 contents()->NavigateAndCommit(url_normalized);
2351 // Check that an IPC with about:whatever is correctly normalized.
2352 contents()->TestDidFinishLoad(url_from_ipc);
2354 EXPECT_EQ(url_normalized, observer.last_url());
2356 // Create and navigate another WebContents.
2357 scoped_ptr<TestWebContents> other_contents(
2358 static_cast<TestWebContents*>(CreateTestWebContents()));
2359 TestWebContentsObserver other_observer(other_contents.get());
2360 other_contents->NavigateAndCommit(url_normalized);
2362 // Check that an IPC with about:whatever is correctly normalized.
2363 other_contents->TestDidFailLoadWithError(
2364 url_from_ipc, 1, base::string16());
2365 EXPECT_EQ(url_normalized, other_observer.last_url());
2368 // Test that if a pending contents is deleted before it is shown, we don't
2369 // crash.
2370 TEST_F(WebContentsImplTest, PendingContents) {
2371 scoped_ptr<TestWebContents> other_contents(
2372 static_cast<TestWebContents*>(CreateTestWebContents()));
2373 contents()->AddPendingContents(other_contents.get());
2374 int route_id = other_contents->GetRenderViewHost()->GetRoutingID();
2375 other_contents.reset();
2376 EXPECT_EQ(nullptr, contents()->GetCreatedWindow(route_id));
2379 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
2380 const gfx::Size original_preferred_size(1024, 768);
2381 contents()->UpdatePreferredSize(original_preferred_size);
2383 // With no capturers, expect the preferred size to be the one propagated into
2384 // WebContentsImpl via the RenderViewHostDelegate interface.
2385 EXPECT_EQ(contents()->GetCapturerCount(), 0);
2386 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2388 // Increment capturer count, but without specifying a capture size. Expect
2389 // a "not set" preferred size.
2390 contents()->IncrementCapturerCount(gfx::Size());
2391 EXPECT_EQ(1, contents()->GetCapturerCount());
2392 EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
2394 // Increment capturer count again, but with an overriding capture size.
2395 // Expect preferred size to now be overridden to the capture size.
2396 const gfx::Size capture_size(1280, 720);
2397 contents()->IncrementCapturerCount(capture_size);
2398 EXPECT_EQ(2, contents()->GetCapturerCount());
2399 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2401 // Increment capturer count a third time, but the expect that the preferred
2402 // size is still the first capture size.
2403 const gfx::Size another_capture_size(720, 480);
2404 contents()->IncrementCapturerCount(another_capture_size);
2405 EXPECT_EQ(3, contents()->GetCapturerCount());
2406 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2408 // Decrement capturer count twice, but expect the preferred size to still be
2409 // overridden.
2410 contents()->DecrementCapturerCount();
2411 contents()->DecrementCapturerCount();
2412 EXPECT_EQ(1, contents()->GetCapturerCount());
2413 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2415 // Decrement capturer count, and since the count has dropped to zero, the
2416 // original preferred size should be restored.
2417 contents()->DecrementCapturerCount();
2418 EXPECT_EQ(0, contents()->GetCapturerCount());
2419 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2422 TEST_F(WebContentsImplTest, CapturerPreventsHiding) {
2423 const gfx::Size original_preferred_size(1024, 768);
2424 contents()->UpdatePreferredSize(original_preferred_size);
2426 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2427 contents()->GetMainFrame()->GetRenderViewHost()->GetView());
2429 // With no capturers, setting and un-setting occlusion should change the
2430 // view's occlusion state.
2431 EXPECT_FALSE(view->is_showing());
2432 contents()->WasShown();
2433 EXPECT_TRUE(view->is_showing());
2434 contents()->WasHidden();
2435 EXPECT_FALSE(view->is_showing());
2436 contents()->WasShown();
2437 EXPECT_TRUE(view->is_showing());
2439 // Add a capturer and try to hide the contents. The view will remain visible.
2440 contents()->IncrementCapturerCount(gfx::Size());
2441 contents()->WasHidden();
2442 EXPECT_TRUE(view->is_showing());
2444 // Remove the capturer, and the WasHidden should take effect.
2445 contents()->DecrementCapturerCount();
2446 EXPECT_FALSE(view->is_showing());
2449 TEST_F(WebContentsImplTest, CapturerPreventsOcclusion) {
2450 const gfx::Size original_preferred_size(1024, 768);
2451 contents()->UpdatePreferredSize(original_preferred_size);
2453 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2454 contents()->GetMainFrame()->GetRenderViewHost()->GetView());
2456 // With no capturers, setting and un-setting occlusion should change the
2457 // view's occlusion state.
2458 EXPECT_FALSE(view->is_occluded());
2459 contents()->WasOccluded();
2460 EXPECT_TRUE(view->is_occluded());
2461 contents()->WasUnOccluded();
2462 EXPECT_FALSE(view->is_occluded());
2463 contents()->WasOccluded();
2464 EXPECT_TRUE(view->is_occluded());
2466 // Add a capturer. This should cause the view to be un-occluded.
2467 contents()->IncrementCapturerCount(gfx::Size());
2468 EXPECT_FALSE(view->is_occluded());
2470 // Try to occlude the view. This will fail to propagate because of the
2471 // active capturer.
2472 contents()->WasOccluded();
2473 EXPECT_FALSE(view->is_occluded());
2475 // Remove the capturer and try again.
2476 contents()->DecrementCapturerCount();
2477 EXPECT_FALSE(view->is_occluded());
2478 contents()->WasOccluded();
2479 EXPECT_TRUE(view->is_occluded());
2482 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
2483 // on activity.
2484 TEST_F(WebContentsImplTest, GetLastActiveTime) {
2485 // The WebContents starts with a valid creation time.
2486 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2488 // Reset the last active time to a known-bad value.
2489 contents()->last_active_time_ = base::TimeTicks();
2490 ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
2492 // Simulate activating the WebContents. The active time should update.
2493 contents()->WasShown();
2494 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2497 class ContentsZoomChangedDelegate : public WebContentsDelegate {
2498 public:
2499 ContentsZoomChangedDelegate() :
2500 contents_zoom_changed_call_count_(0),
2501 last_zoom_in_(false) {
2504 int GetAndResetContentsZoomChangedCallCount() {
2505 int count = contents_zoom_changed_call_count_;
2506 contents_zoom_changed_call_count_ = 0;
2507 return count;
2510 bool last_zoom_in() const {
2511 return last_zoom_in_;
2514 // WebContentsDelegate:
2515 void ContentsZoomChange(bool zoom_in) override {
2516 contents_zoom_changed_call_count_++;
2517 last_zoom_in_ = zoom_in;
2520 private:
2521 int contents_zoom_changed_call_count_;
2522 bool last_zoom_in_;
2524 DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
2527 // Tests that some mouseehweel events get turned into browser zoom requests.
2528 TEST_F(WebContentsImplTest, HandleWheelEvent) {
2529 using blink::WebInputEvent;
2531 scoped_ptr<ContentsZoomChangedDelegate> delegate(
2532 new ContentsZoomChangedDelegate());
2533 contents()->SetDelegate(delegate.get());
2535 int modifiers = 0;
2536 // Verify that normal mouse wheel events do nothing to change the zoom level.
2537 blink::WebMouseWheelEvent event =
2538 SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2539 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2540 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2542 modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey |
2543 WebInputEvent::ControlKey;
2544 event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2545 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2546 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2548 // But whenever the ctrl modifier is applied with canScroll=false, they can
2549 // increase/decrease zoom. Except on MacOS where we never want to adjust zoom
2550 // with mousewheel.
2551 modifiers = WebInputEvent::ControlKey;
2552 event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2553 event.canScroll = false;
2554 bool handled = contents()->HandleWheelEvent(event);
2555 #if defined(OS_MACOSX)
2556 EXPECT_FALSE(handled);
2557 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2558 #else
2559 EXPECT_TRUE(handled);
2560 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2561 EXPECT_TRUE(delegate->last_zoom_in());
2562 #endif
2564 modifiers = WebInputEvent::ControlKey | WebInputEvent::ShiftKey |
2565 WebInputEvent::AltKey;
2566 event = SyntheticWebMouseWheelEventBuilder::Build(2, -5, modifiers, false);
2567 event.canScroll = false;
2568 handled = contents()->HandleWheelEvent(event);
2569 #if defined(OS_MACOSX)
2570 EXPECT_FALSE(handled);
2571 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2572 #else
2573 EXPECT_TRUE(handled);
2574 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2575 EXPECT_FALSE(delegate->last_zoom_in());
2576 #endif
2578 // Unless there is no vertical movement.
2579 event = SyntheticWebMouseWheelEventBuilder::Build(2, 0, modifiers, false);
2580 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2581 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2583 // Events containing precise scrolling deltas also shouldn't result in the
2584 // zoom being adjusted, to avoid accidental adjustments caused by
2585 // two-finger-scrolling on a touchpad.
2586 modifiers = WebInputEvent::ControlKey;
2587 event = SyntheticWebMouseWheelEventBuilder::Build(0, 5, modifiers, true);
2588 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2589 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2591 // Ensure pointers to the delegate aren't kept beyond its lifetime.
2592 contents()->SetDelegate(nullptr);
2595 // Tests that GetRelatedActiveContentsCount is shared between related
2596 // SiteInstances and includes WebContents that have not navigated yet.
2597 TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
2598 scoped_refptr<SiteInstance> instance1(
2599 SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
2600 scoped_refptr<SiteInstance> instance2(
2601 instance1->GetRelatedSiteInstance(GURL("http://b.com")));
2603 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2604 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2606 scoped_ptr<TestWebContents> contents1(
2607 TestWebContents::Create(browser_context(), instance1.get()));
2608 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2609 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2611 scoped_ptr<TestWebContents> contents2(
2612 TestWebContents::Create(browser_context(), instance1.get()));
2613 EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
2614 EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
2616 contents1.reset();
2617 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2618 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2620 contents2.reset();
2621 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2622 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2625 // Tests that GetRelatedActiveContentsCount is preserved correctly across
2626 // same-site and cross-site navigations.
2627 TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
2628 scoped_refptr<SiteInstance> instance(
2629 SiteInstance::Create(browser_context()));
2631 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2633 scoped_ptr<TestWebContents> contents(
2634 TestWebContents::Create(browser_context(), instance.get()));
2635 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2637 // Navigate to a URL.
2638 contents->GetController().LoadURL(GURL("http://a.com/1"),
2639 Referrer(),
2640 ui::PAGE_TRANSITION_TYPED,
2641 std::string());
2642 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2643 contents->CommitPendingNavigation();
2644 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2646 // Navigate to a URL in the same site.
2647 contents->GetController().LoadURL(GURL("http://a.com/2"),
2648 Referrer(),
2649 ui::PAGE_TRANSITION_TYPED,
2650 std::string());
2651 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2652 contents->CommitPendingNavigation();
2653 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2655 // Navigate to a URL in a different site.
2656 const GURL kUrl = GURL("http://b.com");
2657 contents->GetController().LoadURL(kUrl,
2658 Referrer(),
2659 ui::PAGE_TRANSITION_TYPED,
2660 std::string());
2661 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2662 switches::kEnableBrowserSideNavigation)) {
2663 contents->GetMainFrame()->SendBeforeUnloadACK(true);
2665 EXPECT_TRUE(contents->cross_navigation_pending());
2666 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2667 contents->CommitPendingNavigation();
2668 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2670 contents.reset();
2671 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2674 // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
2675 // from WebUI.
2676 TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
2677 scoped_refptr<SiteInstance> instance(
2678 SiteInstance::Create(browser_context()));
2680 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2682 scoped_ptr<TestWebContents> contents(
2683 TestWebContents::Create(browser_context(), instance.get()));
2684 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2686 // Navigate to a URL.
2687 contents->NavigateAndCommit(GURL("http://a.com"));
2688 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2690 // Navigate to a URL with WebUI. This will change BrowsingInstances.
2691 contents->GetController().LoadURL(GURL(kTestWebUIUrl),
2692 Referrer(),
2693 ui::PAGE_TRANSITION_TYPED,
2694 std::string());
2695 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2696 switches::kEnableBrowserSideNavigation)) {
2697 contents->GetMainFrame()->SendBeforeUnloadACK(true);
2699 EXPECT_TRUE(contents->cross_navigation_pending());
2700 scoped_refptr<SiteInstance> instance_webui(
2701 contents->GetPendingMainFrame()->GetSiteInstance());
2702 EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
2704 // At this point, contents still counts for the old BrowsingInstance.
2705 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2706 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2708 // Commit and contents counts for the new one.
2709 contents->CommitPendingNavigation();
2710 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2711 EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
2713 contents.reset();
2714 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2715 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2718 class LoadingWebContentsObserver : public WebContentsObserver {
2719 public:
2720 explicit LoadingWebContentsObserver(WebContents* contents)
2721 : WebContentsObserver(contents),
2722 is_loading_(false) {
2724 ~LoadingWebContentsObserver() override {}
2726 void DidStartLoading() override { is_loading_ = true; }
2727 void DidStopLoading() override { is_loading_ = false; }
2729 bool is_loading() const { return is_loading_; }
2731 private:
2732 bool is_loading_;
2734 DISALLOW_COPY_AND_ASSIGN(LoadingWebContentsObserver);
2737 // Ensure that DidStartLoading/DidStopLoading events balance out properly with
2738 // interleaving cross-process navigations in multiple subframes.
2739 // See https://crbug.com/448601 for details of the underlying issue. The
2740 // sequence of events that reproduce it are as follows:
2741 // * Navigate top-level frame with one subframe.
2742 // * Subframe navigates more than once before the top-level frame has had a
2743 // chance to complete the load.
2744 // The subframe navigations cause the loading_frames_in_progress_ to drop down
2745 // to 0, while the loading_progresses_ map is not reset.
2746 TEST_F(WebContentsImplTest, StartStopEventsBalance) {
2747 // The bug manifests itself in regular mode as well, but browser-initiated
2748 // navigation of subframes is only possible in --site-per-process mode within
2749 // unit tests.
2750 base::CommandLine::ForCurrentProcess()->AppendSwitch(
2751 switches::kSitePerProcess);
2752 const GURL main_url("http://www.chromium.org");
2753 const GURL foo_url("http://foo.chromium.org");
2754 const GURL bar_url("http://bar.chromium.org");
2755 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
2757 // Use a WebContentsObserver to approximate the behavior of the tab's spinner.
2758 LoadingWebContentsObserver observer(contents());
2760 // Navigate the main RenderFrame, simulate the DidStartLoading, and commit.
2761 // The frame should still be loading.
2762 controller().LoadURL(
2763 main_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2764 orig_rfh->OnMessageReceived(
2765 FrameHostMsg_DidStartLoading(orig_rfh->GetRoutingID(), false));
2766 contents()->TestDidNavigate(orig_rfh, 1, main_url, ui::PAGE_TRANSITION_TYPED);
2767 EXPECT_FALSE(contents()->cross_navigation_pending());
2768 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
2769 EXPECT_TRUE(contents()->IsLoading());
2770 EXPECT_TRUE(observer.is_loading());
2772 // Create a child frame to navigate multiple times.
2773 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
2775 // Navigate the child frame to about:blank, which will send both
2776 // DidStartLoading and DidStopLoading messages.
2778 subframe->OnMessageReceived(
2779 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2780 subframe->SendNavigateWithTransition(
2781 1, GURL("about:blank"), ui::PAGE_TRANSITION_AUTO_SUBFRAME);
2782 subframe->OnMessageReceived(
2783 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2786 // Navigate the frame to another URL, which will send again
2787 // DidStartLoading and DidStopLoading messages.
2789 subframe->OnMessageReceived(
2790 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2791 subframe->SendNavigateWithTransition(
2792 1, foo_url, ui::PAGE_TRANSITION_AUTO_SUBFRAME);
2793 subframe->OnMessageReceived(
2794 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2797 // Since the main frame hasn't sent any DidStopLoading messages, it is
2798 // expected that the WebContents is still in loading state.
2799 EXPECT_TRUE(contents()->IsLoading());
2800 EXPECT_TRUE(observer.is_loading());
2802 // Navigate the frame again, this time using LoadURLWithParams. This causes
2803 // RenderFrameHost to call into WebContents::DidStartLoading, which starts
2804 // the spinner.
2806 NavigationController::LoadURLParams load_params(bar_url);
2807 load_params.referrer =
2808 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
2809 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
2810 load_params.extra_headers = "content-type: text/plain";
2811 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
2812 load_params.is_renderer_initiated = false;
2813 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
2814 load_params.frame_tree_node_id =
2815 subframe->frame_tree_node()->frame_tree_node_id();
2816 controller().LoadURLWithParams(load_params);
2818 subframe->OnMessageReceived(
2819 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2821 // Commit the navigation in the child frame and send the DidStopLoading
2822 // message.
2823 contents()->TestDidNavigate(
2824 subframe, 3, bar_url, ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
2825 subframe->OnMessageReceived(
2826 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2829 // At this point the status should still be loading, since the main frame
2830 // hasn't sent the DidstopLoading message yet.
2831 EXPECT_TRUE(contents()->IsLoading());
2832 EXPECT_TRUE(observer.is_loading());
2834 // Send the DidStopLoading for the main frame and ensure it isn't loading
2835 // anymore.
2836 orig_rfh->OnMessageReceived(
2837 FrameHostMsg_DidStopLoading(orig_rfh->GetRoutingID()));
2838 EXPECT_FALSE(contents()->IsLoading());
2839 EXPECT_FALSE(observer.is_loading());
2842 // Ensure that WebContentsImpl does not stop loading too early when there still
2843 // is a pending renderer. This can happen if a same-process non user-initiated
2844 // navigation completes while there is an ongoing cross-process navigation.
2845 // TODO(fdegans): Rewrite the test for PlzNavigate when DidStartLoading and
2846 // DidStopLoading are properly called.
2847 TEST_F(WebContentsImplTest, NoEarlyStop) {
2848 const GURL kUrl1("http://www.chromium.org");
2849 const GURL kUrl2("http://www.google.com");
2850 const GURL kUrl3("http://www.wikipedia.org");
2852 contents()->NavigateAndCommit(kUrl1);
2854 TestRenderFrameHost* current_rfh = contents()->GetMainFrame();
2856 // Start a browser-initiated cross-process navigation to |kUrl2|. There should
2857 // be a pending RenderFrameHost and the WebContents should be loading.
2858 controller().LoadURL(
2859 kUrl2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2860 EXPECT_TRUE(contents()->cross_navigation_pending());
2861 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
2862 ASSERT_TRUE(pending_rfh);
2863 EXPECT_TRUE(contents()->IsLoading());
2865 // The current RenderFrameHost starts a non user-initiated render-initiated
2866 // navigation and sends a DidStartLoading IPC. The WebContents should still be
2867 // loading.
2868 current_rfh->OnMessageReceived(
2869 FrameHostMsg_DidStartLoading(current_rfh->GetRoutingID(), false));
2870 EXPECT_TRUE(contents()->IsLoading());
2872 // Simulate the pending RenderFrameHost DidStartLoading. There should still be
2873 // a pending RenderFrameHost and the WebContents should still be loading.
2874 pending_rfh->PrepareForCommit();
2875 pending_rfh->OnMessageReceived(
2876 FrameHostMsg_DidStartLoading(pending_rfh->GetRoutingID(), false));
2877 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
2878 EXPECT_TRUE(contents()->IsLoading());
2880 // Simulate the commit and DidStopLoading from the renderer-initiated
2881 // navigation in the current RenderFrameHost. There should still be a pending
2882 // RenderFrameHost and the WebContents should still be loading.
2883 current_rfh->SendNavigate(1, kUrl3);
2884 current_rfh->OnMessageReceived(
2885 FrameHostMsg_DidStopLoading(current_rfh->GetRoutingID()));
2886 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
2887 EXPECT_TRUE(contents()->IsLoading());
2889 // Commit the navigation. The formerly pending RenderFrameHost should now be
2890 // the current RenderFrameHost and the WebContents should still be loading.
2891 contents()->TestDidNavigate(pending_rfh, 1, kUrl2,
2892 ui::PAGE_TRANSITION_TYPED);
2893 EXPECT_FALSE(contents()->GetPendingMainFrame());
2894 TestRenderFrameHost* new_current_rfh = contents()->GetMainFrame();
2895 EXPECT_EQ(new_current_rfh, pending_rfh);
2896 EXPECT_TRUE(contents()->IsLoading());
2898 // Simulate the new current RenderFrameHost DidStopLoading. The WebContents
2899 // should now have stopped loading.
2900 new_current_rfh->OnMessageReceived(
2901 FrameHostMsg_DidStopLoading(new_current_rfh->GetRoutingID()));
2902 EXPECT_EQ(contents()->GetMainFrame(), new_current_rfh);
2903 EXPECT_FALSE(contents()->IsLoading());
2906 TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) {
2907 // PlayerIDs are actually pointers cast to int64, so verify that both negative
2908 // and positive player ids don't blow up.
2909 const int kPlayerAudioVideoId = 15;
2910 const int kPlayerAudioOnlyId = -15;
2911 const int kPlayerVideoOnlyId = 30;
2912 const int kPlayerRemoteId = -30;
2914 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2915 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2917 TestRenderFrameHost* rfh = contents()->GetMainFrame();
2918 AudioStateProvider* audio_state = contents()->audio_state_provider();
2920 // The audio power save blocker should not be based on having a media player
2921 // when audio stream monitoring is available.
2922 if (audio_state->IsAudioStateAvailable()) {
2923 // Send a fake audio stream monitor notification. The audio power save
2924 // blocker should be created.
2925 audio_state->set_was_recently_audible_for_testing(true);
2926 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
2927 EXPECT_TRUE(contents()->has_audio_power_save_blocker_for_testing());
2929 // Send another fake notification, this time when WasRecentlyAudible() will
2930 // be false. The power save blocker should be released.
2931 audio_state->set_was_recently_audible_for_testing(false);
2932 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
2933 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2936 // Start a player with both audio and video. A video power save blocker
2937 // should be created. If audio stream monitoring is available, an audio power
2938 // save blocker should be created too.
2939 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2940 0, kPlayerAudioVideoId, true, true, false));
2941 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2942 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2943 !audio_state->IsAudioStateAvailable());
2945 // Upon hiding the video power save blocker should be released.
2946 contents()->WasHidden();
2947 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2949 // Start another player that only has video. There should be no change in
2950 // the power save blockers. The notification should take into account the
2951 // visibility state of the WebContents.
2952 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2953 0, kPlayerVideoOnlyId, true, false, false));
2954 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2955 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2956 !audio_state->IsAudioStateAvailable());
2958 // Showing the WebContents should result in the creation of the blocker.
2959 contents()->WasShown();
2960 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2962 // Start another player that only has audio. There should be no change in
2963 // the power save blockers.
2964 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2965 0, kPlayerAudioOnlyId, false, true, false));
2966 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2967 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2968 !audio_state->IsAudioStateAvailable());
2970 // Start a remote player. There should be no change in the power save
2971 // blockers.
2972 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
2973 0, kPlayerRemoteId, true, true, true));
2974 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2975 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2976 !audio_state->IsAudioStateAvailable());
2978 // Destroy the original audio video player. Both power save blockers should
2979 // remain.
2980 rfh->OnMessageReceived(
2981 FrameHostMsg_MediaPausedNotification(0, kPlayerAudioVideoId));
2982 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2983 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
2984 !audio_state->IsAudioStateAvailable());
2986 // Destroy the audio only player. The video power save blocker should remain.
2987 rfh->OnMessageReceived(
2988 FrameHostMsg_MediaPausedNotification(0, kPlayerAudioOnlyId));
2989 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
2990 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2992 // Destroy the video only player. No power save blockers should remain.
2993 rfh->OnMessageReceived(
2994 FrameHostMsg_MediaPausedNotification(0, kPlayerVideoOnlyId));
2995 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
2996 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
2998 // Destroy the remote player. No power save blockers should remain.
2999 rfh->OnMessageReceived(
3000 FrameHostMsg_MediaPausedNotification(0, kPlayerRemoteId));
3001 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3002 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3004 // Start a player with both audio and video. A video power save blocker
3005 // should be created. If audio stream monitoring is available, an audio power
3006 // save blocker should be created too.
3007 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3008 0, kPlayerAudioVideoId, true, true, false));
3009 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3010 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3011 !audio_state->IsAudioStateAvailable());
3013 // Crash the renderer.
3014 contents()->GetMainFrame()->OnMessageReceived(
3015 FrameHostMsg_RenderProcessGone(
3016 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
3018 // Verify that all the power save blockers have been released.
3019 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3020 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3023 } // namespace content