diff options
30 files changed, 316 insertions, 87 deletions
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 8d624d7..81d63bd 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc @@ -172,8 +172,13 @@ class TestPrerenderContents : public PrerenderContents { // When the PrerenderContents is destroyed, quit the UI message loop. // This happens on navigation to used prerendered pages, and soon // after cancellation of unused prerendered pages. - if (quit_message_loop_on_destruction_) - MessageLoopForUI::current()->Quit(); + if (quit_message_loop_on_destruction_) { + // The message loop may not be running if this is swapped in + // synchronously on a Navigation. + MessageLoop* loop = MessageLoopForUI::current(); + if (loop->is_running()) + loop->Quit(); + } } virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE { @@ -488,7 +493,8 @@ class PrerenderBrowserTest : public InProcessBrowserTest { const std::string& html_file, const std::deque<FinalStatus>& expected_final_status_queue, int expected_number_of_loads) { - PrerenderTestURLImpl(test_server()->GetURL(html_file), + GURL url = test_server()->GetURL(html_file); + PrerenderTestURLImpl(url, url, expected_final_status_queue, expected_number_of_loads); } @@ -499,7 +505,19 @@ class PrerenderBrowserTest : public InProcessBrowserTest { int expected_number_of_loads) { std::deque<FinalStatus> expected_final_status_queue(1, expected_final_status); - PrerenderTestURLImpl(url, + PrerenderTestURLImpl(url, url, + expected_final_status_queue, + expected_number_of_loads); + } + + void PrerenderTestURL( + const GURL& prerender_url, + const GURL& destination_url, + FinalStatus expected_final_status, + int expected_number_of_loads) { + std::deque<FinalStatus> expected_final_status_queue(1, + expected_final_status); + PrerenderTestURLImpl(prerender_url, destination_url, expected_final_status_queue, expected_number_of_loads); } @@ -691,14 +709,18 @@ class PrerenderBrowserTest : public InProcessBrowserTest { private: void PrerenderTestURLImpl( - const GURL& url, + const GURL& prerender_url, + const GURL& destination_url, const std::deque<FinalStatus>& expected_final_status_queue, int expected_number_of_loads) { - dest_url_ = url; + // TODO(cbentzel): Remove dest_url_? + dest_url_ = destination_url; std::vector<net::TestServer::StringPair> replacement_text; replacement_text.push_back( - make_pair("REPLACE_WITH_PRERENDER_URL", dest_url_.spec())); + make_pair("REPLACE_WITH_PRERENDER_URL", prerender_url.spec())); + replacement_text.push_back( + make_pair("REPLACE_WITH_DESTINATION_URL", destination_url.spec())); std::string replacement_path; ASSERT_TRUE(net::TestServer::GetFilePathWithReplacements( loader_path_, @@ -789,17 +811,11 @@ class PrerenderBrowserTest : public InProcessBrowserTest { } } - // ui_test_utils::NavigateToURL waits until DidStopLoading is called on - // the current tab. As that tab is going to end up deleted, and may never - // finish loading before that happens, exit the message loop on the deletion - // of the used prerender contents instead. - // - // As PrerenderTestURL waits until the prerendered page has completely - // loaded, there is no race between loading |dest_url| and swapping the - // prerendered TabContents into the tab. + // Navigate to the prerendered URL, but don't run the message loop. Browser + // issued navigations to prerendered pages will synchronously swap in the + // prerendered page. ui_test_utils::NavigateToURLWithDisposition( browser(), dest_url, disposition, ui_test_utils::BROWSER_TEST_NONE); - ui_test_utils::RunMessageLoop(); // Make sure the PrerenderContents found earlier was used or removed. EXPECT_TRUE(GetPrerenderContents() == NULL); @@ -965,6 +981,19 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, NavigateToURL("files/prerender/prerender_page.html"); } +// Checks that client-issued redirects work with prerendering. +// This version navigates to the final destination page, rather than the +// page which does the redirection via a mouse click. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, + PrerenderClientRedirectNavigateToSecondViaClick) { + GURL prerender_url = test_server()->GetURL( + CreateClientRedirect("files/prerender/prerender_page.html")); + GURL destination_url = test_server()->GetURL( + "files/prerender/prerender_page.html"); + PrerenderTestURL(prerender_url, destination_url, FINAL_STATUS_USED, 2); + OpenDestURLViaClick(); +} + // Checks that a prerender for an https will prevent a prerender from happening. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderHttps) { net::TestServer https_server(net::TestServer::TYPE_HTTPS, @@ -1056,6 +1085,19 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, NavigateToURL("files/prerender/prerender_page.html"); } +// Checks that server-issued redirects work with prerendering. +// This version navigates to the final destination page, rather than the +// page which does the redirection via a mouse click. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, + PrerenderServerRedirectNavigateToSecondViaClick) { + GURL prerender_url = test_server()->GetURL( + CreateServerRedirect("files/prerender/prerender_page.html")); + GURL destination_url = test_server()->GetURL( + "files/prerender/prerender_page.html"); + PrerenderTestURL(prerender_url, destination_url, FINAL_STATUS_USED, 1); + OpenDestURLViaClick(); +} + // Checks that server-issued redirects from an http to an https // location will cancel prerendering. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index 92ba801..93d1029 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -21,7 +21,7 @@ #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/icon_messages.h" -#include "chrome/common/render_messages.h" +#include "chrome/common/prerender_messages.h" #include "chrome/common/url_constants.h" #include "content/browser/renderer_host/resource_request_details.h" #include "content/public/browser/browser_child_process_host.h" @@ -59,6 +59,29 @@ struct PrerenderURLPredicate { GURL url_; }; +// Tells the render process at |child_id| whether |url| is a new prerendered +// page, or whether |url| is being removed as a prerendered page. Currently +// this will only inform the render process that created the prerendered page +// with <link rel="prerender"> tags about it. This means that if the user +// clicks on a link for a prerendered URL in a different page, the prerender +// will not be swapped in. +void InformRenderProcessAboutPrerender(const GURL& url, + bool is_add, + int child_id) { + if (child_id < 0) + return; + content::RenderProcessHost* render_process_host = + content::RenderProcessHost::FromID(child_id); + if (!render_process_host) + return; + IPC::Message* message = NULL; + if (is_add) + message = new PrerenderMsg_AddPrerenderURL(url); + else + message = new PrerenderMsg_RemovePrerenderURL(url); + render_process_host->Send(message); +} + } // end namespace class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { @@ -225,7 +248,8 @@ PrerenderContents::PrerenderContents( child_id_(-1), route_id_(-1), origin_(origin), - experiment_id_(experiment_id) { + experiment_id_(experiment_id), + creator_child_id_(-1) { DCHECK(prerender_manager != NULL); } @@ -246,6 +270,13 @@ void PrerenderContents::StartPrerendering( DCHECK(prerender_contents_.get() == NULL); prerendering_has_started_ = true; + DCHECK(creator_child_id_ == -1); + DCHECK(alias_urls_.size() == 1); + if (source_render_view_host) + creator_child_id_ = source_render_view_host->GetProcess()->GetID(); + InformRenderProcessAboutPrerender(prerender_url_, true, + creator_child_id_); + WebContents* new_contents = WebContents::Create( profile_, NULL, MSG_ROUTING_NONE, NULL, session_storage_namespace); prerender_contents_.reset(new TabContentsWrapper(new_contents)); @@ -361,8 +392,14 @@ PrerenderContents::~PrerenderContents() { match_complete_status_, final_status_); - if (child_id_ != -1 && route_id_ != -1) + if (child_id_ != -1 && route_id_ != -1) { prerender_tracker_->OnPrerenderingFinished(child_id_, route_id_); + for (std::vector<GURL>::const_iterator it = alias_urls_.begin(); + it != alias_urls_.end(); + ++it) { + InformRenderProcessAboutPrerender(*it, false, creator_child_id_); + } + } // If we still have a TabContents, clean up anything we need to and then // destroy it. @@ -417,7 +454,7 @@ void PrerenderContents::Observe(int type, // first navigation, so there's no need to send the message just after // the TabContents is created. new_render_view_host->Send( - new ChromeViewMsg_SetIsPrerendering( + new PrerenderMsg_SetIsPrerendering( new_render_view_host->GetRoutingID(), true)); @@ -474,6 +511,7 @@ bool PrerenderContents::AddAliasURL(const GURL& url) { } alias_urls_.push_back(url); + InformRenderProcessAboutPrerender(url, true, creator_child_id_); prerender_tracker_->AddPrerenderURLOnUIThread(url); return true; } diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h index 139e4ff..85aa433 100644 --- a/chrome/browser/prerender/prerender_contents.h +++ b/chrome/browser/prerender/prerender_contents.h @@ -339,6 +339,9 @@ class PrerenderContents : public content::NotificationObserver, // List of all pages the prerendered page has tried to prerender. PendingPrerenderList pending_prerender_list_; + // The process that created the child id. + int creator_child_id_; + DISALLOW_COPY_AND_ASSIGN(PrerenderContents); }; diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index 0b8589f..d10838c 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -35,7 +35,7 @@ #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/render_messages.h" +#include "chrome/common/prerender_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host_registry.h" #include "content/public/browser/navigation_controller.h" @@ -324,9 +324,9 @@ void PrerenderManager::CancelOmniboxPrerenders() { } bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, - const GURL& url, - const GURL& opener_url) { + const GURL& url) { DCHECK(CalledOnValidThread()); + DCHECK(!IsWebContentsPrerendering(web_contents)); RecordNavigation(url); scoped_ptr<PrerenderContents> prerender_contents( @@ -334,11 +334,8 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, if (prerender_contents.get() == NULL) return false; - // Do not use the prerendered version if the opener url corresponding to the - // window.opener property has the same origin as the url. - // NOTE: This is broken in the cases where the document domain is modified - // using the javascript property for "document.domain". - if (opener_url.GetOrigin() == url.GetOrigin()) { + // Do not use the prerendered version if there is an opener object. + if (web_contents->HasOpener()) { prerender_contents.release()->Destroy(FINAL_STATUS_WINDOW_OPENER); return false; } @@ -424,8 +421,8 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, prerender_contents->set_final_status(FINAL_STATUS_USED); new_render_view_host->Send( - new ChromeViewMsg_SetIsPrerendering(new_render_view_host->GetRoutingID(), - false)); + new PrerenderMsg_SetIsPrerendering(new_render_view_host->GetRoutingID(), + false)); TabContentsWrapper* new_tab_contents = prerender_contents->ReleasePrerenderContents(); diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index 2d515b5..7a39614 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h @@ -125,15 +125,11 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, // Cancels all active prerenders with the ORIGIN_OMNIBOX origin. void CancelOmniboxPrerenders(); - // For a given WebContents that wants to navigate to the URL supplied, - // determines whether a prerendered version of the URL can be used, - // and substitutes the prerendered RVH into the WebContents. |opener_url| is - // set to the window.opener url that the WebContents should have set and - // will be empty if there is no opener set. Returns whether or not a - // prerendered RVH could be used or not. + // If |url| matches a valid prerendered page, try to swap it into + // |web_contents| and merge browsing histories. Returns |true| if a + // prerendered page is swapped in, |false| otherwise. bool MaybeUsePrerenderedPage(content::WebContents* web_contents, - const GURL& url, - const GURL& opener_url); + const GURL& url); // Moves a PrerenderContents to the pending delete list from the list of // active prerenders when prerendering should be cancelled. diff --git a/chrome/browser/prerender/prerender_manager_unittest.cc b/chrome/browser/prerender/prerender_manager_unittest.cc index 8eb1aec..73c27e5 100644 --- a/chrome/browser/prerender/prerender_manager_unittest.cc +++ b/chrome/browser/prerender/prerender_manager_unittest.cc @@ -236,13 +236,7 @@ class PrerenderManagerTest : public testing::Test { TEST_F(PrerenderManagerTest, EmptyTest) { EXPECT_FALSE(prerender_manager()->MaybeUsePrerenderedPage( NULL, - GURL("http://www.google.com/"), - GURL())); - - EXPECT_FALSE(prerender_manager()->MaybeUsePrerenderedPage( - NULL, - GURL("http://www.google.com/search"), - GURL("http://www.google.com"))); + GURL("http://www.google.com/"))); } TEST_F(PrerenderManagerTest, FoundTest) { diff --git a/chrome/browser/prerender/prerender_tab_helper.cc b/chrome/browser/prerender/prerender_tab_helper.cc index badb85c..9c614ce 100644 --- a/chrome/browser/prerender/prerender_tab_helper.cc +++ b/chrome/browser/prerender/prerender_tab_helper.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -184,15 +184,12 @@ void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl( RecordPageviewEvent(PAGEVIEW_EVENT_NEW_URL); if (IsTopSite(url)) RecordPageviewEvent(PAGEVIEW_EVENT_TOP_SITE_NEW_URL); - if (!tab_->core_tab_helper()->delegate()) - return; // PrerenderManager needs a delegate to handle the swap. PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); if (!prerender_manager) return; if (prerender_manager->IsWebContentsPrerendering(web_contents())) return; prerender_manager->MarkWebContentsAsNotPrerendered(web_contents()); - MaybeUsePrerenderedPage(url, opener_url); } void PrerenderTabHelper::UpdateTargetURL(int32 page_id, const GURL& url) { @@ -247,17 +244,6 @@ PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const { Profile::FromBrowserContext(web_contents()->GetBrowserContext())); } -bool PrerenderTabHelper::MaybeUsePrerenderedPage(const GURL& url, - const GURL& opener_url) { - PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); - if (!prerender_manager) - return false; - DCHECK(!prerender_manager->IsWebContentsPrerendering(web_contents())); - return prerender_manager->MaybeUsePrerenderedPage(web_contents(), - url, - opener_url); -} - bool PrerenderTabHelper::IsPrerendering() { PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); if (!prerender_manager) diff --git a/chrome/browser/prerender/prerender_tab_helper.h b/chrome/browser/prerender/prerender_tab_helper.h index 156bcc5..1b3ebe2 100644 --- a/chrome/browser/prerender/prerender_tab_helper.h +++ b/chrome/browser/prerender/prerender_tab_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -47,12 +47,6 @@ class PrerenderTabHelper : public content::WebContentsObserver { // Retrieves the PrerenderManager, or NULL, if none was found. PrerenderManager* MaybeGetPrerenderManager() const; - // Checks with the PrerenderManager if the specified URL has been preloaded, - // and if so, swap the RenderViewHost with the preload into this TabContents - // object. |opener_url| denotes the window.opener url that is set for this - // tab and is empty if there is no opener set. - bool MaybeUsePrerenderedPage(const GURL& url, const GURL& opener_url); - // Returns whether the TabContents being observed is currently prerendering. bool IsPrerendering(); diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc index 6dd05b2..80aa319 100644 --- a/chrome/browser/ui/browser_navigator.cc +++ b/chrome/browser/ui/browser_navigator.cc @@ -16,6 +16,8 @@ #include "chrome/browser/google/google_util.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prerender/prerender_manager.h" +#include "chrome/browser/prerender/prerender_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/rlz/rlz.h" #include "chrome/browser/tab_contents/tab_util.h" @@ -361,6 +363,18 @@ void InitializeExtraHeaders(browser::NavigateParams* params, #endif } +// If a prerendered page exists for |url|, replace the page at |target_contents| +// with it. +bool SwapInPrerender(TabContentsWrapper* target_contents, const GURL& url) { + prerender::PrerenderManager* prerender_manager = + prerender::PrerenderManagerFactory::GetForProfile( + target_contents->profile()); + if (!prerender_manager) + return false; + return prerender_manager->MaybeUsePrerenderedPage( + target_contents->web_contents(), url); +} + } // namespace namespace browser { @@ -535,6 +549,9 @@ void Navigate(NavigateParams* params) { InitializeExtraHeaders(params, params->target_contents->profile(), &extra_headers); + if (SwapInPrerender(params->target_contents, url)) + return; + // Try to handle non-navigational URLs that popup dialogs and such, these // should not actually navigate. if (!HandleNonNavigationAboutURL(url)) { diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc index 79d7a88..03ebc0c 100644 --- a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc @@ -340,6 +340,8 @@ GURL NetInternalsTest::CreatePrerenderLoaderUrl( std::vector<net::TestServer::StringPair> replacement_text; replacement_text.push_back( make_pair("REPLACE_WITH_PRERENDER_URL", prerender_url.spec())); + replacement_text.push_back( + make_pair("REPLACE_WITH_DESTINATION_URL", prerender_url.spec())); std::string replacement_path; EXPECT_TRUE(net::TestServer::GetFilePathWithReplacements( "files/prerender/prerender_loader.html", diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 668d10b..15e708b3 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -181,6 +181,8 @@ 'renderer/plugins/plugin_placeholder.h', 'renderer/plugins/plugin_uma.cc', 'renderer/plugins/plugin_uma.h', + 'renderer/prerender/prerender_dispatcher.cc', + 'renderer/prerender/prerender_dispatcher.h', 'renderer/prerender/prerender_helper.cc', 'renderer/prerender/prerender_helper.h', 'renderer/prerender/prerender_webmediaplayer.cc', diff --git a/chrome/common/common_message_generator.h b/chrome/common/common_message_generator.h index 4b231f2..fc818fb 100644 --- a/chrome/common/common_message_generator.h +++ b/chrome/common/common_message_generator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/icon_messages.h" +#include "chrome/common/prerender_messages.h" #include "chrome/common/print_messages.h" #include "chrome/common/render_messages.h" #include "chrome/common/safe_browsing/safebrowsing_messages.h" diff --git a/chrome/common/prerender_messages.h b/chrome/common/prerender_messages.h new file mode 100644 index 0000000..4c965a9 --- /dev/null +++ b/chrome/common/prerender_messages.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Multiply-included message file, no traditional include guard. +#include "googleurl/src/gurl.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_param_traits.h" + +#define IPC_MESSAGE_START PrerenderMsgStart + +// Tells a renderer if it's currently being prerendered. Must only be set +// to true before any navigation occurs, and only set to false at most once +// after that. +IPC_MESSAGE_ROUTED1(PrerenderMsg_SetIsPrerendering, + bool /* whether the RenderView is prerendering */) + +// Specifies that a URL is currently being prerendered. +IPC_MESSAGE_CONTROL1(PrerenderMsg_AddPrerenderURL, + GURL /* url */) + +// Specifies that a URL is no longer being prerendered. +IPC_MESSAGE_CONTROL1(PrerenderMsg_RemovePrerenderURL, + GURL /* url */) diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 44933a6..a338c89 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -307,12 +307,6 @@ IPC_MESSAGE_ROUTED4(ChromeViewMsg_TranslatePage, IPC_MESSAGE_ROUTED1(ChromeViewMsg_RevertTranslation, int /* page id */) -// Tells a renderer if it's currently being prerendered. Must only be set -// to true before any navigation occurs, and only set to false at most once -// after that. -IPC_MESSAGE_ROUTED1(ChromeViewMsg_SetIsPrerendering, - bool /* whether the RenderView is prerendering */) - // Sent on process startup to indicate whether this process is running in // incognito mode. IPC_MESSAGE_CONTROL1(ChromeViewMsg_SetIsIncognitoProcess, diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index badc417..6da46db 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -50,6 +50,7 @@ #include "chrome/renderer/page_load_histograms.h" #include "chrome/renderer/plugins/plugin_placeholder.h" #include "chrome/renderer/plugins/plugin_uma.h" +#include "chrome/renderer/prerender/prerender_dispatcher.h" #include "chrome/renderer/prerender/prerender_helper.h" #include "chrome/renderer/prerender/prerender_webmediaplayer.h" #include "chrome/renderer/print_web_view_helper.h" @@ -157,6 +158,7 @@ void ChromeContentRendererClient::RenderThreadStarted() { #if defined(ENABLE_SAFE_BROWSING) phishing_classifier_.reset(safe_browsing::PhishingClassifierFilter::Create()); #endif + prerender_dispatcher_.reset(new prerender::PrerenderDispatcher()); RenderThread* thread = RenderThread::Get(); @@ -168,6 +170,7 @@ void ChromeContentRendererClient::RenderThreadStarted() { #endif thread->AddObserver(spellcheck_.get()); thread->AddObserver(visited_link_slave_.get()); + thread->AddObserver(prerender_dispatcher_.get()); thread->RegisterExtension(extensions_v8::ExternalExtension::Get()); thread->RegisterExtension(extensions_v8::LoadTimesExtension::Get()); @@ -642,6 +645,15 @@ bool ChromeContentRendererClient::ShouldFork(WebFrame* frame, const GURL& url, bool is_initial_navigation, bool* send_referrer) { + DCHECK(!frame->parent()); + + // If |url| matches one of the prerendered URLs, stop this navigation and try + // to swap in the prerendered page on the browser process. If the prerendered + // page no longer exists by the time the OpenURL IPC is handled, a normal + // navigation is attempted. + if (prerender_dispatcher_.get() && prerender_dispatcher_->IsPrerenderURL(url)) + return true; + const ExtensionSet* extensions = extension_dispatcher_->extensions(); // Determine if the new URL is an extension (excluding bookmark apps). diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h index 6cbf7d9..c18d866 100644 --- a/chrome/renderer/chrome_content_renderer_client.h +++ b/chrome/renderer/chrome_content_renderer_client.h @@ -26,6 +26,10 @@ class VisitedLinkSlave; struct ChromeViewHostMsg_GetPluginInfo_Status; +namespace prerender { +class PrerenderDispatcher; +} + namespace safe_browsing { class PhishingClassifierFilter; } @@ -159,6 +163,7 @@ class ChromeContentRendererClient : public content::ContentRendererClient { scoped_ptr<SpellCheck> spellcheck_; scoped_ptr<VisitedLinkSlave> visited_link_slave_; scoped_ptr<safe_browsing::PhishingClassifierFilter> phishing_classifier_; + scoped_ptr<prerender::PrerenderDispatcher> prerender_dispatcher_; }; } // namespace chrome diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc index fffdbec..11f5994 100644 --- a/chrome/renderer/chrome_render_view_observer.cc +++ b/chrome/renderer/chrome_render_view_observer.cc @@ -14,6 +14,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/icon_messages.h" +#include "chrome/common/prerender_messages.h" #include "chrome/common/render_messages.h" #include "chrome/common/thumbnail_score.h" #include "chrome/common/url_constants.h" @@ -267,7 +268,7 @@ bool ChromeRenderViewObserver::OnMessageReceived(const IPC::Message& message) { // Filter only. IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewObserver, message) - IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsPrerendering, OnSetIsPrerendering); + IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering); IPC_END_MESSAGE_MAP() return handled; diff --git a/chrome/renderer/plugins/plugin_placeholder.cc b/chrome/renderer/plugins/plugin_placeholder.cc index 5d2d269..0bd9c0c 100644 --- a/chrome/renderer/plugins/plugin_placeholder.cc +++ b/chrome/renderer/plugins/plugin_placeholder.cc @@ -13,6 +13,7 @@ #include "base/values.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/render_messages.h" +#include "chrome/common/prerender_messages.h" #include "chrome/renderer/chrome_content_renderer_client.h" #include "chrome/renderer/custom_menu_commands.h" #include "chrome/renderer/plugins/plugin_uma.h" @@ -219,7 +220,7 @@ bool PluginPlaceholder::OnMessageReceived(const IPC::Message& message) { // interest in them. IPC_BEGIN_MESSAGE_MAP(PluginPlaceholder, message) IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins) - IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsPrerendering, OnSetIsPrerendering) + IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) IPC_END_MESSAGE_MAP() return false; diff --git a/chrome/renderer/prerender/prerender_dispatcher.cc b/chrome/renderer/prerender/prerender_dispatcher.cc new file mode 100644 index 0000000..500d4be --- /dev/null +++ b/chrome/renderer/prerender/prerender_dispatcher.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/prerender/prerender_dispatcher.h" + +#include "base/logging.h" +#include "chrome/common/prerender_messages.h" +#include "googleurl/src/gurl.h" + +namespace prerender { + +PrerenderDispatcher::PrerenderDispatcher() { +} + +PrerenderDispatcher::~PrerenderDispatcher() { +} + +bool PrerenderDispatcher::IsPrerenderURL(const GURL& url) const { + return prerender_urls_.find(url) != prerender_urls_.end(); +} + +bool PrerenderDispatcher::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrerenderDispatcher, message) + IPC_MESSAGE_HANDLER(PrerenderMsg_AddPrerenderURL, OnAddPrerenderURL) + IPC_MESSAGE_HANDLER(PrerenderMsg_RemovePrerenderURL, OnRemovePrerenderURL) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void PrerenderDispatcher::OnAddPrerenderURL(const GURL& url) { + PrerenderMap::iterator it = prerender_urls_.find(url); + if (it != prerender_urls_.end()) { + DCHECK(it->second > 0); + it->second += 1; + } else { + prerender_urls_[url] = 1; + } +} + +void PrerenderDispatcher::OnRemovePrerenderURL(const GURL& url) { + PrerenderMap::iterator it = prerender_urls_.find(url); + // This is possible with a spurious remove. + // TODO(cbentzel): We'd also want to send the map of active prerenders when + // creating a new render process, so the Add/Remove go relative to that. + // This may not be that big of a deal in practice, since the newly created tab + // is unlikely to go to the prerendered page. + if (it == prerender_urls_.end()) + return; + DCHECK(it->second > 0); + it->second -= 1; + if (it->second == 0) + prerender_urls_.erase(it); +} + +} // namespace prerender diff --git a/chrome/renderer/prerender/prerender_dispatcher.h b/chrome/renderer/prerender/prerender_dispatcher.h new file mode 100644 index 0000000..5537401 --- /dev/null +++ b/chrome/renderer/prerender/prerender_dispatcher.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_PRERENDER_PRERENDER_DISPATCHER_H_ +#define CHROME_RENDERER_PRERENDER_PRERENDER_DISPATCHER_H_ +#pragma once + +#include <map> + +#include "base/compiler_specific.h" +#include "content/public/renderer/render_process_observer.h" + +class GURL; + +namespace prerender { + +// PrerenderDispatcher keeps track of which URLs are being prerendered. There +// is only one PrerenderDispatcher per render process, and it will only be +// aware of prerenders that are triggered by this render process. +class PrerenderDispatcher : public content::RenderProcessObserver { + public: + PrerenderDispatcher(); + virtual ~PrerenderDispatcher(); + + bool IsPrerenderURL(const GURL & url) const; + + private: + void OnAddPrerenderURL(const GURL& url); + void OnRemovePrerenderURL(const GURL& url); + + // RenderProcessObserver: + virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; + + typedef std::map<GURL, int> PrerenderMap; + PrerenderMap prerender_urls_; +}; + +} // namespace prerender + +#endif // CHROME_RENDERER_PRERENDER_PRERENDER_DISPATCHER_H_ diff --git a/chrome/renderer/prerender/prerender_helper.cc b/chrome/renderer/prerender/prerender_helper.cc index 84dc1d7..853a2f7 100644 --- a/chrome/renderer/prerender/prerender_helper.cc +++ b/chrome/renderer/prerender/prerender_helper.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,7 +6,7 @@ #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" -#include "chrome/common/render_messages.h" +#include "chrome/common/prerender_messages.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -131,7 +131,7 @@ void PrerenderHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) { bool PrerenderHelper::OnMessageReceived( const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(PrerenderHelper, message) - IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsPrerendering, OnSetIsPrerendering) + IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) IPC_END_MESSAGE_MAP() // Return false on ViewMsg_SetIsPrerendering so other observers can see the // message. diff --git a/chrome/renderer/prerender/prerender_webmediaplayer.cc b/chrome/renderer/prerender/prerender_webmediaplayer.cc index 8189b4f..8fcc299 100644 --- a/chrome/renderer/prerender/prerender_webmediaplayer.cc +++ b/chrome/renderer/prerender/prerender_webmediaplayer.cc @@ -4,7 +4,7 @@ #include "chrome/renderer/prerender/prerender_webmediaplayer.h" -#include "chrome/common/render_messages.h" +#include "chrome/common/prerender_messages.h" #include "content/public/renderer/render_view.h" #include "media/base/filter_collection.h" #include "media/base/media_log.h" @@ -59,7 +59,7 @@ void PrerenderWebMediaPlayer::cancelLoad() { bool PrerenderWebMediaPlayer::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(PrerenderWebMediaPlayer, message) - IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsPrerendering, OnSetIsPrerendering) + IPC_MESSAGE_HANDLER(PrerenderMsg_SetIsPrerendering, OnSetIsPrerendering) IPC_END_MESSAGE_MAP() return false; diff --git a/chrome/test/data/prerender/prerender_loader.html b/chrome/test/data/prerender/prerender_loader.html index 66d4afe..9fed87a 100644 --- a/chrome/test/data/prerender/prerender_loader.html +++ b/chrome/test/data/prerender/prerender_loader.html @@ -56,7 +56,7 @@ } function WindowOpen() { - window.open('REPLACE_WITH_PRERENDER_URL'); + window.open('REPLACE_WITH_DESTINATION_URL'); } </script> @@ -66,7 +66,7 @@ document.write('<link rel="prerender" href="REPLACE_WITH_PRERENDER_URL"/>'); </script> <a target="_blank" id="toClickTarget" - href="REPLACE_WITH_PRERENDER_URL">Link To click in new window</a> -<a id="toClick" href="REPLACE_WITH_PRERENDER_URL">Link to click</a> + href="REPLACE_WITH_DESTINATION_URL">Link To click in new window</a> +<a id="toClick" href="REPLACE_WITH_DESTINATION_URL">Link to click</a> </body> </html> diff --git a/chrome/test/data/prerender/prerender_loader_with_session_storage.html b/chrome/test/data/prerender/prerender_loader_with_session_storage.html index 9b8e282..5221086 100644 --- a/chrome/test/data/prerender/prerender_loader_with_session_storage.html +++ b/chrome/test/data/prerender/prerender_loader_with_session_storage.html @@ -32,6 +32,6 @@ window.onload = loader; </script> -<a id="toClick" href="REPLACE_WITH_PRERENDER_URL">Link to click</a> +<a id="toClick" href="REPLACE_WITH_DESTINATION_URL">Link to click</a> </body> </html> diff --git a/chrome/test/data/prerender/prerender_loader_with_unload.html b/chrome/test/data/prerender/prerender_loader_with_unload.html index 4819015..50336fa 100644 --- a/chrome/test/data/prerender/prerender_loader_with_unload.html +++ b/chrome/test/data/prerender/prerender_loader_with_unload.html @@ -15,6 +15,6 @@ <script> document.write('<link rel="prerender" href="REPLACE_WITH_PRERENDER_URL"/>'); </script> -<a href="REPLACE_WITH_PRERENDER_URL">Link To Click</a> +<a href="REPLACE_WITH_DESTINATION_URL">Link To Click</a> </body> </html> diff --git a/content/browser/tab_contents/tab_contents.cc b/content/browser/tab_contents/tab_contents.cc index 239ec8e..84d54de 100644 --- a/content/browser/tab_contents/tab_contents.cc +++ b/content/browser/tab_contents/tab_contents.cc @@ -265,7 +265,8 @@ TabContents::TabContents(content::BrowserContext* browser_context, static_cast<int>(content::kMaximumZoomFactor * 100)), temporary_zoom_settings_(false), content_restrictions_(0), - view_type_(content::VIEW_TYPE_TAB_CONTENTS) { + view_type_(content::VIEW_TYPE_TAB_CONTENTS), + has_opener_(false) { render_manager_.Init(browser_context, site_instance, routing_id); // We have the initial size of the view be based on the size of the passed in @@ -1351,6 +1352,10 @@ bool TabContents::GotResponseToLockMouseRequest(bool allowed) { GetRenderViewHostImpl()->GotResponseToLockMouseRequest(allowed) : false; } +bool TabContents::HasOpener() const { + return has_opener_; +} + bool TabContents::FocusLocationBarByDefault() { content::WebUI* web_ui = GetWebUIForCurrentState(); if (web_ui) diff --git a/content/browser/tab_contents/tab_contents.h b/content/browser/tab_contents/tab_contents.h index b2bca01..74dab24 100644 --- a/content/browser/tab_contents/tab_contents.h +++ b/content/browser/tab_contents/tab_contents.h @@ -115,6 +115,10 @@ class CONTENT_EXPORT TabContents opener_web_ui_type_ = opener_web_ui_type; } + void set_has_opener(bool has_opener) { + has_opener_ = has_opener; + } + JavaBridgeDispatcherHostManager* java_bridge_dispatcher_host_manager() const { return java_bridge_dispatcher_host_manager_.get(); } @@ -218,6 +222,7 @@ class CONTENT_EXPORT TabContents virtual content::WebUI::TypeID GetWebUITypeForCurrentState() OVERRIDE; virtual content::WebUI* GetWebUIForCurrentState() OVERRIDE; virtual bool GotResponseToLockMouseRequest(bool allowed) OVERRIDE; + virtual bool HasOpener() const OVERRIDE; // Implementation of PageNavigator. virtual content::WebContents* OpenURL( @@ -662,6 +667,9 @@ class CONTENT_EXPORT TabContents // Our view type. Default is VIEW_TYPE_TAB_CONTENTS. content::ViewType view_type_; + // Is there an opener associated with this? + bool has_opener_; + DISALLOW_COPY_AND_ASSIGN(TabContents); }; diff --git a/content/browser/tab_contents/tab_contents_view_helper.cc b/content/browser/tab_contents/tab_contents_view_helper.cc index 2edfab2..3992daa 100644 --- a/content/browser/tab_contents/tab_contents_view_helper.cc +++ b/content/browser/tab_contents/tab_contents_view_helper.cc @@ -81,6 +81,7 @@ TabContents* TabContentsViewHelper::CreateNewWindow( NULL); new_contents->set_opener_web_ui_type( web_contents->GetWebUITypeForCurrentState()); + new_contents->set_has_opener(!params.opener_url.is_empty()); if (params.opener_suppressed) { // When the opener is suppressed, the original renderer cannot access the diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index f73813b..abd656a 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h @@ -363,6 +363,9 @@ class WebContents : public PageNavigator { // Focuses the location bar. virtual void SetFocusToLocationBar(bool select_all) = 0; + + // Does this have an opener associated with it? + virtual bool HasOpener() const = 0; }; } // namespace content diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 3908b12..912bc04 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -100,6 +100,7 @@ enum IPCMessageStart { GamepadMsgStart, ShellMsgStart, AccessibilityMsgStart, + PrerenderMsgStart, LastIPCMsgStart // Must come last. }; |