diff options
author | mmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-26 00:40:39 +0000 |
---|---|---|
committer | mmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-26 00:40:39 +0000 |
commit | 5a8dffaba9d7fecdd74eda2c351299629d690913 (patch) | |
tree | 060ddb794151371b7605ae8b01aea7501e1a2fa7 | |
parent | c37d7883d12066964d3656a426083389313d5177 (diff) | |
download | chromium_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.cc | 149 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.cc | 14 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.h | 26 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 29 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.h | 19 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager_unittest.cc | 9 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/test/data/prerender/prerender_loader.html | 12 | ||||
-rw-r--r-- | chrome/test/data/prerender/prerender_page.html | 24 |
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> |