Safely cancel prerenders on threads other than the UI thread.

Previously, prerendering was cancelled on the IO thread by not
doing something, and then passing a task to the UI thread
to abort the prerender.  This resulted in a race which could
result in swapping in the prerender before the task was
executed.  This fixes that.

BUG=83062
TEST=PrerenderStatusManagerTests, PrerenderBrowserTests
Review URL: http://codereview.chromium.org/7038012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86082 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 0da2a6f..343c61a1 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/prerender/prerender_final_status.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_render_widget_host_view.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_preferences_util.h"
 #include "chrome/browser/ui/login/login_prompt.h"
@@ -61,18 +62,6 @@
   GURL url_;
 };
 
-void AddChildRoutePair(ResourceDispatcherHost* resource_dispatcher_host,
-                       int child_id, int route_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  resource_dispatcher_host->AddPrerenderChildRoutePair(child_id, route_id);
-}
-
-void RemoveChildRoutePair(ResourceDispatcherHost* resource_dispatcher_host,
-                          int child_id, int route_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  resource_dispatcher_host->RemovePrerenderChildRoutePair(child_id, route_id);
-}
-
 }  // end namespace
 
 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
@@ -146,7 +135,9 @@
       ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_observer_registrar_(this)),
       has_stopped_loading_(false),
       final_status_(FINAL_STATUS_MAX),
-      prerendering_has_started_(false) {
+      prerendering_has_started_(false),
+      child_id_(-1),
+      route_id_(-1) {
   DCHECK(prerender_manager != NULL);
 }
 
@@ -187,18 +178,14 @@
       new PrerenderRenderWidgetHostView(render_view_host_, this);
   view->Init(source_render_view_host->view());
 
-  // Register this with the ResourceDispatcherHost as a prerender
-  // RenderViewHost. This must be done before the Navigate message to catch all
-  // resource requests, but as it is on the same thread as the Navigate message
-  // (IO) there is no race condition.
-  int process_id = render_view_host_->process()->id();
-  int view_id = render_view_host_->routing_id();
+  child_id_ = render_view_host_->process()->id();
+  route_id_ = render_view_host_->routing_id();
 
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      NewRunnableFunction(&AddChildRoutePair,
-                          g_browser_process->resource_dispatcher_host(),
-                          process_id, view_id));
+  // Register this with the PrerenderTracker as a prerendering RenderViewHost.
+  // This must be done before the Navigate message to catch all resource
+  // requests.
+  PrerenderTracker::GetInstance()->OnPrerenderingStarted(child_id_, route_id_,
+                                                         prerender_manager_);
 
   // Close ourselves when the application is shutting down.
   notification_registrar_.Add(this, NotificationType::APP_TERMINATING,
@@ -292,20 +279,15 @@
   render_view_host_observer_.reset(
       new PrerenderRenderViewHostObserver(this, render_view_host_mutable()));
 
-  int process_id;
-  int view_id;
-  CHECK(GetChildId(&process_id));
-  CHECK(GetRouteId(&view_id));
+  child_id_ = render_view_host()->process()->id();
+  route_id_ = render_view_host()->routing_id();
 
   // Register this with the ResourceDispatcherHost as a prerender
   // RenderViewHost. This must be done before the Navigate message to catch all
   // resource requests, but as it is on the same thread as the Navigate message
   // (IO) there is no race condition.
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      NewRunnableFunction(&AddChildRoutePair,
-                          g_browser_process->resource_dispatcher_host(),
-                          process_id, view_id));
+  PrerenderTracker::GetInstance()->OnPrerenderingStarted(child_id_, route_id_,
+                                                         prerender_manager_);
 
   // Close ourselves when the application is shutting down.
   notification_registrar_.Add(this, NotificationType::APP_TERMINATING,
@@ -353,22 +335,16 @@
 
 bool PrerenderContents::GetChildId(int* child_id) const {
   CHECK(child_id);
-  const RenderViewHost* prerender_render_view_host = render_view_host();
-  if (prerender_render_view_host) {
-    *child_id = prerender_render_view_host->process()->id();
-    return true;
-  }
-  return false;
+  DCHECK_GE(child_id_, -1);
+  *child_id = child_id_;
+  return child_id_ != -1;
 }
 
 bool PrerenderContents::GetRouteId(int* route_id) const {
   CHECK(route_id);
-  const RenderViewHost* prerender_render_view_host = render_view_host();
-  if (prerender_render_view_host) {
-    *route_id = prerender_render_view_host->routing_id();
-    return true;
-  }
-  return false;
+  DCHECK_GE(route_id_, -1);
+  *route_id = route_id_;
+  return route_id_ != -1;
 }
 
 void PrerenderContents::set_final_status(FinalStatus final_status) {
@@ -396,21 +372,13 @@
   if (prerendering_has_started())
     RecordFinalStatus(final_status_);
 
-  if (render_view_host_ || prerender_contents_.get()) {
-    RenderViewHost* prerender_render_view_host = render_view_host_mutable();
+  // Only delete the RenderViewHost if we own it.
+  if (render_view_host_)
+    render_view_host_->Shutdown();
 
-    int process_id = prerender_render_view_host->process()->id();
-    int view_id = prerender_render_view_host->routing_id();
-
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        NewRunnableFunction(&RemoveChildRoutePair,
-                            g_browser_process->resource_dispatcher_host(),
-                            process_id, view_id));
-
-    // Only delete the RenderViewHost if we own it.
-    if (render_view_host_)
-      render_view_host_->Shutdown();
+  if (child_id_ != -1 && route_id_ != -1) {
+    PrerenderTracker::GetInstance()->OnPrerenderingFinished(
+        child_id_, route_id_);
   }
 }
 
@@ -739,6 +707,24 @@
   if (prerender_manager_->IsPendingDelete(this))
     return;
 
+  if (child_id_ != -1 && route_id_ != -1) {
+    // Cancel the prerender in the PrerenderTracker.  This is needed
+    // because destroy may be called directly from the UI thread without calling
+    // TryCancel().  This is difficult to completely avoid, since prerendering
+    // can be cancelled before a RenderView is created.
+    bool is_cancelled =
+        PrerenderTracker::GetInstance()->TryCancel(child_id_, route_id_,
+                                                   final_status);
+    CHECK(is_cancelled);
+
+    // A different final status may have been set already from another thread.
+    // If so, use it instead.
+    if (!PrerenderTracker::GetInstance()->GetFinalStatus(child_id_, route_id_,
+                                                         &final_status)) {
+      NOTREACHED();
+    }
+  }
+
   prerender_manager_->MoveEntryToPendingDelete(this);
   set_final_status(final_status);
   // We may destroy the PrerenderContents before we have initialized the
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index feaaab2..31c115e 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -323,6 +323,8 @@
 
   bool has_stopped_loading_;
 
+  // This must be the same value as the PrerenderTracker has recorded for
+  // |this|, when |this| has a RenderView.
   FinalStatus final_status_;
 
   bool prerendering_has_started_;
@@ -343,6 +345,10 @@
 
   scoped_ptr<TabContentsDelegateImpl> tab_contents_delegate_;
 
+  // These are -1 before a RenderView is created.
+  int child_id_;
+  int route_id_;
+
   DISALLOW_COPY_AND_ASSIGN(PrerenderContents);
 };
 
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 053de20b..f5ccebec 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/favicon/favicon_tab_helper.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_final_status.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper_delegate.h"
@@ -390,9 +391,7 @@
     PrerenderContentsData data = prerender_list_.front();
     if (IsPrerenderElementFresh(data.start_time_))
       return;
