| 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 da68f892f2c793cbf602a75554e4404cdc2f715b..89b228eadf893a01d742dba1d1e559fe8af46612 100644
|
| --- a/chrome/browser/prerender/prerender_link_manager.cc
|
| +++ b/chrome/browser/prerender/prerender_link_manager.cc
|
| @@ -5,6 +5,7 @@
|
| #include "chrome/browser/prerender/prerender_link_manager.h"
|
|
|
| #include <limits>
|
| +#include <set>
|
| #include <utility>
|
|
|
| #include "base/memory/scoped_ptr.h"
|
| @@ -21,6 +22,8 @@
|
| #include "googleurl/src/gurl.h"
|
| #include "ui/gfx/size.h"
|
|
|
| +using base::TimeDelta;
|
| +using base::TimeTicks;
|
| using content::RenderViewHost;
|
| using content::SessionStorageNamespace;
|
|
|
| @@ -41,131 +44,257 @@ void Send(int child_id, IPC::Message* raw_message) {
|
| namespace prerender {
|
|
|
| PrerenderLinkManager::PrerenderLinkManager(PrerenderManager* manager)
|
| - : manager_(manager) {
|
| + : has_shutdown_(false),
|
| + manager_(manager) {
|
| }
|
|
|
| PrerenderLinkManager::~PrerenderLinkManager() {
|
| - for (IdPairToPrerenderHandleMap::iterator it = ids_to_handle_map_.begin();
|
| - it != ids_to_handle_map_.end();
|
| - ++it) {
|
| - PrerenderHandle* prerender_handle = it->second;
|
| - DCHECK(!prerender_handle->IsPrerendering())
|
| - << "All running prerenders should stop at the same time as the "
|
| - << "PrerenderManager.";
|
| - delete prerender_handle;
|
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (i->handle) {
|
| + DCHECK(!i->handle->IsPrerendering())
|
| + << "All running prerenders should stop at the same time as the "
|
| + << "PrerenderManager.";
|
| + delete i->handle;
|
| + i->handle = 0;
|
| + }
|
| }
|
| }
|
|
|
| -bool PrerenderLinkManager::OnAddPrerender(int child_id,
|
| +void PrerenderLinkManager::OnAddPrerender(int launcher_child_id,
|
| int prerender_id,
|
| const GURL& url,
|
| const content::Referrer& referrer,
|
| const gfx::Size& size,
|
| int render_view_route_id) {
|
| - DVLOG(2) << "OnAddPrerender, child_id = " << child_id
|
| - << ", prerender_id = " << prerender_id
|
| - << ", url = " << url.spec();
|
| - DVLOG(3) << "... referrer url = " << referrer.url.spec()
|
| - << ", size = (" << size.width() << ", " << size.height() << ")"
|
| - << ", render_view_route_id = " << render_view_route_id;
|
| -
|
| -
|
| - PrerenderHandle* prerender_handle =
|
| - manager_->AddPrerenderFromLinkRelPrerender(
|
| - 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;
|
| + DCHECK_EQ(static_cast<LinkPrerender*>(NULL),
|
| + FindByLauncherChildIdAndPrerenderId(launcher_child_id,
|
| + prerender_id));
|
| + LinkPrerender
|
| + prerender(launcher_child_id, prerender_id, url, referrer, size,
|
| + render_view_route_id, manager_->GetCurrentTimeTicks());
|
| + prerenders_.push_back(prerender);
|
| + StartPrerenders();
|
| }
|
|
|
| void PrerenderLinkManager::OnCancelPrerender(int child_id, int prerender_id) {
|
| - DVLOG(2) << "OnCancelPrerender, child_id = " << child_id
|
| - << ", prerender_id = " << prerender_id;
|
| - const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
|
| - IdPairToPrerenderHandleMap::iterator id_to_handle_iter =
|
| - ids_to_handle_map_.find(child_and_prerender_id);
|
| - if (id_to_handle_iter == ids_to_handle_map_.end()) {
|
| - DVLOG(5) << "... canceling a prerender that doesn't exist.";
|
| + LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
|
| + prerender_id);
|
| + if (!prerender)
|
| return;
|
| - }
|
|
|
| - scoped_ptr<PrerenderHandle> prerender_handle(id_to_handle_iter->second);
|
| - ids_to_handle_map_.erase(id_to_handle_iter);
|
| - prerender_handle->OnCancel();
|
| + // Remove the handle from the PrerenderLinkManager before we cancel this
|
| + // prerender, to avoid reentering the PrerenderLinkManager, sending events to
|
| + // the underlying prerender and making a second erase.
|
| + scoped_ptr<PrerenderHandle> own_prerender_handle(prerender->handle);
|
| + prerender->handle = NULL;
|
| + RemovePrerender(prerender);
|
|
|
| - // 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);
|
| + if (own_prerender_handle)
|
| + own_prerender_handle->OnCancel();
|
| +
|
| + StartPrerenders();
|
| }
|
|
|
| void PrerenderLinkManager::OnAbandonPrerender(int child_id, int prerender_id) {
|
| - DVLOG(2) << "OnAbandonPrerender, child_id = " << child_id
|
| - << ", prerender_id = " << prerender_id;
|
| - const ChildAndPrerenderIdPair child_and_prerender_id(child_id, prerender_id);
|
| - IdPairToPrerenderHandleMap::iterator id_to_handle_iter =
|
| - ids_to_handle_map_.find(child_and_prerender_id);
|
| - if (id_to_handle_iter == ids_to_handle_map_.end())
|
| + LinkPrerender* prerender = FindByLauncherChildIdAndPrerenderId(child_id,
|
| + prerender_id);
|
| + if (!prerender)
|
| + return;
|
| +
|
| + if (!prerender->handle) {
|
| + RemovePrerender(prerender);
|
| return;
|
| - PrerenderHandle* prerender_handle = id_to_handle_iter->second;
|
| - prerender_handle->OnNavigateAway();
|
| + }
|
| +
|
| + prerender->handle->OnNavigateAway();
|
| + DCHECK(prerender->handle);
|
| +
|
| + // If the prerender is not running, remove it from the list so it does not
|
| + // leak. If it is running, it will send a cancel event when it stops which
|
| + // will remove it.
|
| + if (!prerender->handle->IsPrerendering())
|
| + RemovePrerender(prerender);
|
| }
|
|
|
| void PrerenderLinkManager::OnChannelClosing(int child_id) {
|
| - DVLOG(2) << "OnChannelClosing, child id = " << child_id;
|
| - const ChildAndPrerenderIdPair child_and_minimum_prerender_id(
|
| - child_id, std::numeric_limits<int>::min());
|
| - const ChildAndPrerenderIdPair child_and_maximum_prerender_id(
|
| - child_id, std::numeric_limits<int>::max());
|
| -
|
| - 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;
|
| + std::list<LinkPrerender>::iterator next = prerenders_.begin();
|
| + while (next != prerenders_.end()) {
|
| + std::list<LinkPrerender>::iterator it = next;
|
| ++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);
|
| + if (child_id != it->launcher_child_id)
|
| + continue;
|
|
|
| - it = next;
|
| + const size_t running_prerender_count = CountRunningPrerenders();
|
| + OnAbandonPrerender(child_id, it->prerender_id);
|
| + DCHECK_EQ(running_prerender_count, CountRunningPrerenders());
|
| }
|
| }
|
|
|
| +PrerenderLinkManager::LinkPrerender::LinkPrerender(
|
| + int launcher_child_id,
|
| + int prerender_id,
|
| + const GURL& url,
|
| + 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) {
|
| +}
|
| +
|
| +PrerenderLinkManager::LinkPrerender::~LinkPrerender() {
|
| + DCHECK_EQ(static_cast<PrerenderHandle*>(NULL), handle)
|
| + << "The PrerenderHandle should be destroyed before its Prerender.";
|
| +}
|
| +
|
| bool PrerenderLinkManager::IsEmpty() const {
|
| - return ids_to_handle_map_.empty();
|
| + return prerenders_.empty();
|
| }
|
|
|
| -void PrerenderLinkManager::RemovePrerender(
|
| - const IdPairToPrerenderHandleMap::iterator& id_to_handle_iter) {
|
| - PrerenderHandle* prerender_handle = id_to_handle_iter->second;
|
| - delete prerender_handle;
|
| - ids_to_handle_map_.erase(id_to_handle_iter);
|
| +size_t PrerenderLinkManager::CountRunningPrerenders() const {
|
| + size_t retval = 0;
|
| + for (std::list<LinkPrerender>::const_iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (i->handle && i->handle->IsPrerendering())
|
| + ++retval;
|
| + }
|
| + return retval;
|
| }
|
|
|
| -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;
|
| +void PrerenderLinkManager::StartPrerenders() {
|
| + if (has_shutdown_)
|
| + return;
|
| +
|
| + size_t total_started_prerender_count = 0;
|
| + std::multiset<std::pair<int, int> >
|
| + running_launcher_and_render_view_routes;
|
| +
|
| + // Scan the list, counting how many prerenders have handles (and so were added
|
| + // to the PrerenderManager). The count is done for the system as a whole, and
|
| + // also per launcher.
|
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (i->handle) {
|
| + ++total_started_prerender_count;
|
| + std::pair<int, int> launcher_and_render_view_route(
|
| + i->launcher_child_id, i->render_view_route_id);
|
| + running_launcher_and_render_view_routes.insert(
|
| + launcher_and_render_view_route);
|
| + DCHECK_GE(manager_->config().max_link_concurrency_per_launcher,
|
| + running_launcher_and_render_view_routes.count(
|
| + launcher_and_render_view_route));
|
| + }
|
| +
|
| + DCHECK_EQ(&(*i), FindByLauncherChildIdAndPrerenderId(i->launcher_child_id,
|
| + i->prerender_id));
|
| + }
|
| + DCHECK_GE(manager_->config().max_link_concurrency,
|
| + total_started_prerender_count);
|
| + DCHECK_LE(CountRunningPrerenders(), total_started_prerender_count);
|
| +
|
| + TimeTicks now = manager_->GetCurrentTimeTicks();
|
| +
|
| + // Scan the list again, starting prerenders as our counts allow.
|
| + std::list<LinkPrerender>::iterator next = prerenders_.begin();
|
| + while (next != prerenders_.end()) {
|
| + std::list<LinkPrerender>::iterator i = next;
|
| + ++next;
|
| +
|
| + if (total_started_prerender_count >=
|
| + manager_->config().max_link_concurrency ||
|
| + total_started_prerender_count >= prerenders_.size()) {
|
| + // The system is already at its prerender concurrency limit.
|
| + return;
|
| + }
|
| +
|
| + if (i->handle) {
|
| + // This prerender has already been added to the prerender manager.
|
| + continue;
|
| + }
|
| +
|
| + TimeDelta prerender_age = now - i->creation_time;
|
| + if (prerender_age >= manager_->config().max_wait_to_launch) {
|
| + // This prerender waited too long in the queue before launching.
|
| + prerenders_.erase(i);
|
| + continue;
|
| + }
|
| +
|
| + std::pair<int, int> launcher_and_render_view_route(
|
| + i->launcher_child_id, i->render_view_route_id);
|
| + if (manager_->config().max_link_concurrency_per_launcher <=
|
| + running_launcher_and_render_view_routes.count(
|
| + launcher_and_render_view_route)) {
|
| + // This prerender's launcher is already at its limit.
|
| + continue;
|
| + }
|
| +
|
| + PrerenderHandle* handle = manager_->AddPrerenderFromLinkRelPrerender(
|
| + i->launcher_child_id, i->render_view_route_id,
|
| + i->url, i->referrer, i->size);
|
| + if (!handle) {
|
| + // This prerender couldn't be launched, it's gone.
|
| + prerenders_.erase(i);
|
| + continue;
|
| + }
|
| +
|
| + // We have successfully started a new prerender.
|
| + i->handle = handle;
|
| + ++total_started_prerender_count;
|
| + handle->SetObserver(this);
|
| + if (handle->IsPrerendering())
|
| + OnPrerenderStart(handle);
|
| +
|
| + running_launcher_and_render_view_routes.insert(
|
| + launcher_and_render_view_route);
|
| }
|
| - return ids_to_handle_map_.end();
|
| +}
|
| +
|
| +PrerenderLinkManager::LinkPrerender*
|
| +PrerenderLinkManager::FindByLauncherChildIdAndPrerenderId(int launcher_child_id,
|
| + int prerender_id) {
|
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (launcher_child_id == i->launcher_child_id &&
|
| + prerender_id == i->prerender_id) {
|
| + return &(*i);
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +PrerenderLinkManager::LinkPrerender*
|
| +PrerenderLinkManager::FindByPrerenderHandle(PrerenderHandle* prerender_handle) {
|
| + DCHECK(prerender_handle);
|
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (prerender_handle == i->handle)
|
| + return &(*i);
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +void PrerenderLinkManager::RemovePrerender(LinkPrerender* prerender) {
|
| + for (std::list<LinkPrerender>::iterator i = prerenders_.begin();
|
| + i != prerenders_.end(); ++i) {
|
| + if (&(*i) == prerender) {
|
| + scoped_ptr<PrerenderHandle> own_handle(i->handle);
|
| + i->handle = NULL;
|
| + prerenders_.erase(i);
|
| + return;
|
| + }
|
| + }
|
| + NOTREACHED();
|
| +}
|
| +
|
| +void PrerenderLinkManager::Shutdown() {
|
| + has_shutdown_ = true;
|
| }
|
|
|
| // In practice, this is always called from either
|
| @@ -173,39 +302,34 @@ PrerenderLinkManager::FindPrerenderHandle(
|
| // 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));
|
| + LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
|
| + if (!prerender)
|
| + return;
|
| + Send(prerender->launcher_child_id,
|
| + new PrerenderMsg_OnPrerenderStart(prerender->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())
|
| + LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
|
| + if (!prerender)
|
| 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));
|
| + Send(prerender->launcher_child_id,
|
| + new PrerenderMsg_OnPrerenderAddAlias(prerender->prerender_id,
|
| + alias_url));
|
| }
|
|
|
| void PrerenderLinkManager::OnPrerenderStop(
|
| PrerenderHandle* prerender_handle) {
|
| - IdPairToPrerenderHandleMap::iterator it =
|
| - FindPrerenderHandle(prerender_handle);
|
| - if (it == ids_to_handle_map_.end())
|
| + LinkPrerender* prerender = FindByPrerenderHandle(prerender_handle);
|
| + if (!prerender)
|
| 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);
|
| + Send(prerender->launcher_child_id,
|
| + new PrerenderMsg_OnPrerenderStop(prerender->prerender_id));
|
| + RemovePrerender(prerender);
|
| + StartPrerenders();
|
| }
|
|
|
| } // namespace prerender
|
|
|