diff options
Diffstat (limited to 'chrome/browser/web_contents_unittest.cc')
-rw-r--r-- | chrome/browser/web_contents_unittest.cc | 1242 |
1 files changed, 1242 insertions, 0 deletions
diff --git a/chrome/browser/web_contents_unittest.cc b/chrome/browser/web_contents_unittest.cc new file mode 100644 index 0000000..728b82e --- /dev/null +++ b/chrome/browser/web_contents_unittest.cc @@ -0,0 +1,1242 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/logging.h" +#include "chrome/browser/navigation_controller.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/render_widget_host_view.h" +#include "chrome/browser/web_contents.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/render_messages.h" +#include "chrome/test/testing_profile.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Subclass the RenderViewHost's view so that we can call Show(), etc., +// without having side-effects. +class TestRenderWidgetHostView : public RenderWidgetHostView { + public: + TestRenderWidgetHostView() {} + void DidBecomeSelected() {} + void WasHidden() {} + void SetSize(const gfx::Size& size) {} + HWND GetPluginHWND() { return NULL; } + HANDLE ModalDialogEvent() { return NULL; } + void ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam) {} + void Focus() {} + void Blur() {} + bool HasFocus() { return true; } + void AdvanceFocus(bool reverse) {} + void Show() {} + void Hide() {} + gfx::Rect GetViewBounds() const { return gfx::Rect(); } + void UpdateCursor(const WebCursor& cursor) {} + void UpdateCursorIfOverSelf() {} + // Indicates if the page has finished loading. + virtual void SetIsLoading(bool is_loading) {} + void IMEUpdateStatus(ViewHostMsg_ImeControl control, int x, int y) {} + void DidPaintRect(const gfx::Rect& rect) {} + void DidScrollRect(const gfx::Rect& rect, int dx, int dy) {} + void RendererGone() {} + void Destroy() {} + void PrepareToDestroy() {} + void SetTooltipText(const std::wstring& tooltip_text) {} +}; + +// Subclass RenderViewHost so that it does not create a process. +class TestRenderViewHost : public RenderViewHost { + public: + TestRenderViewHost( + SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + HANDLE modal_dialog_event) + : RenderViewHost(instance, delegate, routing_id, modal_dialog_event), + is_loading(false), + is_created(false), + immediate_before_unload(true), + delete_counter_(NULL) { + set_view(new TestRenderWidgetHostView()); + } + ~TestRenderViewHost() { + // Track the delete if we've been asked to. + if (delete_counter_) + ++*delete_counter_; + + // Since this isn't a traditional view, we have to delete it. + delete view_; + } + + // If set, *delete_counter is incremented when this object destructs. + void set_delete_counter(int* delete_counter) { + delete_counter_ = delete_counter; + } + + bool CreateRenderView() { + is_created = true; + return true; + } + + bool IsRenderViewLive() const { return is_created; } + + bool IsNavigationSuspended() { return navigations_suspended_; } + + void NavigateToEntry(const NavigationEntry& entry, bool is_reload) { + is_loading = true; + } + + void LoadAlternateHTMLString(const std::string& html_text, + bool new_navigation, + const GURL& display_url, + const std::string& security_info) { + is_loading = true; + } + + // Support for onbeforeunload, onunload + void AttemptToClosePage(bool is_closing_browser) { + // TODO(ojan): Add tests for the case where is_closing_browser is true. + DCHECK(!is_closing_browser); + if (immediate_before_unload) + delegate()->ShouldClosePage(true); + } + void ClosePage(int new_render_process_host_id, int new_request_id) { + // Nothing to do here... This would cause a ClosePage_ACK to be sent to + // ResourceDispatcherHost, so we can simulate that manually. + } + void TestOnMsgShouldClose(bool proceed) { + OnMsgShouldCloseACK(proceed, false); + } + + bool is_loading; + bool is_created; + bool immediate_before_unload; + int* delete_counter_; +}; + +// Factory to create TestRenderViewHosts. +class TestRenderViewHostFactory : public RenderViewHostFactory { + public: + static TestRenderViewHostFactory* GetInstance() { + static TestRenderViewHostFactory instance; + return &instance; + } + + virtual RenderViewHost* CreateRenderViewHost( + SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + HANDLE modal_dialog_event) { + return new TestRenderViewHost( + instance, delegate, routing_id, modal_dialog_event); + } + + private: + TestRenderViewHostFactory() {} +}; + +// Subclass the TestingProfile so that it can return certain services we need. +class WebContentsTestingProfile : public TestingProfile { + public: + WebContentsTestingProfile() : TestingProfile() { } + + virtual PrefService* GetPrefs() { + if (!prefs_.get()) { + std::wstring source_path; + PathService::Get(chrome::DIR_TEST_DATA, &source_path); + file_util::AppendToPath(&source_path, L"profiles"); + file_util::AppendToPath(&source_path, L"chrome_prefs"); + file_util::AppendToPath(&source_path, L"Preferences"); + + prefs_.reset(new PrefService(source_path)); + Profile::RegisterUserPrefs(prefs_.get()); + browser::RegisterAllPrefs(prefs_.get(), prefs_.get()); + } + return prefs_.get(); + } +}; + +// Subclass WebContents to ensure it creates TestRenderViewHosts and does +// not do anything involving views. +class TestWebContents : public WebContents { + public: + TestWebContents(Profile* profile, SiteInstance* instance) + : WebContents(profile, + instance, + TestRenderViewHostFactory::GetInstance(), + MSG_ROUTING_NONE, + NULL), + transition_cross_site(false) {} + + // Accessors for interesting fields + RendererState renderer_state() { return renderer_state_; } + TestRenderViewHost* rvh() { + return static_cast<TestRenderViewHost*>(render_view_host_); + } + TestRenderViewHost* pending_rvh() { + return static_cast<TestRenderViewHost*>(pending_render_view_host_); + } + TestRenderViewHost* interstitial_rvh() { + return static_cast<TestRenderViewHost*>(interstitial_render_view_host_); + } + TestRenderViewHost* original_rvh() { + return static_cast<TestRenderViewHost*>(original_render_view_host_); + } + + // Ensure we create TestRenderViewHosts that don't spawn processes. + RenderViewHost* CreateRenderViewHost(SiteInstance* instance, + RenderViewHostDelegate* delegate, + int routing_id, + HANDLE modal_dialog_event) { + return new TestRenderViewHost( + instance, delegate, routing_id, modal_dialog_event); + } + + // Overrides WebContents::ShouldTransitionCrossSite so that we can test both + // alternatives without using command-line switches. + bool ShouldTransitionCrossSite() { return transition_cross_site; } + + // Promote DidNavigate to public. + void TestDidNavigate(TestRenderViewHost* render_view_host, + const ViewHostMsg_FrameNavigate_Params& params) { + DidNavigate(render_view_host, params); + render_view_host->is_loading = false; + } + + // Promote IsInPageNavigation to public. + bool TestIsInPageNavigation(const GURL& url) { + return IsInPageNavigation(url); + } + + // Promote GetWebkitPrefs to public. + WebPreferences TestGetWebkitPrefs() { + return GetWebkitPrefs(); + } + + // Prevent interaction with views. + bool CreateRenderView(RenderViewHost* render_view_host) { + // This will go to a TestRenderViewHost. + render_view_host->CreateRenderView(); + return true; + } + void UpdateRenderViewSize() {} + + // Set by individual tests. + bool transition_cross_site; +}; + +class WebContentsTest : public testing::Test { + public: + WebContentsTest() : contents(NULL) {} + + void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params, + int pageID, + const GURL& url) { + params->page_id = pageID; + params->url = url; + params->referrer = GURL::EmptyGURL(); + params->transition = PageTransition::TYPED; + params->redirects = std::vector<GURL>(); + params->should_update_history = false; + params->searchable_form_url = GURL::EmptyGURL(); + params->searchable_form_element_name = std::wstring(); + params->searchable_form_encoding = std::string(); + params->password_form = PasswordForm(); + params->security_info = std::string(); + params->gesture = NavigationGestureUser; + params->is_post = false; + } + + // testing::Test methods: + + virtual void SetUp() { + profile.reset(new WebContentsTestingProfile()); + + // This will be deleted when the WebContents goes away + SiteInstance* instance = SiteInstance::CreateSiteInstance(profile.get()); + + contents = new TestWebContents(profile.get(), instance); + contents->SetupController(profile.get()); + } + + virtual void TearDown() { + // This will delete the contents. + contents->CloseContents(); + + // Make sure that we flush any messages related to WebContents destruction + // before we destroy the profile. + MessageLoop::current()->Quit(); + MessageLoop::current()->Run(); + } + + scoped_ptr<WebContentsTestingProfile> profile; + TestWebContents* contents; +}; + +// Test to make sure that title updates get stripped of whitespace +TEST_F(WebContentsTest, OnMessageReceived) { + contents->controller()->DidNavigateToEntry(new NavigationEntry( + contents->type(), contents->site_instance(), 0, GURL("about:blank"), + std::wstring(), PageTransition::TYPED)); + + contents->UpdateTitle(NULL, 0, L" Lots O' Whitespace\n"); + EXPECT_EQ(std::wstring(L"Lots O' Whitespace"), contents->GetTitle()); +} + +// Test simple same-SiteInstance navigation. +TEST_F(WebContentsTest, SimpleNavigation) { + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->site_instance(); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_FALSE(orig_rvh->is_loading); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(orig_rvh->is_loading); + EXPECT_EQ(instance1, orig_rvh->site_instance()); + // Controller's pending entry will have a NULL site instance until we assign + // it in DidNavigate. + EXPECT_TRUE( + contents->controller()->GetActiveEntry()->site_instance() == NULL); + + // DidNavigate from the page + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(orig_rvh, params); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_EQ(instance1, orig_rvh->site_instance()); + // Controller's entry should now have the SiteInstance, or else we won't be + // able to find it later. + EXPECT_EQ(instance1, + contents->controller()->GetActiveEntry()->site_instance()); +} + +// Test navigating to a page that shows an interstitial, then hiding it +// without proceeding. +TEST_F(WebContentsTest, ShowInterstitialDontProceed) { + TestRenderViewHost* orig_rvh = contents->rvh(); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_FALSE(orig_rvh->is_loading); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(orig_rvh->is_loading); + + // Show interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + EXPECT_TRUE(orig_rvh->is_loading); // Still loading in the background + EXPECT_TRUE(interstitial_rvh->is_loading); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_FALSE(interstitial_rvh->is_loading); + + // Hide interstitial (don't proceed) + contents->HideInterstitialPage(false, false); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test navigating to a page that shows an interstitial, then proceeding. +TEST_F(WebContentsTest, ShowInterstitialProceed) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // The RenderViewHost's SiteInstance should not yet have a site. + EXPECT_EQ(GURL(""), contents->rvh()->site_instance()->site()); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + + // Show interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + + // Ensure this DidNavigate hasn't changed the SiteInstance's site. + // Prevents regression for bug 1163298. + EXPECT_EQ(GURL(""), contents->rvh()->site_instance()->site()); + + // Hide interstitial (proceed and wait) + contents->HideInterstitialPage(true, true); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + + // DidNavigate from the destination page + contents->TestDidNavigate(orig_rvh, params); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // The SiteInstance's site should now be updated. + EXPECT_EQ(GURL("http://google.com"), + contents->rvh()->site_instance()->site()); + + // Since we weren't viewing a page before, we shouldn't be able to go back. + EXPECT_FALSE(contents->controller()->CanGoBack()); +} + +// Test navigating to a page that shows an interstitial, then navigating away. +TEST_F(WebContentsTest, ShowInterstitialThenNavigate) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + + // Show interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(interstitial_rvh, params); + + // While interstitial showing, navigate to a new URL. + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_TRUE(orig_rvh->is_loading); + EXPECT_FALSE(interstitial_rvh->is_loading); + + // DidNavigate from the new URL. In the old process model, we'll still have + // the same RenderViewHost. + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(orig_rvh, params2); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_FALSE(orig_rvh->is_loading); +} + +// Ensures that an interstitial cannot be cancelled if a notification for a +// navigation from an IFrame from the previous page is received while the +// interstitial is being shown (bug #1182394). +TEST_F(WebContentsTest, ShowInterstitialIFrameNavigate) { + TestRenderViewHost* orig_rvh = contents->rvh(); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_FALSE(orig_rvh->is_loading); + + // Navigate to URL. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(orig_rvh->is_loading); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Show interstitial (in real world would probably be triggered by a resource + // in the page). + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + EXPECT_TRUE(interstitial_rvh->is_loading); + + // DidNavigate from an IFrame in the initial page. + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, GURL("http://www.iframe.com")); + params2.transition = PageTransition::AUTO_SUBFRAME; + contents->TestDidNavigate(orig_rvh, params2); + + // Now we get the DidNavigate from the interstitial. + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url); + contents->TestDidNavigate(interstitial_rvh, params3); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_FALSE(interstitial_rvh->is_loading); +} + +// Test navigating to an interstitial page from a normal page. Also test +// visiting the interstitial-inducing URL twice (bug 1079784), and test +// that going back shows the first page and not the interstitial. +TEST_F(WebContentsTest, VisitInterstitialURLTwice) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // Navigate to URL + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Now navigate to an interstitial-inducing URL + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + int interstitial_delete_counter = 0; + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + interstitial_rvh->set_delete_counter(&interstitial_delete_counter); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + + // While interstitial showing, navigate to the same URL. + contents->controller()->LoadURL(url2, PageTransition::TYPED); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + + // Interstitial shown a second time in a different RenderViewHost. + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + // We expect the original interstitial has been deleted. + EXPECT_EQ(interstitial_delete_counter, 1); + TestRenderViewHost* interstitial_rvh2 = contents->interstitial_rvh(); + interstitial_rvh2->set_delete_counter(&interstitial_delete_counter); + + // DidNavigate from the interstitial. + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 3, url2); + contents->TestDidNavigate(interstitial_rvh2, params3); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh2, contents->render_view_host()); + + // Proceed. In the old process model, we'll still have the same + // RenderViewHost. + contents->HideInterstitialPage(true, true); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + ViewHostMsg_FrameNavigate_Params params4; + InitNavigateParams(¶ms4, 3, url2); + contents->TestDidNavigate(orig_rvh, params4); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + // We expect the second interstitial has been deleted. + EXPECT_EQ(interstitial_delete_counter, 2); + + // Now go back. Should take us back to the original page. + contents->controller()->GoBack(); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + + // DidNavigate from going back. + contents->TestDidNavigate(orig_rvh, params1); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test that navigating across a site boundary creates a new RenderViewHost +// with a new SiteInstance. Going back should do the same. +TEST_F(WebContentsTest, CrossSiteBoundaries) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + int orig_rvh_delete_count = 0; + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Navigate to new site + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // DidNavigate from the pending page + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(pending_rvh, params2); + SiteInstance* instance2 = contents->site_instance(); + + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(pending_rvh, contents->render_view_host()); + EXPECT_NE(instance1, instance2); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_EQ(orig_rvh_delete_count, 1); + + // Going back should switch SiteInstances again. The first SiteInstance is + // stored in the NavigationEntry, so it should be the same as at the start. + contents->controller()->GoBack(); + TestRenderViewHost* goback_rvh = contents->pending_rvh(); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + + // DidNavigate from the back action + contents->TestDidNavigate(goback_rvh, params1); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(goback_rvh, contents->render_view_host()); + EXPECT_EQ(pending_rvh_delete_count, 1); + EXPECT_EQ(instance1, contents->site_instance()); +} + +// Test that navigating across a site boundary after a crash creates a new +// RVH without requiring a cross-site transition (i.e., PENDING state). +TEST_F(WebContentsTest, CrossSiteBoundariesAfterCrash) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + int orig_rvh_delete_count = 0; + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Crash the renderer. + orig_rvh->is_created = false; + + // Navigate to new site. We should not go into PENDING. + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + TestRenderViewHost* new_rvh = contents->rvh(); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_NE(orig_rvh, new_rvh); + EXPECT_EQ(orig_rvh_delete_count, 1); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(new_rvh, params2); + SiteInstance* instance2 = contents->site_instance(); + + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(new_rvh, contents->render_view_host()); + EXPECT_NE(instance1, instance2); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test state transitions when showing an interstitial in the new process +// model, and then choosing DontProceed. +TEST_F(WebContentsTest, CrossSiteInterstitialDontProceed) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + + // Show an interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + EXPECT_EQ(WebContents::ENTERING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Hide interstitial (don't proceed) + contents->HideInterstitialPage(false, false); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test state transitions when showing an interstitial in the new process +// model, and then choosing Proceed. +TEST_F(WebContentsTest, CrossSiteInterstitialProceed) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Hide interstitial (proceed and wait) + contents->HideInterstitialPage(true, true); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the destination page should transition to new renderer + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 2, url2); + contents->TestDidNavigate(pending_rvh, params3); + SiteInstance* instance2 = contents->site_instance(); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(pending_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + EXPECT_NE(instance1, instance2); + EXPECT_EQ(orig_rvh_delete_count, 1); // The original should be gone. + + // Since we were viewing a page before, we should be able to go back. + EXPECT_TRUE(contents->controller()->CanGoBack()); + + // Going back should switch SiteInstances again. The first SiteInstance is + // stored in the NavigationEntry, so it should be the same as at the start. + contents->controller()->GoBack(); + TestRenderViewHost* goback_rvh = contents->pending_rvh(); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + + // DidNavigate from the back action + contents->TestDidNavigate(goback_rvh, params1); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(goback_rvh, contents->render_view_host()); + EXPECT_EQ(instance1, contents->site_instance()); + EXPECT_EQ(pending_rvh_delete_count, 1); // The second page's rvh should die. +} + +// Tests that we can transition away from an interstitial page. +TEST_F(WebContentsTest, CrossSiteInterstitialThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Show an interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Navigate to a new page. + const GURL url2("http://www.yahoo.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + + TestRenderViewHost* new_rvh = contents->pending_rvh(); + ASSERT_TRUE(new_rvh != NULL); + // Make sure the RVH is not suspended (bug #1236441). + EXPECT_FALSE(new_rvh->IsNavigationSuspended()); + EXPECT_EQ(WebContents::LEAVING_INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url2); + contents->TestDidNavigate(new_rvh, params3); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(new_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_EQ(orig_rvh_delete_count, 1); +} + +// Tests that we can transition away from an interstitial page even if the +// interstitial renderer has crashed. +TEST_F(WebContentsTest, CrossSiteInterstitialCrashThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Crash the interstitial RVH + // (by making IsRenderViewLive() == false) + interstitial_rvh->is_created = false; + + // Navigate to a new page. Since interstitial RVH is dead, we should clean + // it up and go to a new PENDING state, showing the orig_rvh. + const GURL url3("http://www.yahoo.com"); + contents->controller()->LoadURL(url3, PageTransition::TYPED); + TestRenderViewHost* new_rvh = contents->pending_rvh(); + ASSERT_TRUE(new_rvh != NULL); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + EXPECT_EQ(pending_rvh_delete_count, 1); + EXPECT_NE(interstitial_rvh, new_rvh); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url3); + contents->TestDidNavigate(new_rvh, params3); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(new_rvh, contents->render_view_host()); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_EQ(orig_rvh_delete_count, 1); +} + +// Tests that we can transition away from an interstitial page even if both the +// original and interstitial renderers have crashed. +TEST_F(WebContentsTest, CrossSiteInterstitialCrashesThenNavigate) { + contents->transition_cross_site = true; + int orig_rvh_delete_count = 0; + TestRenderViewHost* orig_rvh = contents->rvh(); + orig_rvh->set_delete_counter(&orig_rvh_delete_count); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Navigate to new site + const GURL url2("https://www.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + int pending_rvh_delete_count = 0; + pending_rvh->set_delete_counter(&pending_rvh_delete_count); + + // Show an interstitial + contents->ShowInterstitialPage(std::string("Blocked"), NULL); + TestRenderViewHost* interstitial_rvh = contents->interstitial_rvh(); + + // DidNavigate from the interstitial + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(interstitial_rvh, params2); + EXPECT_EQ(WebContents::INTERSTITIAL, contents->renderer_state()); + EXPECT_EQ(interstitial_rvh, contents->render_view_host()); + EXPECT_EQ(orig_rvh, contents->original_rvh()); + EXPECT_EQ(pending_rvh, contents->pending_rvh()); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // Crash both the original and interstitial RVHs + // (by making IsRenderViewLive() == false) + orig_rvh->is_created = false; + interstitial_rvh->is_created = false; + + // Navigate to a new page. Since both the interstitial and original RVHs are + // dead, we should create a new RVH, jump back to NORMAL, and navigate. + const GURL url3("http://www.yahoo.com"); + contents->controller()->LoadURL(url3, PageTransition::TYPED); + TestRenderViewHost* new_rvh = contents->rvh(); + ASSERT_TRUE(new_rvh != NULL); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh_delete_count, 1); + EXPECT_EQ(pending_rvh_delete_count, 1); + EXPECT_NE(interstitial_rvh, new_rvh); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); + + // DidNavigate from the new page + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 1, url3); + contents->TestDidNavigate(new_rvh, params3); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(new_rvh, contents->render_view_host()); +} + +// Test that opening a new tab in the same SiteInstance and then navigating +// both tabs to a new site will place both tabs in a single SiteInstance. +TEST_F(WebContentsTest, NavigateTwoTabsCrossSite) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Open a new tab with the same SiteInstance, navigated to the same site. + TestWebContents* contents2 = new TestWebContents(profile.get(), instance1); + contents2->transition_cross_site = true; + contents2->SetupController(profile.get()); + contents2->controller()->LoadURL(url, PageTransition::TYPED); + contents2->TestDidNavigate(contents2->rvh(), params1); + + // Navigate first tab to a new site + const GURL url2a("http://www.yahoo.com"); + contents->controller()->LoadURL(url2a, PageTransition::TYPED); + TestRenderViewHost* pending_rvh_a = contents->pending_rvh(); + ViewHostMsg_FrameNavigate_Params params2a; + InitNavigateParams(¶ms2a, 1, url2a); + contents->TestDidNavigate(pending_rvh_a, params2a); + SiteInstance* instance2a = contents->site_instance(); + EXPECT_NE(instance1, instance2a); + + // Navigate second tab to the same site as the first tab + const GURL url2b("http://mail.yahoo.com"); + contents2->controller()->LoadURL(url2b, PageTransition::TYPED); + TestRenderViewHost* pending_rvh_b = contents2->pending_rvh(); + EXPECT_TRUE(pending_rvh_b != NULL); + EXPECT_EQ(WebContents::PENDING, contents2->renderer_state()); + + // NOTE(creis): We used to be in danger of showing a sad tab page here if the + // second tab hadn't navigated somewhere first (bug 1145430). That case is + // now covered by the CrossSiteBoundariesAfterCrash test. + + ViewHostMsg_FrameNavigate_Params params2b; + InitNavigateParams(¶ms2b, 2, url2b); + contents2->TestDidNavigate(pending_rvh_b, params2b); + SiteInstance* instance2b = contents2->site_instance(); + EXPECT_NE(instance1, instance2b); + + // Both tabs should now be in the same SiteInstance. + EXPECT_EQ(instance2a, instance2b); + + contents2->CloseContents(); +} + +// Tests that WebContents uses the current URL, not the SiteInstance's site, to +// determine whether a navigation is cross-site. +TEST_F(WebContentsTest, CrossSiteComparesAgainstCurrentPage) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + + // Open a related tab to a second site. + TestWebContents* contents2 = new TestWebContents(profile.get(), instance1); + contents2->transition_cross_site = true; + contents2->SetupController(profile.get()); + const GURL url2("http://www.yahoo.com"); + contents2->controller()->LoadURL(url2, PageTransition::TYPED); + // The first RVH in contents2 isn't live yet, so we shortcut the PENDING + // state and go straight to NORMAL. + TestRenderViewHost* rvh2 = contents2->rvh(); + EXPECT_EQ(WebContents::NORMAL, contents2->renderer_state()); + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents2->TestDidNavigate(rvh2, params2); + SiteInstance* instance2 = contents2->site_instance(); + EXPECT_NE(instance1, instance2); + EXPECT_EQ(WebContents::NORMAL, contents2->renderer_state()); + + // Simulate a link click in first tab to second site. Doesn't switch + // SiteInstances, because we don't intercept WebKit navigations. + ViewHostMsg_FrameNavigate_Params params3; + InitNavigateParams(¶ms3, 2, url2); + contents->TestDidNavigate(orig_rvh, params3); + SiteInstance* instance3 = contents->site_instance(); + EXPECT_EQ(instance1, instance3); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + + // 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"); + contents->controller()->LoadURL(url3, PageTransition::TYPED); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + ViewHostMsg_FrameNavigate_Params params4; + InitNavigateParams(¶ms4, 3, url3); + contents->TestDidNavigate(orig_rvh, params4); + SiteInstance* instance4 = contents->site_instance(); + EXPECT_EQ(instance1, instance4); + + contents2->CloseContents(); +} + +// Test that the onbeforeunload and onunload handlers run when navigating +// across site boundaries. +TEST_F(WebContentsTest, CrossSiteUnloadHandlers) { + contents->transition_cross_site = true; + TestRenderViewHost* orig_rvh = contents->rvh(); + SiteInstance* instance1 = contents->site_instance(); + + // Navigate to URL. First URL should use first RenderViewHost. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + + // Navigate to new site, but simulate an onbeforeunload denial. + const GURL url2("http://www.yahoo.com"); + orig_rvh->immediate_before_unload = false; + contents->controller()->LoadURL(url2, PageTransition::TYPED); + orig_rvh->TestOnMsgShouldClose(false); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(orig_rvh, contents->render_view_host()); + + // Navigate again, but simulate an onbeforeunload approval. + contents->controller()->LoadURL(url2, PageTransition::TYPED); + orig_rvh->TestOnMsgShouldClose(true); + EXPECT_EQ(WebContents::PENDING, contents->renderer_state()); + TestRenderViewHost* pending_rvh = contents->pending_rvh(); + + // We won't hear DidNavigate until the onunload handler has finished running. + // (No way to simulate that here, but it involves a call from RDH to + // WebContents::OnCrossSiteResponse.) + + // DidNavigate from the pending page + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 1, url2); + contents->TestDidNavigate(pending_rvh, params2); + SiteInstance* instance2 = contents->site_instance(); + EXPECT_EQ(WebContents::NORMAL, contents->renderer_state()); + EXPECT_EQ(pending_rvh, contents->render_view_host()); + EXPECT_NE(instance1, instance2); + EXPECT_TRUE(contents->pending_rvh() == NULL); + EXPECT_TRUE(contents->original_rvh() == NULL); + EXPECT_TRUE(contents->interstitial_rvh() == NULL); +} + +// Test that NavigationEntries have the correct content state after going +// forward and back. Prevents regression for bug 1116137. +TEST_F(WebContentsTest, NavigationEntryContentState) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // Navigate to URL. There should be no committed entry yet. + const GURL url("http://www.google.com"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + NavigationEntry* entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_TRUE(entry == NULL); + + // Committed entry should have content state after DidNavigate. + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_FALSE(entry->GetContentState().empty()); + + // Navigate to same site. + const GURL url2("http://images.google.com"); + contents->controller()->LoadURL(url2, PageTransition::TYPED); + entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_FALSE(entry->GetContentState().empty()); + + // Committed entry should have content state after DidNavigate. + ViewHostMsg_FrameNavigate_Params params2; + InitNavigateParams(¶ms2, 2, url2); + contents->TestDidNavigate(orig_rvh, params2); + entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_FALSE(entry->GetContentState().empty()); + + // Now go back. Committed entry should still have content state. + contents->controller()->GoBack(); + contents->TestDidNavigate(orig_rvh, params1); + entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_FALSE(entry->GetContentState().empty()); +} + +// Test that NavigationEntries have the correct content state after opening +// a new window to about:blank. Prevents regression for bug 1116137. +TEST_F(WebContentsTest, NavigationEntryContentStateNewWindow) { + TestRenderViewHost* orig_rvh = contents->rvh(); + + // When opening a new window, it is navigated to about:blank internally. + // Currently, this results in two DidNavigate events. + const GURL url("about:blank"); + ViewHostMsg_FrameNavigate_Params params1; + InitNavigateParams(¶ms1, 1, url); + contents->TestDidNavigate(orig_rvh, params1); + contents->TestDidNavigate(orig_rvh, params1); + + // Should have a content state here. + NavigationEntry* entry = contents->controller()->GetLastCommittedEntry(); + EXPECT_FALSE(entry->GetContentState().empty()); +} + +// Tests that IsInPageNavigation returns appropriate results. Prevents +// regression for bug 1126349. +TEST_F(WebContentsTest, IsInPageNavigation) { + TestRenderViewHost* rvh = contents->rvh(); + + // Navigate to URL with no refs. + const GURL url("http://www.google.com/home.html"); + contents->controller()->LoadURL(url, PageTransition::TYPED); + ViewHostMsg_FrameNavigate_Params params; + InitNavigateParams(¶ms, 1, url); + contents->TestDidNavigate(rvh, params); + + // Reloading the page is not an in-page navigation. + EXPECT_FALSE(contents->TestIsInPageNavigation(url)); + const GURL other_url("http://www.google.com/add.html"); + EXPECT_FALSE(contents->TestIsInPageNavigation(other_url)); + const GURL url_with_ref("http://www.google.com/home.html#my_ref"); + EXPECT_TRUE(contents->TestIsInPageNavigation(url_with_ref)); + + // Navigate to URL with refs. + contents->controller()->LoadURL(url_with_ref, PageTransition::TYPED); + InitNavigateParams(¶ms, 2, url_with_ref); + contents->TestDidNavigate(rvh, params); + + // Reloading the page is not an in-page navigation. + EXPECT_FALSE(contents->TestIsInPageNavigation(url_with_ref)); + EXPECT_FALSE(contents->TestIsInPageNavigation(url)); + EXPECT_FALSE(contents->TestIsInPageNavigation(other_url)); + const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref"); + EXPECT_TRUE(contents->TestIsInPageNavigation(other_url_with_ref)); +} + +// Tests to see that webkit preferences are properly loaded and copied over +// to a WebPreferences object. +TEST_F(WebContentsTest, WebKitPrefs) { + WebPreferences webkit_prefs = contents->TestGetWebkitPrefs(); + + // These values have been overridden by the profile preferences. + EXPECT_EQ(L"UTF-8", webkit_prefs.default_encoding); + EXPECT_EQ(20, webkit_prefs.default_font_size); + EXPECT_EQ(false, webkit_prefs.text_areas_are_resizable); + EXPECT_EQ(true, webkit_prefs.uses_universal_detector); + + // These should still be the default values. + EXPECT_EQ(L"Times New Roman", webkit_prefs.standard_font_family); + EXPECT_EQ(true, webkit_prefs.javascript_enabled); +} |