|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/browser/frame_host/cross_site_transferring_request.h" | 
|  | #include "content/browser/frame_host/interstitial_page_impl.h" | 
|  | #include "content/browser/frame_host/navigation_entry_impl.h" | 
|  | #include "content/browser/frame_host/render_frame_host_impl.h" | 
|  | #include "content/browser/frame_host/render_frame_proxy_host.h" | 
|  | #include "content/browser/media/audio_stream_monitor.h" | 
|  | #include "content/browser/media/media_web_contents_observer.h" | 
|  | #include "content/browser/renderer_host/render_view_host_impl.h" | 
|  | #include "content/browser/site_instance_impl.h" | 
|  | #include "content/browser/ssl/ssl_policy.h" | 
|  | #include "content/browser/webui/content_web_ui_controller_factory.h" | 
|  | #include "content/browser/webui/web_ui_controller_factory_registry.h" | 
|  | #include "content/common/frame_messages.h" | 
|  | #include "content/common/input/synthetic_web_input_event_builders.h" | 
|  | #include "content/common/media/media_player_delegate_messages.h" | 
|  | #include "content/common/site_isolation_policy.h" | 
|  | #include "content/common/view_messages.h" | 
|  | #include "content/public/browser/global_request_id.h" | 
|  | #include "content/public/browser/interstitial_page_delegate.h" | 
|  | #include "content/public/browser/javascript_dialog_manager.h" | 
|  | #include "content/public/browser/navigation_details.h" | 
|  | #include "content/public/browser/notification_details.h" | 
|  | #include "content/public/browser/notification_source.h" | 
|  | #include "content/public/browser/render_widget_host_view.h" | 
|  | #include "content/public/browser/storage_partition.h" | 
|  | #include "content/public/browser/web_contents_delegate.h" | 
|  | #include "content/public/browser/web_contents_observer.h" | 
|  | #include "content/public/browser/web_ui_controller.h" | 
|  | #include "content/public/common/bindings_policy.h" | 
|  | #include "content/public/common/browser_side_navigation_policy.h" | 
|  | #include "content/public/common/content_constants.h" | 
|  | #include "content/public/common/url_constants.h" | 
|  | #include "content/public/test/mock_render_process_host.h" | 
|  | #include "content/public/test/test_browser_context.h" | 
|  | #include "content/public/test/test_utils.h" | 
|  | #include "content/test/test_content_browser_client.h" | 
|  | #include "content/test/test_content_client.h" | 
|  | #include "content/test/test_render_frame_host.h" | 
|  | #include "content/test/test_render_view_host.h" | 
|  | #include "content/test/test_web_contents.h" | 
|  | #include "net/test/cert_test_util.h" | 
|  | #include "net/test/test_data_directory.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "third_party/skia/include/core/SkColor.h" | 
|  | #include "url/url_constants.h" | 
|  |  | 
|  | namespace content { | 
|  | namespace { | 
|  |  | 
|  | class TestInterstitialPage; | 
|  |  | 
|  | class TestInterstitialPageDelegate : public InterstitialPageDelegate { | 
|  | public: | 
|  | explicit TestInterstitialPageDelegate(TestInterstitialPage* interstitial_page) | 
|  | : interstitial_page_(interstitial_page) {} | 
|  | void CommandReceived(const std::string& command) override; | 
|  | std::string GetHTMLContents() override { return std::string(); } | 
|  | void OnDontProceed() override; | 
|  | void OnProceed() override; | 
|  |  | 
|  | private: | 
|  | TestInterstitialPage* interstitial_page_; | 
|  | }; | 
|  |  | 
|  | class TestInterstitialPage : public InterstitialPageImpl { | 
|  | public: | 
|  | enum InterstitialState { | 
|  | INVALID = 0,    // Hasn't yet been initialized. | 
|  | UNDECIDED,      // Initialized, but no decision taken yet. | 
|  | OKED,           // Proceed was called. | 
|  | CANCELED        // DontProceed was called. | 
|  | }; | 
|  |  | 
|  | class Delegate { | 
|  | public: | 
|  | virtual void TestInterstitialPageDeleted( | 
|  | TestInterstitialPage* interstitial) = 0; | 
|  |  | 
|  | protected: | 
|  | virtual ~Delegate() {} | 
|  | }; | 
|  |  | 
|  | // IMPORTANT NOTE: if you pass stack allocated values for |state| and | 
|  | // |deleted| (like all interstitial related tests do at this point), make sure | 
|  | // to create an instance of the TestInterstitialPageStateGuard class on the | 
|  | // stack in your test.  This will ensure that the TestInterstitialPage states | 
|  | // are cleared when the test finishes. | 
|  | // Not doing so will cause stack trashing if your test does not hide the | 
|  | // interstitial, as in such a case it will be destroyed in the test TearDown | 
|  | // method and will dereference the |deleted| local variable which by then is | 
|  | // out of scope. | 
|  | TestInterstitialPage(WebContentsImpl* contents, | 
|  | bool new_navigation, | 
|  | const GURL& url, | 
|  | InterstitialState* state, | 
|  | bool* deleted) | 
|  | : InterstitialPageImpl( | 
|  | contents, | 
|  | static_cast<RenderWidgetHostDelegate*>(contents), | 
|  | new_navigation, url, new TestInterstitialPageDelegate(this)), | 
|  | state_(state), | 
|  | deleted_(deleted), | 
|  | command_received_count_(0), | 
|  | delegate_(nullptr) { | 
|  | *state_ = UNDECIDED; | 
|  | *deleted_ = false; | 
|  | } | 
|  |  | 
|  | ~TestInterstitialPage() override { | 
|  | if (deleted_) | 
|  | *deleted_ = true; | 
|  | if (delegate_) | 
|  | delegate_->TestInterstitialPageDeleted(this); | 
|  | } | 
|  |  | 
|  | void OnDontProceed() { | 
|  | if (state_) | 
|  | *state_ = CANCELED; | 
|  | } | 
|  | void OnProceed() { | 
|  | if (state_) | 
|  | *state_ = OKED; | 
|  | } | 
|  |  | 
|  | int command_received_count() const { | 
|  | return command_received_count_; | 
|  | } | 
|  |  | 
|  | void TestDomOperationResponse(const std::string& json_string) { | 
|  | if (enabled()) | 
|  | CommandReceived(); | 
|  | } | 
|  |  | 
|  | void TestDidNavigate(int page_id, | 
|  | int nav_entry_id, | 
|  | bool did_create_new_entry, | 
|  | const GURL& url) { | 
|  | FrameHostMsg_DidCommitProvisionalLoad_Params params; | 
|  | InitNavigateParams(¶ms, page_id, nav_entry_id, did_create_new_entry, | 
|  | url, ui::PAGE_TRANSITION_TYPED); | 
|  | DidNavigate(GetMainFrame()->GetRenderViewHost(), params); | 
|  | } | 
|  |  | 
|  | void TestRenderViewTerminated(base::TerminationStatus status, | 
|  | int error_code) { | 
|  | RenderViewTerminated(GetMainFrame()->GetRenderViewHost(), status, | 
|  | error_code); | 
|  | } | 
|  |  | 
|  | bool is_showing() const { | 
|  | return static_cast<TestRenderWidgetHostView*>( | 
|  | GetMainFrame()->GetRenderViewHost()->GetWidget()->GetView()) | 
|  | ->is_showing(); | 
|  | } | 
|  |  | 
|  | void ClearStates() { | 
|  | state_ = nullptr; | 
|  | deleted_ = nullptr; | 
|  | delegate_ = nullptr; | 
|  | } | 
|  |  | 
|  | void CommandReceived() { | 
|  | command_received_count_++; | 
|  | } | 
|  |  | 
|  | void set_delegate(Delegate* delegate) { | 
|  | delegate_ = delegate; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | WebContentsView* CreateWebContentsView() override { return nullptr; } | 
|  |  | 
|  | private: | 
|  | InterstitialState* state_; | 
|  | bool* deleted_; | 
|  | int command_received_count_; | 
|  | Delegate* delegate_; | 
|  | }; | 
|  |  | 
|  | void TestInterstitialPageDelegate::CommandReceived(const std::string& command) { | 
|  | interstitial_page_->CommandReceived(); | 
|  | } | 
|  |  | 
|  | void TestInterstitialPageDelegate::OnDontProceed() { | 
|  | interstitial_page_->OnDontProceed(); | 
|  | } | 
|  |  | 
|  | void TestInterstitialPageDelegate::OnProceed() { | 
|  | interstitial_page_->OnProceed(); | 
|  | } | 
|  |  | 
|  | class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate { | 
|  | public: | 
|  | explicit TestInterstitialPageStateGuard( | 
|  | TestInterstitialPage* interstitial_page) | 
|  | : interstitial_page_(interstitial_page) { | 
|  | DCHECK(interstitial_page_); | 
|  | interstitial_page_->set_delegate(this); | 
|  | } | 
|  | ~TestInterstitialPageStateGuard() override { | 
|  | if (interstitial_page_) | 
|  | interstitial_page_->ClearStates(); | 
|  | } | 
|  |  | 
|  | void TestInterstitialPageDeleted( | 
|  | TestInterstitialPage* interstitial) override { | 
|  | DCHECK(interstitial_page_ == interstitial); | 
|  | interstitial_page_ = nullptr; | 
|  | } | 
|  |  | 
|  | private: | 
|  | TestInterstitialPage* interstitial_page_; | 
|  | }; | 
|  |  | 
|  | class WebContentsImplTestBrowserClient : public TestContentBrowserClient { | 
|  | public: | 
|  | WebContentsImplTestBrowserClient() | 
|  | : assign_site_for_url_(false) {} | 
|  |  | 
|  | ~WebContentsImplTestBrowserClient() override {} | 
|  |  | 
|  | bool ShouldAssignSiteForURL(const GURL& url) override { | 
|  | return assign_site_for_url_; | 
|  | } | 
|  |  | 
|  | void set_assign_site_for_url(bool assign) { | 
|  | assign_site_for_url_ = assign; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool assign_site_for_url_; | 
|  | }; | 
|  |  | 
|  | class WebContentsImplTest : public RenderViewHostImplTestHarness { | 
|  | public: | 
|  | void SetUp() override { | 
|  | RenderViewHostImplTestHarness::SetUp(); | 
|  | WebUIControllerFactory::RegisterFactory( | 
|  | ContentWebUIControllerFactory::GetInstance()); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | WebUIControllerFactory::UnregisterFactoryForTesting( | 
|  | ContentWebUIControllerFactory::GetInstance()); | 
|  | RenderViewHostImplTestHarness::TearDown(); | 
|  | } | 
|  |  | 
|  | bool has_audio_power_save_blocker() { | 
|  | return contents() | 
|  | ->media_web_contents_observer() | 
|  | ->has_audio_power_save_blocker_for_testing(); | 
|  | } | 
|  |  | 
|  | bool has_video_power_save_blocker() { | 
|  | return contents() | 
|  | ->media_web_contents_observer() | 
|  | ->has_video_power_save_blocker_for_testing(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class TestWebContentsObserver : public WebContentsObserver { | 
|  | public: | 
|  | explicit TestWebContentsObserver(WebContents* contents) | 
|  | : WebContentsObserver(contents), | 
|  | last_theme_color_(SK_ColorTRANSPARENT) { | 
|  | } | 
|  | ~TestWebContentsObserver() override {} | 
|  |  | 
|  | void DidFinishLoad(RenderFrameHost* render_frame_host, | 
|  | const GURL& validated_url) override { | 
|  | last_url_ = validated_url; | 
|  | } | 
|  | void DidFailLoad(RenderFrameHost* render_frame_host, | 
|  | const GURL& validated_url, | 
|  | int error_code, | 
|  | const base::string16& error_description, | 
|  | bool was_ignored_by_handler) override { | 
|  | last_url_ = validated_url; | 
|  | } | 
|  |  | 
|  | void DidChangeThemeColor(SkColor theme_color) override { | 
|  | last_theme_color_ = theme_color; | 
|  | } | 
|  |  | 
|  | const GURL& last_url() const { return last_url_; } | 
|  | SkColor last_theme_color() const { return last_theme_color_; } | 
|  |  | 
|  | private: | 
|  | GURL last_url_; | 
|  | SkColor last_theme_color_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TestWebContentsObserver); | 
|  | }; | 
|  |  | 
|  | // Pretends to be a normal browser that receives toggles and transitions to/from | 
|  | // a fullscreened state. | 
|  | class FakeFullscreenDelegate : public WebContentsDelegate { | 
|  | public: | 
|  | FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {} | 
|  | ~FakeFullscreenDelegate() override {} | 
|  |  | 
|  | void EnterFullscreenModeForTab(WebContents* web_contents, | 
|  | const GURL& origin) override { | 
|  | fullscreened_contents_ = web_contents; | 
|  | } | 
|  |  | 
|  | void ExitFullscreenModeForTab(WebContents* web_contents) override { | 
|  | fullscreened_contents_ = nullptr; | 
|  | } | 
|  |  | 
|  | bool IsFullscreenForTabOrPending( | 
|  | const WebContents* web_contents) const override { | 
|  | return fullscreened_contents_ && web_contents == fullscreened_contents_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | WebContents* fullscreened_contents_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(FakeFullscreenDelegate); | 
|  | }; | 
|  |  | 
|  | class FakeValidationMessageDelegate : public WebContentsDelegate { | 
|  | public: | 
|  | FakeValidationMessageDelegate() | 
|  | : hide_validation_message_was_called_(false) {} | 
|  | ~FakeValidationMessageDelegate() override {} | 
|  |  | 
|  | void HideValidationMessage(WebContents* web_contents) override { | 
|  | hide_validation_message_was_called_ = true; | 
|  | } | 
|  |  | 
|  | bool hide_validation_message_was_called() const { | 
|  | return hide_validation_message_was_called_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool hide_validation_message_was_called_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(FakeValidationMessageDelegate); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Test to make sure that title updates get stripped of whitespace. | 
|  | TEST_F(WebContentsImplTest, UpdateTitle) { | 
|  | NavigationControllerImpl& cont = | 
|  | static_cast<NavigationControllerImpl&>(controller()); | 
|  | cont.LoadURL(GURL(url::kAboutBlankURL), Referrer(), ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  |  | 
|  | FrameHostMsg_DidCommitProvisionalLoad_Params params; | 
|  | InitNavigateParams(¶ms, 0, 0, true, GURL(url::kAboutBlankURL), | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | contents()->GetMainFrame()->SendNavigateWithParams(¶ms); | 
|  |  | 
|  | contents()->UpdateTitle(contents()->GetMainFrame(), 0, | 
|  | base::ASCIIToUTF16("    Lots O' Whitespace\n"), | 
|  | base::i18n::LEFT_TO_RIGHT); | 
|  | EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, UpdateTitleBeforeFirstNavigation) { | 
|  | ASSERT_TRUE(controller().IsInitialNavigation()); | 
|  | const base::string16 title = base::ASCIIToUTF16("Initial Entry Title"); | 
|  | contents()->UpdateTitle(contents()->GetMainFrame(), -1, title, | 
|  | base::i18n::LEFT_TO_RIGHT); | 
|  | EXPECT_EQ(title, contents()->GetTitle()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, DontUseTitleFromPendingEntry) { | 
|  | const GURL kGURL("chrome://blah"); | 
|  | controller().LoadURL( | 
|  | kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | EXPECT_EQ(base::string16(), contents()->GetTitle()); | 
|  |  | 
|  | // Also test setting title while the first navigation is still pending. | 
|  | const base::string16 title = base::ASCIIToUTF16("Initial Entry Title"); | 
|  | contents()->UpdateTitle(contents()->GetMainFrame(), -1, title, | 
|  | base::i18n::LEFT_TO_RIGHT); | 
|  | EXPECT_EQ(title, contents()->GetTitle()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, UseTitleFromPendingEntryIfSet) { | 
|  | const GURL kGURL("chrome://blah"); | 
|  | const base::string16 title = base::ASCIIToUTF16("My Title"); | 
|  | controller().LoadURL( | 
|  | kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  |  | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_EQ(kGURL, entry->GetURL()); | 
|  | entry->SetTitle(title); | 
|  |  | 
|  | EXPECT_EQ(title, contents()->GetTitle()); | 
|  | } | 
|  |  | 
|  | // Browser initiated navigations to view-source URLs of WebUI pages should work. | 
|  | TEST_F(WebContentsImplTest, DirectNavigationToViewSourceWebUI) { | 
|  | NavigationControllerImpl& cont = | 
|  | static_cast<NavigationControllerImpl&>(controller()); | 
|  | const GURL kGURL("view-source:chrome://blah"); | 
|  | // NavigationControllerImpl rewrites view-source URLs, simulating that here. | 
|  | const GURL kRewrittenURL("chrome://blah"); | 
|  |  | 
|  | process()->sink().ClearMessages(); | 
|  |  | 
|  | // Use LoadURLWithParams instead of LoadURL, because the former properly | 
|  | // rewrites view-source:chrome://blah URLs to chrome://blah. | 
|  | NavigationController::LoadURLParams load_params(kGURL); | 
|  | load_params.transition_type = ui::PAGE_TRANSITION_TYPED; | 
|  | load_params.extra_headers = "content-type: text/plain"; | 
|  | load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT; | 
|  | load_params.is_renderer_initiated = false; | 
|  | controller().LoadURLWithParams(load_params); | 
|  |  | 
|  | int entry_id = cont.GetPendingEntry()->GetUniqueID(); | 
|  | // Did we get the expected message? | 
|  | EXPECT_TRUE(process()->sink().GetFirstMessageMatching( | 
|  | FrameMsg_EnableViewSourceMode::ID)); | 
|  |  | 
|  | FrameHostMsg_DidCommitProvisionalLoad_Params params; | 
|  | InitNavigateParams(¶ms, 0, entry_id, true, kRewrittenURL, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->GetMainFrame()->OnMessageReceived( | 
|  | FrameHostMsg_DidStartProvisionalLoad(1, kRewrittenURL, | 
|  | base::TimeTicks::Now())); | 
|  | contents()->GetMainFrame()->SendNavigateWithParams(¶ms); | 
|  |  | 
|  | // This is the virtual URL. | 
|  | EXPECT_EQ(base::ASCIIToUTF16("view-source:chrome://blah"), | 
|  | contents()->GetTitle()); | 
|  |  | 
|  | // The actual URL navigated to. | 
|  | EXPECT_EQ(kRewrittenURL, | 
|  | contents()->GetController().GetLastCommittedEntry()->GetURL()); | 
|  | } | 
|  |  | 
|  | // Test to ensure UpdateMaxPageID is working properly. | 
|  | TEST_F(WebContentsImplTest, UpdateMaxPageID) { | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  | scoped_refptr<SiteInstance> instance2(SiteInstance::Create(nullptr)); | 
|  |  | 
|  | // Starts at -1. | 
|  | EXPECT_EQ(-1, contents()->GetMaxPageID()); | 
|  | EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance1)); | 
|  | EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); | 
|  |  | 
|  | // Make sure max_page_id_ is monotonically increasing per SiteInstance. | 
|  | contents()->UpdateMaxPageID(3); | 
|  | contents()->UpdateMaxPageID(1); | 
|  | EXPECT_EQ(3, contents()->GetMaxPageID()); | 
|  | EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); | 
|  | EXPECT_EQ(-1, contents()->GetMaxPageIDForSiteInstance(instance2.get())); | 
|  |  | 
|  | contents()->UpdateMaxPageIDForSiteInstance(instance2.get(), 7); | 
|  | EXPECT_EQ(3, contents()->GetMaxPageID()); | 
|  | EXPECT_EQ(3, contents()->GetMaxPageIDForSiteInstance(instance1)); | 
|  | EXPECT_EQ(7, contents()->GetMaxPageIDForSiteInstance(instance2.get())); | 
|  | } | 
|  |  | 
|  | // Test simple same-SiteInstance navigation. | 
|  | TEST_F(WebContentsImplTest, SimpleNavigation) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  |  | 
|  | // Navigate to URL | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | main_test_rfh()->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(instance1, orig_rfh->GetSiteInstance()); | 
|  | // Controller's pending entry will have a null site instance until we assign | 
|  | // it in DidNavigate. | 
|  | EXPECT_EQ( | 
|  | nullptr, | 
|  | NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())-> | 
|  | site_instance()); | 
|  |  | 
|  | // DidNavigate from the page | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(instance1, orig_rfh->GetSiteInstance()); | 
|  | // Controller's entry should now have the SiteInstance, or else we won't be | 
|  | // able to find it later. | 
|  | EXPECT_EQ( | 
|  | instance1, | 
|  | NavigationEntryImpl::FromNavigationEntry(controller().GetVisibleEntry())-> | 
|  | site_instance()); | 
|  | } | 
|  |  | 
|  | // Test that we reject NavigateToEntry if the url is over kMaxURLChars. | 
|  | TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) { | 
|  | // Construct a URL that's kMaxURLChars + 1 long of all 'a's. | 
|  | const GURL url(std::string("http://example.org/").append( | 
|  | url::kMaxURLChars + 1, 'a')); | 
|  |  | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string()); | 
|  | EXPECT_EQ(nullptr, controller().GetPendingEntry()); | 
|  | } | 
|  |  | 
|  | // Test that we reject NavigateToEntry if the url is invalid. | 
|  | TEST_F(WebContentsImplTest, NavigateToInvalidURL) { | 
|  | // Invalid URLs should not trigger a navigation. | 
|  | const GURL invalid_url("view-source:http://example.org/%00"); | 
|  | controller().LoadURL( | 
|  | invalid_url, Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string()); | 
|  | EXPECT_EQ(nullptr, controller().GetPendingEntry()); | 
|  |  | 
|  | // Empty URLs are supported and should start a navigation. | 
|  | controller().LoadURL( | 
|  | GURL(), Referrer(), ui::PAGE_TRANSITION_GENERATED, std::string()); | 
|  | EXPECT_NE(nullptr, controller().GetPendingEntry()); | 
|  | } | 
|  |  | 
|  | // Test that navigating across a site boundary creates a new RenderViewHost | 
|  | // with a new SiteInstance.  Going back should do the same. | 
|  | TEST_F(WebContentsImplTest, CrossSiteBoundaries) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | int orig_rvh_delete_count = 0; | 
|  | orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use first RenderViewHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Keep the number of active frames in orig_rfh's SiteInstance non-zero so | 
|  | // that orig_rfh doesn't get deleted when it gets swapped out. | 
|  | orig_rfh->GetSiteInstance()->IncrementActiveFrameCount(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost()); | 
|  | EXPECT_EQ(url, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url, contents()->GetVisibleURL()); | 
|  |  | 
|  | // Navigate to new site | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | if (IsBrowserSideNavigationEnabled()) | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(url, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url2, contents()->GetVisibleURL()); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  | EXPECT_TRUE(pending_rfh->GetLastCommittedURL().is_empty()); | 
|  | int pending_rvh_delete_count = 0; | 
|  | pending_rfh->GetRenderViewHost()->set_delete_counter( | 
|  | &pending_rvh_delete_count); | 
|  |  | 
|  | // Navigations should be suspended in pending_rfh until BeforeUnloadACK. | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | EXPECT_TRUE(pending_rfh->are_navigations_suspended()); | 
|  | orig_rfh->SendBeforeUnloadACK(true); | 
|  | EXPECT_FALSE(pending_rfh->are_navigations_suspended()); | 
|  | } | 
|  |  | 
|  | // DidNavigate from the pending page | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Keep the number of active frames in pending_rfh's SiteInstance | 
|  | // non-zero so that orig_rfh doesn't get deleted when it gets | 
|  | // swapped out. | 
|  | pending_rfh->GetSiteInstance()->IncrementActiveFrameCount(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(url2, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url2, contents()->GetVisibleURL()); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  | // We keep a proxy for the original RFH's SiteInstance. | 
|  | EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost( | 
|  | orig_rfh->GetSiteInstance())); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 0); | 
|  |  | 
|  | // Going back should switch SiteInstances again.  The first SiteInstance is | 
|  | // stored in the NavigationEntry, so it should be the same as at the start. | 
|  | // We should use the same RFH as before, swapping it back in. | 
|  | controller().GoBack(); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | if (IsBrowserSideNavigationEnabled()) | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  |  | 
|  | // Navigations should be suspended in goback_rfh until BeforeUnloadACK. | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | EXPECT_TRUE(goback_rfh->are_navigations_suspended()); | 
|  | pending_rfh->SendBeforeUnloadACK(true); | 
|  | EXPECT_FALSE(goback_rfh->are_navigations_suspended()); | 
|  | } | 
|  |  | 
|  | // DidNavigate from the back action | 
|  | contents()->TestDidNavigate(goback_rfh, 1, entry_id, false, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(goback_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(instance1, contents()->GetSiteInstance()); | 
|  | // There should be a proxy for the pending RFH SiteInstance. | 
|  | EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> | 
|  | GetRenderFrameProxyHost(pending_rfh->GetSiteInstance())); | 
|  | EXPECT_EQ(pending_rvh_delete_count, 0); | 
|  | pending_rfh->OnSwappedOut(); | 
|  |  | 
|  | // Close contents and ensure RVHs are deleted. | 
|  | DeleteContents(); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 1); | 
|  | EXPECT_EQ(pending_rvh_delete_count, 1); | 
|  | } | 
|  |  | 
|  | // Test that navigating across a site boundary after a crash creates a new | 
|  | // RFH without requiring a cross-site transition (i.e., PENDING state). | 
|  | TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | int orig_rvh_delete_count = 0; | 
|  | orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use first RenderViewHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost()); | 
|  |  | 
|  | // Simulate a renderer crash. | 
|  | EXPECT_TRUE(orig_rfh->IsRenderFrameLive()); | 
|  | orig_rfh->GetProcess()->SimulateCrash(); | 
|  | EXPECT_FALSE(orig_rfh->IsRenderFrameLive()); | 
|  |  | 
|  | // Navigate to new site.  We should not go into PENDING. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | TestRenderFrameHost* new_rfh = contents()->GetMainFrame(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  | EXPECT_NE(orig_rfh, new_rfh); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 1); | 
|  |  | 
|  | // DidNavigate from the new page | 
|  | contents()->TestDidNavigate(new_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(new_rfh, main_rfh()); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  |  | 
|  | // Close contents and ensure RVHs are deleted. | 
|  | DeleteContents(); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 1); | 
|  | } | 
|  |  | 
|  | // Test that opening a new contents in the same SiteInstance and then navigating | 
|  | // both contentses to a new site will place both contentses in a single | 
|  | // SiteInstance. | 
|  | TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use first RenderViewHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Open a new contents with the same SiteInstance, navigated to the same site. | 
|  | std::unique_ptr<TestWebContents> contents2( | 
|  | TestWebContents::Create(browser_context(), instance1)); | 
|  | contents2->GetController().LoadURL(url, Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID(); | 
|  | contents2->GetMainFrame()->PrepareForCommit(); | 
|  | // Need this page id to be 2 since the site instance is the same (which is the | 
|  | // scope of page IDs) and we want to consider this a new page. | 
|  | contents2->TestDidNavigate(contents2->GetMainFrame(), 2, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Navigate first contents to a new site. | 
|  | const GURL url2a("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2a, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | TestRenderFrameHost* pending_rfh_a = contents()->GetPendingMainFrame(); | 
|  | contents()->TestDidNavigate(pending_rfh_a, 1, entry_id, true, url2a, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2a = contents()->GetSiteInstance(); | 
|  | EXPECT_NE(instance1, instance2a); | 
|  |  | 
|  | // Navigate second contents to the same site as the first tab. | 
|  | const GURL url2b("http://mail.yahoo.com"); | 
|  | contents2->GetController().LoadURL(url2b, Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID(); | 
|  | TestRenderFrameHost* rfh2 = contents2->GetMainFrame(); | 
|  | rfh2->PrepareForCommit(); | 
|  | TestRenderFrameHost* pending_rfh_b = contents2->GetPendingMainFrame(); | 
|  | EXPECT_NE(nullptr, pending_rfh_b); | 
|  | EXPECT_TRUE(contents2->CrossProcessNavigationPending()); | 
|  |  | 
|  | // NOTE(creis): We used to be in danger of showing a crash page here if the | 
|  | // second contents hadn't navigated somewhere first (bug 1145430).  That case | 
|  | // is now covered by the CrossSiteBoundariesAfterCrash test. | 
|  | contents2->TestDidNavigate(pending_rfh_b, 2, entry_id, true, url2b, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2b = contents2->GetSiteInstance(); | 
|  | EXPECT_NE(instance1, instance2b); | 
|  |  | 
|  | // Both contentses should now be in the same SiteInstance. | 
|  | EXPECT_EQ(instance2a, instance2b); | 
|  | } | 
|  |  | 
|  | // The embedder can request sites for certain urls not be be assigned to the | 
|  | // SiteInstance through ShouldAssignSiteForURL() in content browser client, | 
|  | // allowing to reuse the renderer backing certain chrome urls for subsequent | 
|  | // navigation. The test verifies that the override is honored. | 
|  | TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) { | 
|  | WebContentsImplTestBrowserClient browser_client; | 
|  | SetBrowserClientForTesting(&browser_client); | 
|  |  | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | int orig_rvh_delete_count = 0; | 
|  | orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); | 
|  | SiteInstanceImpl* orig_instance = contents()->GetSiteInstance(); | 
|  |  | 
|  | browser_client.set_assign_site_for_url(false); | 
|  | // Navigate to an URL that will not assign a new SiteInstance. | 
|  | const GURL native_url("non-site-url://stuffandthings"); | 
|  | controller().LoadURL( | 
|  | native_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, native_url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(native_url, contents()->GetVisibleURL()); | 
|  | EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); | 
|  | EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); | 
|  | EXPECT_FALSE(orig_instance->HasSite()); | 
|  |  | 
|  | browser_client.set_assign_site_for_url(true); | 
|  | // Navigate to new site (should keep same site instance). | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url, contents()->GetVisibleURL()); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Keep the number of active frames in orig_rfh's SiteInstance | 
|  | // non-zero so that orig_rfh doesn't get deleted when it gets | 
|  | // swapped out. | 
|  | orig_rfh->GetSiteInstance()->IncrementActiveFrameCount(); | 
|  |  | 
|  | EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); | 
|  | EXPECT_TRUE( | 
|  | contents()->GetSiteInstance()->GetSiteURL().DomainIs("google.com")); | 
|  | EXPECT_EQ(url, contents()->GetLastCommittedURL()); | 
|  |  | 
|  | // Navigate to another new site (should create a new site instance). | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | if (IsBrowserSideNavigationEnabled()) | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(url, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url2, contents()->GetVisibleURL()); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  | int pending_rvh_delete_count = 0; | 
|  | pending_rfh->GetRenderViewHost()->set_delete_counter( | 
|  | &pending_rvh_delete_count); | 
|  |  | 
|  | // Navigations should be suspended in pending_rvh until BeforeUnloadACK. | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | EXPECT_TRUE(pending_rfh->are_navigations_suspended()); | 
|  | orig_rfh->SendBeforeUnloadACK(true); | 
|  | EXPECT_FALSE(pending_rfh->are_navigations_suspended()); | 
|  | } | 
|  |  | 
|  | // DidNavigate from the pending page. | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* new_instance = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(url2, contents()->GetLastCommittedURL()); | 
|  | EXPECT_EQ(url2, contents()->GetVisibleURL()); | 
|  | EXPECT_NE(new_instance, orig_instance); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | // We keep a proxy for the original RFH's SiteInstance. | 
|  | EXPECT_TRUE(contents()->GetRenderManagerForTesting()->GetRenderFrameProxyHost( | 
|  | orig_rfh->GetSiteInstance())); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 0); | 
|  | orig_rfh->OnSwappedOut(); | 
|  |  | 
|  | // Close contents and ensure RVHs are deleted. | 
|  | DeleteContents(); | 
|  | EXPECT_EQ(orig_rvh_delete_count, 1); | 
|  | EXPECT_EQ(pending_rvh_delete_count, 1); | 
|  | } | 
|  |  | 
|  | // Regression test for http://crbug.com/386542 - variation of | 
|  | // NavigateFromSitelessUrl in which the original navigation is a session | 
|  | // restore. | 
|  | TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) { | 
|  | WebContentsImplTestBrowserClient browser_client; | 
|  | SetBrowserClientForTesting(&browser_client); | 
|  | SiteInstanceImpl* orig_instance = contents()->GetSiteInstance(); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Restore a navigation entry for URL that should not assign site to the | 
|  | // SiteInstance. | 
|  | browser_client.set_assign_site_for_url(false); | 
|  | const GURL native_url("non-site-url://stuffandthings"); | 
|  | std::vector<std::unique_ptr<NavigationEntry>> entries; | 
|  | std::unique_ptr<NavigationEntry> new_entry = | 
|  | NavigationControllerImpl::CreateNavigationEntry( | 
|  | native_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, | 
|  | std::string(), browser_context()); | 
|  | new_entry->SetPageID(0); | 
|  | entries.push_back(std::move(new_entry)); | 
|  | controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries); | 
|  | ASSERT_EQ(0u, entries.size()); | 
|  | ASSERT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | controller().GoToIndex(0); | 
|  | NavigationEntry* entry = controller().GetPendingEntry(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 0, entry->GetUniqueID(), false, | 
|  | native_url, ui::PAGE_TRANSITION_RELOAD); | 
|  | EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); | 
|  | EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); | 
|  | EXPECT_FALSE(orig_instance->HasSite()); | 
|  |  | 
|  | // Navigate to a regular site and verify that the SiteInstance was kept. | 
|  | browser_client.set_assign_site_for_url(true); | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry = controller().GetPendingEntry(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, entry->GetUniqueID(), true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); | 
|  |  | 
|  | // Cleanup. | 
|  | DeleteContents(); | 
|  | } | 
|  |  | 
|  | // Complement for NavigateFromRestoredSitelessUrl, verifying that when a regular | 
|  | // tab is restored, the SiteInstance will change upon navigation. | 
|  | TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { | 
|  | WebContentsImplTestBrowserClient browser_client; | 
|  | SetBrowserClientForTesting(&browser_client); | 
|  | SiteInstanceImpl* orig_instance = contents()->GetSiteInstance(); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Restore a navigation entry for a regular URL ensuring that the embedder | 
|  | // ShouldAssignSiteForUrl override is disabled (i.e. returns true). | 
|  | browser_client.set_assign_site_for_url(true); | 
|  | const GURL regular_url("http://www.yahoo.com"); | 
|  | std::vector<std::unique_ptr<NavigationEntry>> entries; | 
|  | std::unique_ptr<NavigationEntry> new_entry = | 
|  | NavigationControllerImpl::CreateNavigationEntry( | 
|  | regular_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, | 
|  | std::string(), browser_context()); | 
|  | new_entry->SetPageID(0); | 
|  | entries.push_back(std::move(new_entry)); | 
|  | controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries); | 
|  | ASSERT_EQ(0u, entries.size()); | 
|  |  | 
|  | ASSERT_EQ(1, controller().GetEntryCount()); | 
|  | controller().GoToIndex(0); | 
|  | NavigationEntry* entry = controller().GetPendingEntry(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 0, entry->GetUniqueID(), false, | 
|  | regular_url, ui::PAGE_TRANSITION_RELOAD); | 
|  | EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); | 
|  | EXPECT_TRUE(orig_instance->HasSite()); | 
|  |  | 
|  | // Navigate to another site and verify that a new SiteInstance was created. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry = controller().GetPendingEntry(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(contents()->GetPendingMainFrame(), 2, | 
|  | entry->GetUniqueID(), true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_NE(orig_instance, contents()->GetSiteInstance()); | 
|  |  | 
|  | // Cleanup. | 
|  | DeleteContents(); | 
|  | } | 
|  |  | 
|  | // Test that we can find an opener RVH even if it's pending. | 
|  | // http://crbug.com/176252. | 
|  | TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to a URL. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Start to navigate first tab to a new site, so that it has a pending RVH. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  | SiteInstance* instance = pending_rfh->GetSiteInstance(); | 
|  |  | 
|  | // While it is still pending, simulate opening a new tab with the first tab | 
|  | // as its opener.  This will call CreateOpenerProxies on the opener to ensure | 
|  | // that an RVH exists. | 
|  | std::unique_ptr<TestWebContents> popup( | 
|  | TestWebContents::Create(browser_context(), instance)); | 
|  | popup->SetOpener(contents()); | 
|  | contents()->GetRenderManager()->CreateOpenerProxies(instance, nullptr); | 
|  |  | 
|  | // If swapped out is forbidden, a new proxy should be created for the opener | 
|  | // in |instance|, and we should ensure that its routing ID is returned here. | 
|  | // Otherwise, we should find the pending RFH and not create a new proxy. | 
|  | int opener_frame_routing_id = | 
|  | popup->GetRenderManager()->GetOpenerRoutingID(instance); | 
|  | RenderFrameProxyHost* proxy = | 
|  | contents()->GetRenderManager()->GetRenderFrameProxyHost(instance); | 
|  | EXPECT_TRUE(proxy); | 
|  | EXPECT_EQ(proxy->GetRoutingID(), opener_frame_routing_id); | 
|  |  | 
|  | // Ensure that committing the navigation removes the proxy. | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->TestDidNavigate(pending_rfh, 2, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE( | 
|  | contents()->GetRenderManager()->GetRenderFrameProxyHost(instance)); | 
|  | } | 
|  |  | 
|  | // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site, | 
|  | // to determine whether a navigation is cross-site. | 
|  | TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Open a related contents to a second site. | 
|  | std::unique_ptr<TestWebContents> contents2( | 
|  | TestWebContents::Create(browser_context(), instance1)); | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | contents2->GetController().LoadURL(url2, Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | entry_id = contents2->GetController().GetPendingEntry()->GetUniqueID(); | 
|  | contents2->GetMainFrame()->PrepareForCommit(); | 
|  |  | 
|  | // The first RVH in contents2 isn't live yet, so we shortcut the cross site | 
|  | // pending. | 
|  | TestRenderFrameHost* rfh2 = contents2->GetMainFrame(); | 
|  | EXPECT_FALSE(contents2->CrossProcessNavigationPending()); | 
|  | contents2->TestDidNavigate(rfh2, 2, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2 = contents2->GetSiteInstance(); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_FALSE(contents2->CrossProcessNavigationPending()); | 
|  |  | 
|  | // Simulate a link click in first contents to second site.  Doesn't switch | 
|  | // SiteInstances, because we don't intercept Blink navigations. | 
|  | orig_rfh->SendRendererInitiatedNavigationRequest(url2, true); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, 0, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance3 = contents()->GetSiteInstance(); | 
|  | EXPECT_EQ(instance1, instance3); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  |  | 
|  | // Navigate to the new site.  Doesn't switch SiteInstancees, because we | 
|  | // compare against the current URL, not the SiteInstance's site. | 
|  | const GURL url3("http://mail.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 3, entry_id, true, url3, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance4 = contents()->GetSiteInstance(); | 
|  | EXPECT_EQ(instance1, instance4); | 
|  | } | 
|  |  | 
|  | // Test that the onbeforeunload and onunload handlers run when navigating | 
|  | // across site boundaries. | 
|  | TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use first RenderViewHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Navigate to new site, but simulate an onbeforeunload denial. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | base::TimeTicks now = base::TimeTicks::Now(); | 
|  | orig_rfh->OnMessageReceived( | 
|  | FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); | 
|  | EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Navigate again, but simulate an onbeforeunload approval. | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | now = base::TimeTicks::Now(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  |  | 
|  | // We won't hear DidNavigate until the onunload handler has finished running. | 
|  |  | 
|  | // DidNavigate from the pending page. | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  | } | 
|  |  | 
|  | // Test that during a slow cross-site navigation, the original renderer can | 
|  | // navigate to a different URL and have it displayed, canceling the slow | 
|  | // navigation. | 
|  | TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use first RenderFrameHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Navigate to new site, simulating an onbeforeunload approval. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  |  | 
|  | // Suppose the original renderer navigates before the new one is ready. | 
|  | orig_rfh->SendNavigate(2, 0, true, GURL("http://www.google.com/foo")); | 
|  |  | 
|  | // Verify that the pending navigation is cancelled. | 
|  | EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(instance1, instance2); | 
|  | EXPECT_EQ(nullptr, contents()->GetPendingMainFrame()); | 
|  | } | 
|  |  | 
|  | // Tests that if we go back twice (same-site then cross-site), and the same-site | 
|  | // RFH commits first, the cross-site RFH's navigation is canceled. | 
|  | // TODO(avi,creis): Consider changing this behavior to better match the user's | 
|  | // intent. | 
|  | TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { | 
|  | // Start with a web ui page, which gets a new RVH with WebUI bindings. | 
|  | GURL url1(std::string(kChromeUIScheme) + "://" + | 
|  | std::string(kChromeUIGpuHost)); | 
|  | controller().LoadURL( | 
|  | url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | TestRenderFrameHost* webui_rfh = contents()->GetMainFrame(); | 
|  | webui_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(webui_rfh, 1, entry_id, true, url1, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry1 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(webui_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(url1, entry1->GetURL()); | 
|  | EXPECT_EQ(instance1, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); | 
|  | EXPECT_TRUE(webui_rfh->GetRenderViewHost()->GetEnabledBindings() & | 
|  | BINDINGS_POLICY_WEB_UI); | 
|  |  | 
|  | // Navigate to new site. | 
|  | const GURL url2("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | TestRenderFrameHost* google_rfh = contents()->GetPendingMainFrame(); | 
|  |  | 
|  | // Simulate beforeunload approval. | 
|  | EXPECT_TRUE(webui_rfh->is_waiting_for_beforeunload_ack()); | 
|  | webui_rfh->PrepareForCommit(); | 
|  |  | 
|  | // DidNavigate from the pending page. | 
|  | contents()->TestDidNavigate(google_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry2 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(google_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(url2, entry2->GetURL()); | 
|  | EXPECT_EQ(instance2, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); | 
|  | EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() & | 
|  | BINDINGS_POLICY_WEB_UI); | 
|  |  | 
|  | // Navigate to third page on same site. | 
|  | const GURL url3("http://news.google.com"); | 
|  | controller().LoadURL( | 
|  | url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(google_rfh, 2, entry_id, true, url3, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry3 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance3 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(google_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(instance2, instance3); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(url3, entry3->GetURL()); | 
|  | EXPECT_EQ(instance3, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); | 
|  |  | 
|  | // Go back within the site. | 
|  | controller().GoBack(); | 
|  | NavigationEntry* goback_entry = controller().GetPendingEntry(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(entry2, controller().GetPendingEntry()); | 
|  |  | 
|  | // Before that commits, go back again. | 
|  | controller().GoBack(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_TRUE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(entry1, controller().GetPendingEntry()); | 
|  |  | 
|  | // Simulate beforeunload approval. | 
|  | EXPECT_TRUE(google_rfh->is_waiting_for_beforeunload_ack()); | 
|  | base::TimeTicks now = base::TimeTicks::Now(); | 
|  | google_rfh->PrepareForCommit(); | 
|  | google_rfh->OnMessageReceived( | 
|  | FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); | 
|  |  | 
|  | // DidNavigate from the first back. This aborts the second back's pending RFH. | 
|  | contents()->TestDidNavigate(google_rfh, 1, goback_entry->GetUniqueID(), false, | 
|  | url2, ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // We should commit this page and forget about the second back. | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_FALSE(controller().GetPendingEntry()); | 
|  | EXPECT_EQ(google_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); | 
|  |  | 
|  | // We should not have corrupted the NTP entry. | 
|  | EXPECT_EQ(instance3, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); | 
|  | EXPECT_EQ(instance2, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); | 
|  | EXPECT_EQ(instance1, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); | 
|  | EXPECT_EQ(url1, entry1->GetURL()); | 
|  | } | 
|  |  | 
|  | // Tests that if we go back twice (same-site then cross-site), and the cross- | 
|  | // site RFH commits first, we ignore the now-swapped-out RFH's commit. | 
|  | TEST_F(WebContentsImplTest, CrossSiteNavigationBackOldNavigationIgnored) { | 
|  | // Start with a web ui page, which gets a new RVH with WebUI bindings. | 
|  | GURL url1(std::string(kChromeUIScheme) + "://" + | 
|  | std::string(kChromeUIGpuHost)); | 
|  | controller().LoadURL(url1, Referrer(), ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | TestRenderFrameHost* webui_rfh = contents()->GetMainFrame(); | 
|  | webui_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(webui_rfh, 1, entry_id, true, url1, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry1 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance1 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(webui_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(url1, entry1->GetURL()); | 
|  | EXPECT_EQ(instance1, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); | 
|  | EXPECT_TRUE(webui_rfh->GetRenderViewHost()->GetEnabledBindings() & | 
|  | BINDINGS_POLICY_WEB_UI); | 
|  |  | 
|  | // Navigate to new site. | 
|  | const GURL url2("http://www.google.com"); | 
|  | controller().LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | TestRenderFrameHost* google_rfh = contents()->GetPendingMainFrame(); | 
|  |  | 
|  | // Simulate beforeunload approval. | 
|  | EXPECT_TRUE(webui_rfh->is_waiting_for_beforeunload_ack()); | 
|  | webui_rfh->PrepareForCommit(); | 
|  |  | 
|  | // DidNavigate from the pending page. | 
|  | contents()->TestDidNavigate(google_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry2 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance2 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(google_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_NE(instance1, instance2); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(url2, entry2->GetURL()); | 
|  | EXPECT_EQ(instance2, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); | 
|  | EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() & | 
|  | BINDINGS_POLICY_WEB_UI); | 
|  |  | 
|  | // Navigate to third page on same site. | 
|  | const GURL url3("http://news.google.com"); | 
|  | controller().LoadURL(url3, Referrer(), ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(google_rfh, 2, entry_id, true, url3, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntry* entry3 = controller().GetLastCommittedEntry(); | 
|  | SiteInstance* instance3 = contents()->GetSiteInstance(); | 
|  |  | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(google_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(instance2, instance3); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(url3, entry3->GetURL()); | 
|  | EXPECT_EQ(instance3, | 
|  | NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); | 
|  |  | 
|  | // Go back within the site. | 
|  | controller().GoBack(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(entry2, controller().GetPendingEntry()); | 
|  |  | 
|  | // Before that commits, go back again. | 
|  | controller().GoBack(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_TRUE(contents()->GetPendingMainFrame()); | 
|  | EXPECT_EQ(entry1, controller().GetPendingEntry()); | 
|  | webui_rfh = contents()->GetPendingMainFrame(); | 
|  |  | 
|  | // DidNavigate from the second back. | 
|  | contents()->TestDidNavigate(webui_rfh, 1, entry1->GetUniqueID(), false, url1, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // That should have landed us on the first entry. | 
|  | EXPECT_EQ(entry1, controller().GetLastCommittedEntry()); | 
|  |  | 
|  | // When the second back commits, it should be ignored. | 
|  | contents()->TestDidNavigate(google_rfh, 1, entry2->GetUniqueID(), false, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(entry1, controller().GetLastCommittedEntry()); | 
|  | } | 
|  |  | 
|  | // Test that during a slow cross-site navigation, a sub-frame navigation in the | 
|  | // original renderer will not cancel the slow navigation (bug 42029). | 
|  | TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to URL.  First URL should use the original RenderFrameHost. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Start navigating to new site. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  |  | 
|  | // Simulate a sub-frame navigation arriving and ensure the RVH is still | 
|  | // waiting for a before unload response. | 
|  | TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe"); | 
|  | child_rfh->SendNavigateWithTransition(1, 0, false, | 
|  | GURL("http://google.com/frame"), | 
|  | ui::PAGE_TRANSITION_AUTO_SUBFRAME); | 
|  | EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  |  | 
|  | // Now simulate the onbeforeunload approval and verify the navigation is | 
|  | // not canceled. | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void SetAsNonUserGesture(FrameHostMsg_DidCommitProvisionalLoad_Params* params) { | 
|  | params->gesture = NavigationGestureAuto; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that a cross-site navigation is not preempted if the previous | 
|  | // renderer sends a FrameNavigate message just before being told to stop. | 
|  | // We should only preempt the cross-site navigation if the previous renderer | 
|  | // has started a new navigation.  See http://crbug.com/79176. | 
|  | TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) { | 
|  | // Navigate to WebUI URL. | 
|  | const GURL url("chrome://gpu"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry1_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  |  | 
|  | // Navigate to new site, with the beforeunload request in flight. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry2_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_TRUE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | EXPECT_NE(orig_rfh, pending_rfh); | 
|  |  | 
|  | // Suppose the first navigation tries to commit now, with a | 
|  | // FrameMsg_Stop in flight.  This should not cancel the pending navigation, | 
|  | // but it should act as if the beforeunload ack arrived. | 
|  | orig_rfh->SendNavigateWithModificationCallback( | 
|  | 1, entry1_id, true, url, base::Bind(SetAsNonUserGesture)); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_FALSE(orig_rfh->is_waiting_for_beforeunload_ack()); | 
|  | // It should commit. | 
|  | ASSERT_EQ(1, controller().GetEntryCount()); | 
|  | EXPECT_EQ(url, controller().GetLastCommittedEntry()->GetURL()); | 
|  |  | 
|  | // The pending navigation should be able to commit successfully. | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry2_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  | } | 
|  |  | 
|  | // Test that NavigationEntries have the correct page state after going | 
|  | // forward and back.  Prevents regression for bug 1116137. | 
|  | TEST_F(WebContentsImplTest, NavigationEntryContentState) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to URL.  There should be no committed entry yet. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | NavigationEntry* entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_EQ(nullptr, entry); | 
|  |  | 
|  | // Committed entry should have page state after DidNavigate. | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_TRUE(entry->GetPageState().IsValid()); | 
|  |  | 
|  | // Navigate to same site. | 
|  | const GURL url2("http://images.google.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_TRUE(entry->GetPageState().IsValid()); | 
|  |  | 
|  | // Committed entry should have page state after DidNavigate. | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_TRUE(entry->GetPageState().IsValid()); | 
|  |  | 
|  | // Now go back.  Committed entry should still have page state. | 
|  | controller().GoBack(); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, false, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_TRUE(entry->GetPageState().IsValid()); | 
|  | } | 
|  |  | 
|  | // Test that NavigationEntries have the correct page state and SiteInstance | 
|  | // state after opening a new window to about:blank.  Prevents regression for | 
|  | // bugs b/1116137 and http://crbug.com/111975. | 
|  | TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) { | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to about:blank. | 
|  | const GURL url(url::kAboutBlankURL); | 
|  | orig_rfh->SendRendererInitiatedNavigationRequest(url, false); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, 0, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Should have a page state here. | 
|  | NavigationEntry* entry = controller().GetLastCommittedEntry(); | 
|  | EXPECT_TRUE(entry->GetPageState().IsValid()); | 
|  |  | 
|  | // The SiteInstance should be available for other navigations to use. | 
|  | NavigationEntryImpl* entry_impl = | 
|  | NavigationEntryImpl::FromNavigationEntry(entry); | 
|  | EXPECT_FALSE(entry_impl->site_instance()->HasSite()); | 
|  | int32_t site_instance_id = entry_impl->site_instance()->GetId(); | 
|  |  | 
|  | // Navigating to a normal page should not cause a process swap. | 
|  | const GURL new_url("http://www.google.com"); | 
|  | controller().LoadURL(new_url, Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry = controller().GetPendingEntry(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, entry->GetUniqueID(), true, new_url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry( | 
|  | controller().GetLastCommittedEntry()); | 
|  | EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId()); | 
|  | EXPECT_TRUE(entry_impl2->site_instance()->HasSite()); | 
|  | } | 
|  |  | 
|  | // Tests that fullscreen is exited throughout the object hierarchy when | 
|  | // navigating to a new page. | 
|  | TEST_F(WebContentsImplTest, NavigationExitsFullscreen) { | 
|  | FakeFullscreenDelegate fake_delegate; | 
|  | contents()->SetDelegate(&fake_delegate); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to a site. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Toggle fullscreen mode on (as if initiated via IPC from renderer). | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  | orig_rfh->OnMessageReceived( | 
|  | FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true)); | 
|  | EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | // Navigate to a new site. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | TestRenderFrameHost* const pending_rfh = contents()->GetPendingMainFrame(); | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Confirm fullscreen has exited. | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | contents()->SetDelegate(nullptr); | 
|  | } | 
|  |  | 
|  | // Tests that fullscreen is exited throughout the object hierarchy when | 
|  | // instructing NavigationController to GoBack() or GoForward(). | 
|  | TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) { | 
|  | FakeFullscreenDelegate fake_delegate; | 
|  | contents()->SetDelegate(&fake_delegate); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Navigate to a site. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Now, navigate to another page on the same site. | 
|  | const GURL url2("http://www.google.com/search?q=kittens"); | 
|  | controller().LoadURL( | 
|  | url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | contents()->TestDidNavigate(orig_rfh, 2, entry_id, true, url2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  |  | 
|  | // Sanity-check: Confirm we're not starting out in fullscreen mode. | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | // Toggle fullscreen mode on (as if initiated via IPC from renderer). | 
|  | orig_rfh->OnMessageReceived( | 
|  | FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true)); | 
|  | EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | // Navigate backward (or forward). | 
|  | if (i == 0) | 
|  | controller().GoBack(); | 
|  | else | 
|  | controller().GoForward(); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | orig_rfh->PrepareForCommit(); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | contents()->TestDidNavigate(orig_rfh, i + 1, entry_id, false, url, | 
|  | ui::PAGE_TRANSITION_FORWARD_BACK); | 
|  | orig_rfh->SimulateNavigationStop(); | 
|  |  | 
|  | // Confirm fullscreen has exited. | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  | } | 
|  |  | 
|  | contents()->SetDelegate(nullptr); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) { | 
|  | FakeValidationMessageDelegate fake_delegate; | 
|  | contents()->SetDelegate(&fake_delegate); | 
|  | EXPECT_FALSE(fake_delegate.hide_validation_message_was_called()); | 
|  |  | 
|  | // Initialize the RenderFrame and then simulate crashing the renderer | 
|  | // process. | 
|  | contents()->GetMainFrame()->InitializeRenderFrameIfNeeded(); | 
|  | contents()->GetMainFrame()->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | // Confirm HideValidationMessage was called. | 
|  | EXPECT_TRUE(fake_delegate.hide_validation_message_was_called()); | 
|  |  | 
|  | contents()->SetDelegate(nullptr); | 
|  | } | 
|  |  | 
|  | // Tests that fullscreen is exited throughout the object hierarchy on a renderer | 
|  | // crash. | 
|  | TEST_F(WebContentsImplTest, CrashExitsFullscreen) { | 
|  | FakeFullscreenDelegate fake_delegate; | 
|  | contents()->SetDelegate(&fake_delegate); | 
|  |  | 
|  | // Navigate to a site. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | main_test_rfh()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(contents()->GetMainFrame(), 1, entry_id, true, | 
|  | url, ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Toggle fullscreen mode on (as if initiated via IPC from renderer). | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  | contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_ToggleFullscreen( | 
|  | contents()->GetMainFrame()->GetRoutingID(), true)); | 
|  | EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | // Crash the renderer. | 
|  | main_test_rfh()->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | // Confirm fullscreen has exited. | 
|  | EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); | 
|  | EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); | 
|  |  | 
|  | contents()->SetDelegate(nullptr); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Interstitial Tests | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | // Test navigating to a page (with the navigation initiated from the browser, | 
|  | // as when a URL is typed in the location bar) that shows an interstitial and | 
|  | // creates a new navigation entry, then hiding it without proceeding. | 
|  | TEST_F(WebContentsImplTest, | 
|  | ShowInterstitialFromBrowserWithNewNavigationDontProceed) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Initiate a browser navigation that will trigger the interstitial. | 
|  | controller().LoadURL(GURL("http://www.evil.com"), Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | NavigationEntry* entry = controller().GetPendingEntry(); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url2); | 
|  |  | 
|  | // Now don't proceed. | 
|  | interstitial->DontProceed(); | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page (with the navigation initiated from the renderer, | 
|  | // as when clicking on a link in the page) that shows an interstitial and | 
|  | // creates a new navigation entry, then hiding it without proceeding. | 
|  | TEST_F(WebContentsImplTest, | 
|  | ShowInterstitialFromRendererWithNewNavigationDontProceed) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial (no pending entry, the interstitial would have been | 
|  | // triggered by clicking on a link). | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url2); | 
|  |  | 
|  | // Now don't proceed. | 
|  | interstitial->DontProceed(); | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial without creating a new | 
|  | // navigation entry (this happens when the interstitial is triggered by a | 
|  | // sub-resource in the page), then hiding it without proceeding. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), false, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, 0, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | // The URL specified to the interstitial should have been ignored. | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  |  | 
|  | // Now don't proceed. | 
|  | interstitial->DontProceed(); | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page (with the navigation initiated from the browser, | 
|  | // as when a URL is typed in the location bar) that shows an interstitial and | 
|  | // creates a new navigation entry, then proceeding. | 
|  | TEST_F(WebContentsImplTest, | 
|  | ShowInterstitialFromBrowserNewNavigationProceed) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Initiate a browser navigation that will trigger the interstitial | 
|  | controller().LoadURL(GURL("http://www.evil.com"), Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url2); | 
|  |  | 
|  | // Then proceed. | 
|  | interstitial->Proceed(); | 
|  | // The interstitial should show until the new navigation commits. | 
|  | RunAllPendingInMessageLoop(); | 
|  | ASSERT_FALSE(deleted); | 
|  | EXPECT_EQ(TestInterstitialPage::OKED, state); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  |  | 
|  | // Simulate the navigation to the page, that's when the interstitial gets | 
|  | // hidden. | 
|  | GURL url3("http://www.thepage.com"); | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->GetMainFrame()->SendNavigate(2, 0, true, url3); | 
|  |  | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url3); | 
|  |  | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page (with the navigation initiated from the renderer, | 
|  | // as when clicking on a link in the page) that shows an interstitial and | 
|  | // creates a new navigation entry, then proceeding. | 
|  | TEST_F(WebContentsImplTest, | 
|  | ShowInterstitialFromRendererNewNavigationProceed) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url2); | 
|  |  | 
|  | // Then proceed. | 
|  | interstitial->Proceed(); | 
|  | // The interstitial should show until the new navigation commits. | 
|  | RunAllPendingInMessageLoop(); | 
|  | ASSERT_FALSE(deleted); | 
|  | EXPECT_EQ(TestInterstitialPage::OKED, state); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  |  | 
|  | // Simulate the navigation to the page, that's when the interstitial gets | 
|  | // hidden. | 
|  | GURL url3("http://www.thepage.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url3); | 
|  |  | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url3); | 
|  |  | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial without creating a new | 
|  | // navigation entry (this happens when the interstitial is triggered by a | 
|  | // sub-resource in the page), then proceeding. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), false, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | // The interstitial should not show until its navigation has committed. | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | // Let's commit the interstitial navigation. | 
|  | interstitial->TestDidNavigate(1, 0, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_TRUE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_TRUE(contents()->GetInterstitialPage() == interstitial); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | // The URL specified to the interstitial should have been ignored. | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  |  | 
|  | // Then proceed. | 
|  | interstitial->Proceed(); | 
|  | // Since this is not a new navigation, the previous page is dismissed right | 
|  | // away and shows the original page. | 
|  | EXPECT_EQ(TestInterstitialPage::OKED, state); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == url1); | 
|  |  | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, then navigating away. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) { | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url); | 
|  |  | 
|  | // While interstitial showing, navigate to a new URL. | 
|  | const GURL url2("http://www.yahoo.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2); | 
|  |  | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, then going back. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL interstitial_url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(2, interstitial_entry_id, true, | 
|  | interstitial_url); | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  |  | 
|  | // While the interstitial is showing, go back. This will dismiss the | 
|  | // interstitial and not initiate a navigation, but just show the existing | 
|  | // RenderFrameHost. | 
|  | controller().GoBack(); | 
|  |  | 
|  | // Make sure we are back to the original page and that the interstitial is | 
|  | // gone. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_TRUE(entry); | 
|  | EXPECT_EQ(url1.spec(), entry->GetURL().spec()); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, has a renderer crash, | 
|  | // and then goes back. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  | NavigationEntry* entry = controller().GetLastCommittedEntry(); | 
|  |  | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL interstitial_url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(2, interstitial_entry_id, true, | 
|  | interstitial_url); | 
|  |  | 
|  | // Crash the renderer | 
|  | contents()->GetMainFrame()->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | // While the interstitial is showing, go back. This will dismiss the | 
|  | // interstitial and not initiate a navigation, but just show the existing | 
|  | // RenderFrameHost. | 
|  | controller().GoBack(); | 
|  |  | 
|  | // Make sure we are back to the original page and that the interstitial is | 
|  | // gone. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | entry = controller().GetVisibleEntry(); | 
|  | ASSERT_TRUE(entry); | 
|  | EXPECT_EQ(url1.spec(), entry->GetURL().spec()); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, has the renderer crash, | 
|  | // and then navigates to the interstitial. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL interstitial_url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  |  | 
|  | // Crash the renderer | 
|  | contents()->GetMainFrame()->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | interstitial->TestDidNavigate(2, interstitial_entry_id, true, | 
|  | interstitial_url); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, then close the | 
|  | // contents. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialThenCloseTab) { | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url); | 
|  |  | 
|  | // Now close the contents. | 
|  | DeleteContents(); | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test navigating to a page that shows an interstitial, then close the | 
|  | // contents. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) { | 
|  | // Show interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url); | 
|  | TestRenderFrameHost* rfh = | 
|  | static_cast<TestRenderFrameHost*>(interstitial->GetMainFrame()); | 
|  |  | 
|  | // Now close the contents. | 
|  | DeleteContents(); | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  |  | 
|  | // Before the interstitial has a chance to process its shutdown task, | 
|  | // simulate quitting the browser.  This goes through all processes and | 
|  | // tells them to destruct. | 
|  | rfh->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test that after Proceed is called and an interstitial is still shown, no more | 
|  | // commands get executed. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  |  | 
|  | // Run a command. | 
|  | EXPECT_EQ(0, interstitial->command_received_count()); | 
|  | interstitial->TestDomOperationResponse("toto"); | 
|  | EXPECT_EQ(1, interstitial->command_received_count()); | 
|  |  | 
|  | // Then proceed. | 
|  | interstitial->Proceed(); | 
|  | RunAllPendingInMessageLoop(); | 
|  | ASSERT_FALSE(deleted); | 
|  |  | 
|  | // While the navigation to the new page is pending, send other commands, they | 
|  | // should be ignored. | 
|  | interstitial->TestDomOperationResponse("hello"); | 
|  | interstitial->TestDomOperationResponse("hi"); | 
|  | EXPECT_EQ(1, interstitial->command_received_count()); | 
|  | } | 
|  |  | 
|  | // Test showing an interstitial while another interstitial is already showing. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL start_url("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, start_url); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state1 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted1 = false; | 
|  | GURL url1("http://interstitial1"); | 
|  | TestInterstitialPage* interstitial1 = | 
|  | new TestInterstitialPage(contents(), true, url1, &state1, &deleted1); | 
|  | TestInterstitialPageStateGuard state_guard1(interstitial1); | 
|  | interstitial1->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial1->TestDidNavigate(1, interstitial_entry_id, true, url1); | 
|  |  | 
|  | // Now show another interstitial. | 
|  | TestInterstitialPage::InterstitialState state2 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted2 = false; | 
|  | GURL url2("http://interstitial2"); | 
|  | TestInterstitialPage* interstitial2 = | 
|  | new TestInterstitialPage(contents(), true, url2, &state2, &deleted2); | 
|  | TestInterstitialPageStateGuard state_guard2(interstitial2); | 
|  | interstitial2->Show(); | 
|  | interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial2->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  |  | 
|  | // Showing interstitial2 should have caused interstitial1 to go away. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state1); | 
|  | EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted1); | 
|  | ASSERT_FALSE(deleted2); | 
|  |  | 
|  | // Let's make sure interstitial2 is working as intended. | 
|  | interstitial2->Proceed(); | 
|  | GURL landing_url("http://www.thepage.com"); | 
|  | contents()->GetMainFrame()->SendNavigate(2, 0, true, landing_url); | 
|  |  | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == landing_url); | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted2); | 
|  | } | 
|  |  | 
|  | // Test showing an interstitial, proceeding and then navigating to another | 
|  | // interstitial. | 
|  | TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) { | 
|  | // Navigate to a page so we have a navigation entry in the controller. | 
|  | GURL start_url("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, start_url); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state1 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted1 = false; | 
|  | GURL url1("http://interstitial1"); | 
|  | TestInterstitialPage* interstitial1 = | 
|  | new TestInterstitialPage(contents(), true, url1, &state1, &deleted1); | 
|  | TestInterstitialPageStateGuard state_guard1(interstitial1); | 
|  | interstitial1->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial1->TestDidNavigate(1, interstitial_entry_id, true, url1); | 
|  |  | 
|  | // Take action.  The interstitial won't be hidden until the navigation is | 
|  | // committed. | 
|  | interstitial1->Proceed(); | 
|  | EXPECT_EQ(TestInterstitialPage::OKED, state1); | 
|  |  | 
|  | // Now show another interstitial (simulating the navigation causing another | 
|  | // interstitial). | 
|  | TestInterstitialPage::InterstitialState state2 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted2 = false; | 
|  | GURL url2("http://interstitial2"); | 
|  | TestInterstitialPage* interstitial2 = | 
|  | new TestInterstitialPage(contents(), true, url2, &state2, &deleted2); | 
|  | TestInterstitialPageStateGuard state_guard2(interstitial2); | 
|  | interstitial2->Show(); | 
|  | interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial2->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  |  | 
|  | // Showing interstitial2 should have caused interstitial1 to go away. | 
|  | EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted1); | 
|  | ASSERT_FALSE(deleted2); | 
|  |  | 
|  | // Let's make sure interstitial2 is working as intended. | 
|  | interstitial2->Proceed(); | 
|  | GURL landing_url("http://www.thepage.com"); | 
|  | contents()->GetMainFrame()->SendNavigate(2, 0, true, landing_url); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted2); | 
|  | EXPECT_FALSE(contents()->ShowingInterstitialPage()); | 
|  | EXPECT_EQ(nullptr, contents()->GetInterstitialPage()); | 
|  | NavigationEntry* entry = controller().GetVisibleEntry(); | 
|  | ASSERT_NE(nullptr, entry); | 
|  | EXPECT_TRUE(entry->GetURL() == landing_url); | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  | } | 
|  |  | 
|  | // Test that navigating away from an interstitial while it's loading cause it | 
|  | // not to show. | 
|  | TEST_F(WebContentsImplTest, NavigateBeforeInterstitialShows) { | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL interstitial_url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  |  | 
|  | // Let's simulate a navigation initiated from the browser before the | 
|  | // interstitial finishes loading. | 
|  | const GURL url("http://www.google.com"); | 
|  | controller().LoadURL( | 
|  | url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | EXPECT_FALSE(interstitial->is_showing()); | 
|  | RunAllPendingInMessageLoop(); | 
|  | ASSERT_FALSE(deleted); | 
|  |  | 
|  | // Now let's make the interstitial navigation commit. | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, | 
|  | interstitial_url); | 
|  |  | 
|  | // After it loaded the interstitial should be gone. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Test that a new request to show an interstitial while an interstitial is | 
|  | // pending does not cause problems. htp://crbug/29655 and htp://crbug/9442. | 
|  | TEST_F(WebContentsImplTest, TwoQuickInterstitials) { | 
|  | GURL interstitial_url("http://interstitial"); | 
|  |  | 
|  | // Show a first interstitial. | 
|  | TestInterstitialPage::InterstitialState state1 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted1 = false; | 
|  | TestInterstitialPage* interstitial1 = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state1, &deleted1); | 
|  | TestInterstitialPageStateGuard state_guard1(interstitial1); | 
|  | interstitial1->Show(); | 
|  |  | 
|  | // Show another interstitial on that same contents before the first one had | 
|  | // time to load. | 
|  | TestInterstitialPage::InterstitialState state2 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted2 = false; | 
|  | TestInterstitialPage* interstitial2 = | 
|  | new TestInterstitialPage(contents(), true, interstitial_url, | 
|  | &state2, &deleted2); | 
|  | TestInterstitialPageStateGuard state_guard2(interstitial2); | 
|  | interstitial2->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  |  | 
|  | // The first interstitial should have been closed and deleted. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state1); | 
|  | // The 2nd one should still be OK. | 
|  | EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); | 
|  |  | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted1); | 
|  | ASSERT_FALSE(deleted2); | 
|  |  | 
|  | // Make the interstitial navigation commit it should be showing. | 
|  | interstitial2->TestDidNavigate(1, interstitial_entry_id, true, | 
|  | interstitial_url); | 
|  | EXPECT_EQ(interstitial2, contents()->GetInterstitialPage()); | 
|  | } | 
|  |  | 
|  | // Test showing an interstitial and have its renderer crash. | 
|  | TEST_F(WebContentsImplTest, InterstitialCrasher) { | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | // Simulate a renderer crash before the interstitial is shown. | 
|  | interstitial->TestRenderViewTerminated( | 
|  | base::TERMINATION_STATUS_PROCESS_CRASHED, -1); | 
|  | // The interstitial should have been dismissed. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  |  | 
|  | // Now try again but this time crash the intersitial after it was shown. | 
|  | interstitial = | 
|  | new TestInterstitialPage(contents(), true, url, &state, &deleted); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url); | 
|  | // Simulate a renderer crash. | 
|  | interstitial->TestRenderViewTerminated( | 
|  | base::TERMINATION_STATUS_PROCESS_CRASHED, -1); | 
|  | // The interstitial should have been dismissed. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | } | 
|  |  | 
|  | // Tests that showing an interstitial as a result of a browser initiated | 
|  | // navigation while an interstitial is showing does not remove the pending | 
|  | // entry (see http://crbug.com/9791). | 
|  | TEST_F(WebContentsImplTest, NewInterstitialDoesNotCancelPendingEntry) { | 
|  | const char kUrl[] = "http://www.badguys.com/"; | 
|  | const GURL kGURL(kUrl); | 
|  |  | 
|  | // Start a navigation to a page | 
|  | contents()->GetController().LoadURL( | 
|  | kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  |  | 
|  | // Simulate that navigation triggering an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, kGURL, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, kGURL); | 
|  |  | 
|  | // Initiate a new navigation from the browser that also triggers an | 
|  | // interstitial. | 
|  | contents()->GetController().LoadURL( | 
|  | kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | TestInterstitialPage::InterstitialState state2 = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted2 = false; | 
|  | TestInterstitialPage* interstitial2 = | 
|  | new TestInterstitialPage(contents(), true, kGURL, &state2, &deleted2); | 
|  | TestInterstitialPageStateGuard state_guard2(interstitial2); | 
|  | interstitial2->Show(); | 
|  | interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial2->TestDidNavigate(1, interstitial_entry_id, true, kGURL); | 
|  |  | 
|  | // Make sure we still have an entry. | 
|  | NavigationEntry* entry = contents()->GetController().GetPendingEntry(); | 
|  | ASSERT_TRUE(entry); | 
|  | EXPECT_EQ(kUrl, entry->GetURL().spec()); | 
|  |  | 
|  | // And that the first interstitial is gone, but not the second. | 
|  | EXPECT_EQ(TestInterstitialPage::CANCELED, state); | 
|  | EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2); | 
|  | RunAllPendingInMessageLoop(); | 
|  | EXPECT_TRUE(deleted); | 
|  | EXPECT_FALSE(deleted2); | 
|  | } | 
|  |  | 
|  | // Tests that Javascript messages are not shown while an interstitial is | 
|  | // showing. | 
|  | TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) { | 
|  | const char kUrl[] = "http://www.badguys.com/"; | 
|  | const GURL kGURL(kUrl); | 
|  |  | 
|  | // Start a navigation to a page | 
|  | contents()->GetController().LoadURL( | 
|  | kGURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | main_test_rfh()->PrepareForCommit(); | 
|  | // DidNavigate from the page | 
|  | contents()->TestDidNavigate(contents()->GetMainFrame(), 1, entry_id, true, | 
|  | kGURL, ui::PAGE_TRANSITION_TYPED); | 
|  |  | 
|  | // Simulate showing an interstitial while the page is showing. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, kGURL, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, kGURL); | 
|  |  | 
|  | // While the interstitial is showing, let's simulate the hidden page | 
|  | // attempting to show a JS message. | 
|  | IPC::Message* dummy_message = new IPC::Message; | 
|  | contents()->RunJavaScriptMessage(contents()->GetMainFrame(), | 
|  | base::ASCIIToUTF16("This is an informative message"), | 
|  | base::ASCIIToUTF16("OK"), | 
|  | kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message); | 
|  | EXPECT_TRUE(contents()->last_dialog_suppressed_); | 
|  | } | 
|  |  | 
|  | // Makes sure that if the source passed to CopyStateFromAndPrune has an | 
|  | // interstitial it isn't copied over to the destination. | 
|  | TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1); | 
|  | EXPECT_EQ(1, controller().GetEntryCount()); | 
|  |  | 
|  | // Initiate a browser navigation that will trigger the interstitial | 
|  | controller().LoadURL(GURL("http://www.evil.com"), Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url2("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(contents(), true, url2, &state, &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = controller().GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url2); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_EQ(2, controller().GetEntryCount()); | 
|  |  | 
|  | // Create another NavigationController. | 
|  | GURL url3("http://foo2"); | 
|  | std::unique_ptr<TestWebContents> other_contents( | 
|  | static_cast<TestWebContents*>(CreateTestWebContents())); | 
|  | NavigationControllerImpl& other_controller = other_contents->GetController(); | 
|  | other_contents->NavigateAndCommit(url3); | 
|  | other_contents->ExpectSetHistoryOffsetAndLength(1, 2); | 
|  | other_controller.CopyStateFromAndPrune(&controller(), false); | 
|  |  | 
|  | // The merged controller should only have two entries: url1 and url2. | 
|  | ASSERT_EQ(2, other_controller.GetEntryCount()); | 
|  | EXPECT_EQ(1, other_controller.GetCurrentEntryIndex()); | 
|  | EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL()); | 
|  | EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL()); | 
|  |  | 
|  | // And the merged controller shouldn't be showing an interstitial. | 
|  | EXPECT_FALSE(other_contents->ShowingInterstitialPage()); | 
|  | } | 
|  |  | 
|  | // Makes sure that CopyStateFromAndPrune cannot be called if the target is | 
|  | // showing an interstitial. | 
|  | TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) { | 
|  | // Navigate to a page. | 
|  | GURL url1("http://www.google.com"); | 
|  | contents()->NavigateAndCommit(url1); | 
|  |  | 
|  | // Create another NavigationController. | 
|  | std::unique_ptr<TestWebContents> other_contents( | 
|  | static_cast<TestWebContents*>(CreateTestWebContents())); | 
|  | NavigationControllerImpl& other_controller = other_contents->GetController(); | 
|  |  | 
|  | // Navigate it to url2. | 
|  | GURL url2("http://foo2"); | 
|  | other_contents->NavigateAndCommit(url2); | 
|  |  | 
|  | // Show an interstitial. | 
|  | TestInterstitialPage::InterstitialState state = | 
|  | TestInterstitialPage::INVALID; | 
|  | bool deleted = false; | 
|  | GURL url3("http://interstitial"); | 
|  | TestInterstitialPage* interstitial = | 
|  | new TestInterstitialPage(other_contents.get(), true, url3, &state, | 
|  | &deleted); | 
|  | TestInterstitialPageStateGuard state_guard(interstitial); | 
|  | interstitial->Show(); | 
|  | int interstitial_entry_id = | 
|  | other_controller.GetTransientEntry()->GetUniqueID(); | 
|  | interstitial->TestDidNavigate(1, interstitial_entry_id, true, url3); | 
|  | EXPECT_TRUE(interstitial->is_showing()); | 
|  | EXPECT_EQ(2, other_controller.GetEntryCount()); | 
|  |  | 
|  | // Ensure that we do not allow calling CopyStateFromAndPrune when an | 
|  | // interstitial is showing in the target. | 
|  | EXPECT_FALSE(other_controller.CanPruneAllButLastCommitted()); | 
|  | } | 
|  |  | 
|  | // Regression test for http://crbug.com/168611 - the URLs passed by the | 
|  | // DidFinishLoad and DidFailLoadWithError IPCs should get filtered. | 
|  | TEST_F(WebContentsImplTest, FilterURLs) { | 
|  | TestWebContentsObserver observer(contents()); | 
|  |  | 
|  | // A navigation to about:whatever should always look like a navigation to | 
|  | // about:blank | 
|  | GURL url_normalized(url::kAboutBlankURL); | 
|  | GURL url_from_ipc("about:whatever"); | 
|  |  | 
|  | // We navigate the test WebContents to about:blank, since NavigateAndCommit | 
|  | // will use the given URL to create the NavigationEntry as well, and that | 
|  | // entry should contain the filtered URL. | 
|  | contents()->NavigateAndCommit(url_normalized); | 
|  |  | 
|  | // Check that an IPC with about:whatever is correctly normalized. | 
|  | contents()->TestDidFinishLoad(url_from_ipc); | 
|  |  | 
|  | EXPECT_EQ(url_normalized, observer.last_url()); | 
|  |  | 
|  | // Create and navigate another WebContents. | 
|  | std::unique_ptr<TestWebContents> other_contents( | 
|  | static_cast<TestWebContents*>(CreateTestWebContents())); | 
|  | TestWebContentsObserver other_observer(other_contents.get()); | 
|  | other_contents->NavigateAndCommit(url_normalized); | 
|  |  | 
|  | // Check that an IPC with about:whatever is correctly normalized. | 
|  | other_contents->TestDidFailLoadWithError( | 
|  | url_from_ipc, 1, base::string16(), false); | 
|  | EXPECT_EQ(url_normalized, other_observer.last_url()); | 
|  | } | 
|  |  | 
|  | // Test that if a pending contents is deleted before it is shown, we don't | 
|  | // crash. | 
|  | TEST_F(WebContentsImplTest, PendingContents) { | 
|  | std::unique_ptr<TestWebContents> other_contents( | 
|  | static_cast<TestWebContents*>(CreateTestWebContents())); | 
|  | contents()->AddPendingContents(other_contents.get()); | 
|  | int process_id = other_contents->GetRenderViewHost()->GetProcess()->GetID(); | 
|  | int route_id = other_contents->GetRenderViewHost()->GetRoutingID(); | 
|  | other_contents.reset(); | 
|  | EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, route_id)); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, CapturerOverridesPreferredSize) { | 
|  | const gfx::Size original_preferred_size(1024, 768); | 
|  | contents()->UpdatePreferredSize(original_preferred_size); | 
|  |  | 
|  | // With no capturers, expect the preferred size to be the one propagated into | 
|  | // WebContentsImpl via the RenderViewHostDelegate interface. | 
|  | EXPECT_EQ(contents()->GetCapturerCount(), 0); | 
|  | EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize()); | 
|  |  | 
|  | // Increment capturer count, but without specifying a capture size.  Expect | 
|  | // a "not set" preferred size. | 
|  | contents()->IncrementCapturerCount(gfx::Size()); | 
|  | EXPECT_EQ(1, contents()->GetCapturerCount()); | 
|  | EXPECT_EQ(gfx::Size(), contents()->GetPreferredSize()); | 
|  |  | 
|  | // Increment capturer count again, but with an overriding capture size. | 
|  | // Expect preferred size to now be overridden to the capture size. | 
|  | const gfx::Size capture_size(1280, 720); | 
|  | contents()->IncrementCapturerCount(capture_size); | 
|  | EXPECT_EQ(2, contents()->GetCapturerCount()); | 
|  | EXPECT_EQ(capture_size, contents()->GetPreferredSize()); | 
|  |  | 
|  | // Increment capturer count a third time, but the expect that the preferred | 
|  | // size is still the first capture size. | 
|  | const gfx::Size another_capture_size(720, 480); | 
|  | contents()->IncrementCapturerCount(another_capture_size); | 
|  | EXPECT_EQ(3, contents()->GetCapturerCount()); | 
|  | EXPECT_EQ(capture_size, contents()->GetPreferredSize()); | 
|  |  | 
|  | // Decrement capturer count twice, but expect the preferred size to still be | 
|  | // overridden. | 
|  | contents()->DecrementCapturerCount(); | 
|  | contents()->DecrementCapturerCount(); | 
|  | EXPECT_EQ(1, contents()->GetCapturerCount()); | 
|  | EXPECT_EQ(capture_size, contents()->GetPreferredSize()); | 
|  |  | 
|  | // Decrement capturer count, and since the count has dropped to zero, the | 
|  | // original preferred size should be restored. | 
|  | contents()->DecrementCapturerCount(); | 
|  | EXPECT_EQ(0, contents()->GetCapturerCount()); | 
|  | EXPECT_EQ(original_preferred_size, contents()->GetPreferredSize()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, CapturerPreventsHiding) { | 
|  | const gfx::Size original_preferred_size(1024, 768); | 
|  | contents()->UpdatePreferredSize(original_preferred_size); | 
|  |  | 
|  | TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>( | 
|  | contents()->GetMainFrame()->GetRenderViewHost()->GetWidget()->GetView()); | 
|  |  | 
|  | // With no capturers, setting and un-setting occlusion should change the | 
|  | // view's occlusion state. | 
|  | EXPECT_FALSE(view->is_showing()); | 
|  | contents()->WasShown(); | 
|  | EXPECT_TRUE(view->is_showing()); | 
|  | contents()->WasHidden(); | 
|  | EXPECT_FALSE(view->is_showing()); | 
|  | contents()->WasShown(); | 
|  | EXPECT_TRUE(view->is_showing()); | 
|  |  | 
|  | // Add a capturer and try to hide the contents. The view will remain visible. | 
|  | contents()->IncrementCapturerCount(gfx::Size()); | 
|  | contents()->WasHidden(); | 
|  | EXPECT_TRUE(view->is_showing()); | 
|  |  | 
|  | // Remove the capturer, and the WasHidden should take effect. | 
|  | contents()->DecrementCapturerCount(); | 
|  | EXPECT_FALSE(view->is_showing()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, CapturerPreventsOcclusion) { | 
|  | const gfx::Size original_preferred_size(1024, 768); | 
|  | contents()->UpdatePreferredSize(original_preferred_size); | 
|  |  | 
|  | TestRenderWidgetHostView* view = static_cast<TestRenderWidgetHostView*>( | 
|  | contents()->GetMainFrame()->GetRenderViewHost()->GetWidget()->GetView()); | 
|  |  | 
|  | // With no capturers, setting and un-setting occlusion should change the | 
|  | // view's occlusion state. | 
|  | EXPECT_FALSE(view->is_occluded()); | 
|  | contents()->WasOccluded(); | 
|  | EXPECT_TRUE(view->is_occluded()); | 
|  | contents()->WasUnOccluded(); | 
|  | EXPECT_FALSE(view->is_occluded()); | 
|  | contents()->WasOccluded(); | 
|  | EXPECT_TRUE(view->is_occluded()); | 
|  |  | 
|  | // Add a capturer. This should cause the view to be un-occluded. | 
|  | contents()->IncrementCapturerCount(gfx::Size()); | 
|  | EXPECT_FALSE(view->is_occluded()); | 
|  |  | 
|  | // Try to occlude the view. This will fail to propagate because of the | 
|  | // active capturer. | 
|  | contents()->WasOccluded(); | 
|  | EXPECT_FALSE(view->is_occluded()); | 
|  |  | 
|  | // Remove the capturer and try again. | 
|  | contents()->DecrementCapturerCount(); | 
|  | EXPECT_FALSE(view->is_occluded()); | 
|  | contents()->WasOccluded(); | 
|  | EXPECT_TRUE(view->is_occluded()); | 
|  | } | 
|  |  | 
|  | // Tests that GetLastActiveTime starts with a real, non-zero time and updates | 
|  | // on activity. | 
|  | TEST_F(WebContentsImplTest, GetLastActiveTime) { | 
|  | // The WebContents starts with a valid creation time. | 
|  | EXPECT_FALSE(contents()->GetLastActiveTime().is_null()); | 
|  |  | 
|  | // Reset the last active time to a known-bad value. | 
|  | contents()->last_active_time_ = base::TimeTicks(); | 
|  | ASSERT_TRUE(contents()->GetLastActiveTime().is_null()); | 
|  |  | 
|  | // Simulate activating the WebContents. The active time should update. | 
|  | contents()->WasShown(); | 
|  | EXPECT_FALSE(contents()->GetLastActiveTime().is_null()); | 
|  | } | 
|  |  | 
|  | class ContentsZoomChangedDelegate : public WebContentsDelegate { | 
|  | public: | 
|  | ContentsZoomChangedDelegate() : | 
|  | contents_zoom_changed_call_count_(0), | 
|  | last_zoom_in_(false) { | 
|  | } | 
|  |  | 
|  | int GetAndResetContentsZoomChangedCallCount() { | 
|  | int count = contents_zoom_changed_call_count_; | 
|  | contents_zoom_changed_call_count_ = 0; | 
|  | return count; | 
|  | } | 
|  |  | 
|  | bool last_zoom_in() const { | 
|  | return last_zoom_in_; | 
|  | } | 
|  |  | 
|  | // WebContentsDelegate: | 
|  | void ContentsZoomChange(bool zoom_in) override { | 
|  | contents_zoom_changed_call_count_++; | 
|  | last_zoom_in_ = zoom_in; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int contents_zoom_changed_call_count_; | 
|  | bool last_zoom_in_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ContentsZoomChangedDelegate); | 
|  | }; | 
|  |  | 
|  | // Tests that some mouseehweel events get turned into browser zoom requests. | 
|  | TEST_F(WebContentsImplTest, HandleWheelEvent) { | 
|  | using blink::WebInputEvent; | 
|  |  | 
|  | std::unique_ptr<ContentsZoomChangedDelegate> delegate( | 
|  | new ContentsZoomChangedDelegate()); | 
|  | contents()->SetDelegate(delegate.get()); | 
|  |  | 
|  | int modifiers = 0; | 
|  | // Verify that normal mouse wheel events do nothing to change the zoom level. | 
|  | blink::WebMouseWheelEvent event = | 
|  | SyntheticWebMouseWheelEventBuilder::Build(0, 0, 0, 1, modifiers, false); | 
|  | EXPECT_FALSE(contents()->HandleWheelEvent(event)); | 
|  | EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  |  | 
|  | // But whenever the ctrl modifier is applied zoom can be increased or | 
|  | // decreased. Except on MacOS where we never want to adjust zoom | 
|  | // with mousewheel. | 
|  | modifiers = WebInputEvent::ControlKey; | 
|  | event = | 
|  | SyntheticWebMouseWheelEventBuilder::Build(0, 0, 0, 1, modifiers, false); | 
|  | bool handled = contents()->HandleWheelEvent(event); | 
|  | #if defined(USE_AURA) | 
|  | EXPECT_TRUE(handled); | 
|  | EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  | EXPECT_TRUE(delegate->last_zoom_in()); | 
|  | #else | 
|  | EXPECT_FALSE(handled); | 
|  | EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  | #endif | 
|  |  | 
|  | modifiers = WebInputEvent::ControlKey | WebInputEvent::ShiftKey | | 
|  | WebInputEvent::AltKey; | 
|  | event = | 
|  | SyntheticWebMouseWheelEventBuilder::Build(0, 0, 2, -5, modifiers, false); | 
|  | handled = contents()->HandleWheelEvent(event); | 
|  | #if defined(USE_AURA) | 
|  | EXPECT_TRUE(handled); | 
|  | EXPECT_EQ(1, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  | EXPECT_FALSE(delegate->last_zoom_in()); | 
|  | #else | 
|  | EXPECT_FALSE(handled); | 
|  | EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  | #endif | 
|  |  | 
|  | // Unless there is no vertical movement. | 
|  | event = | 
|  | SyntheticWebMouseWheelEventBuilder::Build(0, 0, 2, 0, modifiers, false); | 
|  | EXPECT_FALSE(contents()->HandleWheelEvent(event)); | 
|  | EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  |  | 
|  | // Events containing precise scrolling deltas also shouldn't result in the | 
|  | // zoom being adjusted, to avoid accidental adjustments caused by | 
|  | // two-finger-scrolling on a touchpad. | 
|  | modifiers = WebInputEvent::ControlKey; | 
|  | event = | 
|  | SyntheticWebMouseWheelEventBuilder::Build(0, 0, 0, 5, modifiers, true); | 
|  | EXPECT_FALSE(contents()->HandleWheelEvent(event)); | 
|  | EXPECT_EQ(0, delegate->GetAndResetContentsZoomChangedCallCount()); | 
|  |  | 
|  | // Ensure pointers to the delegate aren't kept beyond its lifetime. | 
|  | contents()->SetDelegate(nullptr); | 
|  | } | 
|  |  | 
|  | // Tests that GetRelatedActiveContentsCount is shared between related | 
|  | // SiteInstances and includes WebContents that have not navigated yet. | 
|  | TEST_F(WebContentsImplTest, ActiveContentsCountBasic) { | 
|  | scoped_refptr<SiteInstance> instance1( | 
|  | SiteInstance::CreateForURL(browser_context(), GURL("http://a.com"))); | 
|  | scoped_refptr<SiteInstance> instance2( | 
|  | instance1->GetRelatedSiteInstance(GURL("http://b.com"))); | 
|  |  | 
|  | EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | std::unique_ptr<TestWebContents> contents1( | 
|  | TestWebContents::Create(browser_context(), instance1.get())); | 
|  | EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | std::unique_ptr<TestWebContents> contents2( | 
|  | TestWebContents::Create(browser_context(), instance1.get())); | 
|  | EXPECT_EQ(2u, instance1->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(2u, instance2->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | contents1.reset(); | 
|  | EXPECT_EQ(1u, instance1->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(1u, instance2->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | contents2.reset(); | 
|  | EXPECT_EQ(0u, instance1->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(0u, instance2->GetRelatedActiveContentsCount()); | 
|  | } | 
|  |  | 
|  | // Tests that GetRelatedActiveContentsCount is preserved correctly across | 
|  | // same-site and cross-site navigations. | 
|  | TEST_F(WebContentsImplTest, ActiveContentsCountNavigate) { | 
|  | scoped_refptr<SiteInstance> instance( | 
|  | SiteInstance::Create(browser_context())); | 
|  |  | 
|  | EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | std::unique_ptr<TestWebContents> contents( | 
|  | TestWebContents::Create(browser_context(), instance.get())); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL. | 
|  | contents->GetController().LoadURL(GURL("http://a.com/1"), | 
|  | Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  | contents->CommitPendingNavigation(); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL in the same site. | 
|  | contents->GetController().LoadURL(GURL("http://a.com/2"), | 
|  | Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  | contents->CommitPendingNavigation(); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL in a different site. | 
|  | const GURL kUrl = GURL("http://b.com"); | 
|  | contents->GetController().LoadURL(kUrl, | 
|  | Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | int entry_id = contents->GetController().GetPendingEntry()->GetUniqueID(); | 
|  | if (IsBrowserSideNavigationEnabled()) | 
|  | contents->GetMainFrame()->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  | contents->GetPendingMainFrame()->SendNavigate(1, entry_id, true, kUrl); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | contents.reset(); | 
|  | EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); | 
|  | } | 
|  |  | 
|  | // Tests that GetRelatedActiveContentsCount tracks BrowsingInstance changes | 
|  | // from WebUI. | 
|  | TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) { | 
|  | scoped_refptr<SiteInstance> instance( | 
|  | SiteInstance::Create(browser_context())); | 
|  |  | 
|  | EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | std::unique_ptr<TestWebContents> contents( | 
|  | TestWebContents::Create(browser_context(), instance.get())); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL. | 
|  | contents->NavigateAndCommit(GURL("http://a.com")); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL which sort of looks like a chrome:// url. | 
|  | contents->NavigateAndCommit(GURL("http://gpu")); | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Navigate to a URL with WebUI. This will change BrowsingInstances. | 
|  | const GURL kWebUIUrl = GURL("chrome://gpu"); | 
|  | contents->GetController().LoadURL(kWebUIUrl, | 
|  | Referrer(), | 
|  | ui::PAGE_TRANSITION_TYPED, | 
|  | std::string()); | 
|  | int entry_id = contents->GetController().GetPendingEntry()->GetUniqueID(); | 
|  | contents->GetMainFrame()->PrepareForCommit(); | 
|  | EXPECT_TRUE(contents->CrossProcessNavigationPending()); | 
|  | scoped_refptr<SiteInstance> instance_webui( | 
|  | contents->GetPendingMainFrame()->GetSiteInstance()); | 
|  | EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get())); | 
|  |  | 
|  | // At this point, contents still counts for the old BrowsingInstance. | 
|  | EXPECT_EQ(1u, instance->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | // Commit and contents counts for the new one. | 
|  | contents->GetPendingMainFrame()->SendNavigate(1, entry_id, true, kWebUIUrl); | 
|  | EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(1u, instance_webui->GetRelatedActiveContentsCount()); | 
|  |  | 
|  | contents.reset(); | 
|  | EXPECT_EQ(0u, instance->GetRelatedActiveContentsCount()); | 
|  | EXPECT_EQ(0u, instance_webui->GetRelatedActiveContentsCount()); | 
|  | } | 
|  |  | 
|  | class LoadingWebContentsObserver : public WebContentsObserver { | 
|  | public: | 
|  | explicit LoadingWebContentsObserver(WebContents* contents) | 
|  | : WebContentsObserver(contents), | 
|  | is_loading_(false) { | 
|  | } | 
|  | ~LoadingWebContentsObserver() override {} | 
|  |  | 
|  | void DidStartLoading() override { is_loading_ = true; } | 
|  | void DidStopLoading() override { is_loading_ = false; } | 
|  |  | 
|  | bool is_loading() const { return is_loading_; } | 
|  |  | 
|  | private: | 
|  | bool is_loading_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LoadingWebContentsObserver); | 
|  | }; | 
|  |  | 
|  | // Subclass of WebContentsImplTest for cases that need out-of-process iframes. | 
|  | class WebContentsImplTestWithSiteIsolation : public WebContentsImplTest { | 
|  | public: | 
|  | WebContentsImplTestWithSiteIsolation() { | 
|  | IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Ensure that DidStartLoading/DidStopLoading events balance out properly with | 
|  | // interleaving cross-process navigations in multiple subframes. | 
|  | // See https://crbug.com/448601 for details of the underlying issue. The | 
|  | // sequence of events that reproduce it are as follows: | 
|  | // * Navigate top-level frame with one subframe. | 
|  | // * Subframe navigates more than once before the top-level frame has had a | 
|  | //   chance to complete the load. | 
|  | // The subframe navigations cause the loading_frames_in_progress_ to drop down | 
|  | // to 0, while the loading_progresses_ map is not reset. | 
|  | TEST_F(WebContentsImplTestWithSiteIsolation, StartStopEventsBalance) { | 
|  | // The bug manifests itself in regular mode as well, but browser-initiated | 
|  | // navigation of subframes is only possible in --site-per-process mode within | 
|  | // unit tests. | 
|  | const GURL initial_url("about:blank"); | 
|  | const GURL main_url("http://www.chromium.org"); | 
|  | const GURL foo_url("http://foo.chromium.org"); | 
|  | const GURL bar_url("http://bar.chromium.org"); | 
|  | TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Use a WebContentsObserver to approximate the behavior of the tab's spinner. | 
|  | LoadingWebContentsObserver observer(contents()); | 
|  |  | 
|  | // Navigate the main RenderFrame, simulate the DidStartLoading, and commit. | 
|  | // The frame should still be loading. | 
|  | controller().LoadURL( | 
|  | main_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  |  | 
|  | // PlzNavigate: the RenderFrameHost does not expect to receive | 
|  | // DidStartLoading IPCs for navigations to different documents. | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | orig_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(orig_rfh->GetRoutingID(), false)); | 
|  | } | 
|  | contents()->GetMainFrame()->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(orig_rfh, 1, entry_id, true, main_url, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->CrossProcessNavigationPending()); | 
|  | EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  | EXPECT_TRUE(observer.is_loading()); | 
|  |  | 
|  | // Create a child frame to navigate multiple times. | 
|  | TestRenderFrameHost* subframe = orig_rfh->AppendChild("subframe"); | 
|  |  | 
|  | // Navigate the child frame to about:blank, which will send both | 
|  | // DidStartLoading and DidStopLoading messages. | 
|  | { | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true)); | 
|  | } | 
|  | subframe->SendNavigateWithTransition(1, 0, false, initial_url, | 
|  | ui::PAGE_TRANSITION_AUTO_SUBFRAME); | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(subframe->GetRoutingID())); | 
|  | } | 
|  |  | 
|  | // Navigate the frame to another URL, which will send again | 
|  | // DidStartLoading and DidStopLoading messages. | 
|  | { | 
|  | subframe->SendRendererInitiatedNavigationRequest(foo_url, false); | 
|  | subframe->PrepareForCommit(); | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true)); | 
|  | } | 
|  | subframe->SendNavigateWithTransition(1, 0, false, foo_url, | 
|  | ui::PAGE_TRANSITION_AUTO_SUBFRAME); | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(subframe->GetRoutingID())); | 
|  | } | 
|  |  | 
|  | // Since the main frame hasn't sent any DidStopLoading messages, it is | 
|  | // expected that the WebContents is still in loading state. | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  | EXPECT_TRUE(observer.is_loading()); | 
|  |  | 
|  | // Navigate the frame again, this time using LoadURLWithParams. This causes | 
|  | // RenderFrameHost to call into WebContents::DidStartLoading, which starts | 
|  | // the spinner. | 
|  | { | 
|  | NavigationController::LoadURLParams load_params(bar_url); | 
|  | load_params.referrer = | 
|  | Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault); | 
|  | load_params.transition_type = ui::PAGE_TRANSITION_GENERATED; | 
|  | load_params.extra_headers = "content-type: text/plain"; | 
|  | load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT; | 
|  | load_params.is_renderer_initiated = false; | 
|  | load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE; | 
|  | load_params.frame_tree_node_id = | 
|  | subframe->frame_tree_node()->frame_tree_node_id(); | 
|  | controller().LoadURLWithParams(load_params); | 
|  | entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  |  | 
|  | if (!IsBrowserSideNavigationEnabled()) { | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(subframe->GetRoutingID(), true)); | 
|  | } | 
|  |  | 
|  | // Commit the navigation in the child frame and send the DidStopLoading | 
|  | // message. | 
|  | subframe->PrepareForCommit(); | 
|  | contents()->TestDidNavigate(subframe, 3, entry_id, true, bar_url, | 
|  | ui::PAGE_TRANSITION_MANUAL_SUBFRAME); | 
|  | subframe->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(subframe->GetRoutingID())); | 
|  | } | 
|  |  | 
|  | // At this point the status should still be loading, since the main frame | 
|  | // hasn't sent the DidstopLoading message yet. | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  | EXPECT_TRUE(observer.is_loading()); | 
|  |  | 
|  | // Send the DidStopLoading for the main frame and ensure it isn't loading | 
|  | // anymore. | 
|  | orig_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(orig_rfh->GetRoutingID())); | 
|  | EXPECT_FALSE(contents()->IsLoading()); | 
|  | EXPECT_FALSE(observer.is_loading()); | 
|  | } | 
|  |  | 
|  | // Ensure that WebContentsImpl does not stop loading too early when there still | 
|  | // is a pending renderer. This can happen if a same-process non user-initiated | 
|  | // navigation completes while there is an ongoing cross-process navigation. | 
|  | // TODO(fdegans): Rewrite the test for PlzNavigate when DidStartLoading and | 
|  | // DidStopLoading are properly called. | 
|  | TEST_F(WebContentsImplTest, NoEarlyStop) { | 
|  | const GURL kUrl1("http://www.chromium.org"); | 
|  | const GURL kUrl2("http://www.google.com"); | 
|  | const GURL kUrl3("http://www.wikipedia.org"); | 
|  |  | 
|  | contents()->NavigateAndCommit(kUrl1); | 
|  |  | 
|  | TestRenderFrameHost* current_rfh = contents()->GetMainFrame(); | 
|  |  | 
|  | // Start a browser-initiated cross-process navigation to |kUrl2|. There should | 
|  | // be a pending RenderFrameHost and the WebContents should be loading. | 
|  | controller().LoadURL( | 
|  | kUrl2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); | 
|  | int entry_id = controller().GetPendingEntry()->GetUniqueID(); | 
|  | EXPECT_TRUE(contents()->CrossProcessNavigationPending()); | 
|  | TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); | 
|  | ASSERT_TRUE(pending_rfh); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  |  | 
|  | // The current RenderFrameHost starts a non user-initiated render-initiated | 
|  | // navigation and sends a DidStartLoading IPC. The WebContents should still be | 
|  | // loading. | 
|  | current_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(current_rfh->GetRoutingID(), false)); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  |  | 
|  | // Simulate the pending RenderFrameHost DidStartLoading. There should still be | 
|  | // a pending RenderFrameHost and the WebContents should still be loading. | 
|  | pending_rfh->PrepareForCommit(); | 
|  | pending_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStartLoading(pending_rfh->GetRoutingID(), false)); | 
|  | EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  |  | 
|  | // Simulate the commit and DidStopLoading from the renderer-initiated | 
|  | // navigation in the current RenderFrameHost. There should still be a pending | 
|  | // RenderFrameHost and the WebContents should still be loading. | 
|  | current_rfh->SendNavigateWithModificationCallback( | 
|  | 1, 0, true, kUrl3, base::Bind(SetAsNonUserGesture)); | 
|  | current_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(current_rfh->GetRoutingID())); | 
|  | EXPECT_EQ(contents()->GetPendingMainFrame(), pending_rfh); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  | // It should commit. | 
|  | ASSERT_EQ(2, controller().GetEntryCount()); | 
|  | EXPECT_EQ(kUrl3, controller().GetLastCommittedEntry()->GetURL()); | 
|  |  | 
|  | // Commit the navigation. The formerly pending RenderFrameHost should now be | 
|  | // the current RenderFrameHost and the WebContents should still be loading. | 
|  | contents()->TestDidNavigate(pending_rfh, 1, entry_id, true, kUrl2, | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_FALSE(contents()->GetPendingMainFrame()); | 
|  | TestRenderFrameHost* new_current_rfh = contents()->GetMainFrame(); | 
|  | EXPECT_EQ(new_current_rfh, pending_rfh); | 
|  | EXPECT_TRUE(contents()->IsLoading()); | 
|  | EXPECT_EQ(3, controller().GetEntryCount()); | 
|  |  | 
|  | // Simulate the new current RenderFrameHost DidStopLoading. The WebContents | 
|  | // should now have stopped loading. | 
|  | new_current_rfh->OnMessageReceived( | 
|  | FrameHostMsg_DidStopLoading(new_current_rfh->GetRoutingID())); | 
|  | EXPECT_EQ(contents()->GetMainFrame(), new_current_rfh); | 
|  | EXPECT_FALSE(contents()->IsLoading()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, MediaPowerSaveBlocking) { | 
|  | // Verify that both negative and positive player ids don't blow up. | 
|  | const int kPlayerAudioVideoId = 15; | 
|  | const int kPlayerAudioOnlyId = -15; | 
|  | const int kPlayerVideoOnlyId = 30; | 
|  | const int kPlayerRemoteId = -30; | 
|  |  | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  |  | 
|  | TestRenderFrameHost* rfh = contents()->GetMainFrame(); | 
|  | AudioStreamMonitor* monitor = contents()->audio_stream_monitor(); | 
|  |  | 
|  | // Ensure RenderFrame is initialized before simulating events coming from it. | 
|  | main_test_rfh()->InitializeRenderFrameIfNeeded(); | 
|  |  | 
|  | // The audio power save blocker should not be based on having a media player | 
|  | // when audio stream monitoring is available. | 
|  | if (AudioStreamMonitor::monitoring_available()) { | 
|  | // Send a fake audio stream monitor notification.  The audio power save | 
|  | // blocker should be created. | 
|  | monitor->set_was_recently_audible_for_testing(true); | 
|  | contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); | 
|  | EXPECT_TRUE(has_audio_power_save_blocker()); | 
|  |  | 
|  | // Send another fake notification, this time when WasRecentlyAudible() will | 
|  | // be false.  The power save blocker should be released. | 
|  | monitor->set_was_recently_audible_for_testing(false); | 
|  | contents()->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  | } | 
|  |  | 
|  | // Start a player with both audio and video.  A video power save blocker | 
|  | // should be created.  If audio stream monitoring is available, an audio power | 
|  | // save blocker should be created too. | 
|  | rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaPlaying( | 
|  | 0, kPlayerAudioVideoId, true, true, false, | 
|  | media::MediaContentType::Persistent)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Upon hiding the video power save blocker should be released. | 
|  | contents()->WasHidden(); | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  |  | 
|  | // Start another player that only has video.  There should be no change in | 
|  | // the power save blockers.  The notification should take into account the | 
|  | // visibility state of the WebContents. | 
|  | rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaPlaying( | 
|  | 0, kPlayerVideoOnlyId, true, false, false, | 
|  | media::MediaContentType::Persistent)); | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Showing the WebContents should result in the creation of the blocker. | 
|  | contents()->WasShown(); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  |  | 
|  | // Start another player that only has audio.  There should be no change in | 
|  | // the power save blockers. | 
|  | rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaPlaying( | 
|  | 0, kPlayerAudioOnlyId, false, true, false, | 
|  | media::MediaContentType::Persistent)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Start a remote player. There should be no change in the power save | 
|  | // blockers. | 
|  | rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaPlaying( | 
|  | 0, kPlayerRemoteId, true, true, true, | 
|  | media::MediaContentType::Persistent)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Destroy the original audio video player.  Both power save blockers should | 
|  | // remain. | 
|  | rfh->OnMessageReceived( | 
|  | MediaPlayerDelegateHostMsg_OnMediaPaused(0, kPlayerAudioVideoId, false)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Destroy the audio only player.  The video power save blocker should remain. | 
|  | rfh->OnMessageReceived( | 
|  | MediaPlayerDelegateHostMsg_OnMediaPaused(0, kPlayerAudioOnlyId, false)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  |  | 
|  | // Destroy the video only player.  No power save blockers should remain. | 
|  | rfh->OnMessageReceived( | 
|  | MediaPlayerDelegateHostMsg_OnMediaPaused(0, kPlayerVideoOnlyId, false)); | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  |  | 
|  | // Destroy the remote player. No power save blockers should remain. | 
|  | rfh->OnMessageReceived( | 
|  | MediaPlayerDelegateHostMsg_OnMediaPaused(0, kPlayerRemoteId, false)); | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  |  | 
|  | // Start a player with both audio and video.  A video power save blocker | 
|  | // should be created.  If audio stream monitoring is available, an audio power | 
|  | // save blocker should be created too. | 
|  | rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaPlaying( | 
|  | 0, kPlayerAudioVideoId, true, true, false, | 
|  | media::MediaContentType::Persistent)); | 
|  | EXPECT_TRUE(has_video_power_save_blocker()); | 
|  | EXPECT_EQ(has_audio_power_save_blocker(), | 
|  | !AudioStreamMonitor::monitoring_available()); | 
|  |  | 
|  | // Crash the renderer. | 
|  | contents()->GetMainFrame()->GetProcess()->SimulateCrash(); | 
|  |  | 
|  | // Verify that all the power save blockers have been released. | 
|  | EXPECT_FALSE(has_video_power_save_blocker()); | 
|  | EXPECT_FALSE(has_audio_power_save_blocker()); | 
|  | } | 
|  |  | 
|  | TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) { | 
|  | TestWebContentsObserver observer(contents()); | 
|  | TestRenderFrameHost* rfh = contents()->GetMainFrame(); | 
|  | rfh->InitializeRenderFrameIfNeeded(); | 
|  |  | 
|  | SkColor transparent = SK_ColorTRANSPARENT; | 
|  |  | 
|  | EXPECT_EQ(transparent, contents()->GetThemeColor()); | 
|  | EXPECT_EQ(transparent, observer.last_theme_color()); | 
|  |  | 
|  | // Theme color changes should not propagate past the WebContentsImpl before | 
|  | // the first visually non-empty paint has occurred. | 
|  | RenderViewHostTester::TestOnMessageReceived( | 
|  | test_rvh(), | 
|  | FrameHostMsg_DidChangeThemeColor(rfh->GetRoutingID(), SK_ColorRED)); | 
|  |  | 
|  | EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor()); | 
|  | EXPECT_EQ(transparent, observer.last_theme_color()); | 
|  |  | 
|  | // Simulate that the first visually non-empty paint has occurred. This will | 
|  | // propagate the current theme color to the delegates. | 
|  | RenderViewHostTester::TestOnMessageReceived( | 
|  | test_rvh(), | 
|  | ViewHostMsg_DidFirstVisuallyNonEmptyPaint(rfh->GetRoutingID())); | 
|  |  | 
|  | EXPECT_EQ(SK_ColorRED, contents()->GetThemeColor()); | 
|  | EXPECT_EQ(SK_ColorRED, observer.last_theme_color()); | 
|  |  | 
|  | // Additional changes made by the web contents should propagate as well. | 
|  | RenderViewHostTester::TestOnMessageReceived( | 
|  | test_rvh(), | 
|  | FrameHostMsg_DidChangeThemeColor(rfh->GetRoutingID(), SK_ColorGREEN)); | 
|  |  | 
|  | EXPECT_EQ(SK_ColorGREEN, contents()->GetThemeColor()); | 
|  | EXPECT_EQ(SK_ColorGREEN, observer.last_theme_color()); | 
|  | } | 
|  |  | 
|  | // Test that if a resource is loaded with empty security info, the SSLManager | 
|  | // does not mistakenly think it has seen a good certificate and thus forget any | 
|  | // user exceptions for that host. See https://crbug.com/516808. | 
|  | TEST_F(WebContentsImplTest, LoadResourceWithEmptySecurityInfo) { | 
|  | WebContentsImplTestBrowserClient browser_client; | 
|  | SetBrowserClientForTesting(&browser_client); | 
|  |  | 
|  | scoped_refptr<net::X509Certificate> cert = | 
|  | net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"); | 
|  | SSLPolicyBackend* backend = contents()->controller_.ssl_manager()->backend(); | 
|  | const GURL test_url("https://example.test"); | 
|  |  | 
|  | backend->AllowCertForHost(*cert, test_url.host(), 1); | 
|  | EXPECT_TRUE(backend->HasAllowException(test_url.host())); | 
|  |  | 
|  | contents()->controller_.ssl_manager()->policy()->OnRequestStarted( | 
|  | test_url, 0, 0); | 
|  |  | 
|  | EXPECT_TRUE(backend->HasAllowException(test_url.host())); | 
|  |  | 
|  | DeleteContents(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class TestJavaScriptDialogManager : public JavaScriptDialogManager { | 
|  | public: | 
|  | TestJavaScriptDialogManager() {} | 
|  | ~TestJavaScriptDialogManager() override {} | 
|  |  | 
|  | size_t reset_count() { return reset_count_; } | 
|  |  | 
|  | // JavaScriptDialogManager | 
|  |  | 
|  | void RunJavaScriptDialog(WebContents* web_contents, | 
|  | const GURL& origin_url, | 
|  | JavaScriptMessageType javascript_message_type, | 
|  | const base::string16& message_text, | 
|  | const base::string16& default_prompt_text, | 
|  | const DialogClosedCallback& callback, | 
|  | bool* did_suppress_message) override { | 
|  | *did_suppress_message = true; | 
|  | }; | 
|  |  | 
|  | void RunBeforeUnloadDialog(WebContents* web_contents, | 
|  | bool is_reload, | 
|  | const DialogClosedCallback& callback) override {} | 
|  |  | 
|  | bool HandleJavaScriptDialog(WebContents* web_contents, | 
|  | bool accept, | 
|  | const base::string16* prompt_override) override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void CancelActiveAndPendingDialogs(WebContents* web_contents) override {} | 
|  |  | 
|  | void ResetDialogState(WebContents* web_contents) override { ++reset_count_; } | 
|  |  | 
|  | private: | 
|  | size_t reset_count_ = 0; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(WebContentsImplTest, ResetJavaScriptDialogOnUserNavigate) { | 
|  | TestJavaScriptDialogManager dialog_manager; | 
|  | contents()->SetJavaScriptDialogManagerForTesting(&dialog_manager); | 
|  |  | 
|  | // A user-initiated navigation. | 
|  | contents()->TestDidNavigate(contents()->GetMainFrame(), 1, 0, true, | 
|  | GURL("about:whatever"), | 
|  | ui::PAGE_TRANSITION_TYPED); | 
|  | EXPECT_EQ(1u, dialog_manager.reset_count()); | 
|  |  | 
|  | // An automatic navigation. | 
|  | contents()->GetMainFrame()->SendNavigateWithModificationCallback( | 
|  | 2, 0, true, GURL(url::kAboutBlankURL), base::Bind(SetAsNonUserGesture)); | 
|  |  | 
|  | EXPECT_EQ(1u, dialog_manager.reset_count()); | 
|  |  | 
|  | contents()->SetJavaScriptDialogManagerForTesting(nullptr); | 
|  | } | 
|  |  | 
|  | }  // namespace content |