Chromium Code Reviews| Index: chrome/browser/prerender/prerender_link_manager.cc |
| diff --git a/chrome/browser/prerender/prerender_link_manager.cc b/chrome/browser/prerender/prerender_link_manager.cc |
| index 7726016c203f430ee05bb727dc56b28e7e8a7a64..64e336f8ef72aebc8a57f20d9d408892f233df9c 100644 |
| --- a/chrome/browser/prerender/prerender_link_manager.cc |
| +++ b/chrome/browser/prerender/prerender_link_manager.cc |
| @@ -14,6 +14,7 @@ |
| #include "chrome/browser/prerender/prerender_manager.h" |
| #include "chrome/browser/prerender/prerender_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/tab_contents/tab_util.h" |
| #include "chrome/common/prerender_messages.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| @@ -43,9 +44,55 @@ void Send(int child_id, IPC::Message* raw_message) { |
| namespace prerender { |
| +// Helper class to implement PrerenderContents::Observer and watch prerenders |
| +// which launch other prerenders. |
| +class PrerenderLinkManager::PendingPrerenderManager |
| + : public PrerenderContents::Observer { |
| + public: |
| + explicit PendingPrerenderManager(PrerenderLinkManager* link_manager) |
| + : link_manager_(link_manager) { |
| + } |
| + |
| + ~PendingPrerenderManager() { |
| + DCHECK(observed_launchers_.empty()); |
| + for (std::set<PrerenderContents*>::iterator i = observed_launchers_.begin(); |
| + i != observed_launchers_.end(); ++i) { |
| + (*i)->RemoveObserver(this); |
| + } |
| + } |
| + |
| + void ObserveLauncher(PrerenderContents* launcher) { |
| + DCHECK_EQ(FINAL_STATUS_MAX, launcher->final_status()); |
| + if (observed_launchers_.find(launcher) != observed_launchers_.end()) |
| + return; |
| + observed_launchers_.insert(launcher); |
| + launcher->AddObserver(this); |
| + } |
| + |
| + virtual void OnPrerenderStart(PrerenderContents* launcher) OVERRIDE { |
| + } |
| + virtual void OnPrerenderStop(PrerenderContents* launcher) OVERRIDE { |
| + observed_launchers_.erase(launcher); |
| + if (launcher->final_status() == FINAL_STATUS_USED) { |
| + link_manager_->StartPendingPrerendersForLauncher(launcher); |
|
davidben
2014/01/24 17:50:43
(This does result in AddPrerender being called whi
|
| + } else { |
| + link_manager_->CancelPendingPrerendersForLauncher(launcher); |
| + } |
| + } |
| + |
| + private: |
| + // A pointer to the parent PrerenderLinkManager. |
| + PrerenderLinkManager* link_manager_; |
| + |
| + // The set of PrerenderContentses being observed. Lifetimes are managed by |
| + // OnPrerenderStop. |
| + std::set<PrerenderContents*> observed_launchers_; |
| +}; |
| + |
| PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager) |
| : has_shutdown_(false), |
| - manager_(manager) { |
| + manager_(manager), |
| + pending_prerender_manager_(new PendingPrerenderManager(this)) { |
| } |
| PrerenderLinkManager::~PrerenderLinkManager() { |
| @@ -77,11 +124,31 @@ void PrerenderLinkManager::OnAddPrerender(int launcher_child_id, |
| if (rph && rph->IsGuest()) |
| return; |
| + // Check if the launcher is itself an unswapped prerender. |
| + content::WebContents* web_contents = tab_util::GetWebContentsByID( |
| + launcher_child_id, render_view_route_id); |
| + if (web_contents == NULL) |
| + return; |
| + PrerenderContents* prerender_contents = |
| + manager_->GetPrerenderContents(web_contents); |
| + if (prerender_contents && |
| + prerender_contents->final_status() != FINAL_STATUS_MAX) { |
| + // The launcher is a prerender about to be destroyed asynchronously, but |
| + // its AddLinkRelPrerender message raced with shutdown. Ignore it. |
| + DCHECK_NE(FINAL_STATUS_USED, prerender_contents->final_status()); |
| + return; |
| + } |
| + |
| LinkPrerender |
| prerender(launcher_child_id, prerender_id, url, referrer, size, |
| - render_view_route_id, manager_->GetCurrentTimeTicks()); |
| + render_view_route_id, manager_->GetCurrentTimeTicks(), |
| + prerender_contents); |
| prerenders_.push_back(prerender); |
| - StartPrerenders(); |
| + if (prerender_contents) { |
| + pending_prerender_manager_->ObserveLauncher(prerender_contents); |
| + } else { |
| + StartPrerenders(); |
| + } |
| } |
| void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { |
| @@ -138,16 +205,19 @@ PrerenderLinkManager::LinkPrerender::LinkPrerender( |
| const content::Referrer& referrer, |
| const gfx::Size& size, |
| int render_view_route_id, |
| - TimeTicks creation_time) : launcher_child_id(launcher_child_id), |
| - prerender_id(prerender_id), |
| - url(url), |
| - referrer(referrer), |
| - size(size), |
| - render_view_route_id(render_view_route_id), |
| - creation_time(creation_time), |
| - handle(NULL), |
| - is_match_complete_replacement(false), |
| - has_been_abandoned(false) { |
| + TimeTicks creation_time, |
| + PrerenderContents* deferred_launcher) |
| + : launcher_child_id(launcher_child_id), |
| + prerender_id(prerender_id), |
| + url(url), |
| + referrer(referrer), |
| + size(size), |
| + render_view_route_id(render_view_route_id), |
| + creation_time(creation_time), |
| + deferred_launcher(deferred_launcher), |
| + handle(NULL), |
| + is_match_complete_replacement(false), |
| + has_been_abandoned(false) { |
| } |
| PrerenderLinkManager::LinkPrerender::~LinkPrerender() { |
| @@ -184,6 +254,9 @@ void PrerenderLinkManager::StartPrerenders() { |
| // also per launcher. |
| for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| i != prerenders_.end(); ++i) { |
| + // Skip prerenders launched by a prerender. |
| + if (i->deferred_launcher) |
| + continue; |
| if (!i->handle) { |
| pending_prerenders.push_back(i); |
| } else { |
| @@ -320,6 +393,30 @@ void PrerenderLinkManager::CancelPrerender(LinkPrerender* prerender) { |
| NOTREACHED(); |
| } |
| +void PrerenderLinkManager::StartPendingPrerendersForLauncher( |
| + PrerenderContents* launcher) { |
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin(); |
| + i != prerenders_.end(); ++i) { |
| + if (i->deferred_launcher == launcher) |
| + i->deferred_launcher = NULL; |
| + } |
| + StartPrerenders(); |
| +} |
| + |
| +void PrerenderLinkManager::CancelPendingPrerendersForLauncher( |
| + PrerenderContents* launcher) { |
| + // Remove all pending prerenders for this launcher. |
| + std::list<LinkPrerender>::iterator iter = prerenders_.begin(); |
| + while (iter != prerenders_.end()) { |
| + // Increment iterator first so it isn't invalidated. |
| + std::list<LinkPrerender>::iterator current = iter++; |
| + if (current->deferred_launcher == launcher) { |
| + DCHECK(!current->handle); |
| + prerenders_.erase(current); |
| + } |
| + } |
| +} |
| + |
| void PrerenderLinkManager::Shutdown() { |
| has_shutdown_ = true; |
| } |