-    prerender_list_.pop_front();
-    data.contents_->set_final_status(FINAL_STATUS_TIMED_OUT);
-    delete data.contents_;
+    data.contents_->Destroy(FINAL_STATUS_TIMED_OUT);
   }
   MaybeStopSchedulingPeriodicCleanups();
 }
@@ -439,6 +438,16 @@
     return false;
   }
 
+  int child_id, route_id;
+  CHECK(prerender_contents->GetChildId(&child_id));
+  CHECK(prerender_contents->GetRouteId(&route_id));
+
+  // Try to set the prerendered page as used, so any subsequent attempts to
+  // cancel on other threads will fail.  If this fails because the prerender
+  // was already cancelled, possibly on another thread, fail.
+  if (!PrerenderTracker::GetInstance()->TryUse(child_id, route_id))
+    return false;
+
   if (!prerender_contents->load_start_time().is_null())
     RecordTimeUntilUsed(GetCurrentTimeTicks() -
                         prerender_contents->load_start_time());
@@ -447,11 +456,6 @@
                        ++prerenders_per_session_count_);
   prerender_contents->set_final_status(FINAL_STATUS_USED);
 
-  int child_id;
-  int route_id;
-  CHECK(prerender_contents->GetChildId(&child_id));
-  CHECK(prerender_contents->GetRouteId(&route_id));
-
   RenderViewHost* render_view_host =
       prerender_contents->render_view_host_mutable();
   prerender_contents->set_render_view_host(NULL);
@@ -531,6 +535,16 @@
     return false;
   }
 
+  int child_id, route_id;
+  CHECK(prerender_contents->GetChildId(&child_id));
+  CHECK(prerender_contents->GetRouteId(&route_id));
+
+  // Try to set the prerendered page as used, so any subsequent attempts to
+  // cancel on other threads will fail.  If this fails because the prerender
+  // was already cancelled, possibly on another thread, fail.
+  if (!PrerenderTracker::GetInstance()->TryUse(child_id, route_id))
+    return false;
+
   if (!prerender_contents->load_start_time().is_null())
     RecordTimeUntilUsed(GetCurrentTimeTicks() -
                         prerender_contents->load_start_time());
@@ -539,11 +553,6 @@
                        ++prerenders_per_session_count_);
   prerender_contents->set_final_status(FINAL_STATUS_USED);
 
