blob: 328cbc2d53d9044c4fdae8bf2e01c041c1cf6bc3 [file] [log] [blame]
// Copyright (c) 2010 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 "app/message_box_flags.h"
#include "base/logging.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/pref_value_store.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/renderer_host/test/test_render_view_host.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/tab_contents/interstitial_page.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/test_tab_contents.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/testing_profile.h"
#include "ipc/ipc_channel.h"
#include "testing/gtest/include/gtest/gtest.h"
using webkit_glue::PasswordForm;
static void InitNavigateParams(ViewHostMsg_FrameNavigate_Params* params,
int page_id,
const GURL& url) {
params->page_id = page_id;
params->url = url;
params->referrer = GURL();
params->transition = PageTransition::TYPED;
params->redirects = std::vector<GURL>();
params->should_update_history = false;
params->searchable_form_url = GURL();
params->searchable_form_encoding = std::string();
params->password_form = PasswordForm();
params->security_info = std::string();
params->gesture = NavigationGestureUser;
params->is_post = false;
}
// Subclass the TestingProfile so that it can return certain services we need.
class TabContentsTestingProfile : public TestingProfile {
public:
TabContentsTestingProfile() : TestingProfile() { }
virtual PrefService* GetPrefs() {
if (!prefs_.get()) {
FilePath source_path;
PathService::Get(chrome::DIR_TEST_DATA, &source_path);
source_path = source_path.AppendASCII("profiles")
.AppendASCII("chrome_prefs").AppendASCII("Preferences");
// Create a preference service that only contains user defined
// preference values.
prefs_.reset(new PrefService(new PrefValueStore(
NULL, /* No managed preference values */
new JsonPrefStore( /* user defined preference values */
source_path,
ChromeThread::GetMessageLoopProxyForThread(ChromeThread::FILE)),
NULL /* No suggested preference values */)));
Profile::RegisterUserPrefs(prefs_.get());
browser::RegisterAllPrefs(prefs_.get(), prefs_.get());
}
return prefs_.get();
}
};
class TestInterstitialPage : public InterstitialPage {
public:
enum InterstitialState {
UNDECIDED = 0, // No decision taken yet.
OKED, // Proceed was called.
CANCELED // DontProceed was called.
};
class Delegate {
public:
virtual void TestInterstitialPageDeleted(
TestInterstitialPage* interstitial) = 0;
};
// 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(TabContents* tab,
bool new_navigation,
const GURL& url,
InterstitialState* state,
bool* deleted)
: InterstitialPage(tab, new_navigation, url),
state_(state),
deleted_(deleted),
command_received_count_(0),
delegate_(NULL) {
*state_ = UNDECIDED;
*deleted_ = false;
}
virtual ~TestInterstitialPage() {
if (deleted_)
*deleted_ = true;
if (delegate_)
delegate_->TestInterstitialPageDeleted(this);
}
virtual void DontProceed() {
if (state_)
*state_ = CANCELED;
InterstitialPage::DontProceed();
}
virtual void Proceed() {
if (state_)
*state_ = OKED;
InterstitialPage::Proceed();
}
int command_received_count() const {
return command_received_count_;
}
void TestDomOperationResponse(const std::string& json_string) {
DomOperationResponse(json_string, 1);
}
void TestDidNavigate(int page_id, const GURL& url) {
ViewHostMsg_FrameNavigate_Params params;
InitNavigateParams(&params, page_id, url);
DidNavigate(render_view_host(), params);
}
void TestRenderViewGone() {
RenderViewGone(render_view_host());
}
bool is_showing() const {
return static_cast<TestRenderWidgetHostView*>(render_view_host()->view())->
is_showing();
}
void ClearStates() {
state_ = NULL;
deleted_ = NULL;
delegate_ = NULL;
}
void set_delegate(Delegate* delegate) {
delegate_ = delegate;
}
protected:
virtual RenderViewHost* CreateRenderViewHost() {
return new TestRenderViewHost(
SiteInstance::CreateSiteInstance(tab()->profile()),
this, MSG_ROUTING_NONE);
}
virtual TabContentsView* CreateTabContentsView() { return NULL; }
virtual void CommandReceived(const std::string& command) {
command_received_count_++;
}
private:
InterstitialState* state_;
bool* deleted_;
int command_received_count_;
Delegate* delegate_;
};
class TestInterstitialPageStateGuard : public TestInterstitialPage::Delegate {
public:
explicit TestInterstitialPageStateGuard(
TestInterstitialPage* interstitial_page)
: interstitial_page_(interstitial_page) {
DCHECK(interstitial_page_);
interstitial_page_->set_delegate(this);
}
~TestInterstitialPageStateGuard() {
if (interstitial_page_)
interstitial_page_->ClearStates();
}
virtual void TestInterstitialPageDeleted(TestInterstitialPage* interstitial) {
DCHECK(interstitial_page_ == interstitial);
interstitial_page_ = NULL;
}
private:
TestInterstitialPage* interstitial_page_;
};
class TabContentsTest : public RenderViewHostTestHarness {
public:
TabContentsTest()
: RenderViewHostTestHarness(),
ui_thread_(ChromeThread::UI, &message_loop_) {
}
private:
// Supply our own profile so we use the correct profile data. The test harness
// is not supposed to overwrite a profile if it's already created.
virtual void SetUp() {
profile_.reset(new TabContentsTestingProfile());
RenderViewHostTestHarness::SetUp();
}
virtual void TearDown() {
RenderViewHostTestHarness::TearDown();
profile_.reset(NULL);
}
ChromeThread ui_thread_;
};
// Test to make sure that title updates get stripped of whitespace.
TEST_F(TabContentsTest, UpdateTitle) {
ViewHostMsg_FrameNavigate_Params params;
InitNavigateParams(&params, 0, GURL(chrome::kAboutBlankURL));
NavigationController::LoadCommittedDetails details;
controller().RendererDidNavigate(params, 0, &details);
contents()->UpdateTitle(rvh(), 0, L" Lots O' Whitespace\n");
EXPECT_EQ(ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle());
}
// Test view source mode for the new tabs page.
TEST_F(TabContentsTest, NTPViewSource) {
const char kUrl[] = "view-source:chrome://newtab";
const GURL kGURL(kUrl);
process()->sink().ClearMessages();
controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
rvh()->delegate()->RenderViewCreated(rvh());
// Did we get the expected message?
EXPECT_TRUE(process()->sink().GetFirstMessageMatching(
ViewMsg_EnableViewSourceMode::ID));
ViewHostMsg_FrameNavigate_Params params;
InitNavigateParams(&params, 0, kGURL);
NavigationController::LoadCommittedDetails details;
controller().RendererDidNavigate(params, 0, &details);
// Also check title and url.
EXPECT_EQ(ASCIIToUTF16(kUrl), contents()->GetTitle());
EXPECT_EQ(true, contents()->ShouldDisplayURL());
}
// Test simple same-SiteInstance navigation.
TEST_F(TabContentsTest, SimpleNavigation) {
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
EXPECT_TRUE(contents()->pending_rvh() == NULL);
// Navigate to URL
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
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(controller().GetActiveEntry()->site_instance() == NULL);
// DidNavigate from the page
ViewHostMsg_FrameNavigate_Params params;
InitNavigateParams(&params, 1, url);
contents()->TestDidNavigate(orig_rvh, params);
EXPECT_FALSE(contents()->cross_navigation_pending());
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, controller().GetActiveEntry()->site_instance());
}
// Test that navigating across a site boundary creates a new RenderViewHost
// with a new SiteInstance. Going back should do the same.
TEST_F(TabContentsTest, CrossSiteBoundaries) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
int orig_rvh_delete_count = 0;
orig_rvh->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, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate to new site
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
EXPECT_TRUE(contents()->cross_navigation_pending());
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(&params2, 1, url2);
contents()->TestDidNavigate(pending_rvh, params2);
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(pending_rvh, contents()->render_view_host());
EXPECT_NE(instance1, instance2);
EXPECT_TRUE(contents()->pending_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.
controller().GoBack();
TestRenderViewHost* goback_rvh = contents()->pending_rvh();
EXPECT_TRUE(contents()->cross_navigation_pending());
// DidNavigate from the back action
contents()->TestDidNavigate(goback_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(goback_rvh, contents()->render_view_host());
EXPECT_EQ(pending_rvh_delete_count, 1);
EXPECT_EQ(instance1, contents()->GetSiteInstance());
}
// 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(TabContentsTest, CrossSiteBoundariesAfterCrash) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
int orig_rvh_delete_count = 0;
orig_rvh->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, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Crash the renderer.
orig_rvh->set_render_view_created(false);
// Navigate to new site. We should not go into PENDING.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
TestRenderViewHost* new_rvh = rvh();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_TRUE(contents()->pending_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(&params2, 1, url2);
contents()->TestDidNavigate(new_rvh, params2);
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(new_rvh, rvh());
EXPECT_NE(instance1, instance2);
EXPECT_TRUE(contents()->pending_rvh() == NULL);
}
// 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(TabContentsTest, NavigateTwoTabsCrossSite) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
// Open a new tab with the same SiteInstance, navigated to the same site.
TestTabContents contents2(profile(), instance1);
params1.page_id = 2; // Need this since the site instance is the same (which
// is the scope of page IDs) and we want to consider
// this a new page.
contents2.transition_cross_site = true;
contents2.controller().LoadURL(url, GURL(), PageTransition::TYPED);
contents2.TestDidNavigate(contents2.render_view_host(), params1);
// Navigate first tab to a new site
const GURL url2a("http://www.yahoo.com");
controller().LoadURL(url2a, GURL(), PageTransition::TYPED);
TestRenderViewHost* pending_rvh_a = contents()->pending_rvh();
ViewHostMsg_FrameNavigate_Params params2a;
InitNavigateParams(&params2a, 1, url2a);
contents()->TestDidNavigate(pending_rvh_a, params2a);
SiteInstance* instance2a = contents()->GetSiteInstance();
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, GURL(), PageTransition::TYPED);
TestRenderViewHost* pending_rvh_b = contents2.pending_rvh();
EXPECT_TRUE(pending_rvh_b != NULL);
EXPECT_TRUE(contents2.cross_navigation_pending());
// 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(&params2b, 2, url2b);
contents2.TestDidNavigate(pending_rvh_b, params2b);
SiteInstance* instance2b = contents2.GetSiteInstance();
EXPECT_NE(instance1, instance2b);
// Both tabs should now be in the same SiteInstance.
EXPECT_EQ(instance2a, instance2b);
}
// Tests that TabContents uses the current URL, not the SiteInstance's site, to
// determine whether a navigation is cross-site.
TEST_F(TabContentsTest, CrossSiteComparesAgainstCurrentPage) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
// Open a related tab to a second site.
TestTabContents contents2(profile(), instance1);
contents2.transition_cross_site = true;
const GURL url2("http://www.yahoo.com");
contents2.controller().LoadURL(url2, GURL(), PageTransition::TYPED);
// The first RVH in contents2 isn't live yet, so we shortcut the cross site
// pending.
TestRenderViewHost* rvh2 = static_cast<TestRenderViewHost*>(
contents2.render_view_host());
EXPECT_FALSE(contents2.cross_navigation_pending());
ViewHostMsg_FrameNavigate_Params params2;
InitNavigateParams(&params2, 2, url2);
contents2.TestDidNavigate(rvh2, params2);
SiteInstance* instance2 = contents2.GetSiteInstance();
EXPECT_NE(instance1, instance2);
EXPECT_FALSE(contents2.cross_navigation_pending());
// 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(&params3, 2, url2);
contents()->TestDidNavigate(orig_rvh, params3);
SiteInstance* instance3 = contents()->GetSiteInstance();
EXPECT_EQ(instance1, instance3);
EXPECT_FALSE(contents()->cross_navigation_pending());
// 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, GURL(), PageTransition::TYPED);
EXPECT_FALSE(contents()->cross_navigation_pending());
ViewHostMsg_FrameNavigate_Params params4;
InitNavigateParams(&params4, 3, url3);
contents()->TestDidNavigate(orig_rvh, params4);
SiteInstance* instance4 = contents()->GetSiteInstance();
EXPECT_EQ(instance1, instance4);
}
// Test that the onbeforeunload and onunload handlers run when navigating
// across site boundaries.
TEST_F(TabContentsTest, CrossSiteUnloadHandlers) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate to new site, but simulate an onbeforeunload denial.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, false));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate again, but simulate an onbeforeunload approval.
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_TRUE(contents()->cross_navigation_pending());
TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
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
// TabContents::OnCrossSiteResponse.)
// DidNavigate from the pending page
ViewHostMsg_FrameNavigate_Params params2;
InitNavigateParams(&params2, 1, url2);
contents()->TestDidNavigate(pending_rvh, params2);
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(pending_rvh, rvh());
EXPECT_NE(instance1, instance2);
EXPECT_TRUE(contents()->pending_rvh() == NULL);
}
// 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(TabContentsTest, CrossSiteNavigationPreempted) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate to new site, simulating an onbeforeunload approval.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
EXPECT_TRUE(contents()->cross_navigation_pending());
// Suppose the original renderer navigates before the new one is ready.
orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
// Verify that the pending navigation is cancelled.
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, rvh());
EXPECT_EQ(instance1, instance2);
EXPECT_TRUE(contents()->pending_rvh() == NULL);
}
// 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(TabContentsTest, CrossSiteNavigationNotPreemptedByFrame) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Start navigating to new site.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
// Simulate a sub-frame navigation arriving and ensure the RVH is still
// waiting for a before unload response.
orig_rvh->SendNavigateWithTransition(1, GURL("http://google.com/frame"),
PageTransition::AUTO_SUBFRAME);
EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack());
// Now simulate the onbeforeunload approval and verify the navigation is
// not canceled.
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack());
EXPECT_TRUE(contents()->cross_navigation_pending());
}
// Test that the original renderer can preempt a cross-site navigation while the
// beforeunload request is in flight.
TEST_F(TabContentsTest, CrossSitePreemptDuringBeforeUnload) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate to new site, with the befureunload request in flight.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
// Suppose the original renderer navigates now, while the beforeunload request
// is in flight. We must cancel the pending navigation and show this new
// page, because the beforeunload handler might return false.
orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
// Verify that the pending navigation is cancelled.
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, rvh());
EXPECT_EQ(instance1, instance2);
EXPECT_TRUE(contents()->pending_rvh() == NULL);
// Make sure the beforeunload ack doesn't cause problems if it arrives here.
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
}
// Test that the original renderer cannot preempt a cross-site navigation once
// the unload request has been made. At this point, the cross-site navigation
// is almost ready to be displayed, and the original renderer is only given a
// short chance to run an unload handler. Prevents regression of bug 23942.
TEST_F(TabContentsTest, CrossSiteCantPreemptAfterUnload) {
contents()->transition_cross_site = true;
TestRenderViewHost* orig_rvh = rvh();
SiteInstance* instance1 = contents()->GetSiteInstance();
// Navigate to URL. First URL should use first RenderViewHost.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(orig_rvh, contents()->render_view_host());
// Navigate to new site, simulating an onbeforeunload approval.
const GURL url2("http://www.yahoo.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
orig_rvh->TestOnMessageReceived(ViewHostMsg_ShouldClose_ACK(0, true));
EXPECT_TRUE(contents()->cross_navigation_pending());
TestRenderViewHost* pending_rvh = static_cast<TestRenderViewHost*>(
contents()->pending_rvh());
// Simulate the pending renderer's response, which leads to an unload request
// being sent to orig_rvh.
contents()->OnCrossSiteResponse(0, 0);
// Suppose the original renderer navigates now, while the unload request is in
// flight. We should ignore it, wait for the unload ack, and let the pending
// request continue. Otherwise, the tab may close spontaneously or stop
// responding to navigation requests. (See bug 23942.)
ViewHostMsg_FrameNavigate_Params params1a;
InitNavigateParams(&params1a, 2, GURL("http://www.google.com/foo"));
orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo"));
// Verify that the pending navigation is still in progress.
EXPECT_TRUE(contents()->cross_navigation_pending());
EXPECT_TRUE(contents()->pending_rvh() != NULL);
// DidNavigate from the pending page should commit it.
ViewHostMsg_FrameNavigate_Params params2;
InitNavigateParams(&params2, 1, url2);
contents()->TestDidNavigate(pending_rvh, params2);
SiteInstance* instance2 = contents()->GetSiteInstance();
EXPECT_FALSE(contents()->cross_navigation_pending());
EXPECT_EQ(pending_rvh, rvh());
EXPECT_NE(instance1, instance2);
EXPECT_TRUE(contents()->pending_rvh() == NULL);
}
// Test that NavigationEntries have the correct content state after going
// forward and back. Prevents regression for bug 1116137.
TEST_F(TabContentsTest, NavigationEntryContentState) {
TestRenderViewHost* orig_rvh = rvh();
// Navigate to URL. There should be no committed entry yet.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
NavigationEntry* entry = controller().GetLastCommittedEntry();
EXPECT_TRUE(entry == NULL);
// Committed entry should have content state after DidNavigate.
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
entry = controller().GetLastCommittedEntry();
EXPECT_FALSE(entry->content_state().empty());
// Navigate to same site.
const GURL url2("http://images.google.com");
controller().LoadURL(url2, GURL(), PageTransition::TYPED);
entry = controller().GetLastCommittedEntry();
EXPECT_FALSE(entry->content_state().empty());
// Committed entry should have content state after DidNavigate.
ViewHostMsg_FrameNavigate_Params params2;
InitNavigateParams(&params2, 2, url2);
contents()->TestDidNavigate(orig_rvh, params2);
entry = controller().GetLastCommittedEntry();
EXPECT_FALSE(entry->content_state().empty());
// Now go back. Committed entry should still have content state.
controller().GoBack();
contents()->TestDidNavigate(orig_rvh, params1);
entry = controller().GetLastCommittedEntry();
EXPECT_FALSE(entry->content_state().empty());
}
// Test that NavigationEntries have the correct content state after opening
// a new window to about:blank. Prevents regression for bug 1116137.
TEST_F(TabContentsTest, NavigationEntryContentStateNewWindow) {
TestRenderViewHost* orig_rvh = rvh();
// When opening a new window, it is navigated to about:blank internally.
// Currently, this results in two DidNavigate events.
const GURL url(chrome::kAboutBlankURL);
ViewHostMsg_FrameNavigate_Params params1;
InitNavigateParams(&params1, 1, url);
contents()->TestDidNavigate(orig_rvh, params1);
contents()->TestDidNavigate(orig_rvh, params1);
// Should have a content state here.
NavigationEntry* entry = controller().GetLastCommittedEntry();
EXPECT_FALSE(entry->content_state().empty());
}
// Tests to see that webkit preferences are properly loaded and copied over
// to a WebPreferences object.
TEST_F(TabContentsTest, WebKitPrefs) {
WebPreferences webkit_prefs = contents()->TestGetWebkitPrefs();
// These values have been overridden by the profile preferences.
EXPECT_EQ("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.
#if defined(OS_MACOSX)
const wchar_t kDefaultFont[] = L"Times";
#elif defined(OS_CHROMEOS)
const wchar_t kDefaultFont[] = L"Ascender Serif";
#else
const wchar_t kDefaultFont[] = L"Times New Roman";
#endif
EXPECT_EQ(kDefaultFont, webkit_prefs.standard_font_family);
EXPECT_EQ(true, webkit_prefs.javascript_enabled);
}
////////////////////////////////////////////////////////////////////////////////
// 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(TabContentsTest,
ShowInterstitialFromBrowserWithNewNavigationDontProceed) {
// Navigate to a page.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Initiate a browser navigation that will trigger the interstitial
controller().LoadURL(GURL("http://www.evil.com"), GURL(),
PageTransition::TYPED);
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url2("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, 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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url2);
// Now don't proceed.
interstitial->DontProceed();
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url1);
EXPECT_EQ(1, controller().entry_count());
}
// 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(TabContentsTest,
ShowInterstitiaFromRendererlWithNewNavigationDontProceed) {
// Navigate to a page.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial (no pending entry, the interstitial would have been
// triggered by clicking on a link).
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url2("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, 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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url2);
// Now don't proceed.
interstitial->DontProceed();
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url1);
EXPECT_EQ(1, controller().entry_count());
}
// 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(TabContentsTest, ShowInterstitialNoNewNavigationDontProceed) {
// Navigate to a page.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
// The URL specified to the interstitial should have been ignored.
EXPECT_TRUE(entry->url() == url1);
// Now don't proceed.
interstitial->DontProceed();
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url1);
EXPECT_EQ(1, controller().entry_count());
}
// 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(TabContentsTest,
ShowInterstitialFromBrowserNewNavigationProceed) {
// Navigate to a page.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Initiate a browser navigation that will trigger the interstitial
controller().LoadURL(GURL("http://www.evil.com"), GURL(),
PageTransition::TYPED);
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url2("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, 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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url2);
// Then proceed.
interstitial->Proceed();
// The interstitial should show until the new navigation commits.
ASSERT_FALSE(deleted);
EXPECT_EQ(TestInterstitialPage::OKED, state);
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
// Simulate the navigation to the page, that's when the interstitial gets
// hidden.
GURL url3("http://www.thepage.com");
rvh()->SendNavigate(2, url3);
EXPECT_TRUE(deleted);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url3);
EXPECT_EQ(2, controller().entry_count());
}
// 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(TabContentsTest,
ShowInterstitialFromRendererNewNavigationProceed) {
// Navigate to a page.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url2("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, 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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url2);
// Then proceed.
interstitial->Proceed();
// The interstitial should show until the new navigation commits.
ASSERT_FALSE(deleted);
EXPECT_EQ(TestInterstitialPage::OKED, state);
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
// Simulate the navigation to the page, that's when the interstitial gets
// hidden.
GURL url3("http://www.thepage.com");
rvh()->SendNavigate(2, url3);
EXPECT_TRUE(deleted);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url3);
EXPECT_EQ(2, controller().entry_count());
}
// 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(TabContentsTest, ShowInterstitialNoNewNavigationProceed) {
// Navigate to a page so we have a navigation entry in the controller.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
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()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
// Let's commit the interstitial navigation.
interstitial->TestDidNavigate(1, url2);
EXPECT_TRUE(interstitial->is_showing());
EXPECT_TRUE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == interstitial);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
// The URL specified to the interstitial should have been ignored.
EXPECT_TRUE(entry->url() == 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_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::OKED, state);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == url1);
EXPECT_EQ(1, controller().entry_count());
}
// Test navigating to a page that shows an interstitial, then navigating away.
TEST_F(TabContentsTest, ShowInterstitialThenNavigate) {
// Show interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, url, &state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(1, url);
// While interstitial showing, navigate to a new URL.
const GURL url2("http://www.yahoo.com");
rvh()->SendNavigate(1, url2);
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
}
// Test navigating to a page that shows an interstitial, then going back.
TEST_F(TabContentsTest, ShowInterstitialThenGoBack) {
// Navigate to a page so we have a navigation entry in the controller.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL interstitial_url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, interstitial_url,
&state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(2, interstitial_url);
// While the interstitial is showing, go back.
controller().GoBack();
rvh()->SendNavigate(1, url1);
// Make sure we are back to the original page and that the interstitial is
// gone.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(url1.spec(), entry->url().spec());
}
// Test navigating to a page that shows an interstitial, has a renderer crash,
// and then goes back.
TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenGoBack) {
// Navigate to a page so we have a navigation entry in the controller.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL interstitial_url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, interstitial_url,
&state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(2, interstitial_url);
// Crash the renderer
rvh()->TestOnMessageReceived(ViewHostMsg_RenderViewGone(0));
// While the interstitial is showing, go back.
controller().GoBack();
rvh()->SendNavigate(1, url1);
// Make sure we are back to the original page and that the interstitial is
// gone.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(url1.spec(), entry->url().spec());
}
// Test navigating to a page that shows an interstitial, has the renderer crash,
// and then navigates to the interstitial.
TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenNavigate) {
// Navigate to a page so we have a navigation entry in the controller.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL interstitial_url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, interstitial_url,
&state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
// Crash the renderer
rvh()->TestOnMessageReceived(ViewHostMsg_RenderViewGone(0));
interstitial->TestDidNavigate(2, interstitial_url);
}
// Test navigating to a page that shows an interstitial, then close the tab.
TEST_F(TabContentsTest, ShowInterstitialThenCloseTab) {
// Show interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, url, &state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(1, url);
// Now close the tab.
DeleteContents();
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
}
// Test that after Proceed is called and an interstitial is still shown, no more
// commands get executed.
TEST_F(TabContentsTest, ShowInterstitialProceedMultipleCommands) {
// Navigate to a page so we have a navigation entry in the controller.
GURL url1("http://www.google.com");
rvh()->SendNavigate(1, url1);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL url2("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, url2, &state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(1, 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();
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(TabContentsTest, ShowInterstitialOnInterstitial) {
// Navigate to a page so we have a navigation entry in the controller.
GURL start_url("http://www.google.com");
rvh()->SendNavigate(1, start_url);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state1 =
TestInterstitialPage::UNDECIDED;
bool deleted1 = false;
GURL url1("http://interstitial1");
TestInterstitialPage* interstitial1 =
new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
TestInterstitialPageStateGuard state_guard1(interstitial1);
interstitial1->Show();
interstitial1->TestDidNavigate(1, url1);
// Now show another interstitial.
TestInterstitialPage::InterstitialState state2 =
TestInterstitialPage::UNDECIDED;
bool deleted2 = false;
GURL url2("http://interstitial2");
TestInterstitialPage* interstitial2 =
new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
TestInterstitialPageStateGuard state_guard2(interstitial2);
interstitial2->Show();
interstitial2->TestDidNavigate(1, url2);
// Showing interstitial2 should have caused interstitial1 to go away.
EXPECT_TRUE(deleted1);
EXPECT_EQ(TestInterstitialPage::CANCELED, state1);
// Let's make sure interstitial2 is working as intended.
ASSERT_FALSE(deleted2);
EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
interstitial2->Proceed();
GURL landing_url("http://www.thepage.com");
rvh()->SendNavigate(2, landing_url);
EXPECT_TRUE(deleted2);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == landing_url);
EXPECT_EQ(2, controller().entry_count());
}
// Test showing an interstitial, proceeding and then navigating to another
// interstitial.
TEST_F(TabContentsTest, ShowInterstitialProceedShowInterstitial) {
// Navigate to a page so we have a navigation entry in the controller.
GURL start_url("http://www.google.com");
rvh()->SendNavigate(1, start_url);
EXPECT_EQ(1, controller().entry_count());
// Show an interstitial.
TestInterstitialPage::InterstitialState state1 =
TestInterstitialPage::UNDECIDED;
bool deleted1 = false;
GURL url1("http://interstitial1");
TestInterstitialPage* interstitial1 =
new TestInterstitialPage(contents(), true, url1, &state1, &deleted1);
TestInterstitialPageStateGuard state_guard1(interstitial1);
interstitial1->Show();
interstitial1->TestDidNavigate(1, 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::UNDECIDED;
bool deleted2 = false;
GURL url2("http://interstitial2");
TestInterstitialPage* interstitial2 =
new TestInterstitialPage(contents(), true, url2, &state2, &deleted2);
TestInterstitialPageStateGuard state_guard2(interstitial2);
interstitial2->Show();
interstitial2->TestDidNavigate(1, url2);
// Showing interstitial2 should have caused interstitial1 to go away.
EXPECT_TRUE(deleted1);
// Let's make sure interstitial2 is working as intended.
ASSERT_FALSE(deleted2);
EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
interstitial2->Proceed();
GURL landing_url("http://www.thepage.com");
rvh()->SendNavigate(2, landing_url);
EXPECT_TRUE(deleted2);
EXPECT_FALSE(contents()->showing_interstitial_page());
EXPECT_TRUE(contents()->interstitial_page() == NULL);
NavigationEntry* entry = controller().GetActiveEntry();
ASSERT_TRUE(entry != NULL);
EXPECT_TRUE(entry->url() == landing_url);
EXPECT_EQ(2, controller().entry_count());
}
// Test that navigating away from an interstitial while it's loading cause it
// not to show.
TEST_F(TabContentsTest, NavigateBeforeInterstitialShows) {
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
GURL interstitial_url("http://interstitial");
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, interstitial_url,
&state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
// Let's simulate a navigation initiated from the browser before the
// interstitial finishes loading.
const GURL url("http://www.google.com");
controller().LoadURL(url, GURL(), PageTransition::TYPED);
ASSERT_FALSE(deleted);
EXPECT_FALSE(interstitial->is_showing());
// Now let's make the interstitial navigation commit.
interstitial->TestDidNavigate(1, interstitial_url);
// After it loaded the interstitial should be gone.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
}
// Test showing an interstitial and have its renderer crash.
TEST_F(TabContentsTest, InterstitialCrasher) {
// Show an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
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->TestRenderViewGone();
// The interstitial should have been dismissed.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
// Now try again but this time crash the intersitial after it was shown.
interstitial =
new TestInterstitialPage(contents(), true, url, &state, &deleted);
interstitial->Show();
interstitial->TestDidNavigate(1, url);
// Simulate a renderer crash.
interstitial->TestRenderViewGone();
// The interstitial should have been dismissed.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
}
// 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(TabContentsTest, NewInterstitialDoesNotCancelPendingEntry) {
const char kUrl[] = "http://www.badguys.com/";
const GURL kGURL(kUrl);
// Start a navigation to a page
contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
// Simulate that navigation triggering an interstitial.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(1, kGURL);
// Initiate a new navigation from the browser that also triggers an
// interstitial.
contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
TestInterstitialPage::InterstitialState state2 =
TestInterstitialPage::UNDECIDED;
bool deleted2 = false;
TestInterstitialPage* interstitial2 =
new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
TestInterstitialPageStateGuard state_guard2(interstitial2);
interstitial2->Show();
interstitial2->TestDidNavigate(1, kGURL);
// Make sure we still have an entry.
NavigationEntry* entry = contents()->controller().pending_entry();
ASSERT_TRUE(entry);
EXPECT_EQ(kUrl, entry->url().spec());
// And that the first interstitial is gone, but not the second.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
EXPECT_FALSE(deleted2);
EXPECT_EQ(TestInterstitialPage::UNDECIDED, state2);
}
// Tests that Javascript messages are not shown while an interstitial is
// showing.
TEST_F(TabContentsTest, NoJSMessageOnInterstitials) {
const char kUrl[] = "http://www.badguys.com/";
const GURL kGURL(kUrl);
// Start a navigation to a page
contents()->controller().LoadURL(kGURL, GURL(), PageTransition::TYPED);
// DidNavigate from the page
ViewHostMsg_FrameNavigate_Params params;
InitNavigateParams(&params, 1, kGURL);
contents()->TestDidNavigate(rvh(), params);
// Simulate showing an interstitial while the page is showing.
TestInterstitialPage::InterstitialState state =
TestInterstitialPage::UNDECIDED;
bool deleted = false;
TestInterstitialPage* interstitial =
new TestInterstitialPage(contents(), true, kGURL, &state, &deleted);
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
interstitial->TestDidNavigate(1, 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;
bool did_suppress_message = false;
contents()->RunJavaScriptMessage(L"This is an informative message", L"OK",
kGURL, MessageBoxFlags::kIsJavascriptAlert, dummy_message,
&did_suppress_message);
EXPECT_TRUE(did_suppress_message);
}