diff options
author | gavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-15 21:45:18 +0000 |
---|---|---|
committer | gavinp@chromium.org <gavinp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-15 21:45:18 +0000 |
commit | 2610170d745f256d0316a936d0907e02b279d8bd (patch) | |
tree | ad2be6594e2ae6ad54e6cb0b451716565d9f40cd /chrome/browser | |
parent | aeb5ccec77fbc0691e766ee80b7c4f4efb79bd9d (diff) | |
download | chromium_src-2610170d745f256d0316a936d0907e02b279d8bd.zip chromium_src-2610170d745f256d0316a936d0907e02b279d8bd.tar.gz chromium_src-2610170d745f256d0316a936d0907e02b279d8bd.tar.bz2 |
Make PrerenderHandle an observer of PrerenderContents.
The big implication of this is that PrerenderLinkManager is an observer of PrerenderHandle, and so messaging to/from the browser process about prerenders ends up mostly in the same place.
Interestingly, we basically can toss out the pending_prerenders_ list in the PrerenderManager; the only thing it bought us was tracking cancelation, which is now done with a bool in PrerenderHandle, just as easily.
The earlier work on the lifetime of PrerenderContents and PrerenderHandle was building up to this. This CL depends on https://codereview.chromium.org/11348357/ , and can't land until after it.
R=mmenke@chromium.org
BUG=None
Review URL: https://chromiumcodereview.appspot.com/11316311
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173322 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/prerender/prerender_contents.cc | 86 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.h | 22 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_handle.cc | 110 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_handle.h | 58 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_link_manager.cc | 142 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_link_manager.h | 13 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 93 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.h | 22 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_tracker_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_unittest.cc | 85 |
10 files changed, 393 insertions, 239 deletions
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index 5e13374..2737f79 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -8,7 +8,6 @@ #include <functional> #include <utility> -#include "base/process_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/history/history_tab_helper.h" #include "chrome/browser/history/history_types.h" @@ -43,33 +42,6 @@ using content::WebContents; namespace prerender { -namespace { - -// 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); -} - -} // namespace - class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { public: virtual PrerenderContents* CreatePrerenderContents( @@ -167,6 +139,15 @@ class PrerenderContents::WebContentsDelegateImpl PrerenderContents* prerender_contents_; }; +void PrerenderContents::Observer::OnPrerenderAddAlias( + PrerenderContents* contents, + const GURL& alias_url) { +} + +void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* contents, PrerenderContents* replacement) { +} + PrerenderContents::Observer::Observer() { } @@ -193,7 +174,9 @@ void PrerenderContents::AddPendingPrerender( pending_prerenders_.push_back(pending_prerender_info.release()); } -void PrerenderContents::StartPendingPrerenders() { +void PrerenderContents::PrepareForUse() { + NotifyPrerenderStop(); + SessionStorageNamespace* session_storage_namespace = NULL; if (prerender_contents_) { // TODO(ajwong): This does not correctly handle storage for isolated apps. @@ -232,7 +215,7 @@ PrerenderContents::PrerenderContents( DCHECK(prerender_manager != NULL); } -PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() const { +PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() { PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents( prerender_url(), referrer(), origin(), experiment_id()); @@ -248,6 +231,7 @@ PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() const { new_contents->alias_urls_ = alias_urls_; new_contents->set_match_complete_status( PrerenderContents::MATCH_COMPLETE_REPLACEMENT); + NotifyPrerenderCreatedMatchCompleteReplacement(new_contents); return new_contents; } @@ -276,9 +260,6 @@ void PrerenderContents::StartPrerendering( session_storage_namespace_id_ = session_storage_namespace->id(); size_ = size; - InformRenderProcessAboutPrerender(prerender_url_, true, - creator_child_id_); - DCHECK(load_start_time_.is_null()); load_start_time_ = base::TimeTicks::Now(); @@ -369,11 +350,6 @@ void PrerenderContents::SetFinalStatus(FinalStatus final_status) { DCHECK(final_status_ == FINAL_STATUS_MAX); final_status_ = final_status; - - if (!prerender_manager_->IsControlGroup(experiment_id()) && - prerendering_has_started()) { - NotifyPrerenderStop(); - } } PrerenderContents::~PrerenderContents() { @@ -385,14 +361,6 @@ PrerenderContents::~PrerenderContents() { prerender_manager_->RecordFinalStatusWithMatchCompleteStatus( origin(), experiment_id(), match_complete_status(), final_status()); - if (child_id_ != -1 && route_id_ != -1) { - 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 WebContents, clean up anything we need to and then // destroy it. if (prerender_contents_.get()) @@ -404,6 +372,10 @@ void PrerenderContents::AddObserver(Observer* observer) { observer_list_.AddObserver(observer); } +void PrerenderContents::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + void PrerenderContents::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { @@ -500,6 +472,18 @@ void PrerenderContents::NotifyPrerenderStop() { observer_list_.Clear(); } +void PrerenderContents::NotifyPrerenderAddAlias(const GURL& alias_url) { + FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderAddAlias(this, + alias_url)); +} + +void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* replacement) { + FOR_EACH_OBSERVER(Observer, observer_list_, + OnPrerenderCreatedMatchCompleteReplacement(this, + replacement)); +} + void PrerenderContents::DidUpdateFaviconURL( int32 page_id, const std::vector<content::FaviconURL>& urls) { @@ -534,7 +518,7 @@ bool PrerenderContents::AddAliasURL(const GURL& url) { } alias_urls_.push_back(url); - InformRenderProcessAboutPrerender(url, true, creator_child_id_); + NotifyPrerenderAddAlias(url); return true; } @@ -588,6 +572,8 @@ void PrerenderContents::DidFinishLoad(int64 frame_id, } void PrerenderContents::Destroy(FinalStatus final_status) { + DCHECK_NE(final_status, FINAL_STATUS_USED); + if (prerendering_has_been_cancelled_) return; @@ -613,6 +599,12 @@ void PrerenderContents::Destroy(FinalStatus final_status) { prerender_manager_->AddToHistory(this); prerender_manager_->MoveEntryToPendingDelete(this, final_status); + if (!prerender_manager_->IsControlGroup(experiment_id()) && + (prerendering_has_started() || + match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { + NotifyPrerenderStop(); + } + // We may destroy the PrerenderContents before we have initialized the // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to // avoid any more messages being sent. diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h index b978c36..e264906 100644 --- a/chrome/browser/prerender/prerender_contents.h +++ b/chrome/browser/prerender/prerender_contents.h @@ -78,6 +78,15 @@ class PrerenderContents : public content::NotificationObserver, // Signals that the prerender has stopped running. virtual void OnPrerenderStop(PrerenderContents* contents) = 0; + // Signals the discovery, through redirects, of a new alias for this + // prerender. + virtual void OnPrerenderAddAlias(PrerenderContents* contents, + const GURL& alias_url); + + // Signals that this prerender has just become a MatchComplete replacement. + virtual void OnPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* contents, PrerenderContents* replacement); + protected: Observer(); virtual ~Observer() = 0; @@ -128,13 +137,15 @@ class PrerenderContents : public content::NotificationObserver, virtual ~PrerenderContents(); // All observers of a PrerenderContents are removed after the OnPrerenderStop - // event is sent, so there is no need for a RemoveObserver() method. + // event is sent, so there is no need to call RemoveObserver() in the normal + // use case. void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); // For MatchComplete correctness, create a dummy replacement prerender // contents to stand in for this prerender contents that (which we are about // to destroy). - PrerenderContents* CreateMatchCompleteReplacement() const; + PrerenderContents* CreateMatchCompleteReplacement(); bool Init(); @@ -267,8 +278,8 @@ class PrerenderContents : public content::NotificationObserver, scoped_ptr<PendingPrerenderInfo> pending_prerender_info); // Reissues any pending prerender requests from the prerendered page. Also - // clears the list of pending requests. - void StartPendingPrerenders(); + // clears the list of pending requests. Sends notifications. + void PrepareForUse(); protected: PrerenderContents(PrerenderManager* prerender_manager, @@ -282,6 +293,9 @@ class PrerenderContents : public content::NotificationObserver, // that NotifyPrerenderStop() also clears the observer list. void NotifyPrerenderStart(); void NotifyPrerenderStop(); + void NotifyPrerenderAddAlias(const GURL& alias_url); + void NotifyPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* replacement); // Called whenever a RenderViewHost is created for prerendering. Only called // once the RenderViewHost has a RenderView and RenderWidgetHostView. diff --git a/chrome/browser/prerender/prerender_handle.cc b/chrome/browser/prerender/prerender_handle.cc index 24de791..5ef9b4d 100644 --- a/chrome/browser/prerender/prerender_handle.cc +++ b/chrome/browser/prerender/prerender_handle.cc @@ -6,66 +6,118 @@ #include <algorithm> +#include "base/logging.h" #include "chrome/browser/prerender/prerender_contents.h" namespace prerender { -PrerenderHandle::~PrerenderHandle() { - DCHECK(!IsValid()); - // This shouldn't occur, but we also shouldn't leak if it does. - if (IsValid()) - OnCancel(); +PrerenderHandle::Observer::Observer() { } -void PrerenderHandle::OnNavigateAway() { - DCHECK(CalledOnValidThread()); - if (!IsValid()) - return; - prerender_data_->OnNavigateAwayByHandle(); - prerender_data_.reset(); +PrerenderHandle::Observer::~Observer() { } -void PrerenderHandle::OnCancel() { - DCHECK(CalledOnValidThread()); - if (!IsValid()) - return; - prerender_data_->OnCancelByHandle(); - prerender_data_.reset(); +PrerenderHandle::~PrerenderHandle() { + if (prerender_data_) { + prerender_data_->contents()->RemoveObserver(this); + } } -bool PrerenderHandle::IsValid() const { - return prerender_data_ != NULL; +void PrerenderHandle::SetObserver(Observer* observer) { + DCHECK_NE(static_cast<Observer*>(NULL), observer); + observer_ = observer; } -bool PrerenderHandle::IsPending() const { +void PrerenderHandle::OnNavigateAway() { DCHECK(CalledOnValidThread()); - return prerender_data_ && !prerender_data_->contents(); + if (prerender_data_) + prerender_data_->OnHandleNavigatedAway(this); +} + +void PrerenderHandle::OnCancel() { + DCHECK(CalledOnValidThread()); + if (prerender_data_) + prerender_data_->OnHandleCanceled(this); } bool PrerenderHandle::IsPrerendering() const { DCHECK(CalledOnValidThread()); - return prerender_data_ && prerender_data_->contents(); + return prerender_data_ != NULL; } bool PrerenderHandle::IsFinishedLoading() const { DCHECK(CalledOnValidThread()); - if (!prerender_data_ || IsPending()) + if (!prerender_data_) return false; return prerender_data_->contents()->has_finished_loading(); } PrerenderHandle::PrerenderHandle( PrerenderManager::PrerenderData* prerender_data) - : prerender_data_(prerender_data->AsWeakPtr()), + : observer_(NULL), weak_ptr_factory_(this) { - prerender_data->OnNewHandle(); + if (prerender_data) { + prerender_data_ = prerender_data->AsWeakPtr(); + prerender_data->OnHandleCreated(this); + } +} + +void PrerenderHandle::AdoptPrerenderDataFrom(PrerenderHandle* other_handle) { + DCHECK_EQ(static_cast<PrerenderManager::PrerenderData*>(NULL), + prerender_data_); + if (other_handle->prerender_data_ && + other_handle->prerender_data_->contents()) { + other_handle->prerender_data_->contents()->RemoveObserver(other_handle); + } + + prerender_data_ = other_handle->prerender_data_; + other_handle->prerender_data_.reset(); + + if (prerender_data_) { + DCHECK_NE(static_cast<PrerenderContents*>(NULL), + prerender_data_->contents()); + prerender_data_->contents()->AddObserver(this); + // We are joining a prerender that has already started so we fire off an + // extra start event at ourselves. + OnPrerenderStart(prerender_data_->contents()); + } +} + +void PrerenderHandle::OnPrerenderStart(PrerenderContents* prerender_contents) { + DCHECK(CalledOnValidThread()); + DCHECK(prerender_data_); + DCHECK_EQ(prerender_data_->contents(), prerender_contents); + if (observer_) + observer_->OnPrerenderStart(this); +} + +void PrerenderHandle::OnPrerenderAddAlias(PrerenderContents* prerender_contents, + const GURL& alias_url) { + DCHECK(CalledOnValidThread()); + DCHECK(prerender_data_); + DCHECK_EQ(prerender_data_->contents(), prerender_contents); + if (observer_) + observer_->OnPrerenderAddAlias(this, alias_url); } -void PrerenderHandle::SwapPrerenderDataWith( - PrerenderHandle* other_prerender_handle) { +void PrerenderHandle::OnPrerenderStop(PrerenderContents* prerender_contents) { DCHECK(CalledOnValidThread()); - DCHECK(other_prerender_handle); - std::swap(prerender_data_, other_prerender_handle->prerender_data_); + if (observer_) + observer_->OnPrerenderStop(this); +} + +void PrerenderHandle::OnPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* contents, PrerenderContents* replacement) { + DCHECK(CalledOnValidThread()); + + // This should occur in the middle of the surgery on the PrerenderData, and + // so we expect to not have our new contents in our PrerenderData yet. The + // switch occurs in + // PrerenderManager::PrerenderData::MakeIntoMatchCompleteReplacement, so + // this method only needs to switch observing. + + contents->RemoveObserver(this); + replacement->AddObserver(this); } } // namespace prerender diff --git a/chrome/browser/prerender/prerender_handle.h b/chrome/browser/prerender/prerender_handle.h index 90a9dcc..156b92d 100644 --- a/chrome/browser/prerender/prerender_handle.h +++ b/chrome/browser/prerender/prerender_handle.h @@ -23,20 +23,40 @@ class PrerenderContents; // A class representing a running prerender to a client of the PrerenderManager. // Methods on PrerenderManager which start prerenders return a caller-owned // PrerenderHandle* to the client (or NULL if they are unable to start a -// prerender). Because the PrerenderManager can stop running prerenders at any -// time, callers may wish to check PrerenderHandle::IsValid() before operating -// on their prerenders. -class PrerenderHandle : public base::NonThreadSafe { +// prerender). Calls on the handle of a prerender that is not running at no-ops. +// Destroying a handle before a prerender starts will prevent it from ever +// starting. Destroying a handle while a prerendering is running will stop the +// prerender, without making any calls to the observer. +class PrerenderHandle : public base::NonThreadSafe, + public PrerenderContents::Observer { public: + class Observer { + public: + // Signals that the prerender has started running. + virtual void OnPrerenderStart(PrerenderHandle* handle) = 0; + + // Signals that the prerender has stopped running. + virtual void OnPrerenderStop(PrerenderHandle* handle) = 0; + + // Signals the discovery, through redirects, of a new alias for this + // prerender. + virtual void OnPrerenderAddAlias(PrerenderHandle* handle, + const GURL& alias_url) = 0; + + protected: + Observer(); + virtual ~Observer(); + }; + // Before calling the destructor, the caller must invalidate the handle by // calling either OnNavigateAway or OnCancel. - ~PrerenderHandle(); + virtual ~PrerenderHandle(); + + void SetObserver(Observer* observer); // The launcher is navigating away from the context that launched this // prerender. The prerender will likely stay alive briefly though, in case we - // are going through a redirect chain that will target it. This call - // invalidates the handle. If the prerender handle is already invalid, this - // call does nothing. + // are going through a redirect chain that will target it. void OnNavigateAway(); // The launcher has taken explicit action to remove this prerender (for @@ -45,16 +65,6 @@ class PrerenderHandle : public base::NonThreadSafe { // nothing. void OnCancel(); - // True if the prerender handle is still connected to a (pending or running) - // prerender. Handles can become invalid through explicit requests by the - // client, such as calling OnCancel() or OnNavigateAway(), and handles - // also become invalid when the PrerenderManager cancels prerenders. - bool IsValid() const; - - // True if this prerender was launched by a page that was itself being - // prerendered, and so has not yet been started. - bool IsPending() const; - // True if this prerender is currently active. bool IsPrerendering() const; @@ -66,7 +76,17 @@ class PrerenderHandle : public base::NonThreadSafe { explicit PrerenderHandle(PrerenderManager::PrerenderData* prerender_data); - void SwapPrerenderDataWith(PrerenderHandle* other_prerender_handle); + void AdoptPrerenderDataFrom(PrerenderHandle* other_handle); + + // From PrerenderContents::Observer: + virtual void OnPrerenderStart(PrerenderContents* prerender_contents) OVERRIDE; + virtual void OnPrerenderStop(PrerenderContents* prerender_contents) OVERRIDE; + virtual void OnPrerenderAddAlias(PrerenderContents* prerender_contents, + const GURL& alias_url) OVERRIDE; + virtual void OnPrerenderCreatedMatchCompleteReplacement( + PrerenderContents* contents, PrerenderContents* replacement) OVERRIDE; + + Observer* observer_; base::WeakPtr<PrerenderManager::PrerenderData> prerender_data_; base::WeakPtrFactory<PrerenderHandle> weak_ptr_factory_; diff --git a/chrome/browser/prerender/prerender_link_manager.cc b/chrome/browser/prerender/prerender_link_manager.cc index ea55997..ea45012 100644 --- a/chrome/browser/prerender/prerender_link_manager.cc +++ b/chrome/browser/prerender/prerender_link_manager.cc @@ -5,14 +5,16 @@ #include "chrome/browser/prerender/prerender_link_manager.h" #include <limits> -#include <queue> #include <utility> +#include "base/memory/scoped_ptr.h" #include "chrome/browser/prerender/prerender_contents.h" #include "chrome/browser/prerender/prerender_handle.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/prerender/prerender_manager_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/prerender_messages.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/session_storage_namespace.h" #include "content/public/common/referrer.h" @@ -22,6 +24,20 @@ using content::RenderViewHost; using content::SessionStorageNamespace; +namespace { + +void Send(int child_id, IPC::Message* raw_message) { + using content::RenderProcessHost; + scoped_ptr<IPC::Message> own_message(raw_message); + + RenderProcessHost* render_process_host = RenderProcessHost::FromID(child_id); + if (!render_process_host) + return; + render_process_host->Send(own_message.release()); +} + +} // namespace + namespace prerender { PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager) @@ -33,7 +49,9 @@ PrerenderLinkManager::~PrerenderLinkManager() { it != ids_to_handle_map_.end(); ++it) { PrerenderHandle* prerender_handle = it->second; - prerender_handle->OnCancel(); + DCHECK(!prerender_handle->IsPrerendering()) + << "All running prerenders should stop at the same time as the " + << "PrerenderManager."; delete prerender_handle; } } @@ -51,29 +69,25 @@ bool PrerenderLinkManager::OnAddPrerender(int child_id, << ", size = (" << size.width() << ", " << size.height() << ")" << ", render_view_route_id = " << render_view_route_id; - const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); - DCHECK_EQ(0U, ids_to_handle_map_.count(child_and_prerender_id)); - scoped_ptr<PrerenderHandle> prerender_handle( + PrerenderHandle* prerender_handle = manager_->AddPrerenderFromLinkRelPrerender( - child_id, render_view_route_id, url, referrer, size)); - if (prerender_handle.get()) { - std::pair<IdPairToPrerenderHandleMap::iterator, bool> insert_result = - ids_to_handle_map_.insert(std::make_pair( - child_and_prerender_id, static_cast<PrerenderHandle*>(NULL))); - DCHECK(insert_result.second); - delete insert_result.first->second; - insert_result.first->second = prerender_handle.release(); - return true; - } - return false; + child_id, render_view_route_id, url, referrer, size); + if (!prerender_handle) + return false; + + const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id); + DCHECK_EQ(0u, ids_to_handle_map_.count(child_and_prerender_id)); + ids_to_handle_map_[child_and_prerender_id] = prerender_handle; + + // If we are given a prerender that is already prerendering, we have missed + // the start event. + if (prerender_handle->IsPrerendering()) + OnPrerenderStart(prerender_handle); + prerender_handle->SetObserver(this); + return true; } -// TODO(gavinp): Once an observer interface is provided down to the WebKit -// layer, we should add DCHECK_NE(0L, ids_to_url_map_.count(...)) to both -// OnCancelPrerender and OnAbandonPrerender. We can't do this now, since -// the WebKit layer isn't even aware if we didn't add the prerender to the map -// in OnAddPrerender above. void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { DVLOG(2) << "OnCancelPrerender, child_id = " << child_id << ", prerender_id = " << prerender_id; @@ -86,7 +100,12 @@ void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) { } PrerenderHandle* prerender_handle = id_to_handle_iter->second; prerender_handle->OnCancel(); - RemovePrerender(id_to_handle_iter); + + // Because OnCancel() can remove the prerender from the map, we need to + // consider our iterator invalid. + id_to_handle_iter = ids_to_handle_map_.find(child_and_prerender_id); + if (id_to_handle_iter != ids_to_handle_map_.end()) + RemovePrerender(id_to_handle_iter); } void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { @@ -99,7 +118,6 @@ void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) { return; PrerenderHandle* prerender_handle = id_to_handle_iter->second; prerender_handle->OnNavigateAway(); - RemovePrerender(id_to_handle_iter); } void PrerenderLinkManager::OnChannelClosing(int child_id) { @@ -108,18 +126,21 @@ void PrerenderLinkManager::OnChannelClosing(int child_id) { child_id, std::numeric_limits<int>::min()); const ChildAndPrerenderIdPair child_and_maximum_prerender_id( child_id, std::numeric_limits<int>::max()); - std::queue<int> prerender_ids_to_abandon; - for (IdPairToPrerenderHandleMap::iterator - i = ids_to_handle_map_.lower_bound(child_and_minimum_prerender_id), - e = ids_to_handle_map_.upper_bound(child_and_maximum_prerender_id); - i != e; ++i) { - prerender_ids_to_abandon.push(i->first.second); - } - while (!prerender_ids_to_abandon.empty()) { - DVLOG(4) << "---> abandon prerender_id = " - << prerender_ids_to_abandon.front(); - OnAbandonPrerender(child_id, prerender_ids_to_abandon.front()); - prerender_ids_to_abandon.pop(); + + IdPairToPrerenderHandleMap::iterator + it = ids_to_handle_map_.lower_bound(child_and_minimum_prerender_id); + IdPairToPrerenderHandleMap::iterator + end = ids_to_handle_map_.upper_bound(child_and_maximum_prerender_id); + while (it != end) { + IdPairToPrerenderHandleMap::iterator next = it; + ++next; + + size_t size_before_abandon = ids_to_handle_map_.size(); + OnAbandonPrerender(child_id, it->first.second); + DCHECK_EQ(size_before_abandon, ids_to_handle_map_.size()); + RemovePrerender(it); + + it = next; } } @@ -134,4 +155,55 @@ void PrerenderLinkManager::RemovePrerender( ids_to_handle_map_.erase(id_to_handle_iter); } +PrerenderLinkManager::IdPairToPrerenderHandleMap::iterator +PrerenderLinkManager::FindPrerenderHandle( + PrerenderHandle* prerender_handle) { + for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin(); + it != ids_to_handle_map_.end(); ++it) { + if (it->second == prerender_handle) + return it; + } + return ids_to_handle_map_.end(); +} + +// In practice, this is always called from either +// PrerenderLinkManager::OnAddPrerender in the regular case, or in the pending +// prerender case, from PrerenderHandle::AdoptPrerenderDataFrom. +void PrerenderLinkManager::OnPrerenderStart( + PrerenderHandle* prerender_handle) { + IdPairToPrerenderHandleMap::iterator it = + FindPrerenderHandle(prerender_handle); + DCHECK(it != ids_to_handle_map_.end()); + const int child_id = it->first.first; + const int prerender_id = it->first.second; + + Send(child_id, new PrerenderMsg_OnPrerenderStart(prerender_id)); +} + +void PrerenderLinkManager::OnPrerenderAddAlias( + PrerenderHandle* prerender_handle, + const GURL& alias_url) { + IdPairToPrerenderHandleMap::iterator it = + FindPrerenderHandle(prerender_handle); + if (it == ids_to_handle_map_.end()) + return; + const int child_id = it->first.first; + const int prerender_id = it->first.second; + + Send(child_id, new PrerenderMsg_OnPrerenderAddAlias(prerender_id, alias_url)); +} + +void PrerenderLinkManager::OnPrerenderStop( + PrerenderHandle* prerender_handle) { + IdPairToPrerenderHandleMap::iterator it = + FindPrerenderHandle(prerender_handle); + if (it == ids_to_handle_map_.end()) + return; + const int child_id = it->first.first; + const int prerender_id = it->first.second; + + Send(child_id, new PrerenderMsg_OnPrerenderStop(prerender_id)); + RemovePrerender(it); +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_link_manager.h b/chrome/browser/prerender/prerender_link_manager.h index d9e9c00..8795f0a 100644 --- a/chrome/browser/prerender/prerender_link_manager.h +++ b/chrome/browser/prerender/prerender_link_manager.h @@ -9,6 +9,7 @@ #include <utility> #include "base/basictypes.h" +#include "chrome/browser/prerender/prerender_handle.h" #include "chrome/browser/profiles/profile_keyed_service.h" #include "googleurl/src/gurl.h" @@ -31,7 +32,8 @@ class PrerenderManager; // being rendered in this chrome instance. It receives messages from the // renderer indicating addition, cancelation and abandonment of link elements, // and controls the PrerenderManager accordingly. -class PrerenderLinkManager : public ProfileKeyedService { +class PrerenderLinkManager : public ProfileKeyedService, + public PrerenderHandle::Observer { public: explicit PrerenderLinkManager(PrerenderManager* manager); virtual ~PrerenderLinkManager(); @@ -77,6 +79,15 @@ class PrerenderLinkManager : public ProfileKeyedService { bool IsEmpty() const; + IdPairToPrerenderHandleMap::iterator FindPrerenderHandle( + PrerenderHandle* prerender_handle); + + // From PrerenderHandle::Observer: + virtual void OnPrerenderStart(PrerenderHandle* prerender_handle) OVERRIDE; + virtual void OnPrerenderStop(PrerenderHandle* prerender_handle) OVERRIDE; + virtual void OnPrerenderAddAlias(PrerenderHandle* prerender_handle, + const GURL& alias_url) OVERRIDE; + PrerenderManager* manager_; // A map from child process id and prerender id to PrerenderHandles. We map diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index b2121cf..ab6c7aa 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -223,7 +223,6 @@ PrerenderManager::~PrerenderManager() { // The earlier call to ProfileKeyedService::Shutdown() should have emptied // these vectors already. DCHECK(active_prerenders_.empty()); - DCHECK(pending_prerenders_.empty()); DCHECK(to_delete_prerenders_.empty()); } @@ -238,7 +237,6 @@ void PrerenderManager::Shutdown() { profile_ = NULL; DCHECK(active_prerenders_.empty()); - pending_prerenders_.clear(); } PrerenderHandle* PrerenderManager::AddPrerenderFromLinkRelPrerender( @@ -278,11 +276,8 @@ PrerenderHandle* PrerenderManager::AddPrerenderFromLinkRelPrerender( // Instead of prerendering from inside of a running prerender, we will defer // this request until its launcher is made visible. if (PrerenderContents* contents = parent_prerender_data->contents()) { - pending_prerenders_.push_back(new PrerenderData(this)); PrerenderHandle* prerender_handle = - new PrerenderHandle(pending_prerenders_.back()); - DCHECK(prerender_handle->IsPending()); - + new PrerenderHandle(static_cast<PrerenderData*>(NULL)); scoped_ptr<PrerenderContents::PendingPrerenderInfo> pending_prerender_info(new PrerenderContents::PendingPrerenderInfo( prerender_handle->weak_ptr_factory_.GetWeakPtr(), @@ -429,7 +424,7 @@ bool PrerenderManager::MaybeUsePrerenderedPage(WebContents* web_contents, // Start pending prerender requests from the PrerenderContents, if there are // any. - prerender_contents->StartPendingPrerenders(); + prerender_contents->PrepareForUse(); WebContents* new_web_contents = prerender_contents->ReleasePrerenderContents(); @@ -501,7 +496,7 @@ void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, ActuallyPrerendering()) { // TODO(tburkard): I'd like to DCHECK that we are actually prerendering. // However, what if new conditions are added and - // NeedMatchCompleteDummyForFinalStatus, is not being updated. Not sure + // NeedMatchCompleteDummyForFinalStatus is not being updated. Not sure // what's the best thing to do here. For now, I will just check whether // we are actually prerendering. (*it)->MakeIntoMatchCompleteReplacement(); @@ -511,7 +506,7 @@ void PrerenderManager::MoveEntryToPendingDelete(PrerenderContents* entry, } // Destroy the old WebContents relatively promptly to reduce resource usage, - // and in the case of HTML5 media, reduce the change of playing any sound. + // and in the case of HTML5 media, reduce the chance of playing any sound. PostCleanupTask(); } @@ -851,10 +846,6 @@ struct PrerenderManager::PrerenderData::OrderByExpiryTime { } }; -PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager) - : manager_(manager), contents_(NULL), handle_count_(0) { -} - PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager, PrerenderContents* contents, base::TimeTicks expiry_time) @@ -862,6 +853,7 @@ PrerenderManager::PrerenderData::PrerenderData(PrerenderManager* manager, contents_(contents), handle_count_(0), expiry_time_(expiry_time) { + DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); } PrerenderManager::PrerenderData::~PrerenderData() { @@ -877,39 +869,29 @@ void PrerenderManager::PrerenderData::MakeIntoMatchCompleteReplacement() { manager_->to_delete_prerenders_.push_back(to_delete); } -void PrerenderManager::PrerenderData::OnNewHandle() { - DCHECK(contents_ || handle_count_ == 0) << - "Cannot create multiple handles to a pending prerender."; +void PrerenderManager::PrerenderData::OnHandleCreated(PrerenderHandle* handle) { + DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); ++handle_count_; + contents_->AddObserver(handle); } -void PrerenderManager::PrerenderData::OnNavigateAwayByHandle() { - if (!contents_) { - DCHECK_EQ(1, handle_count_); - // Pending prerenders are not maintained in the active_prerenders_, so they - // will not get normal expiry. Since this prerender hasn't even been - // launched yet, and it's held by a page that is being prerendered, we will - // just delete it. - manager_->DestroyPendingPrerenderData(this); - } else { - DCHECK_LE(0, handle_count_); - // We intentionally don't decrement the handle count here, so that the - // prerender won't be canceled until it times out. - manager_->SourceNavigatedAway(this); - } +void PrerenderManager::PrerenderData::OnHandleNavigatedAway( + PrerenderHandle* handle) { + DCHECK_LT(0, handle_count_); + DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); + // We intentionally don't decrement the handle count here, so that the + // prerender won't be canceled until it times out. + manager_->SourceNavigatedAway(this); } -void PrerenderManager::PrerenderData::OnCancelByHandle() { - DCHECK_LE(1, handle_count_); - DCHECK(contents_ || handle_count_ == 1); +void PrerenderManager::PrerenderData::OnHandleCanceled( + PrerenderHandle* handle) { + DCHECK_LT(0, handle_count_); + DCHECK_NE(static_cast<PrerenderContents*>(NULL), contents_); if (--handle_count_ == 0) { - if (contents_) { - // This will eventually remove this object from active_prerenders_. - contents_->Destroy(FINAL_STATUS_CANCELLED); - } else { - manager_->DestroyPendingPrerenderData(this); - } + // This will eventually remove this object from active_prerenders_. + contents_->Destroy(FINAL_STATUS_CANCELLED); } } @@ -933,31 +915,24 @@ void PrerenderManager::StartPendingPrerenders( PrerenderContents::PendingPrerenderInfo* info = *it; PrerenderHandle* existing_prerender_handle = info->weak_prerender_handle.get(); - if (!existing_prerender_handle || !existing_prerender_handle->IsValid()) + if (!existing_prerender_handle) continue; - DCHECK(existing_prerender_handle->IsPending()); + DCHECK(!existing_prerender_handle->IsPrerendering()); DCHECK(process_id == -1 || session_storage_namespace); - scoped_ptr<PrerenderHandle> swap_prerender_handle(AddPrerender( + scoped_ptr<PrerenderHandle> new_prerender_handle(AddPrerender( info->origin, process_id, info->url, info->referrer, info->size, session_storage_namespace)); - if (swap_prerender_handle.get()) { + if (new_prerender_handle) { // AddPrerender has returned a new prerender handle to us. We want to make - // |existing_prerender_handle| active, so swap the underlying - // PrerenderData between the two handles, and delete our old handle (which - // will release our entry in the pending_prerender_list_). - existing_prerender_handle->SwapPrerenderDataWith( - swap_prerender_handle.get()); - swap_prerender_handle->OnCancel(); + // |existing_prerender_handle| active, so move the underlying + // PrerenderData to our new handle. + existing_prerender_handle->AdoptPrerenderDataFrom( + new_prerender_handle.get()); continue; } - - // We could not start our Prerender. Canceling the existing handle will make - // it return false for PrerenderHandle::IsPending(), and will release the - // PrerenderData from pending_prerender_list_. - existing_prerender_handle->OnCancel(); } } @@ -976,16 +951,6 @@ void PrerenderManager::SourceNavigatedAway(PrerenderData* prerender_data) { SortActivePrerenders(); } -void PrerenderManager::DestroyPendingPrerenderData( - PrerenderData* pending_prerender_data) { - ScopedVector<PrerenderData>::iterator it = - std::find(pending_prerenders_.begin(), pending_prerenders_.end(), - pending_prerender_data); - if (it == pending_prerenders_.end()) - return; - pending_prerenders_.erase(it); -} - // private PrerenderHandle* PrerenderManager::AddPrerender( Origin origin, diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index b462b62..d241358 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h @@ -268,10 +268,6 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, public: struct OrderByExpiryTime; - // Constructor for a pending prerender, which will get its contents later. - explicit PrerenderData(PrerenderManager* manager); - - // Constructor for an active prerender. PrerenderData(PrerenderManager* manager, PrerenderContents* contents, base::TimeTicks expiry_time); @@ -283,18 +279,18 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, void MakeIntoMatchCompleteReplacement(); // A new PrerenderHandle has been created for this PrerenderData. - void OnNewHandle(); + void OnHandleCreated(PrerenderHandle* prerender_handle); // The launcher associated with a handle is navigating away from the context // that launched this prerender. If the prerender is active, it may stay // alive briefly though, in case we we going through a redirect chain that // will eventually land at it. - void OnNavigateAwayByHandle(); + void OnHandleNavigatedAway(PrerenderHandle* prerender_handle); // The launcher associated with a handle has taken explicit action to cancel // this prerender. We may well destroy the prerender in this case if no // other handles continue to track it. - void OnCancelByHandle(); + void OnHandleCanceled(PrerenderHandle* prerender_handle); PrerenderContents* contents() { return contents_.get(); } @@ -312,8 +308,8 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, scoped_ptr<PrerenderContents> contents_; // The number of distinct PrerenderHandles created for |this|, including - // ones that have called PrerenderData::OnNavigateAwayByHandle(), but not - // counting the ones that have called PrerenderData::OnCancelByHandle(). For + // ones that have called PrerenderData::OnHandleNavigatedAway(), but not + // counting the ones that have called PrerenderData::OnHandleCanceled(). For // pending prerenders, this will always be 1, since the PrerenderManager // only merges handles of running prerenders. int handle_count_; @@ -335,11 +331,6 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, ScopedVector<PrerenderContents::PendingPrerenderInfo>* pending_prerenders, content::SessionStorageNamespace* session_storage_namespace); - // Called by a PrerenderData to self-destroy, but only when the PrerenderData - // is pending (as in not yet active). Should not be called except for - // objects known to be in |pending_prerender_list_|. - void DestroyPendingPrerenderData(PrerenderData* pending_prerender_data); - // Called by a PrerenderData to signal that the launcher has navigated away // from the context that launched the prerender. A user may have clicked // a link in a page containing a <link rel=prerender> element, or the user @@ -519,9 +510,6 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>, // All running prerenders. Sorted by expiry time, in ascending order. ScopedVector<PrerenderData> active_prerenders_; - // All pending prerenders. - ScopedVector<PrerenderData> pending_prerenders_; - // Prerenders awaiting deletion. ScopedVector<PrerenderData> to_delete_prerenders_; diff --git a/chrome/browser/prerender/prerender_tracker_unittest.cc b/chrome/browser/prerender/prerender_tracker_unittest.cc index 7e9a4a6..a736b0b 100644 --- a/chrome/browser/prerender/prerender_tracker_unittest.cc +++ b/chrome/browser/prerender/prerender_tracker_unittest.cc @@ -58,6 +58,7 @@ class TestPrerenderContents : public PrerenderContents { void Use() { SetFinalStatus(FINAL_STATUS_USED); + PrepareForUse(); } private: diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc index 27fa12a..45e48f8 100644 --- a/chrome/browser/prerender/prerender_unittest.cc +++ b/chrome/browser/prerender/prerender_unittest.cc @@ -135,7 +135,7 @@ class UnitTestPrerenderManager : public PrerenderManager { active_prerenders_.erase(to_erase); prerender_contents->SetFinalStatus(FINAL_STATUS_USED); - prerender_contents->StartPendingPrerenders(); + prerender_contents->PrepareForUse(); return prerender_contents; } @@ -362,12 +362,10 @@ TEST_F(PrerenderTest, DuplicateTest) { kDefaultChildId, kDefaultRenderViewRouteId, url, Referrer(url, WebKit::WebReferrerPolicyDefault), kSize)); - EXPECT_TRUE(duplicate_prerender_handle->IsValid()); EXPECT_TRUE(duplicate_prerender_handle->IsPrerendering()); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); - EXPECT_FALSE(duplicate_prerender_handle->IsValid()); EXPECT_FALSE(duplicate_prerender_handle->IsPrerendering()); } @@ -649,13 +647,12 @@ TEST_F(PrerenderTest, PendingPrerenderTest) { child_id, route_id, pending_url, Referrer(url, WebKit::WebReferrerPolicyDefault), kSize)); CHECK(pending_prerender_handle.get()); - EXPECT_TRUE(pending_prerender_handle->IsValid()); - EXPECT_TRUE(pending_prerender_handle->IsPending()); + EXPECT_FALSE(pending_prerender_handle->IsPrerendering()); EXPECT_TRUE(prerender_contents->prerendering_has_started()); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); - EXPECT_FALSE(pending_prerender_handle->IsPending()); + EXPECT_TRUE(pending_prerender_handle->IsPrerendering()); ASSERT_EQ(pending_prerender_contents, prerender_manager()->FindAndUseEntry(pending_url)); } @@ -686,14 +683,12 @@ TEST_F(PrerenderTest, InvalidPendingPrerenderTest) { child_id, route_id, pending_url, Referrer(url, WebKit::WebReferrerPolicyDefault), kSize)); DCHECK(pending_prerender_handle.get()); - EXPECT_TRUE(pending_prerender_handle->IsValid()); - EXPECT_TRUE(pending_prerender_handle->IsPending()); + EXPECT_FALSE(pending_prerender_handle->IsPrerendering()); EXPECT_TRUE(prerender_contents->prerendering_has_started()); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); - EXPECT_FALSE(pending_prerender_handle->IsValid()); - EXPECT_FALSE(pending_prerender_handle->IsPending()); + EXPECT_FALSE(pending_prerender_handle->IsPrerendering()); } TEST_F(PrerenderTest, CancelPendingPrerenderTest) { @@ -716,13 +711,11 @@ TEST_F(PrerenderTest, CancelPendingPrerenderTest) { child_id, route_id, pending_url, Referrer(url, WebKit::WebReferrerPolicyDefault), kSize)); CHECK(pending_prerender_handle.get()); - EXPECT_TRUE(pending_prerender_handle->IsValid()); - EXPECT_TRUE(pending_prerender_handle->IsPending()); + EXPECT_FALSE(pending_prerender_handle->IsPrerendering()); EXPECT_TRUE(prerender_contents->prerendering_has_started()); - pending_prerender_handle->OnCancel(); - EXPECT_FALSE(pending_prerender_handle->IsValid()); + pending_prerender_handle.reset(); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); } @@ -793,6 +786,7 @@ TEST_F(PrerenderTest, PPLTDummy) { url, FINAL_STATUS_UNSUPPORTED_SCHEME); EXPECT_TRUE(AddSimplePrerender(url)); EXPECT_TRUE(prerender_contents->prerendering_has_started()); + EXPECT_FALSE(IsEmptyPrerenderLinkManager()); DummyPrerenderContents* pplt_dummy_contents = prerender_manager()->CreateNextPrerenderContents(url, @@ -800,8 +794,10 @@ TEST_F(PrerenderTest, PPLTDummy) { GURL ftp_url("ftp://ftp.google.com/"); // Adding this ftp URL will force the expected unsupported scheme error. prerender_contents->AddAliasURL(ftp_url); + EXPECT_FALSE(IsEmptyPrerenderLinkManager()); ASSERT_EQ(pplt_dummy_contents, prerender_manager()->FindAndUseEntry(url)); + EXPECT_TRUE(IsEmptyPrerenderLinkManager()); } // Tests that our PPLT dummy prerender gets created properly, even @@ -839,13 +835,14 @@ TEST_F(PrerenderTest, PPLTLateCancel) { prerender_contents->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT); ASSERT_EQ(duplicate_prerender_contents, prerender_manager()->FindEntry(url)); + // Make sure that events on prerender handles propogate to the match + // complete replacement. + DummyPrerenderContents* null = NULL; prerender_link_manager()->OnCancelPrerender(kDefaultChildId, last_prerender_id()); - DummyPrerenderContents* null = NULL; ASSERT_EQ(null, prerender_manager()->FindEntry(url)); } - // Tests that the prerender manager matches include the fragment. TEST_F(PrerenderTest, FragmentMatchesTest) { GURL fragment_url("http://www.google.com/#test"); @@ -966,8 +963,6 @@ TEST_F(PrerenderTest, LinkManagerCancelThenAbandon) { ASSERT_EQ(null, prerender_manager()->FindEntry(url)); } -// TODO(gavinp): Re-enabmed this test after abandon has an effect on Prerenders, -// like shortening the timeouts. TEST_F(PrerenderTest, LinkManagerAbandon) { EXPECT_TRUE(IsEmptyPrerenderLinkManager()); GURL url("http://www.myexample.com"); @@ -986,7 +981,33 @@ TEST_F(PrerenderTest, LinkManagerAbandon) { EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled()); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); +} + +TEST_F(PrerenderTest, LinkManagerAbandonThenCancel) { EXPECT_TRUE(IsEmptyPrerenderLinkManager()); + GURL url("http://www.myexample.com"); + DummyPrerenderContents* prerender_contents = + prerender_manager()->CreateNextPrerenderContents( + url, FINAL_STATUS_CANCELLED); + + EXPECT_TRUE(AddSimplePrerender(url)); + + EXPECT_TRUE(prerender_contents->prerendering_has_started()); + EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled()); + ASSERT_EQ(prerender_contents, prerender_manager()->FindEntry(url)); + EXPECT_FALSE(IsEmptyPrerenderLinkManager()); + prerender_link_manager()->OnAbandonPrerender(kDefaultChildId, + last_prerender_id()); + + EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled()); + ASSERT_EQ(prerender_contents, prerender_manager()->FindEntry(url)); + + prerender_link_manager()->OnCancelPrerender(kDefaultChildId, + last_prerender_id()); + EXPECT_TRUE(IsEmptyPrerenderLinkManager()); + EXPECT_TRUE(prerender_contents->prerendering_has_been_cancelled()); + DummyPrerenderContents* null = NULL; + ASSERT_EQ(null, prerender_manager()->FindEntry(url)); } TEST_F(PrerenderTest, LinkManagerCancelTwice) { @@ -1116,7 +1137,6 @@ TEST_F(PrerenderTest, LinkManagerAddTwiceAbandonTwice) { prerender_link_manager()->OnAbandonPrerender(kDefaultChildId, second_prerender_id); - EXPECT_TRUE(IsEmptyPrerenderLinkManager()); EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled()); ASSERT_EQ(prerender_contents, prerender_manager()->FindAndUseEntry(url)); } @@ -1172,9 +1192,6 @@ TEST_F(PrerenderTest, LinkManagerExpireThenAddAgain) { EXPECT_TRUE(second_prerender_contents->prerendering_has_started()); ASSERT_EQ(second_prerender_contents, prerender_manager()->FindAndUseEntry(url)); - // The PrerenderLinkManager is not empty since we never removed the first - // prerender. - EXPECT_FALSE(IsEmptyPrerenderLinkManager()); } TEST_F(PrerenderTest, LinkManagerCancelThenAddAgain) { @@ -1200,7 +1217,29 @@ TEST_F(PrerenderTest, LinkManagerCancelThenAddAgain) { EXPECT_TRUE(second_prerender_contents->prerendering_has_started()); ASSERT_EQ(second_prerender_contents, prerender_manager()->FindAndUseEntry(url)); - EXPECT_FALSE(IsEmptyPrerenderLinkManager()); +} + +TEST_F(PrerenderTest, LinkManagerChannelClosing) { + EXPECT_TRUE(IsEmptyPrerenderLinkManager()); + GURL url("http://www.myexample.com"); + DummyPrerenderContents* prerender_contents = + prerender_manager()->CreateNextPrerenderContents( + url, FINAL_STATUS_TIMED_OUT); + + EXPECT_TRUE(AddSimplePrerender(url)); + EXPECT_TRUE(prerender_contents->prerendering_has_started()); + EXPECT_FALSE(prerender_contents->prerendering_has_been_cancelled()); + ASSERT_EQ(prerender_contents, prerender_manager()->FindEntry(url)); + + prerender_link_manager()->OnChannelClosing(kDefaultChildId); + + prerender_manager()->AdvanceTimeTicks( + prerender_manager()->config().abandon_time_to_live + + TimeDelta::FromSeconds(1)); + + DummyPrerenderContents* null = NULL; + EXPECT_EQ(null, prerender_manager()->FindEntry(url)); + EXPECT_TRUE(IsEmptyPrerenderLinkManager()); } } // namespace prerender |