-  int child_id;
-  int route_id;
-  CHECK(prerender_contents->GetChildId(&child_id));
-  CHECK(prerender_contents->GetRouteId(&route_id));
-
   RenderViewHost* render_view_host =
       prerender_contents->prerender_contents()->render_view_host();
   DCHECK(render_view_host);
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index a5e60d7..8917abc 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -48,15 +48,6 @@
     const GURL& referrer,
     bool make_pending);
 
-// Given a renderer process id and view id, this will destroy any preloads and
-// pending preloads than are using or originated in the given render view.
-// Must be called on the UI thread.
-void DestroyPreloadForRenderView(
-    const base::WeakPtr<PrerenderManager>& prerender_manager_weak_ptr,
-    int render_process_id,
-    int render_view_id,
-    FinalStatus final_status);
-
 // PrerenderManager is responsible for initiating and keeping prerendered
 // views of webpages. All methods must be called on the UI thread unless
 // indicated otherwise.
@@ -93,7 +84,7 @@
 
   // Destroy all preloads for the given child route id pair and assign a final
   // status to them.
-  void DestroyPreloadForChildRouteIdPair(
+  virtual void DestroyPreloadForChildRouteIdPair(
       const std::pair<int, int>& child_route_id_pair,
       FinalStatus final_status);
 
diff --git a/chrome/browser/prerender/prerender_tracker.cc b/chrome/browser/prerender/prerender_tracker.cc
new file mode 100644
index 0000000..c82acd3
--- /dev/null
+++ b/chrome/browser/prerender/prerender_tracker.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
+#include "content/browser/browser_thread.h"
+
+namespace prerender {
+
+namespace {
+
+void DestroyPreloadForRenderView(
+    const base::WeakPtr<PrerenderManager>& prerender_manager_weak_ptr,
+    int child_id,
+    int route_id,
+    FinalStatus final_status) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  PrerenderManager* prerender_manager = prerender_manager_weak_ptr.get();
+  if (!prerender_manager)
+    return;
+
+  prerender_manager->DestroyPreloadForChildRouteIdPair(
+      std::make_pair(child_id, route_id),
+      final_status);
+}
+
+}  // namespace
+
+struct RenderViewInfo {
+  explicit RenderViewInfo(PrerenderManager* prerender_manager)
+      : final_status(FINAL_STATUS_MAX),
+        prerender_manager(prerender_manager->AsWeakPtr()) {
+  }
+
+  FinalStatus final_status;
+  base::WeakPtr<PrerenderManager> prerender_manager;
+};
+
+// static
+PrerenderTracker* PrerenderTracker::GetInstance() {
+  return Singleton<PrerenderTracker>::get();
+}
+
+bool PrerenderTracker::TryUse(int child_id, int route_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  FinalStatus final_status = SetFinalStatus(child_id, route_id,
+                                            FINAL_STATUS_USED);
+  return final_status == FINAL_STATUS_USED;
+}
+
+bool PrerenderTracker::TryCancel(
+    int child_id,
+    int route_id,
+    FinalStatus final_status) {
+  DCHECK_NE(FINAL_STATUS_USED, final_status);
+  DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX);
+
+  final_status = SetFinalStatus(child_id, route_id, final_status);
+  return final_status != FINAL_STATUS_USED && final_status != FINAL_STATUS_MAX;
+}
+
+bool PrerenderTracker::TryCancelOnIOThread(
+    int child_id,
+    int route_id,
+    FinalStatus final_status) {
+  DCHECK_NE(FINAL_STATUS_USED, final_status);
+  DCHECK(final_status >= 0 && final_status < FINAL_STATUS_MAX);
+
+  if (!IsPrerenderingOnIOThread(child_id, route_id))
+    return false;
+  return TryCancel(child_id, route_id, final_status);
+}
+
+bool PrerenderTracker::IsPrerenderingOnIOThread(int child_id,
+                                                int route_id) const {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  ChildRouteIdPair child_route_id_pair(child_id, route_id);
+  return possibly_prerendering_io_thread_set_.end() !=
+         possibly_prerendering_io_thread_set_.find(child_route_id_pair);
+}
+
+bool PrerenderTracker::GetFinalStatus(int child_id, int route_id,
+                                      FinalStatus* final_status) const {
+  ChildRouteIdPair child_route_id_pair(child_id, route_id);
+
+  base::AutoLock lock(final_status_map_lock_);
+  FinalStatusMap::const_iterator final_status_it =
+      final_status_map_.find(child_route_id_pair);
+  if (final_status_map_.end() == final_status_map_.find(child_route_id_pair))
+    return false;
+  *final_status = final_status_it->second.final_status;
+  return true;
+}
+
+void PrerenderTracker::OnPrerenderingStarted(
+    int child_id, int route_id, PrerenderManager* prerender_manager) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_GE(child_id, 0);
+  DCHECK_GE(route_id, 0);
+
+  ChildRouteIdPair child_route_id_pair(child_id, route_id);
+
+  // The RenderView should not already be prerendering.
+  DCHECK(final_status_map_.end() ==
+         final_status_map_.find(child_route_id_pair));
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      NewRunnableFunction(&AddPrerenderOnIOThreadTask, child_route_id_pair));
+
+  base::AutoLock lock(final_status_map_lock_);
+
+  final_status_map_.insert(
+      std::make_pair(child_route_id_pair, RenderViewInfo(prerender_manager)));
+}
+
+void PrerenderTracker::OnPrerenderingFinished(int child_id, int route_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_GE(child_id, 0);
+  DCHECK_GE(route_id, 0);
+
+  ChildRouteIdPair child_route_id_pair(child_id, route_id);
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      NewRunnableFunction(&RemovePrerenderOnIOThreadTask, child_route_id_pair));
+
+  base::AutoLock lock(final_status_map_lock_);
+  size_t num_erased = final_status_map_.erase(child_route_id_pair);
+  DCHECK_EQ(1u, num_erased);
+}
+
+PrerenderTracker::PrerenderTracker() {
+}
+
+PrerenderTracker::~PrerenderTracker() {
+}
+
+FinalStatus PrerenderTracker::SetFinalStatus(int child_id, int route_id,
+                                             FinalStatus final_status) {
+  DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
+
+  ChildRouteIdPair child_route_id_pair(child_id, route_id);
+
+  base::AutoLock lock(final_status_map_lock_);
+  FinalStatusMap::iterator final_status_it =
+      final_status_map_.find(child_route_id_pair);
+  if (final_status_it == final_status_map_.end()) {
+    // The RenderView has already been either used or destroyed.
+    return FINAL_STATUS_MAX;
+  }
+
+  if (final_status_it->second.final_status == FINAL_STATUS_MAX) {
+    final_status_it->second.final_status = final_status;
+    if (final_status != FINAL_STATUS_USED) {
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          NewRunnableFunction(&DestroyPreloadForRenderView,
+                              final_status_it->second.prerender_manager,
+                              child_id,
+                              route_id,
+                              final_status));
+    }
+  }
+  return final_status_it->second.final_status;
+}
+
+void PrerenderTracker::AddPrerenderOnIOThread(
+    const ChildRouteIdPair& child_route_id_pair) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!IsPrerenderingOnIOThread(child_route_id_pair.first,
+                                   child_route_id_pair.second));
+
+  possibly_prerendering_io_thread_set_.insert(child_route_id_pair);
+}
+
+void PrerenderTracker::RemovePrerenderOnIOThread(
+    const ChildRouteIdPair& child_route_id_pair) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(IsPrerenderingOnIOThread(child_route_id_pair.first,
+                                  child_route_id_pair.second));
+
+  possibly_prerendering_io_thread_set_.erase(child_route_id_pair);
+}
+
+// static
+void PrerenderTracker::AddPrerenderOnIOThreadTask(
+    const ChildRouteIdPair& child_route_id_pair) {
+  GetInstance()->AddPrerenderOnIOThread(child_route_id_pair);
+}
+
+// static
+void PrerenderTracker::RemovePrerenderOnIOThreadTask(
+    const ChildRouteIdPair& child_route_id_pair) {
+  GetInstance()->RemovePrerenderOnIOThread(child_route_id_pair);
+}
+
+}  // namespace prerender
diff --git a/chrome/browser/prerender/prerender_tracker.h b/chrome/browser/prerender/prerender_tracker.h
new file mode 100644
index 0000000..f263052
--- /dev/null
+++ b/chrome/browser/prerender/prerender_tracker.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
+#define CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
+#pragma once
+
+#include <map>
+#include <set>
+
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/prerender/prerender_final_status.h"
+
+namespace prerender {
+
+class PrerenderManager;
+struct RenderViewInfo;
+
+// PrerenderTracker is responsible for keeping track of all prerendering
+// RenderViews and their statuses.  Its list is guaranteed to be up to date
+// and can be modified on any thread.
+class PrerenderTracker {
+ public:
+  // Returns the PrerenderTracker singleton.
+  static PrerenderTracker* GetInstance();
+
+  // Attempts to set the status of the specified RenderViewHost to
+  // FINAL_STATUS_USED.  Returns true on success.  Returns false if it has
+  // already been cancelled for any reason, or is no longer prerendering.
+  // Can only be called only on the IO thread.  This method will not call
+  // PrerenderContents::set_final_status() on the corresponding
+  // PrerenderContents.
+  //
+  // If it returns true, all subsequent calls to TryCancel for the RenderView
+  // will return false.
+  bool TryUse(int child_id, int route_id);
+
+  // Attempts to cancel prerendering by the specified RenderView, setting the
+  // FinalStatus to |final_status|.  Returns true if the specified prerender has
+  // been cancelled, either as a result of this call or for any other reason.
+  // If the call results in cancelling a PrerenderContents, a task to destroy
+  // it is also posted to the UI thread.
+  //
+  // When true is returned, it is guaranteed that the RenderView will never
+  // be displayed.  When false is returned, the RenderView has either been
+  // swapped into a tab or has already been destroyed.
+  bool TryCancel(int child_id, int route_id, FinalStatus final_status);
+
+  // Same as above, but can only called on the IO Thread.  Does not acquire a
+  // lock when the RenderView is not being prerendered.
+  bool TryCancelOnIOThread(int child_id, int route_id,
+                           FinalStatus final_status);
+
+  // Returns whether or not a RenderView is prerendering.  Can only be called on
+  // the IO thread.  Does not acquire a lock, so may claim a RenderView that has
+  // been displayed or destroyed is still prerendering.
+  // TODO(mmenke):  Remove external use of this method and make it private.
+  bool IsPrerenderingOnIOThread(int child_id, int route_id) const;
+
+  // Gets the FinalStatus of the specified prerendered RenderView.  Returns
+  // |true| and sets |final_status| to the status of the RenderView if it
+  // is found, returns false otherwise.
+  bool GetFinalStatus(int child_id, int route_id,
+                      FinalStatus* final_status) const;
+
+ protected:
+  friend class PrerenderContents;
+
+  FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerUsed);
+  FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelled);
+  FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelledOnIO);
+  FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerCancelledFast);
+  FRIEND_TEST_ALL_PREFIXES(PrerenderTrackerTest, PrerenderTrackerMultiple);
+
+  // Must be called when a RenderView starts prerendering, before the first
+  // navigation starts to avoid any races.
+  void OnPrerenderingStarted(int child_id, int route_id,
+                             PrerenderManager* prerender_manager);
+
+  // Must be called when a RenderView stops prerendering, either because the
+  // RenderView was used or prerendering was cancelled and it is being
+  // destroyed.
+  void OnPrerenderingFinished(int child_id, int route_id);
+
+ private:
+  friend struct DefaultSingletonTraits<PrerenderTracker>;
+
+  typedef std::pair<int, int> ChildRouteIdPair;
+
+  // Map of child/route id pairs to final statuses.
+  typedef std::map<ChildRouteIdPair, RenderViewInfo> FinalStatusMap;
+  // Set of child/route id pairs that may be prerendering.
+  typedef std::set<ChildRouteIdPair> PossiblyPrerenderingChildRouteIdPairs;
+
+  PrerenderTracker();
+  ~PrerenderTracker();
+
+  // Attempts to set the FinalStatus of the specified RenderView to
+  // |final_status|.  If the FinalStatus of the RenderView has already been
+  // set, does nothing.  Returns the resulting FinalStatus of that RenderView,
+  // regardless of success or failure.  If the RenderView isn't currently
+  // prerendering, returns FINAL_STATUS_MAX.
+  FinalStatus SetFinalStatus(int child_id, int route_id,
+                             FinalStatus final_status);
+
+  // Add/remove the specified pair to |possibly_prerendering_io_thread_set_| on
+  // the IO Thread.
+  void AddPrerenderOnIOThread(const ChildRouteIdPair& child_route_id_pair);
+  void RemovePrerenderOnIOThread(const ChildRouteIdPair& child_route_id_pair);
+
+  // Tasks posted to the IO Thread to call the above functions.
+  static void AddPrerenderOnIOThreadTask(
+      const ChildRouteIdPair& child_route_id_pair);
+  static void RemovePrerenderOnIOThreadTask(
+      const ChildRouteIdPair& child_route_id_pair);
+
+  // |final_status_map_lock_| protects access to |final_status_map_|.
+  mutable base::Lock final_status_map_lock_;
+  // Map containing child/route id pairs and their final statuses.  Must only be
+  // accessed while the lock is held.  Values are always accurate and up to
+  // date.
+  FinalStatusMap final_status_map_;
+
+  // Superset of child/route id pairs that are prerendering.  Can only access on
+  // the IO thread.  May contain entries that have since been displayed.  Only
+  // used to prevent locking when not needed.
+  PossiblyPrerenderingChildRouteIdPairs possibly_prerendering_io_thread_set_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrerenderTracker);
+};
+
+}  // namespace prerender
+
+#endif  // CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
diff --git a/chrome/browser/prerender/prerender_tracker_unittest.cc b/chrome/browser/prerender/prerender_tracker_unittest.cc
new file mode 100644
index 0000000..4ed6f83
--- /dev/null
+++ b/chrome/browser/prerender/prerender_tracker_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/logging.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
+#include "content/browser/browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace prerender {
+
+namespace {
+
+class TestPrerenderManager : public PrerenderManager {
+ public:
+  TestPrerenderManager() : PrerenderManager(NULL) {
+    rate_limit_enabled_ = false;
+  }
+
+  virtual void DestroyPreloadForChildRouteIdPair(
+      const std::pair<int, int>& child_route_id_pair,
+      FinalStatus final_status) OVERRIDE {
+    cancelled_id_pairs_.insert(child_route_id_pair);
+  }
+
+  bool WasPrerenderCancelled(int child_id, int route_id) {
+    std::pair<int, int> child_route_id_pair(child_id, route_id);
+    return cancelled_id_pairs_.count(child_route_id_pair) != 0;
+  }
+
+  // Set of all the RenderViews that have been cancelled.
+  std::set<std::pair<int, int> > cancelled_id_pairs_;
+};
+
+}  // namespace
+
+class PrerenderTrackerTest : public testing::Test {
+ public:
+  PrerenderTrackerTest() :
+      ui_thread_(BrowserThread::UI, &message_loop_),
+      io_thread_(BrowserThread::IO, &message_loop_),
+      prerender_manager_(new TestPrerenderManager()) {
+  }
+
+  TestPrerenderManager* prerender_manager() {
+    return prerender_manager_.get();
+  }
+
+  PrerenderTracker* prerender_tracker() {
+    return PrerenderTracker::GetInstance();
+  }
+
+  int GetCurrentStatus(int child_id, int route_id) {
+    FinalStatus final_status;
+    if (!prerender_tracker()->GetFinalStatus(child_id, route_id,
+                                             &final_status)) {
+      return -1;
+    }
+    return final_status;
+  }
+
+  // Runs any tasks queued on either thread.
+  void RunEvents() {
+    message_loop_.RunAllPending();
+  }
+
+ private:
+  MessageLoop message_loop_;
+  BrowserThread ui_thread_;
+  BrowserThread io_thread_;
+
+  scoped_ptr<TestPrerenderManager> prerender_manager_;
+};
+
+// Check that a non-existant RenderView is handled correctly.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerNull) {
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->TryUse(0, 0));
+  EXPECT_FALSE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_FALSE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_manager()->WasPrerenderCancelled(0, 0));
+}
+
+// Check that a page that is used is handled correctly.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerUsed) {
+  prerender_tracker()->OnPrerenderingStarted(0, 0, prerender_manager());
+  EXPECT_EQ(FINAL_STATUS_MAX, GetCurrentStatus(0, 0));
+
+  // This calls AddPrerenderOnIOThreadTask().
+  RunEvents();
+
+  EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+  EXPECT_EQ(FINAL_STATUS_MAX, GetCurrentStatus(0, 0));
+
+  // Display the prerendered RenderView.
+  EXPECT_TRUE(prerender_tracker()->TryUse(0, 0));
+
+  // Make sure the page can't be destroyed or claim it was destroyed after
+  // it's been used.
+  EXPECT_FALSE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_FALSE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_EQ(FINAL_STATUS_USED, GetCurrentStatus(0, 0));
+
+  // This would call DestroyPreloadForChildRouteIdPair(), if the prerender were
+  // cancelled.
+  RunEvents();
+
+  // These functions should all behave as before.
+  EXPECT_FALSE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_FALSE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_EQ(FINAL_STATUS_USED, GetCurrentStatus(0, 0));
+
+  // This calls DestroyPreloadForChildRouteIdPair().
+  prerender_tracker()->OnPrerenderingFinished(0, 0);
+  EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+
+  // This calls RemovePrerenderOnIOThreadTask().
+  RunEvents();
+
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+  EXPECT_FALSE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_FALSE(prerender_manager()->WasPrerenderCancelled(0, 0));
+}
+
+// Check that a prerendered page cancelled by TryCancel() is handled correctly.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerCancelled) {
+  prerender_tracker()->OnPrerenderingStarted(0, 0, prerender_manager());
+  EXPECT_EQ(FINAL_STATUS_MAX, GetCurrentStatus(0, 0));
+
+  // This calls AddPrerenderOnIOThreadTask().
+  RunEvents();
+
+  // Cancel the prerender.
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+
+  EXPECT_FALSE(prerender_tracker()->TryUse(0, 0));
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_EQ(FINAL_STATUS_HTTPS, GetCurrentStatus(0, 0));
+
+  // This calls DestroyPreloadForChildRouteIdPair().
+  RunEvents();
+  EXPECT_TRUE(prerender_manager()->WasPrerenderCancelled(0, 0));
+
+  // These should all work until the prerendering RenderViewHost is destroyed.
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_EQ(FINAL_STATUS_HTTPS, GetCurrentStatus(0, 0));
+
+  prerender_tracker()->OnPrerenderingFinished(0, 0);
+  EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+
+  // This calls RemovePrerenderOnIOThreadTask().
+  RunEvents();
+
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+}
+
+// Check that a prerendered page cancelled on the IO thread by
+// TryCancelOnIOThread() is handled correctly.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerCancelledOnIO) {
+  prerender_tracker()->OnPrerenderingStarted(0, 0, prerender_manager());
+  EXPECT_EQ(FINAL_STATUS_MAX, GetCurrentStatus(0, 0));
+
+  // This calls AddPrerenderOnIOThreadTask().
+  RunEvents();
+
+  // Cancel the prerender.
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+
+  EXPECT_FALSE(prerender_tracker()->TryUse(0, 0));
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_EQ(FINAL_STATUS_TIMED_OUT, GetCurrentStatus(0, 0));
+
+  // This calls DestroyPreloadForChildRouteIdPair().
+  RunEvents();
+  EXPECT_TRUE(prerender_manager()->WasPrerenderCancelled(0, 0));
+
+  // These should all work until the prerendering RenderViewHost is destroyed.
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_EQ(FINAL_STATUS_TIMED_OUT, GetCurrentStatus(0, 0));
+
+  prerender_tracker()->OnPrerenderingFinished(0, 0);
+  EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+
+  // This calls RemovePrerenderOnIOThreadTask().
+  RunEvents();
+
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+}
+
+// Check that a prerendered page cancelled before it reaches the IO thread is
+// handled correctly.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerCancelledFast) {
+  prerender_tracker()->OnPrerenderingStarted(0, 0, prerender_manager());
+  // Cancel the prerender.
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+
+  EXPECT_FALSE(prerender_tracker()->TryUse(0, 0));
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_TIMED_OUT));
+
+  // This calls AddPrerenderOnIOThreadTask() and
+  // DestroyPreloadForChildRouteIdPair().
+  RunEvents();
+  EXPECT_TRUE(prerender_manager()->WasPrerenderCancelled(0, 0));
+
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_TRUE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_TIMED_OUT));
+  EXPECT_EQ(FINAL_STATUS_HTTPS, GetCurrentStatus(0, 0));
+
+  prerender_tracker()->OnPrerenderingFinished(0, 0);
+
+  // This calls RemovePrerenderOnIOThreadTask().
+  RunEvents();
+
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+}
+
+// Check that handling two pages at once works.
+TEST_F(PrerenderTrackerTest, PrerenderTrackerMultiple) {
+  prerender_tracker()->OnPrerenderingStarted(0, 0, prerender_manager());
+
+  // This calls AddPrerenderOnIOThreadTask().
+  RunEvents();
+  EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(1, 2));
+  EXPECT_FALSE(prerender_tracker()->TryUse(1, 2));
+  EXPECT_FALSE(prerender_tracker()->TryCancel(1, 2, FINAL_STATUS_HTTPS));
+
+  // Start second prerender.
+  prerender_tracker()->OnPrerenderingStarted(1, 2, prerender_manager());
+  // This calls AddPrerenderOnIOThreadTask().
+  RunEvents();
+
+  // Use (0, 0).
+  EXPECT_TRUE(prerender_tracker()->TryUse(0, 0));
+  EXPECT_EQ(FINAL_STATUS_USED, GetCurrentStatus(0, 0));
+  EXPECT_EQ(FINAL_STATUS_MAX, GetCurrentStatus(1, 2));
+
+  // Cancel (1, 2).
+  EXPECT_TRUE(prerender_tracker()->TryCancelOnIOThread(
+      1, 2, FINAL_STATUS_HTTPS));
+
+  EXPECT_FALSE(prerender_tracker()->TryCancel(0, 0, FINAL_STATUS_HTTPS));
+  EXPECT_EQ(FINAL_STATUS_USED, GetCurrentStatus(0, 0));
+
+  EXPECT_FALSE(prerender_tracker()->TryUse(1, 2));
+  EXPECT_TRUE(prerender_tracker()->TryCancel(1, 2, FINAL_STATUS_HTTPS));
+  EXPECT_EQ(FINAL_STATUS_HTTPS, GetCurrentStatus(1, 2));
+
+  // This calls DestroyPreloadForChildRouteIdPair().
+  RunEvents();
+  EXPECT_FALSE(prerender_manager()->WasPrerenderCancelled(0, 0));
+  EXPECT_TRUE(prerender_manager()->WasPrerenderCancelled(1, 2));
+
+  prerender_tracker()->OnPrerenderingFinished(0, 0);
+  prerender_tracker()->OnPrerenderingFinished(1, 2);
+
+  // This calls RemovePrerenderOnIOThreadTask().
+  RunEvents();
+
+  FinalStatus final_status;
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(0, 0, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(0, 0));
+
+  EXPECT_FALSE(prerender_tracker()->GetFinalStatus(1, 2, &final_status));
+  EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(1, 2));
+}
+
+}  // namespace prerender
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 5465fc8..d1c0605 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1653,6 +1653,8 @@
         'browser/prerender/prerender_render_view_host_observer.h',
         'browser/prerender/prerender_render_widget_host_view.cc',
         'browser/prerender/prerender_render_widget_host_view.h',
