Update V8 to version 4.6.55.
[chromium-blink-merge.git] / content / browser / web_contents / web_contents_impl_unittest.cc
blob2400b98485343819758a359850c242ad9535e62e
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/frame_host/render_frame_proxy_host.h"
13 #include "content/browser/media/audio_state_provider.h"
14 #include "content/browser/renderer_host/render_view_host_impl.h"
15 #include "content/browser/site_instance_impl.h"
16 #include "content/browser/webui/content_web_ui_controller_factory.h"
17 #include "content/browser/webui/web_ui_controller_factory_registry.h"
18 #include "content/common/frame_messages.h"
19 #include "content/common/input/synthetic_web_input_event_builders.h"
20 #include "content/common/site_isolation_policy.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/global_request_id.h"
23 #include "content/public/browser/interstitial_page_delegate.h"
24 #include "content/public/browser/navigation_details.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_source.h"
27 #include "content/public/browser/render_widget_host_view.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/browser/web_ui_controller.h"
31 #include "content/public/common/bindings_policy.h"
32 #include "content/public/common/content_constants.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/url_constants.h"
35 #include "content/public/common/url_utils.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_utils.h"
38 #include "content/test/test_content_browser_client.h"
39 #include "content/test/test_content_client.h"
40 #include "content/test/test_render_frame_host.h"
41 #include "content/test/test_render_view_host.h"
42 #include "content/test/test_web_contents.h"
43 #include "testing/gtest/include/gtest/gtest.h"
44 #include "third_party/skia/include/core/SkColor.h"
46 namespace content {
47 namespace {
49 class TestInterstitialPage;
51 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
52 public:
53 explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page)
54 : interstitial_page_(interstitial_page) {}
55 void CommandReceived(const std::string& command) override;
56 std::string GetHTMLContents() override { return std::string(); }
57 void OnDontProceed() override;
58 void OnProceed() override;
60 private:
61 TestInterstitialPage* interstitial_page_;
64 class TestInterstitialPage : public InterstitialPageImpl {
65 public:
66 enum InterstitialState {
67 INVALID = 0, // Hasn't yet been initialized.
68 UNDECIDED, // Initialized, but no decision taken yet.
69 OKED, // Proceed was called.
70 CANCELED // DontProceed was called.
73 class Delegate {
74 public:
75 virtual void TestInterstitialPageDeleted(
76 TestInterstitialPage* interstitial) = 0;
78 protected:
79 virtual ~Delegate() {}
82 // IMPORTANT NOTE: if you pass stack allocated values for |state| and
83 // |deleted| (like all interstitial related tests do at this point), make sure
84 // to create an instance of the TestInterstitialPageStateGuard class on the
85 // stack in your test. This will ensure that the TestInterstitialPage states
86 // are cleared when the test finishes.
87 // Not doing so will cause stack trashing if your test does not hide the
88 // interstitial, as in such a case it will be destroyed in the test TearDown
89 // method and will dereference the |deleted| local variable which by then is
90 // out of scope.
91 TestInterstitialPage(WebContentsImpl* contents,
92 bool new_navigation,
93 const GURL& url,
94 InterstitialState* state,
95 bool* deleted)
96 : InterstitialPageImpl(
97 contents,
98 static_cast<RenderWidgetHostDelegate*>(contents),
99 new_navigation, url, new TestInterstitialPageDelegate(this)),
100 state_(state),
101 deleted_(deleted),
102 command_received_count_(0),
103 delegate_(nullptr) {
104 *state_ = UNDECIDED;
105 *deleted_ = false;
108 ~TestInterstitialPage() override {
109 if (deleted_)
110 *deleted_ = true;
111 if (delegate_)
112 delegate_->TestInterstitialPageDeleted(this);
115 void OnDontProceed() {
116 if (state_)
117 *state_ = CANCELED;
119 void OnProceed() {
120 if (state_)
121 *state_ = OKED;
124 int command_received_count() const {
125 return command_received_count_;
128 void TestDomOperationResponse(const std::string& json_string) {
129 if (enabled())
130 CommandReceived();
133 void TestDidNavigate(int page_id,
134 int nav_entry_id,
135 bool did_create_new_entry,
136 const GURL& url) {
137 FrameHostMsg_DidCommitProvisionalLoad_Params params;
138 InitNavigateParams(&params, page_id, nav_entry_id, did_create_new_entry,
139 url, ui::PAGE_TRANSITION_TYPED);
140 DidNavigate(GetMainFrame()->GetRenderViewHost(), params);
143 void TestRenderViewTerminated(base::TerminationStatus status,
144 int error_code) {
145 RenderViewTerminated(GetMainFrame()->GetRenderViewHost(), status,
146 error_code);
149 bool is_showing() const {
150 return static_cast<TestRenderWidgetHostView*>(
151 GetMainFrame()->GetRenderViewHost()->GetView())->is_showing();
154 void ClearStates() {
155 state_ = nullptr;
156 deleted_ = nullptr;
157 delegate_ = nullptr;
160 void CommandReceived() {
161 command_received_count_++;
164 void set_delegate(Delegate* delegate) {
165 delegate_ = delegate;
168 protected:
169 WebContentsView* CreateWebContentsView() override { return nullptr; }
171 private:
172 InterstitialState* state_;
173 bool* deleted_;
174 int command_received_count_;
175 Delegate* delegate_;
178 void TestInterstitialPageDelegate::CommandReceived(const std::string& command) {
179 interstitial_page_->CommandReceived();
182 void TestInterstitialPageDelegate::OnDontProceed() {
183 interstitial_page_->OnDontProceed();
186 void TestInterstitialPageDelegate::OnProceed() {
187 interstitial_page_->OnProceed();
190 class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
191 public:
192 explicit TestInterstitialPageStateGuard(
193 TestInterstitialPage* interstitial_page)
194 : interstitial_page_(interstitial_page) {
195 DCHECK(interstitial_page_);
196 interstitial_page_->set_delegate(this);
198 ~TestInterstitialPageStateGuard() override {
199 if (interstitial_page_)
200 interstitial_page_->ClearStates();
203 void TestInterstitialPageDeleted(
204 TestInterstitialPage* interstitial) override {
205 DCHECK(interstitial_page_ == interstitial);
206 interstitial_page_ = nullptr;
209 private:
210 TestInterstitialPage* interstitial_page_;
213 class WebContentsImplTestBrowserClient : public TestContentBrowserClient {
214 public:
215 WebContentsImplTestBrowserClient()
216 : assign_site_for_url_(false) {}
218 ~WebContentsImplTestBrowserClient() override {}
220 bool ShouldAssignSiteForURL(const GURL& url) override {
221 return assign_site_for_url_;
224 void set_assign_site_for_url(bool assign) {
225 assign_site_for_url_ = assign;
228 private:
229 bool assign_site_for_url_;
232 class WebContentsImplTest : public RenderViewHostImplTestHarness {
233 public:
234 void SetUp() override {
235 RenderViewHostImplTestHarness::SetUp();
236 WebUIControllerFactory::RegisterFactory(
237 ContentWebUIControllerFactory::GetInstance());
240 void TearDown() override {
241 WebUIControllerFactory::UnregisterFactoryForTesting(
242 ContentWebUIControllerFactory::GetInstance());
243 RenderViewHostImplTestHarness::TearDown();
247 class TestWebContentsObserver : public WebContentsObserver {
248 public:
249 explicit TestWebContentsObserver(WebContents* contents)
250 : WebContentsObserver(contents),
251 last_theme_color_(SK_ColorTRANSPARENT) {
253 ~TestWebContentsObserver() override {}
255 void DidFinishLoad(RenderFrameHost* render_frame_host,
256 const GURL& validated_url) override {
257 last_url_ = validated_url;
259 void DidFailLoad(RenderFrameHost* render_frame_host,
260 const GURL& validated_url,
261 int error_code,
262 const base::string16& error_description,
263 bool was_ignored_by_handler) override {
264 last_url_ = validated_url;
267 void DidChangeThemeColor(SkColor theme_color) override {
268 last_theme_color_ = theme_color;
271 const GURL& last_url() const { return last_url_; }
272 SkColor last_theme_color() const { return last_theme_color_; }
274 private:
275 GURL last_url_;
276 SkColor last_theme_color_;
278 DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver);
281 // Pretends to be a normal browser that receives toggles and transitions to/from
282 // a fullscreened state.
283 class FakeFullscreenDelegate : public WebContentsDelegate {
284 public:
285 FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {}
286 ~FakeFullscreenDelegate() override {}
288 void EnterFullscreenModeForTab(WebContents* web_contents,
289 const GURL& origin) override {
290 fullscreened_contents_ = web_contents;
293 void ExitFullscreenModeForTab(WebContents* web_contents) override {
294 fullscreened_contents_ = nullptr;
297 bool IsFullscreenForTabOrPending(
298 const WebContents* web_contents) const override {
299 return fullscreened_contents_ && web_contents == fullscreened_contents_;
302 private:
303 WebContents* fullscreened_contents_;
305 DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate);
308 class FakeValidationMessageDelegate : public WebContentsDelegate {
309 public:
310 FakeValidationMessageDelegate()
311 : hide_validation_message_was_called_(false) {}
312 ~FakeValidationMessageDelegate() override {}
314 void HideValidationMessage(WebContents* web_contents) override {
315 hide_validation_message_was_called_ = true;
318 bool hide_validation_message_was_called() const {
319 return hide_validation_message_was_called_;
322 private:
323 bool hide_validation_message_was_called_;
325 DISALLOW_COPY_AND_ASSIGN(FakeValidationMessageDelegate);
328 } // namespace
330 // Test to make sure that title updates get stripped of whitespace.
331 TEST_F(WebContentsImplTest, UpdateTitle) {
332 NavigationControllerImpl& cont =
333 static_cast<NavigationControllerImpl&>(controller());
334 FrameHostMsg_DidCommitProvisionalLoad_Params params;
335 InitNavigateParams(&params, 0, 0, true, GURL(url::kAboutBlankURL),
336 ui::PAGE_TRANSITION_TYPED);
338 LoadCommittedDetails details;
339 cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details);
341 contents()->UpdateTitle(contents()->GetMainFrame(), 0,
342 base::ASCIIToUTF16(" Lots O' Whitespace\n"),
343 base::i18n::LEFT_TO_RIGHT);
344 EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
347 TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) {
348 const GURL kGURL("chrome://blah");
349 controller().LoadURL(
350 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
351 EXPECT_EQ(base::string16(), contents()->GetTitle());
354 TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) {
355 const GURL kGURL("chrome://blah");
356 const base::string16 title = base::ASCIIToUTF16("My Title");
357 controller().LoadURL(
358 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
360 NavigationEntry* entry = controller().GetVisibleEntry();
361 ASSERT_EQ(kGURL, entry->GetURL());
362 entry->SetTitle(title);
364 EXPECT_EQ(title, contents()->GetTitle());
367 // Test view source mode for a webui page.
368 TEST_F(WebContentsImplTest, NTPViewSource) {
369 NavigationControllerImpl& cont =
370 static_cast<NavigationControllerImpl&>(controller());
371 const char kUrl[] = "view-source:chrome://blah";
372 const GURL kGURL(kUrl);
374 process()->sink().ClearMessages();
376 cont.LoadURL(
377 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
378 int entry_id = cont.GetPendingEntry()->GetUniqueID();
379 rvh()->GetDelegate()->RenderViewCreated(rvh());
380 // Did we get the expected message?
381 EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
382 ViewMsg_EnableViewSourceMode::ID));
384 FrameHostMsg_DidCommitProvisionalLoad_Params params;
385 InitNavigateParams(&params, 0, entry_id, true, kGURL,
386 ui::PAGE_TRANSITION_TYPED);
387 LoadCommittedDetails details;
388 cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details);
389 // Also check title and url.
390 EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle());
393 // Test to ensure UpdateMaxPageID is working properly.
394 TEST_F(WebContentsImplTest, UpdateMaxPageID) {
395 SiteInstance* instance1 = contents()->GetSiteInstance();
396 scoped_refptr<SiteInstance> instance2(SiteInstance::Create(nullptr));
398 // Starts at -1.
399 EXPECT_EQ(-1, contents()->GetMaxPageID());
400 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1));
401 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
403 // Make sure max_page_id_ is monotonically increasing per SiteInstance.
404 contents()->UpdateMaxPageID(3);
405 contents()->UpdateMaxPageID(1);
406 EXPECT_EQ(3, contents()->GetMaxPageID());
407 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
408 EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
410 contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7);
411 EXPECT_EQ(3, contents()->GetMaxPageID());
412 EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1));
413 EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get()));
416 // Test simple same-SiteInstance navigation.
417 TEST_F(WebContentsImplTest, SimpleNavigation) {
418 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
419 SiteInstance* instance1 = contents()->GetSiteInstance();
420 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
422 // Navigate to URL
423 const GURL url("http://www.google.com");
424 controller().LoadURL(
425 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
426 int entry_id = controller().GetPendingEntry()->GetUniqueID();
427 main_test_rfh()->PrepareForCommit();
428 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
429 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
430 // Controller's pending entry will have a null site instance until we assign
431 // it in DidNavigate.
432 EXPECT_EQ(
433 nullptr,
434 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
435 site_instance());
437 // DidNavigate from the page
438 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
439 ui::PAGE_TRANSITION_TYPED);
440 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
441 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
442 EXPECT_EQ(instance1, orig_rfh->GetSiteInstance());
443 // Controller's entry should now have the SiteInstance, or else we won't be
444 // able to find it later.
445 EXPECT_EQ(
446 instance1,
447 NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())->
448 site_instance());
451 // Test that we reject NavigateToEntry if the url is over kMaxURLChars.
452 TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) {
453 // Construct a URL that's kMaxURLChars + 1 long of all 'a's.
454 const GURL url(std::string("http://example.org/").append(
455 GetMaxURLChars() + 1, 'a'));
457 controller().LoadURL(
458 url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string());
459 EXPECT_EQ(nullptr, controller().GetVisibleEntry());
462 // Test that navigating across a site boundary creates a new RenderViewHost
463 // with a new SiteInstance. Going back should do the same.
464 TEST_F(WebContentsImplTest, CrossSiteBoundaries) {
465 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
466 int orig_rvh_delete_count = 0;
467 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
468 SiteInstance* instance1 = contents()->GetSiteInstance();
470 // Navigate to URL. First URL should use first RenderViewHost.
471 const GURL url("http://www.google.com");
472 controller().LoadURL(
473 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
474 int entry_id = controller().GetPendingEntry()->GetUniqueID();
475 orig_rfh->PrepareForCommit();
476 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
477 ui::PAGE_TRANSITION_TYPED);
479 // Keep the number of active frames in orig_rfh's SiteInstance non-zero so
480 // that orig_rfh doesn't get deleted when it gets swapped out.
481 orig_rfh->GetSiteInstance()->increment_active_frame_count();
483 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
484 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
485 EXPECT_EQ(url, contents()->GetLastCommittedURL());
486 EXPECT_EQ(url, contents()->GetVisibleURL());
488 // Navigate to new site
489 const GURL url2("http://www.yahoo.com");
490 controller().LoadURL(
491 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
492 entry_id = controller().GetPendingEntry()->GetUniqueID();
493 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
494 switches::kEnableBrowserSideNavigation)) {
495 orig_rfh->PrepareForCommit();
497 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
498 EXPECT_EQ(url, contents()->GetLastCommittedURL());
499 EXPECT_EQ(url2, contents()->GetVisibleURL());
500 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
501 int pending_rvh_delete_count = 0;
502 pending_rfh->GetRenderViewHost()->set_delete_counter(
503 &pending_rvh_delete_count);
505 // Navigations should be suspended in pending_rfh until BeforeUnloadACK.
506 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
507 switches::kEnableBrowserSideNavigation)) {
508 EXPECT_TRUE(pending_rfh->are_navigations_suspended());
509 orig_rfh->SendBeforeUnloadACK(true);
510 EXPECT_FALSE(pending_rfh->are_navigations_suspended());
513 // DidNavigate from the pending page
514 contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2,
515 ui::PAGE_TRANSITION_TYPED);
516 SiteInstance* instance2 = contents()->GetSiteInstance();
518 // Keep the number of active frames in pending_rfh's SiteInstance
519 // non-zero so that orig_rfh doesn't get deleted when it gets
520 // swapped out.
521 pending_rfh->GetSiteInstance()->increment_active_frame_count();
523 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
524 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
525 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
526 EXPECT_EQ(url2, contents()->GetVisibleURL());
527 EXPECT_NE(instance1, instance2);
528 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
529 // We keep a proxy for the original RFH's SiteInstance.
530 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
531 orig_rfh->GetSiteInstance()));
532 EXPECT_EQ(orig_rvh_delete_count, 0);
534 // Going back should switch SiteInstances again. The first SiteInstance is
535 // stored in the NavigationEntry, so it should be the same as at the start.
536 // We should use the same RFH as before, swapping it back in.
537 controller().GoBack();
538 entry_id = controller().GetPendingEntry()->GetUniqueID();
539 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
540 switches::kEnableBrowserSideNavigation)) {
541 contents()->GetMainFrame()->PrepareForCommit();
543 TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame();
544 if (!SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
545 // Recycling the rfh is a behavior specific to swappedout://
546 EXPECT_EQ(orig_rfh, goback_rfh);
548 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
550 // Navigations should be suspended in goback_rfh until BeforeUnloadACK.
551 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
552 switches::kEnableBrowserSideNavigation)) {
553 EXPECT_TRUE(goback_rfh->are_navigations_suspended());
554 pending_rfh->SendBeforeUnloadACK(true);
555 EXPECT_FALSE(goback_rfh->are_navigations_suspended());
558 // DidNavigate from the back action
559 contents()->TestDidNavigate(goback_rfh, 1, entry_id, false, url2,
560 ui::PAGE_TRANSITION_TYPED);
561 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
562 EXPECT_EQ(goback_rfh, contents()->GetMainFrame());
563 EXPECT_EQ(instance1, contents()->GetSiteInstance());
564 // There should be a proxy for the pending RFH SiteInstance.
565 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
566 GetRenderFrameProxyHost(pending_rfh->GetSiteInstance()));
567 EXPECT_EQ(pending_rvh_delete_count, 0);
568 pending_rfh->OnSwappedOut();
570 // Close contents and ensure RVHs are deleted.
571 DeleteContents();
572 EXPECT_EQ(orig_rvh_delete_count, 1);
573 EXPECT_EQ(pending_rvh_delete_count, 1);
576 // Test that navigating across a site boundary after a crash creates a new
577 // RFH without requiring a cross-site transition (i.e., PENDING state).
578 TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) {
579 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
581 int orig_rvh_delete_count = 0;
582 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
583 SiteInstance* instance1 = contents()->GetSiteInstance();
585 // Navigate to URL. First URL should use first RenderViewHost.
586 const GURL url("http://www.google.com");
587 controller().LoadURL(
588 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
589 int entry_id = controller().GetPendingEntry()->GetUniqueID();
590 contents()->GetMainFrame()->PrepareForCommit();
591 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
592 ui::PAGE_TRANSITION_TYPED);
594 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
595 EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost());
597 // Simulate a renderer crash.
598 EXPECT_TRUE(orig_rfh->IsRenderFrameLive());
599 orig_rfh->GetProcess()->SimulateCrash();
600 EXPECT_FALSE(orig_rfh->IsRenderFrameLive());
602 // Navigate to new site. We should not go into PENDING.
603 const GURL url2("http://www.yahoo.com");
604 controller().LoadURL(
605 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
606 entry_id = controller().GetPendingEntry()->GetUniqueID();
607 contents()->GetMainFrame()->PrepareForCommit();
608 TestRenderFrameHost* new_rfh = contents()->GetMainFrame();
609 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
610 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
611 EXPECT_NE(orig_rfh, new_rfh);
612 EXPECT_EQ(orig_rvh_delete_count, 1);
614 // DidNavigate from the new page
615 contents()->TestDidNavigate(new_rfh, 1, entry_id, true, url2,
616 ui::PAGE_TRANSITION_TYPED);
617 SiteInstance* instance2 = contents()->GetSiteInstance();
619 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
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 int entry_id = controller().GetPendingEntry()->GetUniqueID();
641 contents()->GetMainFrame()->PrepareForCommit();
642 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
643 ui::PAGE_TRANSITION_TYPED);
645 // Open a new contents with the same SiteInstance, navigated to the same site.
646 scoped_ptr<TestWebContents> contents2(
647 TestWebContents::Create(browser_context(), instance1));
648 contents2->GetController().LoadURL(url, Referrer(),
649 ui::PAGE_TRANSITION_TYPED,
650 std::string());
651 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
652 contents2->GetMainFrame()->PrepareForCommit();
653 // Need this page id to be 2 since the site instance is the same (which is the
654 // scope of page IDs) and we want to consider this a new page.
655 contents2->TestDidNavigate(contents2->GetMainFrame(), 2, entry_id, true, url,
656 ui::PAGE_TRANSITION_TYPED);
658 // Navigate first contents to a new site.
659 const GURL url2a("http://www.yahoo.com");
660 controller().LoadURL(
661 url2a, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
662 entry_id = controller().GetPendingEntry()->GetUniqueID();
663 orig_rfh->PrepareForCommit();
664 TestRenderFrameHost* pending_rfh_a = contents()->GetPendingMainFrame();
665 contents()->TestDidNavigate(pending_rfh_a, 1, entry_id, true, url2a,
666 ui::PAGE_TRANSITION_TYPED);
667 SiteInstance* instance2a = contents()->GetSiteInstance();
668 EXPECT_NE(instance1, instance2a);
670 // Navigate second contents to the same site as the first tab.
671 const GURL url2b("http://mail.yahoo.com");
672 contents2->GetController().LoadURL(url2b, Referrer(),
673 ui::PAGE_TRANSITION_TYPED,
674 std::string());
675 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
676 TestRenderFrameHost* rfh2 = contents2->GetMainFrame();
677 rfh2->PrepareForCommit();
678 TestRenderFrameHost* pending_rfh_b = contents2->GetPendingMainFrame();
679 EXPECT_NE(nullptr, pending_rfh_b);
680 EXPECT_TRUE(contents2->CrossProcessNavigationPending());
682 // NOTE(creis): We used to be in danger of showing a crash page here if the
683 // second contents hadn't navigated somewhere first (bug 1145430). That case
684 // is now covered by the CrossSiteBoundariesAfterCrash test.
685 contents2->TestDidNavigate(pending_rfh_b, 2, entry_id, true, url2b,
686 ui::PAGE_TRANSITION_TYPED);
687 SiteInstance* instance2b = contents2->GetSiteInstance();
688 EXPECT_NE(instance1, instance2b);
690 // Both contentses should now be in the same SiteInstance.
691 EXPECT_EQ(instance2a, instance2b);
694 // The embedder can request sites for certain urls not be be assigned to the
695 // SiteInstance through ShouldAssignSiteForURL() in content browser client,
696 // allowing to reuse the renderer backing certain chrome urls for subsequent
697 // navigation. The test verifies that the override is honored.
698 TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) {
699 WebContentsImplTestBrowserClient browser_client;
700 SetBrowserClientForTesting(&browser_client);
702 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
703 int orig_rvh_delete_count = 0;
704 orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count);
705 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
707 browser_client.set_assign_site_for_url(false);
708 // Navigate to an URL that will not assign a new SiteInstance.
709 const GURL native_url("non-site-url://stuffandthings");
710 controller().LoadURL(
711 native_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
712 int entry_id = controller().GetPendingEntry()->GetUniqueID();
713 contents()->GetMainFrame()->PrepareForCommit();
714 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, native_url,
715 ui::PAGE_TRANSITION_TYPED);
717 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
718 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
719 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
720 EXPECT_EQ(native_url, contents()->GetVisibleURL());
721 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
722 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
723 EXPECT_FALSE(orig_instance->HasSite());
725 browser_client.set_assign_site_for_url(true);
726 // Navigate to new site (should keep same site instance).
727 const GURL url("http://www.google.com");
728 controller().LoadURL(
729 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
730 entry_id = controller().GetPendingEntry()->GetUniqueID();
731 contents()->GetMainFrame()->PrepareForCommit();
732 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
733 EXPECT_EQ(native_url, contents()->GetLastCommittedURL());
734 EXPECT_EQ(url, contents()->GetVisibleURL());
735 EXPECT_FALSE(contents()->GetPendingMainFrame());
736 contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url,
737 ui::PAGE_TRANSITION_TYPED);
739 // Keep the number of active frames in orig_rfh's SiteInstance
740 // non-zero so that orig_rfh doesn't get deleted when it gets
741 // swapped out.
742 orig_rfh->GetSiteInstance()->increment_active_frame_count();
744 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
745 EXPECT_TRUE(
746 contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com"));
747 EXPECT_EQ(url, contents()->GetLastCommittedURL());
749 // Navigate to another new site (should create a new site instance).
750 const GURL url2("http://www.yahoo.com");
751 controller().LoadURL(
752 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
753 entry_id = controller().GetPendingEntry()->GetUniqueID();
754 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
755 switches::kEnableBrowserSideNavigation)) {
756 orig_rfh->PrepareForCommit();
758 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
759 EXPECT_EQ(url, contents()->GetLastCommittedURL());
760 EXPECT_EQ(url2, contents()->GetVisibleURL());
761 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
762 int pending_rvh_delete_count = 0;
763 pending_rfh->GetRenderViewHost()->set_delete_counter(
764 &pending_rvh_delete_count);
766 // Navigations should be suspended in pending_rvh until BeforeUnloadACK.
767 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
768 switches::kEnableBrowserSideNavigation)) {
769 EXPECT_TRUE(pending_rfh->are_navigations_suspended());
770 orig_rfh->SendBeforeUnloadACK(true);
771 EXPECT_FALSE(pending_rfh->are_navigations_suspended());
774 // DidNavigate from the pending page.
775 contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2,
776 ui::PAGE_TRANSITION_TYPED);
777 SiteInstance* new_instance = contents()->GetSiteInstance();
779 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
780 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
781 EXPECT_EQ(url2, contents()->GetLastCommittedURL());
782 EXPECT_EQ(url2, contents()->GetVisibleURL());
783 EXPECT_NE(new_instance, orig_instance);
784 EXPECT_FALSE(contents()->GetPendingMainFrame());
785 // We keep a proxy for the original RFH's SiteInstance.
786 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost(
787 orig_rfh->GetSiteInstance()));
788 EXPECT_EQ(orig_rvh_delete_count, 0);
789 orig_rfh->OnSwappedOut();
791 // Close contents and ensure RVHs are deleted.
792 DeleteContents();
793 EXPECT_EQ(orig_rvh_delete_count, 1);
794 EXPECT_EQ(pending_rvh_delete_count, 1);
797 // Regression test for http://crbug.com/386542 - variation of
798 // NavigateFromSitelessUrl in which the original navigation is a session
799 // restore.
800 TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) {
801 WebContentsImplTestBrowserClient browser_client;
802 SetBrowserClientForTesting(&browser_client);
803 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
804 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
806 // Restore a navigation entry for URL that should not assign site to the
807 // SiteInstance.
808 browser_client.set_assign_site_for_url(false);
809 const GURL native_url("non-site-url://stuffandthings");
810 ScopedVector<NavigationEntry> entries;
811 scoped_ptr<NavigationEntry> new_entry =
812 NavigationControllerImpl::CreateNavigationEntry(
813 native_url, Referrer(), ui::PAGE_TRANSITION_LINK, false,
814 std::string(), browser_context());
815 new_entry->SetPageID(0);
816 entries.push_back(new_entry.Pass());
817 controller().Restore(
819 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
820 &entries);
821 ASSERT_EQ(0u, entries.size());
822 ASSERT_EQ(1, controller().GetEntryCount());
824 controller().GoToIndex(0);
825 NavigationEntry* entry = controller().GetPendingEntry();
826 orig_rfh->PrepareForCommit();
827 contents()->TestDidNavigate(orig_rfh, 0, entry->GetUniqueID(), false,
828 native_url, ui::PAGE_TRANSITION_RELOAD);
829 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
830 EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL());
831 EXPECT_FALSE(orig_instance->HasSite());
833 // Navigate to a regular site and verify that the SiteInstance was kept.
834 browser_client.set_assign_site_for_url(true);
835 const GURL url("http://www.google.com");
836 controller().LoadURL(
837 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
838 entry = controller().GetPendingEntry();
839 orig_rfh->PrepareForCommit();
840 contents()->TestDidNavigate(orig_rfh, 2, entry->GetUniqueID(), true, url,
841 ui::PAGE_TRANSITION_TYPED);
842 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
844 // Cleanup.
845 DeleteContents();
848 // Complement for NavigateFromRestoredSitelessUrl, verifying that when a regular
849 // tab is restored, the SiteInstance will change upon navigation.
850 TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) {
851 WebContentsImplTestBrowserClient browser_client;
852 SetBrowserClientForTesting(&browser_client);
853 SiteInstanceImpl* orig_instance = contents()->GetSiteInstance();
854 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
856 // Restore a navigation entry for a regular URL ensuring that the embedder
857 // ShouldAssignSiteForUrl override is disabled (i.e. returns true).
858 browser_client.set_assign_site_for_url(true);
859 const GURL regular_url("http://www.yahoo.com");
860 ScopedVector<NavigationEntry> entries;
861 scoped_ptr<NavigationEntry> new_entry =
862 NavigationControllerImpl::CreateNavigationEntry(
863 regular_url, Referrer(), ui::PAGE_TRANSITION_LINK, false,
864 std::string(), browser_context());
865 new_entry->SetPageID(0);
866 entries.push_back(new_entry.Pass());
867 controller().Restore(
869 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
870 &entries);
871 ASSERT_EQ(0u, entries.size());
873 ASSERT_EQ(1, controller().GetEntryCount());
874 controller().GoToIndex(0);
875 NavigationEntry* entry = controller().GetPendingEntry();
876 orig_rfh->PrepareForCommit();
877 contents()->TestDidNavigate(orig_rfh, 0, entry->GetUniqueID(), false,
878 regular_url, ui::PAGE_TRANSITION_RELOAD);
879 EXPECT_EQ(orig_instance, contents()->GetSiteInstance());
880 EXPECT_TRUE(orig_instance->HasSite());
882 // Navigate to another site and verify that a new SiteInstance was created.
883 const GURL url("http://www.google.com");
884 controller().LoadURL(
885 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
886 entry = controller().GetPendingEntry();
887 orig_rfh->PrepareForCommit();
888 contents()->TestDidNavigate(contents()->GetPendingMainFrame(), 2,
889 entry->GetUniqueID(), true, url,
890 ui::PAGE_TRANSITION_TYPED);
891 EXPECT_NE(orig_instance, contents()->GetSiteInstance());
893 // Cleanup.
894 DeleteContents();
897 // Test that we can find an opener RVH even if it's pending.
898 // http://crbug.com/176252.
899 TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) {
900 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
902 // Navigate to a URL.
903 const GURL url("http://www.google.com");
904 controller().LoadURL(
905 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
906 int entry_id = controller().GetPendingEntry()->GetUniqueID();
907 orig_rfh->PrepareForCommit();
908 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
909 ui::PAGE_TRANSITION_TYPED);
911 // Start to navigate first tab to a new site, so that it has a pending RVH.
912 const GURL url2("http://www.yahoo.com");
913 controller().LoadURL(
914 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
915 orig_rfh->PrepareForCommit();
916 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
917 SiteInstance* instance = pending_rfh->GetSiteInstance();
919 // While it is still pending, simulate opening a new tab with the first tab
920 // as its opener. This will call CreateOpenerProxies on the opener to ensure
921 // that an RVH exists.
922 scoped_ptr<TestWebContents> popup(
923 TestWebContents::Create(browser_context(), instance));
924 popup->SetOpener(contents());
925 contents()->GetRenderManager()->CreateOpenerProxies(instance);
927 // If swapped out is forbidden, a new proxy should be created for the opener
928 // in |instance|, and we should ensure that its routing ID is returned here.
929 // Otherwise, we should find the pending RFH and not create a new proxy.
930 int opener_frame_routing_id =
931 popup->GetRenderManager()->GetOpenerRoutingID(instance);
932 RenderFrameProxyHost* proxy =
933 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance);
934 if (SiteIsolationPolicy::IsSwappedOutStateForbidden()) {
935 EXPECT_TRUE(proxy);
936 EXPECT_EQ(proxy->GetRoutingID(), opener_frame_routing_id);
938 // Ensure that committing the navigation removes the proxy.
939 int entry_id = controller().GetPendingEntry()->GetUniqueID();
940 contents()->TestDidNavigate(pending_rfh, 2, entry_id, true, url2,
941 ui::PAGE_TRANSITION_TYPED);
942 EXPECT_FALSE(
943 contents()->GetRenderManager()->GetRenderFrameProxyHost(instance));
944 } else {
945 EXPECT_FALSE(proxy);
946 EXPECT_EQ(pending_rfh->GetRoutingID(), opener_frame_routing_id);
950 // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site,
951 // to determine whether a navigation is cross-site.
952 TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) {
953 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
954 SiteInstance* instance1 = contents()->GetSiteInstance();
956 // Navigate to URL.
957 const GURL url("http://www.google.com");
958 controller().LoadURL(
959 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
960 int entry_id = controller().GetPendingEntry()->GetUniqueID();
961 contents()->GetMainFrame()->PrepareForCommit();
962 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
963 ui::PAGE_TRANSITION_TYPED);
965 // Open a related contents to a second site.
966 scoped_ptr<TestWebContents> contents2(
967 TestWebContents::Create(browser_context(), instance1));
968 const GURL url2("http://www.yahoo.com");
969 contents2->GetController().LoadURL(url2, Referrer(),
970 ui::PAGE_TRANSITION_TYPED,
971 std::string());
972 entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID();
973 contents2->GetMainFrame()->PrepareForCommit();
975 // The first RVH in contents2 isn't live yet, so we shortcut the cross site
976 // pending.
977 TestRenderFrameHost* rfh2 = contents2->GetMainFrame();
978 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
979 contents2->TestDidNavigate(rfh2, 2, entry_id, true, url2,
980 ui::PAGE_TRANSITION_TYPED);
981 SiteInstance* instance2 = contents2->GetSiteInstance();
982 EXPECT_NE(instance1, instance2);
983 EXPECT_FALSE(contents2->CrossProcessNavigationPending());
985 // Simulate a link click in first contents to second site. Doesn't switch
986 // SiteInstances, because we don't intercept Blink navigations.
987 orig_rfh->SendRendererInitiatedNavigationRequest(url2, true);
988 orig_rfh->PrepareForCommit();
989 contents()->TestDidNavigate(orig_rfh, 2, 0, true, url2,
990 ui::PAGE_TRANSITION_TYPED);
991 SiteInstance* instance3 = contents()->GetSiteInstance();
992 EXPECT_EQ(instance1, instance3);
993 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
995 // Navigate to the new site. Doesn't switch SiteInstancees, because we
996 // compare against the current URL, not the SiteInstance's site.
997 const GURL url3("http://mail.yahoo.com");
998 controller().LoadURL(
999 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1000 entry_id = controller().GetPendingEntry()->GetUniqueID();
1001 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1002 contents()->GetMainFrame()->PrepareForCommit();
1003 contents()->TestDidNavigate(orig_rfh, 3, entry_id, true, url3,
1004 ui::PAGE_TRANSITION_TYPED);
1005 SiteInstance* instance4 = contents()->GetSiteInstance();
1006 EXPECT_EQ(instance1, instance4);
1009 // Test that the onbeforeunload and onunload handlers run when navigating
1010 // across site boundaries.
1011 TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) {
1012 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1013 SiteInstance* instance1 = contents()->GetSiteInstance();
1015 // Navigate to URL. First URL should use first RenderViewHost.
1016 const GURL url("http://www.google.com");
1017 controller().LoadURL(
1018 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1019 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1020 contents()->GetMainFrame()->PrepareForCommit();
1021 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1022 ui::PAGE_TRANSITION_TYPED);
1023 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1024 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1026 // Navigate to new site, but simulate an onbeforeunload denial.
1027 const GURL url2("http://www.yahoo.com");
1028 controller().LoadURL(
1029 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1030 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack());
1031 base::TimeTicks now = base::TimeTicks::Now();
1032 orig_rfh->OnMessageReceived(
1033 FrameHostMsg_BeforeUnload_ACK(0, false, now, now));
1034 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack());
1035 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1036 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1038 // Navigate again, but simulate an onbeforeunload approval.
1039 controller().LoadURL(
1040 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1041 entry_id = controller().GetPendingEntry()->GetUniqueID();
1042 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack());
1043 now = base::TimeTicks::Now();
1044 orig_rfh->PrepareForCommit();
1045 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack());
1046 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1047 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1049 // We won't hear DidNavigate until the onunload handler has finished running.
1051 // DidNavigate from the pending page.
1052 contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2,
1053 ui::PAGE_TRANSITION_TYPED);
1054 SiteInstance* instance2 = contents()->GetSiteInstance();
1055 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1056 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
1057 EXPECT_NE(instance1, instance2);
1058 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1061 // Test that during a slow cross-site navigation, the original renderer can
1062 // navigate to a different URL and have it displayed, canceling the slow
1063 // navigation.
1064 TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) {
1065 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1066 SiteInstance* instance1 = contents()->GetSiteInstance();
1068 // Navigate to URL. First URL should use first RenderFrameHost.
1069 const GURL url("http://www.google.com");
1070 controller().LoadURL(
1071 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1072 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1073 contents()->GetMainFrame()->PrepareForCommit();
1074 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1075 ui::PAGE_TRANSITION_TYPED);
1076 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1077 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1079 // Navigate to new site, simulating an onbeforeunload approval.
1080 const GURL url2("http://www.yahoo.com");
1081 controller().LoadURL(
1082 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1083 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack());
1084 orig_rfh->PrepareForCommit();
1085 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1087 // Suppose the original renderer navigates before the new one is ready.
1088 orig_rfh->SendNavigate(2, 0, true, GURL("http://www.google.com/foo"));
1090 // Verify that the pending navigation is cancelled.
1091 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack());
1092 SiteInstance* instance2 = contents()->GetSiteInstance();
1093 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1094 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1095 EXPECT_EQ(instance1, instance2);
1096 EXPECT_EQ(nullptr, contents()->GetPendingMainFrame());
1099 TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) {
1100 // Start with a web ui page, which gets a new RVH with WebUI bindings.
1101 const GURL url1("chrome://gpu");
1102 controller().LoadURL(
1103 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1104 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1105 TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame();
1106 ntp_rfh->PrepareForCommit();
1107 contents()->TestDidNavigate(ntp_rfh, 1, entry_id, true, url1,
1108 ui::PAGE_TRANSITION_TYPED);
1109 NavigationEntry* entry1 = controller().GetLastCommittedEntry();
1110 SiteInstance* instance1 = contents()->GetSiteInstance();
1112 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1113 EXPECT_EQ(ntp_rfh, contents()->GetMainFrame());
1114 EXPECT_EQ(url1, entry1->GetURL());
1115 EXPECT_EQ(instance1,
1116 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1117 EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->GetEnabledBindings() &
1118 BINDINGS_POLICY_WEB_UI);
1120 // Navigate to new site.
1121 const GURL url2("http://www.google.com");
1122 controller().LoadURL(
1123 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1124 entry_id = controller().GetPendingEntry()->GetUniqueID();
1125 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1126 TestRenderFrameHost* google_rfh = contents()->GetPendingMainFrame();
1128 // Simulate beforeunload approval.
1129 EXPECT_TRUE(ntp_rfh->is_waiting_for_beforeunload_ack());
1130 base::TimeTicks now = base::TimeTicks::Now();
1131 ntp_rfh->PrepareForCommit();
1133 // DidNavigate from the pending page.
1134 contents()->TestDidNavigate(google_rfh, 1, entry_id, true, url2,
1135 ui::PAGE_TRANSITION_TYPED);
1136 NavigationEntry* entry2 = controller().GetLastCommittedEntry();
1137 SiteInstance* instance2 = contents()->GetSiteInstance();
1139 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1140 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1141 EXPECT_NE(instance1, instance2);
1142 EXPECT_FALSE(contents()->GetPendingMainFrame());
1143 EXPECT_EQ(url2, entry2->GetURL());
1144 EXPECT_EQ(instance2,
1145 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1146 EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() &
1147 BINDINGS_POLICY_WEB_UI);
1149 // Navigate to third page on same site.
1150 const GURL url3("http://news.google.com");
1151 controller().LoadURL(
1152 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1153 entry_id = controller().GetPendingEntry()->GetUniqueID();
1154 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1155 contents()->GetMainFrame()->PrepareForCommit();
1156 contents()->TestDidNavigate(google_rfh, 2, entry_id, true, url3,
1157 ui::PAGE_TRANSITION_TYPED);
1158 NavigationEntry* entry3 = controller().GetLastCommittedEntry();
1159 SiteInstance* instance3 = contents()->GetSiteInstance();
1161 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1162 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1163 EXPECT_EQ(instance2, instance3);
1164 EXPECT_FALSE(contents()->GetPendingMainFrame());
1165 EXPECT_EQ(url3, entry3->GetURL());
1166 EXPECT_EQ(instance3,
1167 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1169 // Go back within the site.
1170 controller().GoBack();
1171 NavigationEntry* goback_entry = controller().GetPendingEntry();
1172 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1173 EXPECT_EQ(entry2, controller().GetPendingEntry());
1175 // Before that commits, go back again.
1176 controller().GoBack();
1177 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1178 EXPECT_TRUE(contents()->GetPendingMainFrame());
1179 EXPECT_EQ(entry1, controller().GetPendingEntry());
1181 // Simulate beforeunload approval.
1182 EXPECT_TRUE(google_rfh->is_waiting_for_beforeunload_ack());
1183 now = base::TimeTicks::Now();
1184 google_rfh->PrepareForCommit();
1185 google_rfh->OnMessageReceived(
1186 FrameHostMsg_BeforeUnload_ACK(0, true, now, now));
1188 // DidNavigate from the first back. This aborts the second back's pending RFH.
1189 contents()->TestDidNavigate(google_rfh, 1, goback_entry->GetUniqueID(), false,
1190 url2, ui::PAGE_TRANSITION_TYPED);
1192 // We should commit this page and forget about the second back.
1193 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1194 EXPECT_FALSE(controller().GetPendingEntry());
1195 EXPECT_EQ(google_rfh, contents()->GetMainFrame());
1196 EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL());
1198 // We should not have corrupted the NTP entry.
1199 EXPECT_EQ(instance3,
1200 NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance());
1201 EXPECT_EQ(instance2,
1202 NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance());
1203 EXPECT_EQ(instance1,
1204 NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance());
1205 EXPECT_EQ(url1, entry1->GetURL());
1208 // Test that during a slow cross-site navigation, a sub-frame navigation in the
1209 // original renderer will not cancel the slow navigation (bug 42029).
1210 TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) {
1211 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1213 // Navigate to URL. First URL should use the original RenderFrameHost.
1214 const GURL url("http://www.google.com");
1215 controller().LoadURL(
1216 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1217 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1218 contents()->GetMainFrame()->PrepareForCommit();
1219 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1220 ui::PAGE_TRANSITION_TYPED);
1221 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1222 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1224 // Start navigating to new site.
1225 const GURL url2("http://www.yahoo.com");
1226 controller().LoadURL(
1227 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1229 // Simulate a sub-frame navigation arriving and ensure the RVH is still
1230 // waiting for a before unload response.
1231 TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe");
1232 child_rfh->SendNavigateWithTransition(1, 0, false,
1233 GURL("http://google.com/frame"),
1234 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
1235 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack());
1237 // Now simulate the onbeforeunload approval and verify the navigation is
1238 // not canceled.
1239 orig_rfh->PrepareForCommit();
1240 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack());
1241 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1244 namespace {
1245 void SetAsNonUserGesture(FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
1246 params->gesture = NavigationGestureAuto;
1250 // Test that a cross-site navigation is not preempted if the previous
1251 // renderer sends a FrameNavigate message just before being told to stop.
1252 // We should only preempt the cross-site navigation if the previous renderer
1253 // has started a new navigation. See http://crbug.com/79176.
1254 TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) {
1255 // Navigate to WebUI URL.
1256 const GURL url("chrome://gpu");
1257 controller().LoadURL(
1258 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1259 int entry1_id = controller().GetPendingEntry()->GetUniqueID();
1260 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1261 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1263 // Navigate to new site, with the beforeunload request in flight.
1264 const GURL url2("http://www.yahoo.com");
1265 controller().LoadURL(
1266 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1267 int entry2_id = controller().GetPendingEntry()->GetUniqueID();
1268 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
1269 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1270 EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack());
1271 EXPECT_NE(orig_rfh, pending_rfh);
1273 // Suppose the first navigation tries to commit now, with a
1274 // FrameMsg_Stop in flight. This should not cancel the pending navigation,
1275 // but it should act as if the beforeunload ack arrived.
1276 orig_rfh->SendNavigateWithModificationCallback(
1277 1, entry1_id, true, url, base::Bind(SetAsNonUserGesture));
1278 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
1279 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1280 EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack());
1281 // It should commit.
1282 ASSERT_EQ(1, controller().GetEntryCount());
1283 EXPECT_EQ(url, controller().GetLastCommittedEntry()->GetURL());
1285 // The pending navigation should be able to commit successfully.
1286 contents()->TestDidNavigate(pending_rfh, 1, entry2_id, true, url2,
1287 ui::PAGE_TRANSITION_TYPED);
1288 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1289 EXPECT_EQ(pending_rfh, contents()->GetMainFrame());
1290 EXPECT_EQ(2, controller().GetEntryCount());
1293 // Test that NavigationEntries have the correct page state after going
1294 // forward and back. Prevents regression for bug 1116137.
1295 TEST_F(WebContentsImplTest, NavigationEntryContentState) {
1296 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1298 // Navigate to URL. There should be no committed entry yet.
1299 const GURL url("http://www.google.com");
1300 controller().LoadURL(
1301 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1302 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1303 NavigationEntry* entry = controller().GetLastCommittedEntry();
1304 EXPECT_EQ(nullptr, entry);
1306 // Committed entry should have page state after DidNavigate.
1307 orig_rfh->PrepareForCommit();
1308 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1309 ui::PAGE_TRANSITION_TYPED);
1310 entry = controller().GetLastCommittedEntry();
1311 EXPECT_TRUE(entry->GetPageState().IsValid());
1313 // Navigate to same site.
1314 const GURL url2("http://images.google.com");
1315 controller().LoadURL(
1316 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1317 entry_id = controller().GetPendingEntry()->GetUniqueID();
1318 entry = controller().GetLastCommittedEntry();
1319 EXPECT_TRUE(entry->GetPageState().IsValid());
1321 // Committed entry should have page state after DidNavigate.
1322 orig_rfh->PrepareForCommit();
1323 contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url2,
1324 ui::PAGE_TRANSITION_TYPED);
1325 entry = controller().GetLastCommittedEntry();
1326 EXPECT_TRUE(entry->GetPageState().IsValid());
1328 // Now go back. Committed entry should still have page state.
1329 controller().GoBack();
1330 entry_id = controller().GetPendingEntry()->GetUniqueID();
1331 orig_rfh->PrepareForCommit();
1332 contents()->TestDidNavigate(orig_rfh, 1, entry_id, false, url,
1333 ui::PAGE_TRANSITION_TYPED);
1334 entry = controller().GetLastCommittedEntry();
1335 EXPECT_TRUE(entry->GetPageState().IsValid());
1338 // Test that NavigationEntries have the correct page state and SiteInstance
1339 // state after opening a new window to about:blank. Prevents regression for
1340 // bugs b/1116137 and http://crbug.com/111975.
1341 TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) {
1342 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1344 // Navigate to about:blank.
1345 const GURL url(url::kAboutBlankURL);
1346 orig_rfh->SendRendererInitiatedNavigationRequest(url, false);
1347 contents()->TestDidNavigate(orig_rfh, 1, 0, true, url,
1348 ui::PAGE_TRANSITION_TYPED);
1350 // Should have a page state here.
1351 NavigationEntry* entry = controller().GetLastCommittedEntry();
1352 EXPECT_TRUE(entry->GetPageState().IsValid());
1354 // The SiteInstance should be available for other navigations to use.
1355 NavigationEntryImpl* entry_impl =
1356 NavigationEntryImpl::FromNavigationEntry(entry);
1357 EXPECT_FALSE(entry_impl->site_instance()->HasSite());
1358 int32 site_instance_id = entry_impl->site_instance()->GetId();
1360 // Navigating to a normal page should not cause a process swap.
1361 const GURL new_url("http://www.google.com");
1362 controller().LoadURL(new_url, Referrer(),
1363 ui::PAGE_TRANSITION_TYPED, std::string());
1364 entry = controller().GetPendingEntry();
1365 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1366 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1367 orig_rfh->PrepareForCommit();
1368 contents()->TestDidNavigate(orig_rfh, 2, entry->GetUniqueID(), true, new_url,
1369 ui::PAGE_TRANSITION_TYPED);
1370 NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry(
1371 controller().GetLastCommittedEntry());
1372 EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId());
1373 EXPECT_TRUE(entry_impl2->site_instance()->HasSite());
1376 // Tests that fullscreen is exited throughout the object hierarchy when
1377 // navigating to a new page.
1378 TEST_F(WebContentsImplTest, NavigationExitsFullscreen) {
1379 FakeFullscreenDelegate fake_delegate;
1380 contents()->SetDelegate(&fake_delegate);
1381 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1382 TestRenderViewHost* orig_rvh = orig_rfh->GetRenderViewHost();
1384 // Navigate to a site.
1385 const GURL url("http://www.google.com");
1386 controller().LoadURL(
1387 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1388 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1389 contents()->GetMainFrame()->PrepareForCommit();
1390 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1391 ui::PAGE_TRANSITION_TYPED);
1392 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1394 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1395 EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
1396 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1397 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1398 orig_rfh->OnMessageReceived(
1399 FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
1400 EXPECT_TRUE(orig_rvh->IsFullscreenGranted());
1401 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1402 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1404 // Navigate to a new site.
1405 const GURL url2("http://www.yahoo.com");
1406 controller().LoadURL(
1407 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1408 entry_id = controller().GetPendingEntry()->GetUniqueID();
1409 contents()->GetMainFrame()->PrepareForCommit();
1410 TestRenderFrameHost* const pending_rfh = contents()->GetPendingMainFrame();
1411 contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2,
1412 ui::PAGE_TRANSITION_TYPED);
1414 // Confirm fullscreen has exited.
1415 EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
1416 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1417 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1419 contents()->SetDelegate(nullptr);
1422 // Tests that fullscreen is exited throughout the object hierarchy when
1423 // instructing NavigationController to GoBack() or GoForward().
1424 TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) {
1425 FakeFullscreenDelegate fake_delegate;
1426 contents()->SetDelegate(&fake_delegate);
1427 TestRenderFrameHost* const orig_rfh = contents()->GetMainFrame();
1428 TestRenderViewHost* const orig_rvh = orig_rfh->GetRenderViewHost();
1430 // Navigate to a site.
1431 const GURL url("http://www.google.com");
1432 controller().LoadURL(
1433 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1434 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1435 orig_rfh->PrepareForCommit();
1436 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url,
1437 ui::PAGE_TRANSITION_TYPED);
1438 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1440 // Now, navigate to another page on the same site.
1441 const GURL url2("http://www.google.com/search?q=kittens");
1442 controller().LoadURL(
1443 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1444 entry_id = controller().GetPendingEntry()->GetUniqueID();
1445 orig_rfh->PrepareForCommit();
1446 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1447 contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url2,
1448 ui::PAGE_TRANSITION_TYPED);
1449 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1451 // Sanity-check: Confirm we're not starting out in fullscreen mode.
1452 EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
1453 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1454 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1456 for (int i = 0; i < 2; ++i) {
1457 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1458 orig_rfh->OnMessageReceived(
1459 FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
1460 EXPECT_TRUE(orig_rvh->IsFullscreenGranted());
1461 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1462 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1464 // Navigate backward (or forward).
1465 if (i == 0)
1466 controller().GoBack();
1467 else
1468 controller().GoForward();
1469 entry_id = controller().GetPendingEntry()->GetUniqueID();
1470 orig_rfh->PrepareForCommit();
1471 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
1472 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
1473 contents()->TestDidNavigate(orig_rfh, i + 1, entry_id, false, url,
1474 ui::PAGE_TRANSITION_FORWARD_BACK);
1475 orig_rfh->SimulateNavigationStop();
1477 // Confirm fullscreen has exited.
1478 EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
1479 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1480 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1483 contents()->SetDelegate(nullptr);
1486 TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) {
1487 FakeValidationMessageDelegate fake_delegate;
1488 contents()->SetDelegate(&fake_delegate);
1489 EXPECT_FALSE(fake_delegate.hide_validation_message_was_called());
1491 // Initialize the RenderFrame and then simulate crashing the renderer
1492 // process.
1493 contents()->GetMainFrame()->InitializeRenderFrameIfNeeded();
1494 contents()->GetMainFrame()->GetProcess()->SimulateCrash();
1496 // Confirm HideValidationMessage was called.
1497 EXPECT_TRUE(fake_delegate.hide_validation_message_was_called());
1499 contents()->SetDelegate(nullptr);
1502 // Tests that fullscreen is exited throughout the object hierarchy on a renderer
1503 // crash.
1504 TEST_F(WebContentsImplTest, CrashExitsFullscreen) {
1505 FakeFullscreenDelegate fake_delegate;
1506 contents()->SetDelegate(&fake_delegate);
1508 // Navigate to a site.
1509 const GURL url("http://www.google.com");
1510 controller().LoadURL(
1511 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1512 int entry_id = controller().GetPendingEntry()->GetUniqueID();
1513 main_test_rfh()->PrepareForCommit();
1514 contents()->TestDidNavigate(contents()->GetMainFrame(), 1, entry_id, true,
1515 url, ui::PAGE_TRANSITION_TYPED);
1517 // Toggle fullscreen mode on (as if initiated via IPC from renderer).
1518 EXPECT_FALSE(test_rvh()->IsFullscreenGranted());
1519 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1520 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1521 contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_ToggleFullscreen(
1522 contents()->GetMainFrame()->GetRoutingID(), true));
1523 EXPECT_TRUE(test_rvh()->IsFullscreenGranted());
1524 EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
1525 EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1527 // Crash the renderer.
1528 main_test_rfh()->GetProcess()->SimulateCrash();
1530 // Confirm fullscreen has exited.
1531 EXPECT_FALSE(test_rvh()->IsFullscreenGranted());
1532 EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
1533 EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
1535 contents()->SetDelegate(nullptr);
1538 ////////////////////////////////////////////////////////////////////////////////
1539 // Interstitial Tests
1540 ////////////////////////////////////////////////////////////////////////////////
1542 // Test navigating to a page (with the navigation initiated from the browser,
1543 // as when a URL is typed in the location bar) that shows an interstitial and
1544 // creates a new navigation entry, then hiding it without proceeding.
1545 TEST_F(WebContentsImplTest,
1546 ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
1547 // Navigate to a page.
1548 GURL url1("http://www.google.com");
1549 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1550 EXPECT_EQ(1, controller().GetEntryCount());
1552 // Initiate a browser navigation that will trigger the interstitial.
1553 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1554 ui::PAGE_TRANSITION_TYPED, std::string());
1555 NavigationEntry* entry = controller().GetPendingEntry();
1557 // Show an interstitial.
1558 TestInterstitialPage::InterstitialState state =
1559 TestInterstitialPage::INVALID;
1560 bool deleted = false;
1561 GURL url2("http://interstitial");
1562 TestInterstitialPage* interstitial =
1563 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1564 TestInterstitialPageStateGuard state_guard(interstitial);
1565 interstitial->Show();
1566 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1567 // The interstitial should not show until its navigation has committed.
1568 EXPECT_FALSE(interstitial->is_showing());
1569 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1570 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1571 // Let's commit the interstitial navigation.
1572 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
1573 EXPECT_TRUE(interstitial->is_showing());
1574 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1575 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1576 entry = controller().GetVisibleEntry();
1577 ASSERT_NE(nullptr, entry);
1578 EXPECT_TRUE(entry->GetURL() == url2);
1580 // Now don't proceed.
1581 interstitial->DontProceed();
1582 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1583 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1584 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1585 entry = controller().GetVisibleEntry();
1586 ASSERT_NE(nullptr, entry);
1587 EXPECT_TRUE(entry->GetURL() == url1);
1588 EXPECT_EQ(1, controller().GetEntryCount());
1590 RunAllPendingInMessageLoop();
1591 EXPECT_TRUE(deleted);
1594 // Test navigating to a page (with the navigation initiated from the renderer,
1595 // as when clicking on a link in the page) that shows an interstitial and
1596 // creates a new navigation entry, then hiding it without proceeding.
1597 TEST_F(WebContentsImplTest,
1598 ShowInterstitialFromRendererWithNewNavigationDontProceed) {
1599 // Navigate to a page.
1600 GURL url1("http://www.google.com");
1601 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1602 EXPECT_EQ(1, controller().GetEntryCount());
1604 // Show an interstitial (no pending entry, the interstitial would have been
1605 // triggered by clicking on a link).
1606 TestInterstitialPage::InterstitialState state =
1607 TestInterstitialPage::INVALID;
1608 bool deleted = false;
1609 GURL url2("http://interstitial");
1610 TestInterstitialPage* interstitial =
1611 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1612 TestInterstitialPageStateGuard state_guard(interstitial);
1613 interstitial->Show();
1614 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1615 // The interstitial should not show until its navigation has committed.
1616 EXPECT_FALSE(interstitial->is_showing());
1617 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1618 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1619 // Let's commit the interstitial navigation.
1620 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
1621 EXPECT_TRUE(interstitial->is_showing());
1622 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1623 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1624 NavigationEntry* entry = controller().GetVisibleEntry();
1625 ASSERT_NE(nullptr, entry);
1626 EXPECT_TRUE(entry->GetURL() == url2);
1628 // Now don't proceed.
1629 interstitial->DontProceed();
1630 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
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() == url1);
1636 EXPECT_EQ(1, controller().GetEntryCount());
1638 RunAllPendingInMessageLoop();
1639 EXPECT_TRUE(deleted);
1642 // Test navigating to a page that shows an interstitial without creating a new
1643 // navigation entry (this happens when the interstitial is triggered by a
1644 // sub-resource in the page), then hiding it without proceeding.
1645 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) {
1646 // Navigate to a page.
1647 GURL url1("http://www.google.com");
1648 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1649 EXPECT_EQ(1, controller().GetEntryCount());
1651 // Show an interstitial.
1652 TestInterstitialPage::InterstitialState state =
1653 TestInterstitialPage::INVALID;
1654 bool deleted = false;
1655 GURL url2("http://interstitial");
1656 TestInterstitialPage* interstitial =
1657 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1658 TestInterstitialPageStateGuard state_guard(interstitial);
1659 interstitial->Show();
1660 // The interstitial should not show until its navigation has committed.
1661 EXPECT_FALSE(interstitial->is_showing());
1662 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1663 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1664 // Let's commit the interstitial navigation.
1665 interstitial->TestDidNavigate(1, 0, true, url2);
1666 EXPECT_TRUE(interstitial->is_showing());
1667 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1668 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1669 NavigationEntry* entry = controller().GetVisibleEntry();
1670 ASSERT_NE(nullptr, entry);
1671 // The URL specified to the interstitial should have been ignored.
1672 EXPECT_TRUE(entry->GetURL() == url1);
1674 // Now don't proceed.
1675 interstitial->DontProceed();
1676 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1677 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1678 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1679 entry = controller().GetVisibleEntry();
1680 ASSERT_NE(nullptr, entry);
1681 EXPECT_TRUE(entry->GetURL() == url1);
1682 EXPECT_EQ(1, controller().GetEntryCount());
1684 RunAllPendingInMessageLoop();
1685 EXPECT_TRUE(deleted);
1688 // Test navigating to a page (with the navigation initiated from the browser,
1689 // as when a URL is typed in the location bar) that shows an interstitial and
1690 // creates a new navigation entry, then proceeding.
1691 TEST_F(WebContentsImplTest,
1692 ShowInterstitialFromBrowserNewNavigationProceed) {
1693 // Navigate to a page.
1694 GURL url1("http://www.google.com");
1695 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1696 EXPECT_EQ(1, controller().GetEntryCount());
1698 // Initiate a browser navigation that will trigger the interstitial
1699 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
1700 ui::PAGE_TRANSITION_TYPED, std::string());
1702 // Show an interstitial.
1703 TestInterstitialPage::InterstitialState state =
1704 TestInterstitialPage::INVALID;
1705 bool deleted = false;
1706 GURL url2("http://interstitial");
1707 TestInterstitialPage* interstitial =
1708 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1709 TestInterstitialPageStateGuard state_guard(interstitial);
1710 interstitial->Show();
1711 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1712 // The interstitial should not show until its navigation has committed.
1713 EXPECT_FALSE(interstitial->is_showing());
1714 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1715 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1716 // Let's commit the interstitial navigation.
1717 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
1718 EXPECT_TRUE(interstitial->is_showing());
1719 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1720 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1721 NavigationEntry* entry = controller().GetVisibleEntry();
1722 ASSERT_NE(nullptr, entry);
1723 EXPECT_TRUE(entry->GetURL() == url2);
1725 // Then proceed.
1726 interstitial->Proceed();
1727 // The interstitial should show until the new navigation commits.
1728 RunAllPendingInMessageLoop();
1729 ASSERT_FALSE(deleted);
1730 EXPECT_EQ(TestInterstitialPage::OKED, state);
1731 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1732 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1734 // Simulate the navigation to the page, that's when the interstitial gets
1735 // hidden.
1736 GURL url3("http://www.thepage.com");
1737 contents()->GetMainFrame()->PrepareForCommit();
1738 contents()->GetMainFrame()->SendNavigate(2, 0, true, url3);
1740 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1741 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1742 entry = controller().GetVisibleEntry();
1743 ASSERT_NE(nullptr, entry);
1744 EXPECT_TRUE(entry->GetURL() == url3);
1746 EXPECT_EQ(2, controller().GetEntryCount());
1748 RunAllPendingInMessageLoop();
1749 EXPECT_TRUE(deleted);
1752 // Test navigating to a page (with the navigation initiated from the renderer,
1753 // as when clicking on a link in the page) that shows an interstitial and
1754 // creates a new navigation entry, then proceeding.
1755 TEST_F(WebContentsImplTest,
1756 ShowInterstitialFromRendererNewNavigationProceed) {
1757 // Navigate to a page.
1758 GURL url1("http://www.google.com");
1759 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1760 EXPECT_EQ(1, controller().GetEntryCount());
1762 // Show an interstitial.
1763 TestInterstitialPage::InterstitialState state =
1764 TestInterstitialPage::INVALID;
1765 bool deleted = false;
1766 GURL url2("http://interstitial");
1767 TestInterstitialPage* interstitial =
1768 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
1769 TestInterstitialPageStateGuard state_guard(interstitial);
1770 interstitial->Show();
1771 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1772 // The interstitial should not show until its navigation has committed.
1773 EXPECT_FALSE(interstitial->is_showing());
1774 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1775 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1776 // Let's commit the interstitial navigation.
1777 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
1778 EXPECT_TRUE(interstitial->is_showing());
1779 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1780 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1781 NavigationEntry* entry = controller().GetVisibleEntry();
1782 ASSERT_NE(nullptr, entry);
1783 EXPECT_TRUE(entry->GetURL() == url2);
1785 // Then proceed.
1786 interstitial->Proceed();
1787 // The interstitial should show until the new navigation commits.
1788 RunAllPendingInMessageLoop();
1789 ASSERT_FALSE(deleted);
1790 EXPECT_EQ(TestInterstitialPage::OKED, state);
1791 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1792 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1794 // Simulate the navigation to the page, that's when the interstitial gets
1795 // hidden.
1796 GURL url3("http://www.thepage.com");
1797 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url3);
1799 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1800 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1801 entry = controller().GetVisibleEntry();
1802 ASSERT_NE(nullptr, entry);
1803 EXPECT_TRUE(entry->GetURL() == url3);
1805 EXPECT_EQ(2, controller().GetEntryCount());
1807 RunAllPendingInMessageLoop();
1808 EXPECT_TRUE(deleted);
1811 // Test navigating to a page that shows an interstitial without creating a new
1812 // navigation entry (this happens when the interstitial is triggered by a
1813 // sub-resource in the page), then proceeding.
1814 TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) {
1815 // Navigate to a page so we have a navigation entry in the controller.
1816 GURL url1("http://www.google.com");
1817 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1818 EXPECT_EQ(1, controller().GetEntryCount());
1820 // Show an interstitial.
1821 TestInterstitialPage::InterstitialState state =
1822 TestInterstitialPage::INVALID;
1823 bool deleted = false;
1824 GURL url2("http://interstitial");
1825 TestInterstitialPage* interstitial =
1826 new TestInterstitialPage(contents(), false, url2, &state, &deleted);
1827 TestInterstitialPageStateGuard state_guard(interstitial);
1828 interstitial->Show();
1829 // The interstitial should not show until its navigation has committed.
1830 EXPECT_FALSE(interstitial->is_showing());
1831 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1832 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1833 // Let's commit the interstitial navigation.
1834 interstitial->TestDidNavigate(1, 0, true, url2);
1835 EXPECT_TRUE(interstitial->is_showing());
1836 EXPECT_TRUE(contents()->ShowingInterstitialPage());
1837 EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial);
1838 NavigationEntry* entry = controller().GetVisibleEntry();
1839 ASSERT_NE(nullptr, entry);
1840 // The URL specified to the interstitial should have been ignored.
1841 EXPECT_TRUE(entry->GetURL() == url1);
1843 // Then proceed.
1844 interstitial->Proceed();
1845 // Since this is not a new navigation, the previous page is dismissed right
1846 // away and shows the original page.
1847 EXPECT_EQ(TestInterstitialPage::OKED, state);
1848 EXPECT_FALSE(contents()->ShowingInterstitialPage());
1849 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
1850 entry = controller().GetVisibleEntry();
1851 ASSERT_NE(nullptr, entry);
1852 EXPECT_TRUE(entry->GetURL() == url1);
1854 EXPECT_EQ(1, controller().GetEntryCount());
1856 RunAllPendingInMessageLoop();
1857 EXPECT_TRUE(deleted);
1860 // Test navigating to a page that shows an interstitial, then navigating away.
1861 TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) {
1862 // Show interstitial.
1863 TestInterstitialPage::InterstitialState state =
1864 TestInterstitialPage::INVALID;
1865 bool deleted = false;
1866 GURL url("http://interstitial");
1867 TestInterstitialPage* interstitial =
1868 new TestInterstitialPage(contents(), true, url, &state, &deleted);
1869 TestInterstitialPageStateGuard state_guard(interstitial);
1870 interstitial->Show();
1871 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1872 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url);
1874 // While interstitial showing, navigate to a new URL.
1875 const GURL url2("http://www.yahoo.com");
1876 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2);
1878 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1880 RunAllPendingInMessageLoop();
1881 EXPECT_TRUE(deleted);
1884 // Test navigating to a page that shows an interstitial, then going back.
1885 TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) {
1886 // Navigate to a page so we have a navigation entry in the controller.
1887 GURL url1("http://www.google.com");
1888 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1889 EXPECT_EQ(1, controller().GetEntryCount());
1891 // Show interstitial.
1892 TestInterstitialPage::InterstitialState state =
1893 TestInterstitialPage::INVALID;
1894 bool deleted = false;
1895 GURL interstitial_url("http://interstitial");
1896 TestInterstitialPage* interstitial =
1897 new TestInterstitialPage(contents(), true, interstitial_url,
1898 &state, &deleted);
1899 TestInterstitialPageStateGuard state_guard(interstitial);
1900 interstitial->Show();
1901 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1902 interstitial->TestDidNavigate(2, interstitial_entry_id, true,
1903 interstitial_url);
1904 EXPECT_EQ(2, controller().GetEntryCount());
1906 // While the interstitial is showing, go back. This will dismiss the
1907 // interstitial and not initiate a navigation, but just show the existing
1908 // RenderFrameHost.
1909 controller().GoBack();
1911 // Make sure we are back to the original page and that the interstitial is
1912 // gone.
1913 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1914 NavigationEntry* entry = controller().GetVisibleEntry();
1915 ASSERT_TRUE(entry);
1916 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1917 EXPECT_EQ(1, controller().GetEntryCount());
1919 RunAllPendingInMessageLoop();
1920 EXPECT_TRUE(deleted);
1923 // Test navigating to a page that shows an interstitial, has a renderer crash,
1924 // and then goes back.
1925 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) {
1926 // Navigate to a page so we have a navigation entry in the controller.
1927 GURL url1("http://www.google.com");
1928 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1929 EXPECT_EQ(1, controller().GetEntryCount());
1930 NavigationEntry* entry = controller().GetLastCommittedEntry();
1932 // Show interstitial.
1933 TestInterstitialPage::InterstitialState state =
1934 TestInterstitialPage::INVALID;
1935 bool deleted = false;
1936 GURL interstitial_url("http://interstitial");
1937 TestInterstitialPage* interstitial =
1938 new TestInterstitialPage(contents(), true, interstitial_url,
1939 &state, &deleted);
1940 TestInterstitialPageStateGuard state_guard(interstitial);
1941 interstitial->Show();
1942 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1943 interstitial->TestDidNavigate(2, interstitial_entry_id, true,
1944 interstitial_url);
1946 // Crash the renderer
1947 contents()->GetMainFrame()->GetProcess()->SimulateCrash();
1949 // While the interstitial is showing, go back. This will dismiss the
1950 // interstitial and not initiate a navigation, but just show the existing
1951 // RenderFrameHost.
1952 controller().GoBack();
1954 // Make sure we are back to the original page and that the interstitial is
1955 // gone.
1956 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
1957 entry = controller().GetVisibleEntry();
1958 ASSERT_TRUE(entry);
1959 EXPECT_EQ(url1.spec(), entry->GetURL().spec());
1961 RunAllPendingInMessageLoop();
1962 EXPECT_TRUE(deleted);
1965 // Test navigating to a page that shows an interstitial, has the renderer crash,
1966 // and then navigates to the interstitial.
1967 TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) {
1968 // Navigate to a page so we have a navigation entry in the controller.
1969 GURL url1("http://www.google.com");
1970 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
1971 EXPECT_EQ(1, controller().GetEntryCount());
1973 // Show interstitial.
1974 TestInterstitialPage::InterstitialState state =
1975 TestInterstitialPage::INVALID;
1976 bool deleted = false;
1977 GURL interstitial_url("http://interstitial");
1978 TestInterstitialPage* interstitial =
1979 new TestInterstitialPage(contents(), true, interstitial_url,
1980 &state, &deleted);
1981 TestInterstitialPageStateGuard state_guard(interstitial);
1982 interstitial->Show();
1983 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
1985 // Crash the renderer
1986 contents()->GetMainFrame()->GetProcess()->SimulateCrash();
1988 interstitial->TestDidNavigate(2, interstitial_entry_id, true,
1989 interstitial_url);
1992 // Test navigating to a page that shows an interstitial, then close the
1993 // contents.
1994 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) {
1995 // Show interstitial.
1996 TestInterstitialPage::InterstitialState state =
1997 TestInterstitialPage::INVALID;
1998 bool deleted = false;
1999 GURL url("http://interstitial");
2000 TestInterstitialPage* interstitial =
2001 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2002 TestInterstitialPageStateGuard state_guard(interstitial);
2003 interstitial->Show();
2004 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2005 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url);
2007 // Now close the contents.
2008 DeleteContents();
2009 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2011 RunAllPendingInMessageLoop();
2012 EXPECT_TRUE(deleted);
2015 // Test navigating to a page that shows an interstitial, then close the
2016 // contents.
2017 TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) {
2018 // Show interstitial.
2019 TestInterstitialPage::InterstitialState state =
2020 TestInterstitialPage::INVALID;
2021 bool deleted = false;
2022 GURL url("http://interstitial");
2023 TestInterstitialPage* interstitial =
2024 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2025 TestInterstitialPageStateGuard state_guard(interstitial);
2026 interstitial->Show();
2027 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2028 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url);
2029 TestRenderFrameHost* rfh =
2030 static_cast<TestRenderFrameHost*>(interstitial->GetMainFrame());
2032 // Now close the contents.
2033 DeleteContents();
2034 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2036 // Before the interstitial has a chance to process its shutdown task,
2037 // simulate quitting the browser. This goes through all processes and
2038 // tells them to destruct.
2039 rfh->GetProcess()->SimulateCrash();
2041 RunAllPendingInMessageLoop();
2042 EXPECT_TRUE(deleted);
2045 // Test that after Proceed is called and an interstitial is still shown, no more
2046 // commands get executed.
2047 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
2048 // Navigate to a page so we have a navigation entry in the controller.
2049 GURL url1("http://www.google.com");
2050 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
2051 EXPECT_EQ(1, controller().GetEntryCount());
2053 // Show an interstitial.
2054 TestInterstitialPage::InterstitialState state =
2055 TestInterstitialPage::INVALID;
2056 bool deleted = false;
2057 GURL url2("http://interstitial");
2058 TestInterstitialPage* interstitial =
2059 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2060 TestInterstitialPageStateGuard state_guard(interstitial);
2061 interstitial->Show();
2062 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2063 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
2065 // Run a command.
2066 EXPECT_EQ(0, interstitial->command_received_count());
2067 interstitial->TestDomOperationResponse("toto");
2068 EXPECT_EQ(1, interstitial->command_received_count());
2070 // Then proceed.
2071 interstitial->Proceed();
2072 RunAllPendingInMessageLoop();
2073 ASSERT_FALSE(deleted);
2075 // While the navigation to the new page is pending, send other commands, they
2076 // should be ignored.
2077 interstitial->TestDomOperationResponse("hello");
2078 interstitial->TestDomOperationResponse("hi");
2079 EXPECT_EQ(1, interstitial->command_received_count());
2082 // Test showing an interstitial while another interstitial is already showing.
2083 TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) {
2084 // Navigate to a page so we have a navigation entry in the controller.
2085 GURL start_url("http://www.google.com");
2086 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, start_url);
2087 EXPECT_EQ(1, controller().GetEntryCount());
2089 // Show an interstitial.
2090 TestInterstitialPage::InterstitialState state1 =
2091 TestInterstitialPage::INVALID;
2092 bool deleted1 = false;
2093 GURL url1("http://interstitial1");
2094 TestInterstitialPage* interstitial1 =
2095 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2096 TestInterstitialPageStateGuard state_guard1(interstitial1);
2097 interstitial1->Show();
2098 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2099 interstitial1->TestDidNavigate(1, interstitial_entry_id, true, url1);
2101 // Now show another interstitial.
2102 TestInterstitialPage::InterstitialState state2 =
2103 TestInterstitialPage::INVALID;
2104 bool deleted2 = false;
2105 GURL url2("http://interstitial2");
2106 TestInterstitialPage* interstitial2 =
2107 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2108 TestInterstitialPageStateGuard state_guard2(interstitial2);
2109 interstitial2->Show();
2110 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2111 interstitial2->TestDidNavigate(1, interstitial_entry_id, true, url2);
2113 // Showing interstitial2 should have caused interstitial1 to go away.
2114 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2115 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2117 RunAllPendingInMessageLoop();
2118 EXPECT_TRUE(deleted1);
2119 ASSERT_FALSE(deleted2);
2121 // Let's make sure interstitial2 is working as intended.
2122 interstitial2->Proceed();
2123 GURL landing_url("http://www.thepage.com");
2124 contents()->GetMainFrame()->SendNavigate(2, 0, true, landing_url);
2126 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2127 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2128 NavigationEntry* entry = controller().GetVisibleEntry();
2129 ASSERT_NE(nullptr, entry);
2130 EXPECT_TRUE(entry->GetURL() == landing_url);
2131 EXPECT_EQ(2, controller().GetEntryCount());
2132 RunAllPendingInMessageLoop();
2133 EXPECT_TRUE(deleted2);
2136 // Test showing an interstitial, proceeding and then navigating to another
2137 // interstitial.
2138 TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) {
2139 // Navigate to a page so we have a navigation entry in the controller.
2140 GURL start_url("http://www.google.com");
2141 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, start_url);
2142 EXPECT_EQ(1, controller().GetEntryCount());
2144 // Show an interstitial.
2145 TestInterstitialPage::InterstitialState state1 =
2146 TestInterstitialPage::INVALID;
2147 bool deleted1 = false;
2148 GURL url1("http://interstitial1");
2149 TestInterstitialPage* interstitial1 =
2150 new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
2151 TestInterstitialPageStateGuard state_guard1(interstitial1);
2152 interstitial1->Show();
2153 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2154 interstitial1->TestDidNavigate(1, interstitial_entry_id, true, url1);
2156 // Take action. The interstitial won't be hidden until the navigation is
2157 // committed.
2158 interstitial1->Proceed();
2159 EXPECT_EQ(TestInterstitialPage::OKED, state1);
2161 // Now show another interstitial (simulating the navigation causing another
2162 // interstitial).
2163 TestInterstitialPage::InterstitialState state2 =
2164 TestInterstitialPage::INVALID;
2165 bool deleted2 = false;
2166 GURL url2("http://interstitial2");
2167 TestInterstitialPage* interstitial2 =
2168 new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
2169 TestInterstitialPageStateGuard state_guard2(interstitial2);
2170 interstitial2->Show();
2171 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2172 interstitial2->TestDidNavigate(1, interstitial_entry_id, true, url2);
2174 // Showing interstitial2 should have caused interstitial1 to go away.
2175 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2176 RunAllPendingInMessageLoop();
2177 EXPECT_TRUE(deleted1);
2178 ASSERT_FALSE(deleted2);
2180 // Let's make sure interstitial2 is working as intended.
2181 interstitial2->Proceed();
2182 GURL landing_url("http://www.thepage.com");
2183 contents()->GetMainFrame()->SendNavigate(2, 0, true, landing_url);
2185 RunAllPendingInMessageLoop();
2186 EXPECT_TRUE(deleted2);
2187 EXPECT_FALSE(contents()->ShowingInterstitialPage());
2188 EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
2189 NavigationEntry* entry = controller().GetVisibleEntry();
2190 ASSERT_NE(nullptr, entry);
2191 EXPECT_TRUE(entry->GetURL() == landing_url);
2192 EXPECT_EQ(2, controller().GetEntryCount());
2195 // Test that navigating away from an interstitial while it's loading cause it
2196 // not to show.
2197 TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) {
2198 // Show an interstitial.
2199 TestInterstitialPage::InterstitialState state =
2200 TestInterstitialPage::INVALID;
2201 bool deleted = false;
2202 GURL interstitial_url("http://interstitial");
2203 TestInterstitialPage* interstitial =
2204 new TestInterstitialPage(contents(), true, interstitial_url,
2205 &state, &deleted);
2206 TestInterstitialPageStateGuard state_guard(interstitial);
2207 interstitial->Show();
2208 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2210 // Let's simulate a navigation initiated from the browser before the
2211 // interstitial finishes loading.
2212 const GURL url("http://www.google.com");
2213 controller().LoadURL(
2214 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2215 EXPECT_FALSE(interstitial->is_showing());
2216 RunAllPendingInMessageLoop();
2217 ASSERT_FALSE(deleted);
2219 // Now let's make the interstitial navigation commit.
2220 interstitial->TestDidNavigate(1, interstitial_entry_id, true,
2221 interstitial_url);
2223 // After it loaded the interstitial should be gone.
2224 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2226 RunAllPendingInMessageLoop();
2227 EXPECT_TRUE(deleted);
2230 // Test that a new request to show an interstitial while an interstitial is
2231 // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442.
2232 TEST_F(WebContentsImplTest, TwoQuickInterstitials) {
2233 GURL interstitial_url("http://interstitial");
2235 // Show a first interstitial.
2236 TestInterstitialPage::InterstitialState state1 =
2237 TestInterstitialPage::INVALID;
2238 bool deleted1 = false;
2239 TestInterstitialPage* interstitial1 =
2240 new TestInterstitialPage(contents(), true, interstitial_url,
2241 &state1, &deleted1);
2242 TestInterstitialPageStateGuard state_guard1(interstitial1);
2243 interstitial1->Show();
2245 // Show another interstitial on that same contents before the first one had
2246 // time to load.
2247 TestInterstitialPage::InterstitialState state2 =
2248 TestInterstitialPage::INVALID;
2249 bool deleted2 = false;
2250 TestInterstitialPage* interstitial2 =
2251 new TestInterstitialPage(contents(), true, interstitial_url,
2252 &state2, &deleted2);
2253 TestInterstitialPageStateGuard state_guard2(interstitial2);
2254 interstitial2->Show();
2255 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2257 // The first interstitial should have been closed and deleted.
2258 EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
2259 // The 2nd one should still be OK.
2260 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2262 RunAllPendingInMessageLoop();
2263 EXPECT_TRUE(deleted1);
2264 ASSERT_FALSE(deleted2);
2266 // Make the interstitial navigation commit it should be showing.
2267 interstitial2->TestDidNavigate(1, interstitial_entry_id, true,
2268 interstitial_url);
2269 EXPECT_EQ(interstitial2, contents()->GetInterstitialPage());
2272 // Test showing an interstitial and have its renderer crash.
2273 TEST_F(WebContentsImplTest, InterstitialCrasher) {
2274 // Show an interstitial.
2275 TestInterstitialPage::InterstitialState state =
2276 TestInterstitialPage::INVALID;
2277 bool deleted = false;
2278 GURL url("http://interstitial");
2279 TestInterstitialPage* interstitial =
2280 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2281 TestInterstitialPageStateGuard state_guard(interstitial);
2282 interstitial->Show();
2283 // Simulate a renderer crash before the interstitial is shown.
2284 interstitial->TestRenderViewTerminated(
2285 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2286 // The interstitial should have been dismissed.
2287 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2288 RunAllPendingInMessageLoop();
2289 EXPECT_TRUE(deleted);
2291 // Now try again but this time crash the intersitial after it was shown.
2292 interstitial =
2293 new TestInterstitialPage(contents(), true, url, &state, &deleted);
2294 interstitial->Show();
2295 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2296 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url);
2297 // Simulate a renderer crash.
2298 interstitial->TestRenderViewTerminated(
2299 base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
2300 // The interstitial should have been dismissed.
2301 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2302 RunAllPendingInMessageLoop();
2303 EXPECT_TRUE(deleted);
2306 // Tests that showing an interstitial as a result of a browser initiated
2307 // navigation while an interstitial is showing does not remove the pending
2308 // entry (see http://crbug.com/9791).
2309 TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) {
2310 const char kUrl[] = "http://www.badguys.com/";
2311 const GURL kGURL(kUrl);
2313 // Start a navigation to a page
2314 contents()->GetController().LoadURL(
2315 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2317 // Simulate that navigation triggering an interstitial.
2318 TestInterstitialPage::InterstitialState state =
2319 TestInterstitialPage::INVALID;
2320 bool deleted = false;
2321 TestInterstitialPage* interstitial =
2322 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2323 TestInterstitialPageStateGuard state_guard(interstitial);
2324 interstitial->Show();
2325 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2326 interstitial->TestDidNavigate(1, interstitial_entry_id, true, kGURL);
2328 // Initiate a new navigation from the browser that also triggers an
2329 // interstitial.
2330 contents()->GetController().LoadURL(
2331 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2332 TestInterstitialPage::InterstitialState state2 =
2333 TestInterstitialPage::INVALID;
2334 bool deleted2 = false;
2335 TestInterstitialPage* interstitial2 =
2336 new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2);
2337 TestInterstitialPageStateGuard state_guard2(interstitial2);
2338 interstitial2->Show();
2339 interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2340 interstitial2->TestDidNavigate(1, interstitial_entry_id, true, kGURL);
2342 // Make sure we still have an entry.
2343 NavigationEntry* entry = contents()->GetController().GetPendingEntry();
2344 ASSERT_TRUE(entry);
2345 EXPECT_EQ(kUrl, entry->GetURL().spec());
2347 // And that the first interstitial is gone, but not the second.
2348 EXPECT_EQ(TestInterstitialPage::CANCELED, state);
2349 EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
2350 RunAllPendingInMessageLoop();
2351 EXPECT_TRUE(deleted);
2352 EXPECT_FALSE(deleted2);
2355 // Tests that Javascript messages are not shown while an interstitial is
2356 // showing.
2357 TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) {
2358 const char kUrl[] = "http://www.badguys.com/";
2359 const GURL kGURL(kUrl);
2361 // Start a navigation to a page
2362 contents()->GetController().LoadURL(
2363 kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2364 int entry_id = controller().GetPendingEntry()->GetUniqueID();
2365 main_test_rfh()->PrepareForCommit();
2366 // DidNavigate from the page
2367 contents()->TestDidNavigate(contents()->GetMainFrame(), 1, entry_id, true,
2368 kGURL, ui::PAGE_TRANSITION_TYPED);
2370 // Simulate showing an interstitial while the page is showing.
2371 TestInterstitialPage::InterstitialState state =
2372 TestInterstitialPage::INVALID;
2373 bool deleted = false;
2374 TestInterstitialPage* interstitial =
2375 new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
2376 TestInterstitialPageStateGuard state_guard(interstitial);
2377 interstitial->Show();
2378 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2379 interstitial->TestDidNavigate(1, interstitial_entry_id, true, kGURL);
2381 // While the interstitial is showing, let's simulate the hidden page
2382 // attempting to show a JS message.
2383 IPC::Message* dummy_message = new IPC::Message;
2384 contents()->RunJavaScriptMessage(contents()->GetMainFrame(),
2385 base::ASCIIToUTF16("This is an informative message"),
2386 base::ASCIIToUTF16("OK"),
2387 kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message);
2388 EXPECT_TRUE(contents()->last_dialog_suppressed_);
2391 // Makes sure that if the source passed to CopyStateFromAndPrune has an
2392 // interstitial it isn't copied over to the destination.
2393 TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) {
2394 // Navigate to a page.
2395 GURL url1("http://www.google.com");
2396 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
2397 EXPECT_EQ(1, controller().GetEntryCount());
2399 // Initiate a browser navigation that will trigger the interstitial
2400 controller().LoadURL(GURL("http://www.evil.com"), Referrer(),
2401 ui::PAGE_TRANSITION_TYPED, std::string());
2403 // Show an interstitial.
2404 TestInterstitialPage::InterstitialState state =
2405 TestInterstitialPage::INVALID;
2406 bool deleted = false;
2407 GURL url2("http://interstitial");
2408 TestInterstitialPage* interstitial =
2409 new TestInterstitialPage(contents(), true, url2, &state, &deleted);
2410 TestInterstitialPageStateGuard state_guard(interstitial);
2411 interstitial->Show();
2412 int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID();
2413 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2);
2414 EXPECT_TRUE(interstitial->is_showing());
2415 EXPECT_EQ(2, controller().GetEntryCount());
2417 // Create another NavigationController.
2418 GURL url3("http://foo2");
2419 scoped_ptr<TestWebContents> other_contents(
2420 static_cast<TestWebContents*>(CreateTestWebContents()));
2421 NavigationControllerImpl& other_controller = other_contents->GetController();
2422 other_contents->NavigateAndCommit(url3);
2423 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
2424 other_controller.CopyStateFromAndPrune(&controller(), false);
2426 // The merged controller should only have two entries: url1 and url2.
2427 ASSERT_EQ(2, other_controller.GetEntryCount());
2428 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
2429 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
2430 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
2432 // And the merged controller shouldn't be showing an interstitial.
2433 EXPECT_FALSE(other_contents->ShowingInterstitialPage());
2436 // Makes sure that CopyStateFromAndPrune cannot be called if the target is
2437 // showing an interstitial.
2438 TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) {
2439 // Navigate to a page.
2440 GURL url1("http://www.google.com");
2441 contents()->NavigateAndCommit(url1);
2443 // Create another NavigationController.
2444 scoped_ptr<TestWebContents> other_contents(
2445 static_cast<TestWebContents*>(CreateTestWebContents()));
2446 NavigationControllerImpl& other_controller = other_contents->GetController();
2448 // Navigate it to url2.
2449 GURL url2("http://foo2");
2450 other_contents->NavigateAndCommit(url2);
2452 // Show an interstitial.
2453 TestInterstitialPage::InterstitialState state =
2454 TestInterstitialPage::INVALID;
2455 bool deleted = false;
2456 GURL url3("http://interstitial");
2457 TestInterstitialPage* interstitial =
2458 new TestInterstitialPage(other_contents.get(), true, url3, &state,
2459 &deleted);
2460 TestInterstitialPageStateGuard state_guard(interstitial);
2461 interstitial->Show();
2462 int interstitial_entry_id =
2463 other_controller.GetTransientEntry()->GetUniqueID();
2464 interstitial->TestDidNavigate(1, interstitial_entry_id, true, url3);
2465 EXPECT_TRUE(interstitial->is_showing());
2466 EXPECT_EQ(2, other_controller.GetEntryCount());
2468 // Ensure that we do not allow calling CopyStateFromAndPrune when an
2469 // interstitial is showing in the target.
2470 EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted());
2473 // Regression test for http://crbug.com/168611 - the URLs passed by the
2474 // DidFinishLoad and DidFailLoadWithError IPCs should get filtered.
2475 TEST_F(WebContentsImplTest, FilterURLs) {
2476 TestWebContentsObserver observer(contents());
2478 // A navigation to about:whatever should always look like a navigation to
2479 // about:blank
2480 GURL url_normalized(url::kAboutBlankURL);
2481 GURL url_from_ipc("about:whatever");
2483 // We navigate the test WebContents to about:blank, since NavigateAndCommit
2484 // will use the given URL to create the NavigationEntry as well, and that
2485 // entry should contain the filtered URL.
2486 contents()->NavigateAndCommit(url_normalized);
2488 // Check that an IPC with about:whatever is correctly normalized.
2489 contents()->TestDidFinishLoad(url_from_ipc);
2491 EXPECT_EQ(url_normalized, observer.last_url());
2493 // Create and navigate another WebContents.
2494 scoped_ptr<TestWebContents> other_contents(
2495 static_cast<TestWebContents*>(CreateTestWebContents()));
2496 TestWebContentsObserver other_observer(other_contents.get());
2497 other_contents->NavigateAndCommit(url_normalized);
2499 // Check that an IPC with about:whatever is correctly normalized.
2500 other_contents->TestDidFailLoadWithError(
2501 url_from_ipc, 1, base::string16(), false);
2502 EXPECT_EQ(url_normalized, other_observer.last_url());
2505 // Test that if a pending contents is deleted before it is shown, we don't
2506 // crash.
2507 TEST_F(WebContentsImplTest, PendingContents) {
2508 scoped_ptr<TestWebContents> other_contents(
2509 static_cast<TestWebContents*>(CreateTestWebContents()));
2510 contents()->AddPendingContents(other_contents.get());
2511 int route_id = other_contents->GetRenderViewHost()->GetRoutingID();
2512 other_contents.reset();
2513 EXPECT_EQ(nullptr, contents()->GetCreatedWindow(route_id));
2516 TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) {
2517 const gfx::Size original_preferred_size(1024, 768);
2518 contents()->UpdatePreferredSize(original_preferred_size);
2520 // With no capturers, expect the preferred size to be the one propagated into
2521 // WebContentsImpl via the RenderViewHostDelegate interface.
2522 EXPECT_EQ(contents()->GetCapturerCount(), 0);
2523 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2525 // Increment capturer count, but without specifying a capture size. Expect
2526 // a "not set" preferred size.
2527 contents()->IncrementCapturerCount(gfx::Size());
2528 EXPECT_EQ(1, contents()->GetCapturerCount());
2529 EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize());
2531 // Increment capturer count again, but with an overriding capture size.
2532 // Expect preferred size to now be overridden to the capture size.
2533 const gfx::Size capture_size(1280, 720);
2534 contents()->IncrementCapturerCount(capture_size);
2535 EXPECT_EQ(2, contents()->GetCapturerCount());
2536 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2538 // Increment capturer count a third time, but the expect that the preferred
2539 // size is still the first capture size.
2540 const gfx::Size another_capture_size(720, 480);
2541 contents()->IncrementCapturerCount(another_capture_size);
2542 EXPECT_EQ(3, contents()->GetCapturerCount());
2543 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2545 // Decrement capturer count twice, but expect the preferred size to still be
2546 // overridden.
2547 contents()->DecrementCapturerCount();
2548 contents()->DecrementCapturerCount();
2549 EXPECT_EQ(1, contents()->GetCapturerCount());
2550 EXPECT_EQ(capture_size, contents()->GetPreferredSize());
2552 // Decrement capturer count, and since the count has dropped to zero, the
2553 // original preferred size should be restored.
2554 contents()->DecrementCapturerCount();
2555 EXPECT_EQ(0, contents()->GetCapturerCount());
2556 EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize());
2559 TEST_F(WebContentsImplTest, CapturerPreventsHiding) {
2560 const gfx::Size original_preferred_size(1024, 768);
2561 contents()->UpdatePreferredSize(original_preferred_size);
2563 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2564 contents()->GetMainFrame()->GetRenderViewHost()->GetView());
2566 // With no capturers, setting and un-setting occlusion should change the
2567 // view's occlusion state.
2568 EXPECT_FALSE(view->is_showing());
2569 contents()->WasShown();
2570 EXPECT_TRUE(view->is_showing());
2571 contents()->WasHidden();
2572 EXPECT_FALSE(view->is_showing());
2573 contents()->WasShown();
2574 EXPECT_TRUE(view->is_showing());
2576 // Add a capturer and try to hide the contents. The view will remain visible.
2577 contents()->IncrementCapturerCount(gfx::Size());
2578 contents()->WasHidden();
2579 EXPECT_TRUE(view->is_showing());
2581 // Remove the capturer, and the WasHidden should take effect.
2582 contents()->DecrementCapturerCount();
2583 EXPECT_FALSE(view->is_showing());
2586 TEST_F(WebContentsImplTest, CapturerPreventsOcclusion) {
2587 const gfx::Size original_preferred_size(1024, 768);
2588 contents()->UpdatePreferredSize(original_preferred_size);
2590 TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>(
2591 contents()->GetMainFrame()->GetRenderViewHost()->GetView());
2593 // With no capturers, setting and un-setting occlusion should change the
2594 // view's occlusion state.
2595 EXPECT_FALSE(view->is_occluded());
2596 contents()->WasOccluded();
2597 EXPECT_TRUE(view->is_occluded());
2598 contents()->WasUnOccluded();
2599 EXPECT_FALSE(view->is_occluded());
2600 contents()->WasOccluded();
2601 EXPECT_TRUE(view->is_occluded());
2603 // Add a capturer. This should cause the view to be un-occluded.
2604 contents()->IncrementCapturerCount(gfx::Size());
2605 EXPECT_FALSE(view->is_occluded());
2607 // Try to occlude the view. This will fail to propagate because of the
2608 // active capturer.
2609 contents()->WasOccluded();
2610 EXPECT_FALSE(view->is_occluded());
2612 // Remove the capturer and try again.
2613 contents()->DecrementCapturerCount();
2614 EXPECT_FALSE(view->is_occluded());
2615 contents()->WasOccluded();
2616 EXPECT_TRUE(view->is_occluded());
2619 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
2620 // on activity.
2621 TEST_F(WebContentsImplTest, GetLastActiveTime) {
2622 // The WebContents starts with a valid creation time.
2623 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2625 // Reset the last active time to a known-bad value.
2626 contents()->last_active_time_ = base::TimeTicks();
2627 ASSERT_TRUE(contents()->GetLastActiveTime().is_null());
2629 // Simulate activating the WebContents. The active time should update.
2630 contents()->WasShown();
2631 EXPECT_FALSE(contents()->GetLastActiveTime().is_null());
2634 class ContentsZoomChangedDelegate : public WebContentsDelegate {
2635 public:
2636 ContentsZoomChangedDelegate() :
2637 contents_zoom_changed_call_count_(0),
2638 last_zoom_in_(false) {
2641 int GetAndResetContentsZoomChangedCallCount() {
2642 int count = contents_zoom_changed_call_count_;
2643 contents_zoom_changed_call_count_ = 0;
2644 return count;
2647 bool last_zoom_in() const {
2648 return last_zoom_in_;
2651 // WebContentsDelegate:
2652 void ContentsZoomChange(bool zoom_in) override {
2653 contents_zoom_changed_call_count_++;
2654 last_zoom_in_ = zoom_in;
2657 private:
2658 int contents_zoom_changed_call_count_;
2659 bool last_zoom_in_;
2661 DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate);
2664 // Tests that some mouseehweel events get turned into browser zoom requests.
2665 TEST_F(WebContentsImplTest, HandleWheelEvent) {
2666 using blink::WebInputEvent;
2668 scoped_ptr<ContentsZoomChangedDelegate> delegate(
2669 new ContentsZoomChangedDelegate());
2670 contents()->SetDelegate(delegate.get());
2672 int modifiers = 0;
2673 // Verify that normal mouse wheel events do nothing to change the zoom level.
2674 blink::WebMouseWheelEvent event =
2675 SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2676 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2677 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2679 modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey |
2680 WebInputEvent::ControlKey;
2681 event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2682 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2683 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2685 // But whenever the ctrl modifier is applied with canScroll=false, they can
2686 // increase/decrease zoom. Except on MacOS where we never want to adjust zoom
2687 // with mousewheel.
2688 modifiers = WebInputEvent::ControlKey;
2689 event = SyntheticWebMouseWheelEventBuilder::Build(0, 1, modifiers, false);
2690 event.canScroll = false;
2691 bool handled = contents()->HandleWheelEvent(event);
2692 #if defined(OS_MACOSX)
2693 EXPECT_FALSE(handled);
2694 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2695 #else
2696 EXPECT_TRUE(handled);
2697 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2698 EXPECT_TRUE(delegate->last_zoom_in());
2699 #endif
2701 modifiers = WebInputEvent::ControlKey | WebInputEvent::ShiftKey |
2702 WebInputEvent::AltKey;
2703 event = SyntheticWebMouseWheelEventBuilder::Build(2, -5, modifiers, false);
2704 event.canScroll = false;
2705 handled = contents()->HandleWheelEvent(event);
2706 #if defined(OS_MACOSX)
2707 EXPECT_FALSE(handled);
2708 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2709 #else
2710 EXPECT_TRUE(handled);
2711 EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount());
2712 EXPECT_FALSE(delegate->last_zoom_in());
2713 #endif
2715 // Unless there is no vertical movement.
2716 event = SyntheticWebMouseWheelEventBuilder::Build(2, 0, modifiers, false);
2717 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2718 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2720 // Events containing precise scrolling deltas also shouldn't result in the
2721 // zoom being adjusted, to avoid accidental adjustments caused by
2722 // two-finger-scrolling on a touchpad.
2723 modifiers = WebInputEvent::ControlKey;
2724 event = SyntheticWebMouseWheelEventBuilder::Build(0, 5, modifiers, true);
2725 EXPECT_FALSE(contents()->HandleWheelEvent(event));
2726 EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount());
2728 // Ensure pointers to the delegate aren't kept beyond its lifetime.
2729 contents()->SetDelegate(nullptr);
2732 // Tests that GetRelatedActiveContentsCount is shared between related
2733 // SiteInstances and includes WebContents that have not navigated yet.
2734 TEST_F(WebContentsImplTest, ActiveContentsCountBasic) {
2735 scoped_refptr<SiteInstance> instance1(
2736 SiteInstance::CreateForURL(browser_context(), GURL("http://a.com")));
2737 scoped_refptr<SiteInstance> instance2(
2738 instance1->GetRelatedSiteInstance(GURL("http://b.com")));
2740 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2741 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2743 scoped_ptr<TestWebContents> contents1(
2744 TestWebContents::Create(browser_context(), instance1.get()));
2745 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2746 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2748 scoped_ptr<TestWebContents> contents2(
2749 TestWebContents::Create(browser_context(), instance1.get()));
2750 EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount());
2751 EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount());
2753 contents1.reset();
2754 EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount());
2755 EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount());
2757 contents2.reset();
2758 EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount());
2759 EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount());
2762 // Tests that GetRelatedActiveContentsCount is preserved correctly across
2763 // same-site and cross-site navigations.
2764 TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) {
2765 scoped_refptr<SiteInstance> instance(
2766 SiteInstance::Create(browser_context()));
2768 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2770 scoped_ptr<TestWebContents> contents(
2771 TestWebContents::Create(browser_context(), instance.get()));
2772 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2774 // Navigate to a URL.
2775 contents->GetController().LoadURL(GURL("http://a.com/1"),
2776 Referrer(),
2777 ui::PAGE_TRANSITION_TYPED,
2778 std::string());
2779 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2780 contents->CommitPendingNavigation();
2781 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2783 // Navigate to a URL in the same site.
2784 contents->GetController().LoadURL(GURL("http://a.com/2"),
2785 Referrer(),
2786 ui::PAGE_TRANSITION_TYPED,
2787 std::string());
2788 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2789 contents->CommitPendingNavigation();
2790 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2792 // Navigate to a URL in a different site.
2793 const GURL kUrl = GURL("http://b.com");
2794 contents->GetController().LoadURL(kUrl,
2795 Referrer(),
2796 ui::PAGE_TRANSITION_TYPED,
2797 std::string());
2798 int entry_id = contents->GetController().GetPendingEntry()->GetUniqueID();
2799 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2800 switches::kEnableBrowserSideNavigation)) {
2801 contents->GetMainFrame()->PrepareForCommit();
2803 EXPECT_TRUE(contents->CrossProcessNavigationPending());
2804 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2805 contents->GetPendingMainFrame()->SendNavigate(1, entry_id, true, kUrl);
2806 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2808 contents.reset();
2809 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2812 // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes
2813 // from WebUI.
2814 TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) {
2815 scoped_refptr<SiteInstance> instance(
2816 SiteInstance::Create(browser_context()));
2818 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2820 scoped_ptr<TestWebContents> contents(
2821 TestWebContents::Create(browser_context(), instance.get()));
2822 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2824 // Navigate to a URL.
2825 contents->NavigateAndCommit(GURL("http://a.com"));
2826 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2828 // Navigate to a URL which sort of looks like a chrome:// url.
2829 contents->NavigateAndCommit(GURL("http://gpu"));
2830 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2832 // Navigate to a URL with WebUI. This will change BrowsingInstances.
2833 const GURL kWebUIUrl = GURL("chrome://gpu");
2834 contents->GetController().LoadURL(kWebUIUrl,
2835 Referrer(),
2836 ui::PAGE_TRANSITION_TYPED,
2837 std::string());
2838 int entry_id = contents->GetController().GetPendingEntry()->GetUniqueID();
2839 contents->GetMainFrame()->PrepareForCommit();
2840 EXPECT_TRUE(contents->CrossProcessNavigationPending());
2841 scoped_refptr<SiteInstance> instance_webui(
2842 contents->GetPendingMainFrame()->GetSiteInstance());
2843 EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get()));
2845 // At this point, contents still counts for the old BrowsingInstance.
2846 EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount());
2847 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2849 // Commit and contents counts for the new one.
2850 contents->GetPendingMainFrame()->SendNavigate(1, entry_id, true, kWebUIUrl);
2851 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2852 EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount());
2854 contents.reset();
2855 EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount());
2856 EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount());
2859 class LoadingWebContentsObserver : public WebContentsObserver {
2860 public:
2861 explicit LoadingWebContentsObserver(WebContents* contents)
2862 : WebContentsObserver(contents),
2863 is_loading_(false) {
2865 ~LoadingWebContentsObserver() override {}
2867 void DidStartLoading() override { is_loading_ = true; }
2868 void DidStopLoading() override { is_loading_ = false; }
2870 bool is_loading() const { return is_loading_; }
2872 private:
2873 bool is_loading_;
2875 DISALLOW_COPY_AND_ASSIGN(LoadingWebContentsObserver);
2878 // Ensure that DidStartLoading/DidStopLoading events balance out properly with
2879 // interleaving cross-process navigations in multiple subframes.
2880 // See https://crbug.com/448601 for details of the underlying issue. The
2881 // sequence of events that reproduce it are as follows:
2882 // * Navigate top-level frame with one subframe.
2883 // * Subframe navigates more than once before the top-level frame has had a
2884 // chance to complete the load.
2885 // The subframe navigations cause the loading_frames_in_progress_ to drop down
2886 // to 0, while the loading_progresses_ map is not reset.
2887 TEST_F(WebContentsImplTest, StartStopEventsBalance) {
2888 // The bug manifests itself in regular mode as well, but browser-initiated
2889 // navigation of subframes is only possible in --site-per-process mode within
2890 // unit tests.
2891 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
2892 const GURL initial_url("about:blank");
2893 const GURL main_url("http://www.chromium.org");
2894 const GURL foo_url("http://foo.chromium.org");
2895 const GURL bar_url("http://bar.chromium.org");
2896 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
2898 // Use a WebContentsObserver to approximate the behavior of the tab's spinner.
2899 LoadingWebContentsObserver observer(contents());
2901 // Navigate the main RenderFrame, simulate the DidStartLoading, and commit.
2902 // The frame should still be loading.
2903 controller().LoadURL(
2904 main_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2905 int entry_id = controller().GetPendingEntry()->GetUniqueID();
2906 orig_rfh->PrepareForCommit();
2907 orig_rfh->OnMessageReceived(
2908 FrameHostMsg_DidStartLoading(orig_rfh->GetRoutingID(), false));
2909 contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, main_url,
2910 ui::PAGE_TRANSITION_TYPED);
2911 EXPECT_FALSE(contents()->CrossProcessNavigationPending());
2912 EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
2913 EXPECT_TRUE(contents()->IsLoading());
2914 EXPECT_TRUE(observer.is_loading());
2916 // Create a child frame to navigate multiple times.
2917 TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe");
2919 // Navigate the child frame to about:blank, which will send both
2920 // DidStartLoading and DidStopLoading messages.
2922 subframe->SendRendererInitiatedNavigationRequest(initial_url, false);
2923 subframe->OnMessageReceived(
2924 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2925 subframe->SendNavigateWithTransition(1, 0, false, initial_url,
2926 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
2927 subframe->OnMessageReceived(
2928 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2931 // Navigate the frame to another URL, which will send again
2932 // DidStartLoading and DidStopLoading messages.
2934 subframe->SendRendererInitiatedNavigationRequest(foo_url, false);
2935 subframe->PrepareForCommit();
2936 subframe->OnMessageReceived(
2937 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2938 subframe->SendNavigateWithTransition(1, 0, false, foo_url,
2939 ui::PAGE_TRANSITION_AUTO_SUBFRAME);
2940 subframe->OnMessageReceived(
2941 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2944 // Since the main frame hasn't sent any DidStopLoading messages, it is
2945 // expected that the WebContents is still in loading state.
2946 EXPECT_TRUE(contents()->IsLoading());
2947 EXPECT_TRUE(observer.is_loading());
2949 // Navigate the frame again, this time using LoadURLWithParams. This causes
2950 // RenderFrameHost to call into WebContents::DidStartLoading, which starts
2951 // the spinner.
2953 NavigationController::LoadURLParams load_params(bar_url);
2954 load_params.referrer =
2955 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
2956 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
2957 load_params.extra_headers = "content-type: text/plain";
2958 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
2959 load_params.is_renderer_initiated = false;
2960 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
2961 load_params.frame_tree_node_id =
2962 subframe->frame_tree_node()->frame_tree_node_id();
2963 controller().LoadURLWithParams(load_params);
2964 entry_id = controller().GetPendingEntry()->GetUniqueID();
2966 subframe->OnMessageReceived(
2967 FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true));
2969 // Commit the navigation in the child frame and send the DidStopLoading
2970 // message.
2971 subframe->PrepareForCommit();
2972 contents()->TestDidNavigate(subframe, 3, entry_id, true, bar_url,
2973 ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
2974 subframe->OnMessageReceived(
2975 FrameHostMsg_DidStopLoading(subframe->GetRoutingID()));
2978 // At this point the status should still be loading, since the main frame
2979 // hasn't sent the DidstopLoading message yet.
2980 EXPECT_TRUE(contents()->IsLoading());
2981 EXPECT_TRUE(observer.is_loading());
2983 // Send the DidStopLoading for the main frame and ensure it isn't loading
2984 // anymore.
2985 orig_rfh->OnMessageReceived(
2986 FrameHostMsg_DidStopLoading(orig_rfh->GetRoutingID()));
2987 EXPECT_FALSE(contents()->IsLoading());
2988 EXPECT_FALSE(observer.is_loading());
2991 // Ensure that WebContentsImpl does not stop loading too early when there still
2992 // is a pending renderer. This can happen if a same-process non user-initiated
2993 // navigation completes while there is an ongoing cross-process navigation.
2994 // TODO(fdegans): Rewrite the test for PlzNavigate when DidStartLoading and
2995 // DidStopLoading are properly called.
2996 TEST_F(WebContentsImplTest, NoEarlyStop) {
2997 const GURL kUrl1("http://www.chromium.org");
2998 const GURL kUrl2("http://www.google.com");
2999 const GURL kUrl3("http://www.wikipedia.org");
3001 contents()->NavigateAndCommit(kUrl1);
3003 TestRenderFrameHost* current_rfh = contents()->GetMainFrame();
3005 // Start a browser-initiated cross-process navigation to |kUrl2|. There should
3006 // be a pending RenderFrameHost and the WebContents should be loading.
3007 controller().LoadURL(
3008 kUrl2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3009 int entry_id = controller().GetPendingEntry()->GetUniqueID();
3010 EXPECT_TRUE(contents()->CrossProcessNavigationPending());
3011 TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame();
3012 ASSERT_TRUE(pending_rfh);
3013 EXPECT_TRUE(contents()->IsLoading());
3015 // The current RenderFrameHost starts a non user-initiated render-initiated
3016 // navigation and sends a DidStartLoading IPC. The WebContents should still be
3017 // loading.
3018 current_rfh->OnMessageReceived(
3019 FrameHostMsg_DidStartLoading(current_rfh->GetRoutingID(), false));
3020 EXPECT_TRUE(contents()->IsLoading());
3022 // Simulate the pending RenderFrameHost DidStartLoading. There should still be
3023 // a pending RenderFrameHost and the WebContents should still be loading.
3024 pending_rfh->PrepareForCommit();
3025 pending_rfh->OnMessageReceived(
3026 FrameHostMsg_DidStartLoading(pending_rfh->GetRoutingID(), false));
3027 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
3028 EXPECT_TRUE(contents()->IsLoading());
3030 // Simulate the commit and DidStopLoading from the renderer-initiated
3031 // navigation in the current RenderFrameHost. There should still be a pending
3032 // RenderFrameHost and the WebContents should still be loading.
3033 current_rfh->SendNavigateWithModificationCallback(
3034 1, 0, true, kUrl3, base::Bind(SetAsNonUserGesture));
3035 current_rfh->OnMessageReceived(
3036 FrameHostMsg_DidStopLoading(current_rfh->GetRoutingID()));
3037 EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh);
3038 EXPECT_TRUE(contents()->IsLoading());
3039 // It should commit.
3040 ASSERT_EQ(2, controller().GetEntryCount());
3041 EXPECT_EQ(kUrl3, controller().GetLastCommittedEntry()->GetURL());
3043 // Commit the navigation. The formerly pending RenderFrameHost should now be
3044 // the current RenderFrameHost and the WebContents should still be loading.
3045 contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, kUrl2,
3046 ui::PAGE_TRANSITION_TYPED);
3047 EXPECT_FALSE(contents()->GetPendingMainFrame());
3048 TestRenderFrameHost* new_current_rfh = contents()->GetMainFrame();
3049 EXPECT_EQ(new_current_rfh, pending_rfh);
3050 EXPECT_TRUE(contents()->IsLoading());
3051 EXPECT_EQ(3, controller().GetEntryCount());
3053 // Simulate the new current RenderFrameHost DidStopLoading. The WebContents
3054 // should now have stopped loading.
3055 new_current_rfh->OnMessageReceived(
3056 FrameHostMsg_DidStopLoading(new_current_rfh->GetRoutingID()));
3057 EXPECT_EQ(contents()->GetMainFrame(), new_current_rfh);
3058 EXPECT_FALSE(contents()->IsLoading());
3061 TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) {
3062 // PlayerIDs are actually pointers cast to int64, so verify that both negative
3063 // and positive player ids don't blow up.
3064 const int kPlayerAudioVideoId = 15;
3065 const int kPlayerAudioOnlyId = -15;
3066 const int kPlayerVideoOnlyId = 30;
3067 const int kPlayerRemoteId = -30;
3069 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3070 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3072 TestRenderFrameHost* rfh = contents()->GetMainFrame();
3073 AudioStateProvider* audio_state = contents()->audio_state_provider();
3075 // Ensure RenderFrame is initialized before simulating events coming from it.
3076 main_test_rfh()->InitializeRenderFrameIfNeeded();
3078 // The audio power save blocker should not be based on having a media player
3079 // when audio stream monitoring is available.
3080 if (audio_state->IsAudioStateAvailable()) {
3081 // Send a fake audio stream monitor notification. The audio power save
3082 // blocker should be created.
3083 audio_state->set_was_recently_audible_for_testing(true);
3084 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
3085 EXPECT_TRUE(contents()->has_audio_power_save_blocker_for_testing());
3087 // Send another fake notification, this time when WasRecentlyAudible() will
3088 // be false. The power save blocker should be released.
3089 audio_state->set_was_recently_audible_for_testing(false);
3090 contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
3091 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3094 // Start a player with both audio and video. A video power save blocker
3095 // should be created. If audio stream monitoring is available, an audio power
3096 // save blocker should be created too.
3097 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3098 0, kPlayerAudioVideoId, true, true, false));
3099 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3100 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3101 !audio_state->IsAudioStateAvailable());
3103 // Upon hiding the video power save blocker should be released.
3104 contents()->WasHidden();
3105 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3107 // Start another player that only has video. There should be no change in
3108 // the power save blockers. The notification should take into account the
3109 // visibility state of the WebContents.
3110 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3111 0, kPlayerVideoOnlyId, true, false, false));
3112 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3113 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3114 !audio_state->IsAudioStateAvailable());
3116 // Showing the WebContents should result in the creation of the blocker.
3117 contents()->WasShown();
3118 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3120 // Start another player that only has audio. There should be no change in
3121 // the power save blockers.
3122 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3123 0, kPlayerAudioOnlyId, false, true, false));
3124 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3125 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3126 !audio_state->IsAudioStateAvailable());
3128 // Start a remote player. There should be no change in the power save
3129 // blockers.
3130 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3131 0, kPlayerRemoteId, true, true, true));
3132 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3133 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3134 !audio_state->IsAudioStateAvailable());
3136 // Destroy the original audio video player. Both power save blockers should
3137 // remain.
3138 rfh->OnMessageReceived(
3139 FrameHostMsg_MediaPausedNotification(0, kPlayerAudioVideoId));
3140 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3141 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3142 !audio_state->IsAudioStateAvailable());
3144 // Destroy the audio only player. The video power save blocker should remain.
3145 rfh->OnMessageReceived(
3146 FrameHostMsg_MediaPausedNotification(0, kPlayerAudioOnlyId));
3147 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3148 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3150 // Destroy the video only player. No power save blockers should remain.
3151 rfh->OnMessageReceived(
3152 FrameHostMsg_MediaPausedNotification(0, kPlayerVideoOnlyId));
3153 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3154 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3156 // Destroy the remote player. No power save blockers should remain.
3157 rfh->OnMessageReceived(
3158 FrameHostMsg_MediaPausedNotification(0, kPlayerRemoteId));
3159 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3160 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3162 // Start a player with both audio and video. A video power save blocker
3163 // should be created. If audio stream monitoring is available, an audio power
3164 // save blocker should be created too.
3165 rfh->OnMessageReceived(FrameHostMsg_MediaPlayingNotification(
3166 0, kPlayerAudioVideoId, true, true, false));
3167 EXPECT_TRUE(contents()->has_video_power_save_blocker_for_testing());
3168 EXPECT_EQ(contents()->has_audio_power_save_blocker_for_testing(),
3169 !audio_state->IsAudioStateAvailable());
3171 // Crash the renderer.
3172 contents()->GetMainFrame()->GetProcess()->SimulateCrash();
3174 // Verify that all the power save blockers have been released.
3175 EXPECT_FALSE(contents()->has_video_power_save_blocker_for_testing());
3176 EXPECT_FALSE(contents()->has_audio_power_save_blocker_for_testing());
3179 TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) {
3180 TestWebContentsObserver observer(contents());
3181 TestRenderFrameHost* rfh = contents()->GetMainFrame();
3183 SkColor transparent = SK_ColorTRANSPARENT;
3185 EXPECT_EQ(transparent, contents()->GetThemeColor());
3186 EXPECT_EQ(transparent, observer.last_theme_color());
3188 // Theme color changes should not propagate past the WebContentsImpl before
3189 // the first visually non-empty paint has occurred.
3190 RenderViewHostTester::TestOnMessageReceived(
3191 test_rvh(),
3192 FrameHostMsg_DidChangeThemeColor(rfh->GetRoutingID(), SK_ColorRED));
3194 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
3195 EXPECT_EQ(transparent, observer.last_theme_color());
3197 // Simulate that the first visually non-empty paint has occurred. This will
3198 // propagate the current theme color to the delegates.
3199 RenderViewHostTester::TestOnMessageReceived(
3200 test_rvh(),
3201 FrameHostMsg_DidFirstVisuallyNonEmptyPaint(rfh->GetRoutingID()));
3203 EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor());
3204 EXPECT_EQ(SK_ColorRED, observer.last_theme_color());
3206 // Additional changes made by the web contents should propagate as well.
3207 RenderViewHostTester::TestOnMessageReceived(
3208 test_rvh(),
3209 FrameHostMsg_DidChangeThemeColor(rfh->GetRoutingID(), SK_ColorGREEN));
3211 EXPECT_EQ(SK_ColorGREEN, contents()->GetThemeColor());
3212 EXPECT_EQ(SK_ColorGREEN, observer.last_theme_color());
3215 // Test that if a renderer reports that it has loaded a resource from
3216 // memory cache with bad security info (i.e. can't be deserialized), the
3217 // renderer gets killed.
3218 TEST_F(WebContentsImplTest, LoadResourceFromMemoryCacheWithBadSecurityInfo) {
3219 MockRenderProcessHost* rph = contents()->GetMainFrame()->GetProcess();
3220 EXPECT_EQ(0, rph->bad_msg_count());
3222 contents()->OnDidLoadResourceFromMemoryCache(
3223 GURL("http://example.test"), "not valid security info", "GET",
3224 "mime type", RESOURCE_TYPE_MAIN_FRAME);
3225 EXPECT_EQ(1, rph->bad_msg_count());
3228 } // namespace content