summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-26 00:40:39 +0000
committermmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-26 00:40:39 +0000
commit5a8dffaba9d7fecdd74eda2c351299629d690913 (patch)
tree060ddb794151371b7605ae8b01aea7501e1a2fa7
parentc37d7883d12066964d3656a426083389313d5177 (diff)
downloadchromium_src-5a8dffaba9d7fecdd74eda2c351299629d690913.zip
chromium_src-5a8dffaba9d7fecdd74eda2c351299629d690913.tar.gz
chromium_src-5a8dffaba9d7fecdd74eda2c351299629d690913.tar.bz2
Browser test for prerendering in general
(PrerenderBrowserTest.PrerenderPage) Also switch PrerenderManager from inheriting from NonThreadSafe to using explicit DCHECKs, so doesn't cause a debug assertion when destroyed on another thread. BUG=70398 TEST=PrerenderBrowserTest.PrerenderPage Review URL: http://codereview.chromium.org/6255005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72573 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/prerender/prerender_browsertest.cc149
-rw-r--r--chrome/browser/prerender/prerender_contents.cc14
-rw-r--r--chrome/browser/prerender/prerender_contents.h26
-rw-r--r--chrome/browser/prerender/prerender_manager.cc29
-rw-r--r--chrome/browser/prerender/prerender_manager.h19
-rw-r--r--chrome/browser/prerender/prerender_manager_unittest.cc9
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/test/data/prerender/prerender_loader.html12
-rw-r--r--chrome/test/data/prerender/prerender_page.html24
9 files changed, 271 insertions, 12 deletions
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
new file mode 100644
index 0000000..f968402
--- /dev/null
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2011 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 "base/command_line.h"
+#include "base/path_service.h"
+#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "net/url_request/url_request_context.h"
+
+// Prerender tests work as follows:
+//
+// A page with a prefetch link to the test page is loaded. Once prerendered,
+// its Javascript function DidPrerenderPass() is called, which returns true if
+// the page behaves as expected when prerendered.
+//
+// The prerendered page is then displayed on a tab. The Javascript function
+// DidDisplayPass() is called, and returns true if the page behaved as it
+// should while being displayed.
+
+namespace {
+
+// PrerenderContents that stops the UI message loop on DidStopLoading().
+class TestPrerenderContents : public PrerenderContents {
+ public:
+ TestPrerenderContents(
+ PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
+ const std::vector<GURL>& alias_urls)
+ : PrerenderContents(prerender_manager, profile, url, alias_urls),
+ did_finish_loading_(false) {
+ }
+
+ virtual void DidStopLoading() {
+ PrerenderContents::DidStopLoading();
+ did_finish_loading_ = true;
+ MessageLoopForUI::current()->Quit();
+ }
+
+ bool did_finish_loading() const { return did_finish_loading_; }
+
+ private:
+ bool did_finish_loading_;
+};
+
+// PrerenderManager that uses TestPrerenderContents.
+class WaitForLoadPrerenderContentsFactory : public PrerenderContents::Factory {
+ public:
+ virtual PrerenderContents* CreatePrerenderContents(
+ PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
+ const std::vector<GURL>& alias_urls) {
+ return new TestPrerenderContents(prerender_manager, profile, url,
+ alias_urls);
+ }
+};
+
+} // namespace
+
+class PrerenderBrowserTest : public InProcessBrowserTest {
+ public:
+ PrerenderBrowserTest() {
+ EnableDOMAutomation();
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) {
+ command_line->AppendSwitch(switches::kEnablePagePrerender);
+#if defined(OS_MACOSX)
+ // The plugins directory isn't read by default on the Mac, so it needs to be
+ // explicitly registered.
+ FilePath app_dir;
+ PathService::Get(chrome::DIR_APP, &app_dir);
+ command_line->AppendSwitchPath(
+ switches::kExtraPluginDir,
+ app_dir.Append(FILE_PATH_LITERAL("plugins")));
+#endif
+ }
+
+ void RunTestURL(const std::string& html_file) {
+ ASSERT_TRUE(test_server()->Start());
+
+ std::string src_path = "files/prerender/prerender_loader.html?";
+ src_path.append(html_file);
+ std::string dest_path = "files/prerender/";
+ dest_path.append(html_file);
+
+ GURL src_url = test_server()->GetURL(src_path);
+ GURL dest_url = test_server()->GetURL(dest_path);
+
+ Profile* profile = browser()->GetSelectedTabContents()->profile();
+ PrerenderManager* prerender_manager = profile->GetPrerenderManager();
+ ASSERT_TRUE(prerender_manager);
+
+ // This is needed to exit the event loop once the prerendered page has
+ // stopped loading.
+ prerender_manager->SetPrerenderContentsFactory(
+ new WaitForLoadPrerenderContentsFactory());
+
+ // ui_test_utils::NavigateToURL uses its own observer and message loop.
+ // Since the test needs to wait until the prerendered page has stopped
+ // loading, rathather than the page directly navigated to, need to
+ // handle browser navigation directly.
+ browser()->OpenURL(src_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
+
+ ui_test_utils::RunMessageLoop();
+
+ TestPrerenderContents* prerender_contents =
+ static_cast<TestPrerenderContents*>(
+ prerender_manager->FindEntry(dest_url));
+
+ // Make sure the prefetech link was caught and the page was prerendered.
+ ASSERT_TRUE(prerender_contents != NULL);
+ ASSERT_TRUE(prerender_contents->did_finish_loading());
+
+ // Check if page behaves as expected while in prerendered state.
+ bool prerender_test_result;
+ ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ prerender_contents->render_view_host(), L"",
+ L"window.domAutomationController.send(DidPrerenderPass())",
+ &prerender_test_result));
+ EXPECT_TRUE(prerender_test_result);
+
+ ui_test_utils::NavigateToURL(browser(), dest_url);
+
+ // Make sure the PrerenderContents found earlier was used.
+ EXPECT_TRUE(prerender_manager->FindEntry(dest_url) == NULL);
+
+ // Check if page behaved as expected when actually displayed.
+ bool display_test_result;
+ ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ browser()->GetSelectedTabContents()->render_view_host(), L"",
+ L"window.domAutomationController.send(DidDisplayPass())",
+ &display_test_result));
+ EXPECT_TRUE(display_test_result);
+ }
+};
+
+// Checks that a page is correctly prerendered in the case of a
+// <link rel=prefetch> tag and then loaded into a tab in response to a
+// navigation.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPage) {
+ RunTestURL("prerender_page.html");
+}
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 5048124..bd84b8e 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -20,6 +20,15 @@
#include "chrome/common/render_messages_params.h"
#include "gfx/rect.h"
+class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
+ public:
+ virtual PrerenderContents* CreatePrerenderContents(
+ PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
+ const std::vector<GURL>& alias_urls) {
+ return new PrerenderContents(prerender_manager, profile, url, alias_urls);
+ }
+};
+
PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager,
Profile* profile,
const GURL& url,
@@ -38,6 +47,11 @@ PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager,
}
}
+// static
+PrerenderContents::Factory* PrerenderContents::CreateFactory() {
+ return new PrerenderContentsFactoryImpl();
+}
+
void PrerenderContents::StartPrerendering() {
DCHECK(profile_ != NULL);
SiteInstance* site_instance = SiteInstance::CreateSiteInstance(profile_);
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index 8bda7a4..4f55aba 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -36,9 +36,25 @@ class PrerenderContents : public RenderViewHostDelegate,
public NotificationObserver,
public JavaScriptAppModalDialogDelegate {
public:
- PrerenderContents(PrerenderManager* prerender_manager, Profile* profile,
- const GURL& url, const std::vector<GURL>& alias_urls);
+ // PrerenderContents::Create uses the currently registered Factory to create
+ // the PrerenderContents. Factory is intended for testing.
+ class Factory {
+ public:
+ Factory() {}
+ virtual ~Factory() {}
+
+ virtual PrerenderContents* CreatePrerenderContents(
+ PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
+ const std::vector<GURL>& alias_urls) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+ };
+
virtual ~PrerenderContents();
+
+ static Factory* CreateFactory();
+
virtual void StartPrerendering();
RenderViewHost* render_view_host() { return render_view_host_; }
@@ -137,10 +153,16 @@ class PrerenderContents : public RenderViewHostDelegate,
virtual void ClearInspectorSettings();
protected:
+ PrerenderContents(PrerenderManager* prerender_manager, Profile* profile,
+ const GURL& url, const std::vector<GURL>& alias_urls);
+
// from RenderViewHostDelegate.
virtual bool OnMessageReceived(const IPC::Message& message);
private:
+ // Needs to be able to call the constructor.
+ friend class PrerenderContentsFactoryImpl;
+
// Message handlers.
void OnDidStartProvisionalLoadForFrame(int64 frame_id,
bool main_frame,
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 1b75670..cc5b683 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
@@ -30,7 +31,8 @@ PrerenderManager::PrerenderManager(Profile* profile)
: profile_(profile),
max_prerender_age_(base::TimeDelta::FromSeconds(
kDefaultMaxPrerenderAgeSeconds)),
- max_elements_(kDefaultMaxPrerenderElements) {
+ max_elements_(kDefaultMaxPrerenderElements),
+ prerender_contents_factory_(PrerenderContents::CreateFactory()) {
}
PrerenderManager::~PrerenderManager() {
@@ -41,9 +43,14 @@ PrerenderManager::~PrerenderManager() {
}
}
+void PrerenderManager::SetPrerenderContentsFactory(
+ PrerenderContents::Factory* prerender_contents_factory) {
+ prerender_contents_factory_.reset(prerender_contents_factory);
+}
+
void PrerenderManager::AddPreload(const GURL& url,
const std::vector<GURL>& alias_urls) {
- DCHECK(CalledOnValidThread());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DeleteOldEntries();
// If the URL already exists in the set of preloaded URLs, don't do anything.
for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
@@ -90,7 +97,7 @@ PrerenderContents* PrerenderManager::GetEntry(const GURL& url) {
}
bool PrerenderManager::MaybeUsePreloadedPage(TabContents* tc, const GURL& url) {
- DCHECK(CalledOnValidThread());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_ptr<PrerenderContents> pc(GetEntry(url));
if (pc.get() == NULL)
return false;
@@ -112,7 +119,7 @@ bool PrerenderManager::MaybeUsePreloadedPage(TabContents* tc, const GURL& url) {
}
void PrerenderManager::RemoveEntry(PrerenderContents* entry) {
- DCHECK(CalledOnValidThread());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
it != prerender_list_.end();
++it) {
@@ -137,5 +144,17 @@ bool PrerenderManager::IsPrerenderElementFresh(const base::Time start) const {
PrerenderContents* PrerenderManager::CreatePrerenderContents(
const GURL& url,
const std::vector<GURL>& alias_urls) {
- return new PrerenderContents(this, profile_, url, alias_urls);
+ return prerender_contents_factory_->CreatePrerenderContents(
+ this, profile_, url, alias_urls);
+}
+
+PrerenderContents* PrerenderManager::FindEntry(const GURL& url) {
+ for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin();
+ it != prerender_list_.end();
+ ++it) {
+ if (it->contents_->MatchesURL(url))
+ return it->contents_;
+ }
+ // Entry not found.
+ return NULL;
}
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index c4a6375..d23cbe5e 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -11,18 +11,16 @@
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
#include "base/time.h"
+#include "chrome/browser/prerender/prerender_contents.h"
#include "googleurl/src/gurl.h"
-class PrerenderContents;
class Profile;
class TabContents;
// PrerenderManager is responsible for initiating and keeping prerendered
// views of webpages.
-class PrerenderManager : public base::RefCounted<PrerenderManager>,
- private base::NonThreadSafe {
+class PrerenderManager : public base::RefCounted<PrerenderManager> {
public:
// Owned by a Profile object for the lifetime of the profile.
explicit PrerenderManager(Profile* profile);
@@ -55,7 +53,13 @@ class PrerenderManager : public base::RefCounted<PrerenderManager>,
protected:
virtual ~PrerenderManager();
+ void SetPrerenderContentsFactory(
+ PrerenderContents::Factory* prerender_contents_factory);
+
private:
+ // Test that needs needs access to internal functions.
+ friend class PrerenderBrowserTest;
+
friend class base::RefCounted<PrerenderManager>;
struct PrerenderContentsData;
@@ -66,6 +70,11 @@ class PrerenderManager : public base::RefCounted<PrerenderManager>,
const GURL& url,
const std::vector<GURL>& alias_urls);
+ // Finds the specified PrerenderContents and returns it, if it exists.
+ // Returns NULL otherwise. Unlike GetEntry, the PrerenderManager maintains
+ // ownership of the PrerenderContents.
+ PrerenderContents* FindEntry(const GURL& url);
+
Profile* profile_;
base::TimeDelta max_prerender_age_;
@@ -80,6 +89,8 @@ class PrerenderManager : public base::RefCounted<PrerenderManager>,
// Default maximum age a prerendered element may have, in seconds.
static const int kDefaultMaxPrerenderAgeSeconds = 20;
+ scoped_ptr<PrerenderContents::Factory> prerender_contents_factory_;
+
DISALLOW_COPY_AND_ASSIGN(PrerenderManager);
};
diff --git a/chrome/browser/prerender/prerender_manager_unittest.cc b/chrome/browser/prerender/prerender_manager_unittest.cc
index 97a573d..8cc51d0 100644
--- a/chrome/browser/prerender/prerender_manager_unittest.cc
+++ b/chrome/browser/prerender/prerender_manager_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/time.h"
+#include "chrome/browser/browser_thread.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "googleurl/src/gurl.h"
@@ -80,11 +81,17 @@ class TestPrerenderManager : public PrerenderManager {
class PrerenderManagerTest : public testing::Test {
public:
- PrerenderManagerTest() : prerender_manager_(new TestPrerenderManager()) {
+ PrerenderManagerTest() : prerender_manager_(new TestPrerenderManager()),
+ ui_thread_(BrowserThread::UI, &message_loop_) {
}
protected:
scoped_refptr<TestPrerenderManager> prerender_manager_;
+
+ private:
+ // Needed to pass PrerenderManager's DCHECKs.
+ MessageLoop message_loop_;
+ BrowserThread ui_thread_;
};
TEST_F(PrerenderManagerTest, EmptyTest) {
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index a4a36d4..0f3211b 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2138,6 +2138,7 @@
'browser/policy/device_management_backend_mock.h',
'browser/policy/device_management_service_browsertest.cc',
'browser/popup_blocker_browsertest.cc',
+ 'browser/prerender/prerender_browsertest.cc',
'browser/printing/print_dialog_cloud_uitest.cc',
'browser/renderer_host/test/render_process_host_browsertest.cc',
'browser/renderer_host/test/render_view_host_browsertest.cc',
diff --git a/chrome/test/data/prerender/prerender_loader.html b/chrome/test/data/prerender/prerender_loader.html
new file mode 100644
index 0000000..0e21794
--- /dev/null
+++ b/chrome/test/data/prerender/prerender_loader.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<title>Preloader</title>
+</head>
+<script>
+document.write(
+ '<link rel="prefetch" href="' +
+ window.location.search.substring(1) +
+ '">');
+</script>
+</body>
+</html>
diff --git a/chrome/test/data/prerender/prerender_page.html b/chrome/test/data/prerender/prerender_page.html
new file mode 100644
index 0000000..c1e502a
--- /dev/null
+++ b/chrome/test/data/prerender/prerender_page.html
@@ -0,0 +1,24 @@
+<html>
+<!--
+This test checks to make sure that a prerendered page is loaded.
+-->
+<head>
+<title>Prerender Plugin Delay Loading</title>
+
+<script>
+// Make sure plugin was not loaded while prerendering.
+function DidPrerenderPass() {
+ pageWasPrerendered = true;
+ return true;
+}
+
+// Make sure DidPrerenderPass() was called first. Otherwise, the page was
+// most likely reloaded instead of using the prerendered page.
+function DidDisplayPass() {
+ return (pageWasPrerendered == true);
+}
+</script>
+
+</head>
+<body></body>
+</html>