+        'browser/prerender/prerender_tracker.cc',
+        'browser/prerender/prerender_tracker.h',
         'browser/printing/background_printing_manager.cc',
         'browser/printing/background_printing_manager.h',
         'browser/printing/cloud_print/cloud_print_proxy_service.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 3fae730..b987f2c 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1502,6 +1502,7 @@
         'browser/prefs/scoped_user_pref_update_unittest.cc',
         'browser/prefs/session_startup_pref_unittest.cc',
         'browser/prerender/prerender_manager_unittest.cc',
+        'browser/prerender/prerender_tracker_unittest.cc',
         'browser/printing/cloud_print/cloud_print_setup_source_unittest.cc',
         'browser/printing/print_dialog_cloud_unittest.cc',
         'browser/printing/print_job_unittest.cc',
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 055ff1a..5678284 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -62,6 +62,7 @@
 

   # http://crbug.com/77090

   "+chrome/browser/prerender/prerender_manager.h",

+  "+chrome/browser/prerender/prerender_tracker.h",

 

   # http://crbug.com/76788

   "+chrome/browser/profiles/profile.h",

diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index 8fdc0c3..451f99e 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/external_protocol_handler.h"
 #include "chrome/browser/net/url_request_tracking.h"
 #include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_host/download_resource_handler.h"
 #include "chrome/browser/renderer_host/safe_browsing_resource_handler.h"
@@ -432,7 +433,9 @@
   }
 
   const GURL referrer = MaybeStripReferrer(request_data.referrer);
-  const bool is_prerendering = IsPrerenderingChildRoutePair(child_id, route_id);
+  const bool is_prerendering =
+      prerender::PrerenderTracker::GetInstance()->IsPrerenderingOnIOThread(
+          child_id, route_id);
 
   // Handle a PREFETCH resource type. If prefetch is disabled, squelch the
   // request. If prerendering is enabled, trigger a prerender for the URL
@@ -467,16 +470,11 @@
   // Abort any prerenders that spawn requests that use invalid HTTP methods.
   if (is_prerendering &&
       !prerender::PrerenderManager::IsValidHttpMethod(request_data.method)) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        NewRunnableFunction(
-            prerender::DestroyPreloadForRenderView,
-            resource_context.prerender_manager(),
-            child_id,
-            route_id,
-            prerender::FINAL_STATUS_INVALID_HTTP_METHOD));
-    AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id);
-    return;
+    if (prerender::PrerenderTracker::GetInstance()->TryCancelOnIOThread(
+            child_id, route_id, prerender::FINAL_STATUS_INVALID_HTTP_METHOD)) {
+      AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id);
+      return;
+    }
   }
 
   // Construct the event handler.
@@ -2036,28 +2034,6 @@
   }
 }
 
-void ResourceDispatcherHost::AddPrerenderChildRoutePair(int child_id,
-                                                        int route_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK(!IsPrerenderingChildRoutePair(child_id, route_id));
-  prerender_child_route_pairs_.insert(std::make_pair(child_id, route_id));
-}
-
-void ResourceDispatcherHost::RemovePrerenderChildRoutePair(int child_id,
-                                                           int route_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK(IsPrerenderingChildRoutePair(child_id, route_id));
-  prerender_child_route_pairs_.erase(std::make_pair(child_id, route_id));
-}
-
-bool ResourceDispatcherHost::IsPrerenderingChildRoutePair(int child_id,
-                                                          int route_id) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  std::pair<int, int> c_r_pair = std::make_pair(child_id, route_id);
-  return (prerender_child_route_pairs_.find(c_r_pair) !=
-            prerender_child_route_pairs_.end());
-}
-
 
 // static
 bool ResourceDispatcherHost::is_prefetch_enabled() {
diff --git a/content/browser/renderer_host/resource_dispatcher_host.h b/content/browser/renderer_host/resource_dispatcher_host.h
index f7a5c64..f8643fed 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.h
+++ b/content/browser/renderer_host/resource_dispatcher_host.h
@@ -244,14 +244,6 @@
   static bool is_prefetch_enabled();
   static void set_is_prefetch_enabled(bool value);
 
-  void AddPrerenderChildRoutePair(int child_id, int route_id);
-  void RemovePrerenderChildRoutePair(int child_id, int route_id);
-
-  typedef std::set<std::pair<int, int> > PrerenderChildRouteIdPairs;
-  const PrerenderChildRouteIdPairs& prerender_child_route_id_pairs() const {
-    return prerender_child_route_pairs_;
-  }
-
  private:
   FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
                            TestBlockedRequestsProcessDies);
@@ -384,8 +376,6 @@
                         const GURL& new_first_party_for_cookies);
   void OnReleaseDownloadedFile(int request_id);
 
-  bool IsPrerenderingChildRoutePair(int child_id, int route_id) const;
-
   ResourceHandler* CreateSafeBrowsingResourceHandler(
       ResourceHandler* handler, int child_id, int route_id,
       ResourceType::Type resource_type);
@@ -487,7 +477,6 @@
   ResourceMessageFilter* filter_;
 
   static bool is_prefetch_enabled_;
-  PrerenderChildRouteIdPairs prerender_child_route_pairs_;
 
 
   DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost);