predictors: Refactor resource_prefetch_predictor triggering.

Triggering and policy move to LoadingPredictor, instead of being in
ResourcePrefetchPredictor. As a consequence, rename
ResourcePrefetchPredictorObserver.

BUG=715525

Review-Url: https://codereview.chromium.org/2887133003
Cr-Commit-Position: refs/heads/master@{#476313}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f5c2bb7..189880ba 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -696,6 +696,8 @@
     "net/file_downloader.h",
     "net/http_server_properties_manager_factory.cc",
     "net/http_server_properties_manager_factory.h",
+    "net/loading_predictor_observer.cc",
+    "net/loading_predictor_observer.h",
     "net/net_error_diagnostics_dialog.h",
     "net/net_error_diagnostics_dialog_mac.cc",
     "net/net_error_diagnostics_dialog_win.cc",
@@ -723,8 +725,6 @@
     "net/referrer.h",
     "net/request_source_bandwidth_histograms.cc",
     "net/request_source_bandwidth_histograms.h",
-    "net/resource_prefetch_predictor_observer.cc",
-    "net/resource_prefetch_predictor_observer.h",
     "net/safe_search_util.cc",
     "net/safe_search_util.h",
     "net/sdch_owner_pref_storage.cc",
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index 0d8316e..4168a51 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -26,7 +26,7 @@
 #include "chrome/browser/loader/predictor_resource_throttle.h"
 #include "chrome/browser/loader/safe_browsing_resource_throttle.h"
 #include "chrome/browser/mod_pagespeed/mod_pagespeed_metrics.h"
-#include "chrome/browser/net/resource_prefetch_predictor_observer.h"
+#include "chrome/browser/net/loading_predictor_observer.h"
 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/browser/prerender/prerender_manager.h"
@@ -536,8 +536,8 @@
                                   resource_type, throttles);
 #endif  // !defined(DISABLE_NACL)
 
-  if (io_data->resource_prefetch_predictor_observer()) {
-    io_data->resource_prefetch_predictor_observer()->OnRequestStarted(
+  if (io_data->loading_predictor_observer()) {
+    io_data->loading_predictor_observer()->OnRequestStarted(
         request, resource_type, info->GetWebContentsGetterForRequest());
   }
 }
@@ -811,8 +811,8 @@
   }
 #endif
 
-  if (io_data->resource_prefetch_predictor_observer())
-    io_data->resource_prefetch_predictor_observer()->OnResponseStarted(
+  if (io_data->loading_predictor_observer())
+    io_data->loading_predictor_observer()->OnResponseStarted(
         request, info->GetWebContentsGetterForRequest());
 
   mod_pagespeed::RecordMetrics(info->GetResourceType(), request->url(),
@@ -837,8 +837,8 @@
   signin::FixMirrorRequestHeaderHelper(request, redirect_url, io_data,
                                        info->GetChildID(), info->GetRouteID());
 
-  if (io_data->resource_prefetch_predictor_observer()) {
-    io_data->resource_prefetch_predictor_observer()->OnRequestRedirected(
+  if (io_data->loading_predictor_observer()) {
+    io_data->loading_predictor_observer()->OnRequestRedirected(
         request, redirect_url, info->GetWebContentsGetterForRequest());
   }
 
diff --git a/chrome/browser/net/resource_prefetch_predictor_observer.cc b/chrome/browser/net/loading_predictor_observer.cc
similarity index 76%
rename from chrome/browser/net/resource_prefetch_predictor_observer.cc
rename to chrome/browser/net/loading_predictor_observer.cc
index 4dc2b0b..8d48d01 100644
--- a/chrome/browser/net/resource_prefetch_predictor_observer.cc
+++ b/chrome/browser/net/loading_predictor_observer.cc
@@ -1,8 +1,8 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/net/resource_prefetch_predictor_observer.h"
+#include "chrome/browser/net/loading_predictor_observer.h"
 
 #include <memory>
 #include <string>
@@ -20,6 +20,7 @@
 }
 
 using content::BrowserThread;
+using predictors::LoadingPredictor;
 using predictors::ResourcePrefetchPredictor;
 using URLRequestSummary =
     predictors::ResourcePrefetchPredictor::URLRequestSummary;
@@ -48,15 +49,13 @@
 };
 
 void ReportRequestStats(RequestStats stat) {
-  UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.RequestStats",
-                            stat,
+  UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.RequestStats", stat,
                             REQUEST_STATS_MAX);
 }
 
 void ReportMainFrameRequestStats(MainFrameRequestStats stat) {
   UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.MainFrameRequestStats",
-                            stat,
-                            MAIN_FRAME_REQUEST_STATS_MAX);
+                            stat, MAIN_FRAME_REQUEST_STATS_MAX);
 }
 
 bool TryToFillNavigationID(
@@ -79,18 +78,17 @@
 
 namespace chrome_browser_net {
 
-ResourcePrefetchPredictorObserver::ResourcePrefetchPredictorObserver(
-    ResourcePrefetchPredictor* predictor)
+LoadingPredictorObserver::LoadingPredictorObserver(LoadingPredictor* predictor)
     : predictor_(predictor->AsWeakPtr()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
-ResourcePrefetchPredictorObserver::~ResourcePrefetchPredictorObserver() {
+LoadingPredictorObserver::~LoadingPredictorObserver() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
         BrowserThread::CurrentlyOn(BrowserThread::IO));
 }
 
-void ResourcePrefetchPredictorObserver::OnRequestStarted(
+void LoadingPredictorObserver::OnRequestStarted(
     net::URLRequest* request,
     content::ResourceType resource_type,
     const content::ResourceRequestInfo::WebContentsGetter&
@@ -109,17 +107,16 @@
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(
-          &ResourcePrefetchPredictorObserver::OnRequestStartedOnUIThread,
-          base::Unretained(this), base::Passed(std::move(summary)),
-          web_contents_getter, request->first_party_for_cookies(),
-          request->creation_time()));
+      base::BindOnce(&LoadingPredictorObserver::OnRequestStartedOnUIThread,
+                     base::Unretained(this), base::Passed(std::move(summary)),
+                     web_contents_getter, request->first_party_for_cookies(),
+                     request->creation_time()));
 
   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
     ReportMainFrameRequestStats(MAIN_FRAME_REQUEST_STATS_PROCESSED_REQUESTS);
 }
 
-void ResourcePrefetchPredictorObserver::OnRequestRedirected(
+void LoadingPredictorObserver::OnRequestRedirected(
     net::URLRequest* request,
     const GURL& redirect_url,
     const content::ResourceRequestInfo::WebContentsGetter&
@@ -145,11 +142,10 @@
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(
-          &ResourcePrefetchPredictorObserver::OnRequestRedirectedOnUIThread,
-          base::Unretained(this), base::Passed(std::move(summary)),
-          web_contents_getter, request->first_party_for_cookies(),
-          request->creation_time()));
+      base::BindOnce(&LoadingPredictorObserver::OnRequestRedirectedOnUIThread,
+                     base::Unretained(this), base::Passed(std::move(summary)),
+                     web_contents_getter, request->first_party_for_cookies(),
+                     request->creation_time()));
 
   if (request_info &&
       request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
@@ -157,7 +153,7 @@
   }
 }
 
-void ResourcePrefetchPredictorObserver::OnResponseStarted(
+void LoadingPredictorObserver::OnResponseStarted(
     net::URLRequest* request,
     const content::ResourceRequestInfo::WebContentsGetter&
         web_contents_getter) {
@@ -182,11 +178,10 @@
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(
-          &ResourcePrefetchPredictorObserver::OnResponseStartedOnUIThread,
-          base::Unretained(this), base::Passed(std::move(summary)),
-          web_contents_getter, request->first_party_for_cookies(),
-          request->creation_time()));
+      base::BindOnce(&LoadingPredictorObserver::OnResponseStartedOnUIThread,
+                     base::Unretained(this), base::Passed(std::move(summary)),
+                     web_contents_getter, request->first_party_for_cookies(),
+                     request->creation_time()));
 
   ReportRequestStats(REQUEST_STATS_TOTAL_PROCESSED_RESPONSES);
   if (request_info &&
@@ -195,7 +190,7 @@
   }
 }
 
-void ResourcePrefetchPredictorObserver::OnRequestStartedOnUIThread(
+void LoadingPredictorObserver::OnRequestStartedOnUIThread(
     std::unique_ptr<URLRequestSummary> summary,
     const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
     const GURL& main_frame_url,
@@ -205,10 +200,12 @@
                              main_frame_url, creation_time)) {
     return;
   }
-  predictor_->RecordURLRequest(*summary);
+  if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
+    predictor_->OnMainFrameRequest(*summary);
+  predictor_->resource_prefetch_predictor()->RecordURLRequest(*summary);
 }
 
-void ResourcePrefetchPredictorObserver::OnRequestRedirectedOnUIThread(
+void LoadingPredictorObserver::OnRequestRedirectedOnUIThread(
     std::unique_ptr<URLRequestSummary> summary,
     const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
     const GURL& main_frame_url,
@@ -218,10 +215,12 @@
                              main_frame_url, creation_time)) {
     return;
   }
-  predictor_->RecordURLRedirect(*summary);
+  if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
+    predictor_->OnMainFrameRedirect(*summary);
+  predictor_->resource_prefetch_predictor()->RecordURLRedirect(*summary);
 }
 
-void ResourcePrefetchPredictorObserver::OnResponseStartedOnUIThread(
+void LoadingPredictorObserver::OnResponseStartedOnUIThread(
     std::unique_ptr<URLRequestSummary> summary,
     const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
     const GURL& main_frame_url,
@@ -231,7 +230,9 @@
                              main_frame_url, creation_time)) {
     return;
   }
-  predictor_->RecordURLResponse(*summary);
+  if (summary->resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
+    predictor_->OnMainFrameResponse(*summary);
+  predictor_->resource_prefetch_predictor()->RecordURLResponse(*summary);
 }
 
 }  // namespace chrome_browser_net
diff --git a/chrome/browser/net/resource_prefetch_predictor_observer.h b/chrome/browser/net/loading_predictor_observer.h
similarity index 79%
rename from chrome/browser/net/resource_prefetch_predictor_observer.h
rename to chrome/browser/net/loading_predictor_observer.h
index 4801540..4753f51b 100644
--- a/chrome/browser/net/resource_prefetch_predictor_observer.h
+++ b/chrome/browser/net/loading_predictor_observer.h
@@ -1,14 +1,15 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 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_NET_RESOURCE_PREFETCH_PREDICTOR_OBSERVER_H_
-#define CHROME_BROWSER_NET_RESOURCE_PREFETCH_PREDICTOR_OBSERVER_H_
+#ifndef CHROME_BROWSER_NET_LOADING_PREDICTOR_OBSERVER_H_
+#define CHROME_BROWSER_NET_LOADING_PREDICTOR_OBSERVER_H_
 
 #include <memory>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/resource_type.h"
@@ -22,17 +23,16 @@
 namespace chrome_browser_net {
 
 // Observes resource requests in the ResourceDispatcherHostDelegate and notifies
-// the ResourcePrefetchPredictor about the ones it is interested in.
+// the LoadingPredictor about the ones it is interested in.
 //  - Has an instance per profile, and is owned by the corresponding
 //    ProfileIOData.
 //  - Needs to be constructed on UI thread. Can be destroyed on UI or IO thread.
 //    As for member functions, public members are meant to be called on the IO
 //    thread and private members from the UI thread.
-class ResourcePrefetchPredictorObserver {
+class LoadingPredictorObserver {
  public:
-  explicit ResourcePrefetchPredictorObserver(
-      predictors::ResourcePrefetchPredictor* predictor);
-  ~ResourcePrefetchPredictorObserver();
+  explicit LoadingPredictorObserver(predictors::LoadingPredictor* predictor);
+  ~LoadingPredictorObserver();
 
   // Parts of the ResourceDispatcherHostDelegate that we want to observe.
   void OnRequestStarted(net::URLRequest* request,
@@ -72,11 +72,11 @@
       const base::TimeTicks& creation_time) const;
 
   // Owned by profile.
-  base::WeakPtr<predictors::ResourcePrefetchPredictor> predictor_;
+  base::WeakPtr<predictors::LoadingPredictor> predictor_;
 
-  DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorObserver);
+  DISALLOW_COPY_AND_ASSIGN(LoadingPredictorObserver);
 };
 
 }  // namespace chrome_browser_net
 
-#endif  // CHROME_BROWSER_NET_RESOURCE_PREFETCH_PREDICTOR_OBSERVER_H_
+#endif  // CHROME_BROWSER_NET_LOADING_PREDICTOR_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/resource_prefetch_predictor_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/resource_prefetch_predictor_page_load_metrics_observer_unittest.cc
index 1251655..64cbc254 100644
--- a/chrome/browser/page_load_metrics/observers/resource_prefetch_predictor_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/resource_prefetch_predictor_page_load_metrics_observer_unittest.cc
@@ -23,7 +23,7 @@
       Profile* profile)
       : ResourcePrefetchPredictor(config, profile) {}
 
-  MOCK_METHOD1(IsUrlPrefetchable, bool(const GURL& main_frame_url));
+  MOCK_CONST_METHOD1(IsUrlPrefetchable, bool(const GURL& main_frame_url));
 
   ~MockResourcePrefetchPredictor() override {}
 };
diff --git a/chrome/browser/predictors/loading_predictor.cc b/chrome/browser/predictors/loading_predictor.cc
index d5bd264..e818418 100644
--- a/chrome/browser/predictors/loading_predictor.cc
+++ b/chrome/browser/predictors/loading_predictor.cc
@@ -5,24 +5,37 @@
 #include "chrome/browser/predictors/loading_predictor.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/predictors/resource_prefetch_common.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 
 namespace predictors {
 
+using URLRequestSummary = ResourcePrefetchPredictor::URLRequestSummary;
+
 LoadingPredictor::LoadingPredictor(const LoadingPredictorConfig& config,
-                                   Profile* profile) {
-  resource_prefetch_predictor_ =
-      base::MakeUnique<ResourcePrefetchPredictor>(config, profile);
-}
+                                   Profile* profile)
+    : config_(config),
+      profile_(profile),
+      resource_prefetch_predictor_(
+          base::MakeUnique<ResourcePrefetchPredictor>(config, profile)) {}
 
 LoadingPredictor::~LoadingPredictor() = default;
 
 void LoadingPredictor::PrepareForPageLoad(const GURL& url, HintOrigin origin) {
-  resource_prefetch_predictor_->StartPrefetching(url, origin);
+  if (active_hints_.find(url) != active_hints_.end() ||
+      !resource_prefetch_predictor_->IsUrlPrefetchable(url))
+    return;
+
+  // To report hint durations.
+  active_hints_.emplace(url, base::TimeTicks::Now());
+
+  if (config_.IsPrefetchingEnabledForOrigin(profile_, origin))
+    resource_prefetch_predictor_->StartPrefetching(url);
 }
 
 void LoadingPredictor::CancelPageLoadHint(const GURL& url) {
-  resource_prefetch_predictor_->StopPrefetching(url);
+  CancelActiveHint(active_hints_.find(url));
 }
 
 void LoadingPredictor::StartInitialization() {
@@ -38,4 +51,87 @@
   resource_prefetch_predictor_->Shutdown();
 }
 
+void LoadingPredictor::OnMainFrameRequest(const URLRequestSummary& summary) {
+  DCHECK(summary.resource_type == content::RESOURCE_TYPE_MAIN_FRAME);
+
+  const NavigationID& navigation_id = summary.navigation_id;
+  CleanupAbandonedHintsAndNavigations(navigation_id);
+  active_navigations_.emplace(navigation_id, navigation_id.main_frame_url);
+  PrepareForPageLoad(navigation_id.main_frame_url, HintOrigin::NAVIGATION);
+}
+
+void LoadingPredictor::OnMainFrameRedirect(const URLRequestSummary& summary) {
+  DCHECK(summary.resource_type == content::RESOURCE_TYPE_MAIN_FRAME);
+
+  auto it = active_navigations_.find(summary.navigation_id);
+  if (it != active_navigations_.end()) {
+    if (summary.navigation_id.main_frame_url == summary.redirect_url)
+      return;
+    NavigationID navigation_id = summary.navigation_id;
+    navigation_id.main_frame_url = summary.redirect_url;
+    active_navigations_.emplace(navigation_id, it->second);
+    active_navigations_.erase(it);
+  }
+}
+
+void LoadingPredictor::OnMainFrameResponse(const URLRequestSummary& summary) {
+  DCHECK(summary.resource_type == content::RESOURCE_TYPE_MAIN_FRAME);
+
+  const NavigationID& navigation_id = summary.navigation_id;
+  auto it = active_navigations_.find(navigation_id);
+  if (it != active_navigations_.end()) {
+    const GURL& initial_url = it->second;
+    CancelPageLoadHint(initial_url);
+    active_navigations_.erase(it);
+  } else {
+    CancelPageLoadHint(navigation_id.main_frame_url);
+  }
+}
+
+std::map<GURL, base::TimeTicks>::iterator LoadingPredictor::CancelActiveHint(
+    std::map<GURL, base::TimeTicks>::iterator hint_it) {
+  if (hint_it == active_hints_.end())
+    return hint_it;
+
+  const GURL& url = hint_it->first;
+  resource_prefetch_predictor_->StopPrefetching(url);
+
+  UMA_HISTOGRAM_TIMES(
+      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram,
+      base::TimeTicks::Now() - hint_it->second);
+  return active_hints_.erase(hint_it);
+}
+
+void LoadingPredictor::CleanupAbandonedHintsAndNavigations(
+    const NavigationID& navigation_id) {
+  base::TimeTicks time_now = base::TimeTicks::Now();
+  const base::TimeDelta max_navigation_age =
+      base::TimeDelta::FromSeconds(config_.max_navigation_lifetime_seconds);
+
+  // Hints.
+  for (auto it = active_hints_.begin(); it != active_hints_.end();) {
+    base::TimeDelta prefetch_age = time_now - it->second;
+    if (prefetch_age > max_navigation_age) {
+      // Will go to the last bucket in the duration reported in
+      // CancelActiveHint() meaning that the duration was unlimited.
+      it = CancelActiveHint(it);
+    } else {
+      ++it;
+    }
+  }
+
+  // Navigations.
+  for (auto it = active_navigations_.begin();
+       it != active_navigations_.end();) {
+    if ((it->first.tab_id == navigation_id.tab_id) ||
+        (time_now - it->first.creation_time > max_navigation_age)) {
+      const GURL& initial_url = it->second;
+      CancelActiveHint(active_hints_.find(initial_url));
+      it = active_navigations_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+}
+
 }  // namespace predictors
diff --git a/chrome/browser/predictors/loading_predictor.h b/chrome/browser/predictors/loading_predictor.h
index 9bcdd81..4588febb 100644
--- a/chrome/browser/predictors/loading_predictor.h
+++ b/chrome/browser/predictors/loading_predictor.h
@@ -5,12 +5,17 @@
 #ifndef CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_
 #define CHROME_BROWSER_PREDICTORS_LOADING_PREDICTOR_H_
 
+#include <map>
 #include <memory>
+#include <utility>
 
+#include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "chrome/browser/predictors/resource_prefetch_common.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "url/gurl.h"
 
 class Profile;
 
@@ -22,6 +27,9 @@
 // From a high-level request (GURL and motivation) and a database of historical
 // data, initiates predictive actions to speed up page loads.
 //
+// Also listens to main frame requests/redirects/responses to initiate and
+// cancel page load hints.
+//
 // See ResourcePrefetchPredictor for a description of the resource prefetch
 // predictor.
 //
@@ -48,8 +56,45 @@
   // KeyedService:
   void Shutdown() override;
 
+  void OnMainFrameRequest(
+      const ResourcePrefetchPredictor::URLRequestSummary& summary);
+  void OnMainFrameRedirect(
+      const ResourcePrefetchPredictor::URLRequestSummary& summary);
+  void OnMainFrameResponse(
+      const ResourcePrefetchPredictor::URLRequestSummary& summary);
+
  private:
+  // Cancels an active hint, from its iterator inside |active_hints_|. If the
+  // iterator is .end(), does nothing. Returns the iterator after deletion of
+  // the entry.
+  std::map<GURL, base::TimeTicks>::iterator CancelActiveHint(
+      std::map<GURL, base::TimeTicks>::iterator hint_it);
+  void CleanupAbandonedHintsAndNavigations(const NavigationID& navigation_id);
+
+  // For testing.
+  void set_mock_resource_prefetch_predictor(
+      std::unique_ptr<ResourcePrefetchPredictor> predictor) {
+    resource_prefetch_predictor_ = std::move(predictor);
+  }
+
+  LoadingPredictorConfig config_;
+  Profile* profile_;
   std::unique_ptr<ResourcePrefetchPredictor> resource_prefetch_predictor_;
+  std::map<GURL, base::TimeTicks> active_hints_;
+  // Initial URL.
+  std::map<NavigationID, GURL> active_navigations_;
+
+  friend class LoadingPredictorTest;
+  FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
+                           TestMainFrameResponseCancelsHint);
+  FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
+                           TestMainFrameRequestCancelsStaleNavigations);
+  FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
+                           TestMainFrameResponseClearsNavigations);
+  FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
+                           TestMainFrameRequestDoesntCancelExternalHint);
+  FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
+                           TestDontTrackNonPrefetchableUrls);
 
   DISALLOW_COPY_AND_ASSIGN(LoadingPredictor);
 };
diff --git a/chrome/browser/predictors/loading_predictor_unittest.cc b/chrome/browser/predictors/loading_predictor_unittest.cc
new file mode 100644
index 0000000..f9faee40
--- /dev/null
+++ b/chrome/browser/predictors/loading_predictor_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/predictors/loading_predictor.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/run_loop.h"
+#include "base/test/histogram_tester.h"
+#include "chrome/browser/predictors/resource_prefetch_predictor_test_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace predictors {
+
+namespace {
+// First two are prefetchable, last one is not (see SetUp()).
+const char kUrl[] = "http://www.google.com/cats";
+const char kUrl2[] = "http://www.google.com/dogs";
+const char kUrl3[] = "https://unknown.website/catsanddogs";
+}
+
+// Does nothing, controls which URLs are prefetchable.
+class MockResourcePrefetcherPredictor : public ResourcePrefetchPredictor {
+ public:
+  MockResourcePrefetcherPredictor(const LoadingPredictorConfig& config,
+                                  Profile* profile)
+      : ResourcePrefetchPredictor(config, profile) {}
+
+  bool IsUrlPrefetchable(const GURL& main_frame_url) const override {
+    return prefetchable_urls_.find(main_frame_url) != prefetchable_urls_.end();
+  }
+
+  void AddPrefetchableUrl(const GURL& url) { prefetchable_urls_.insert(url); }
+
+  MOCK_METHOD0(StartInitialization, void());
+  MOCK_METHOD0(Shutdown, void());
+  MOCK_METHOD1(StartPrefetching, void(const GURL&));
+  MOCK_METHOD1(StopPrefeching, void(const GURL&));
+
+ private:
+  std::set<GURL> prefetchable_urls_;
+};
+
+class LoadingPredictorTest : public testing::Test {
+ public:
+  LoadingPredictorTest();
+  ~LoadingPredictorTest() override;
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  content::TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<LoadingPredictor> predictor_;
+  std::unique_ptr<TestingProfile> profile_;
+};
+
+LoadingPredictorTest::LoadingPredictorTest()
+    : profile_(base::MakeUnique<TestingProfile>()) {}
+
+LoadingPredictorTest::~LoadingPredictorTest() {
+  profile_ = nullptr;
+  base::RunLoop().RunUntilIdle();
+}
+
+void LoadingPredictorTest::SetUp() {
+  LoadingPredictorConfig config;
+  PopulateTestConfig(&config);
+  predictor_ = base::MakeUnique<LoadingPredictor>(config, profile_.get());
+  auto mock =
+      base::MakeUnique<MockResourcePrefetcherPredictor>(config, profile_.get());
+  mock->AddPrefetchableUrl(GURL(kUrl));
+  mock->AddPrefetchableUrl(GURL(kUrl2));
+  predictor_->set_mock_resource_prefetch_predictor(std::move(mock));
+  predictor_->StartInitialization();
+  base::RunLoop().RunUntilIdle();
+}
+
+void LoadingPredictorTest::TearDown() {
+  predictor_ = nullptr;
+  profile_->DestroyHistoryService();
+}
+
+TEST_F(LoadingPredictorTest, TestPrefetchingDurationHistogram) {
+  base::HistogramTester histogram_tester;
+
+  const GURL url = GURL(kUrl);
+  const GURL url2 = GURL(kUrl2);
+  const GURL url3 = GURL(kUrl3);
+  predictor_->PrepareForPageLoad(url, HintOrigin::EXTERNAL);
+  predictor_->CancelPageLoadHint(url);
+  histogram_tester.ExpectTotalCount(
+      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 1);
+
+  // Mismatched start / end.
+  predictor_->PrepareForPageLoad(url, HintOrigin::EXTERNAL);
+  predictor_->CancelPageLoadHint(url2);
+  // No increment.
+  histogram_tester.ExpectTotalCount(
+      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 1);
+
+  // Can track a navigation (url2) while one is still in progress (url).
+  predictor_->PrepareForPageLoad(url2, HintOrigin::EXTERNAL);
+  predictor_->CancelPageLoadHint(url2);
+  histogram_tester.ExpectTotalCount(
+      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 2);
+
+  // Do not track non-prefetchable URLs.
+  predictor_->PrepareForPageLoad(url3, HintOrigin::EXTERNAL);
+  predictor_->CancelPageLoadHint(url3);
+  // No increment.
+  histogram_tester.ExpectTotalCount(
+      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 2);
+}
+
+TEST_F(LoadingPredictorTest, TestMainFrameResponseCancelsHint) {
+  const GURL url = GURL(kUrl);
+  predictor_->PrepareForPageLoad(url, HintOrigin::EXTERNAL);
+  EXPECT_EQ(1UL, predictor_->active_hints_.size());
+
+  auto summary = CreateURLRequestSummary(12, url.spec());
+  predictor_->OnMainFrameResponse(summary);
+  EXPECT_TRUE(predictor_->active_hints_.empty());
+}
+
+TEST_F(LoadingPredictorTest, TestMainFrameRequestCancelsStaleNavigations) {
+  const std::string url = kUrl;
+  const std::string url2 = kUrl2;
+  const int tab_id = 12;
+  const auto& active_navigations = predictor_->active_navigations_;
+  const auto& active_hints = predictor_->active_hints_;
+
+  auto summary = CreateURLRequestSummary(tab_id, url);
+  auto navigation_id = CreateNavigationID(tab_id, url);
+
+  predictor_->OnMainFrameRequest(summary);
+  EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
+  EXPECT_NE(active_hints.find(GURL(url)), active_hints.end());
+
+  summary = CreateURLRequestSummary(tab_id, url2);
+  predictor_->OnMainFrameRequest(summary);
+  EXPECT_EQ(active_navigations.find(navigation_id), active_navigations.end());
+  EXPECT_EQ(active_hints.find(GURL(url)), active_hints.end());
+
+  auto navigation_id2 = CreateNavigationID(tab_id, url2);
+  EXPECT_NE(active_navigations.find(navigation_id2), active_navigations.end());
+}
+
+TEST_F(LoadingPredictorTest, TestMainFrameResponseClearsNavigations) {
+  const std::string url = kUrl;
+  const std::string redirected = kUrl2;
+  const int tab_id = 12;
+  const auto& active_navigations = predictor_->active_navigations_;
+  const auto& active_hints = predictor_->active_hints_;
+
+  auto summary = CreateURLRequestSummary(tab_id, url);
+  auto navigation_id = CreateNavigationID(tab_id, url);
+
+  predictor_->OnMainFrameRequest(summary);
+  EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
+  EXPECT_FALSE(active_hints.empty());
+
+  predictor_->OnMainFrameResponse(summary);
+  EXPECT_TRUE(active_navigations.empty());
+  EXPECT_TRUE(active_hints.empty());
+
+  // With redirects.
+  predictor_->OnMainFrameRequest(summary);
+  EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
+  EXPECT_FALSE(active_hints.empty());
+
+  summary.redirect_url = GURL(redirected);
+  predictor_->OnMainFrameRedirect(summary);
+  EXPECT_FALSE(active_navigations.empty());
+  EXPECT_FALSE(active_hints.empty());
+
+  summary.navigation_id.main_frame_url = GURL(redirected);
+  predictor_->OnMainFrameResponse(summary);
+  EXPECT_TRUE(active_navigations.empty());
+  EXPECT_TRUE(active_hints.empty());
+}
+
+TEST_F(LoadingPredictorTest, TestMainFrameRequestDoesntCancelExternalHint) {
+  const GURL url = GURL(kUrl);
+  const int tab_id = 12;
+  const auto& active_navigations = predictor_->active_navigations_;
+  auto& active_hints = predictor_->active_hints_;
+
+  predictor_->PrepareForPageLoad(url, HintOrigin::EXTERNAL);
+  auto it = active_hints.find(url);
+  EXPECT_NE(it, active_hints.end());
+  EXPECT_TRUE(active_navigations.empty());
+
+  // To check that the hint is not replaced, set the start time in the past,
+  // and check later that it didn't change.
+  base::TimeTicks start_time = it->second - base::TimeDelta::FromSeconds(10);
+  it->second = start_time;
+
+  auto summary = CreateURLRequestSummary(tab_id, url.spec());
+  predictor_->OnMainFrameRequest(summary);
+  EXPECT_NE(active_navigations.find(summary.navigation_id),
+            active_navigations.end());
+  it = active_hints.find(url);
+  EXPECT_NE(it, active_hints.end());
+  EXPECT_EQ(start_time, it->second);
+}
+
+TEST_F(LoadingPredictorTest, TestDontTrackNonPrefetchableUrls) {
+  const GURL url3 = GURL(kUrl3);
+  predictor_->PrepareForPageLoad(url3, HintOrigin::EXTERNAL);
+  EXPECT_TRUE(predictor_->active_hints_.empty());
+}
+
+}  // namespace predictors
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc
index ecb50a4..11f0b234 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -628,9 +628,7 @@
   if (initialization_state_ != INITIALIZED)
     return;
 
-  if (response.resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
-    OnMainFrameResponse(response);
-  else
+  if (response.resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
     OnSubresourceResponse(response);
 }
 
@@ -678,56 +676,6 @@
     nav_it->second->first_contentful_paint = first_contentful_paint;
 }
 
-void ResourcePrefetchPredictor::StartPrefetching(const GURL& url,
-                                                 HintOrigin origin) {
-  TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StartPrefetching", "url",
-               url.spec());
-  // Save prefetch start time to report prefetching duration.
-  if (inflight_prefetches_.find(url) == inflight_prefetches_.end() &&
-      IsUrlPrefetchable(url)) {
-    inflight_prefetches_.insert(std::make_pair(url, base::TimeTicks::Now()));
-  }
-
-  if (!prefetch_manager_.get())  // Prefetching not enabled.
-    return;
-  if (!config_.IsPrefetchingEnabledForOrigin(profile_, origin))
-    return;
-
-  ResourcePrefetchPredictor::Prediction prediction;
-  if (!GetPrefetchData(url, &prediction))
-    return;
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&ResourcePrefetcherManager::MaybeAddPrefetch,
-                     prefetch_manager_, url, prediction.subresource_urls));
-
-  if (observer_)
-    observer_->OnPrefetchingStarted(url);
-}
-
-void ResourcePrefetchPredictor::StopPrefetching(const GURL& url) {
-  TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StopPrefetching", "url",
-               url.spec());
-  auto it = inflight_prefetches_.find(url);
-  if (it != inflight_prefetches_.end()) {
-    UMA_HISTOGRAM_TIMES(
-        internal::kResourcePrefetchPredictorPrefetchingDurationHistogram,
-        base::TimeTicks::Now() - it->second);
-    inflight_prefetches_.erase(it);
-  }
-  if (!prefetch_manager_.get())  // Not enabled.
-    return;
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&ResourcePrefetcherManager::MaybeRemovePrefetch,
-                     prefetch_manager_, url));
-
-  if (observer_)
-    observer_->OnPrefetchingStopped(url);
-}
-
 void ResourcePrefetchPredictor::OnPrefetchingFinished(
     const GURL& main_frame_url,
     std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats) {
@@ -737,7 +685,8 @@
   prefetcher_stats_.insert(std::make_pair(main_frame_url, std::move(stats)));
 }
 
-bool ResourcePrefetchPredictor::IsUrlPrefetchable(const GURL& main_frame_url) {
+bool ResourcePrefetchPredictor::IsUrlPrefetchable(
+    const GURL& main_frame_url) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (initialization_state_ != INITIALIZED)
     return false;
@@ -771,30 +720,13 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(INITIALIZED, initialization_state_);
 
-  const GURL& main_frame_url = request.navigation_id.main_frame_url;
-  StartPrefetching(main_frame_url, HintOrigin::NAVIGATION);
-
   CleanupAbandonedNavigations(request.navigation_id);
 
   // New empty navigation entry.
-  inflight_navigations_.insert(
-      std::make_pair(request.navigation_id,
-                     base::MakeUnique<PageRequestSummary>(main_frame_url)));
-}
-
-void ResourcePrefetchPredictor::OnMainFrameResponse(
-    const URLRequestSummary& response) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(INITIALIZED, initialization_state_);
-
-  NavigationMap::iterator nav_it =
-      inflight_navigations_.find(response.navigation_id);
-  if (nav_it != inflight_navigations_.end()) {
-    // To match an URL in StartPrefetching().
-    StopPrefetching(nav_it->second->initial_url);
-  } else {
-    StopPrefetching(response.navigation_id.main_frame_url);
-  }
+  const GURL& main_frame_url = request.navigation_id.main_frame_url;
+  inflight_navigations_.emplace(
+      request.navigation_id,
+      base::MakeUnique<PageRequestSummary>(main_frame_url));
 }
 
 void ResourcePrefetchPredictor::OnMainFrameRedirect(
@@ -825,8 +757,7 @@
   NavigationID navigation_id(response.navigation_id);
   navigation_id.main_frame_url = response.redirect_url;
   summary->main_frame_url = response.redirect_url;
-  inflight_navigations_.insert(
-      std::make_pair(navigation_id, std::move(summary)));
+  inflight_navigations_.emplace(navigation_id, std::move(summary));
 }
 
 void ResourcePrefetchPredictor::OnSubresourceResponse(
@@ -1079,20 +1010,6 @@
     }
   }
 
-  for (auto it = inflight_prefetches_.begin();
-       it != inflight_prefetches_.end();) {
-    base::TimeDelta prefetch_age = time_now - it->second;
-    if (prefetch_age > max_navigation_age) {
-      // It goes to the last bucket meaning that the duration was unlimited.
-      UMA_HISTOGRAM_TIMES(
-          internal::kResourcePrefetchPredictorPrefetchingDurationHistogram,
-          prefetch_age);
-      it = inflight_prefetches_.erase(it);
-    } else {
-      ++it;
-    }
-  }
-
   // Remove old prefetches that haven't been claimed.
   for (auto stats_it = prefetcher_stats_.begin();
        stats_it != prefetcher_stats_.end();) {
@@ -1599,6 +1516,40 @@
   }
 }
 
+void ResourcePrefetchPredictor::StartPrefetching(const GURL& url) {
+  TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StartPrefetching", "url",
+               url.spec());
+  if (!prefetch_manager_.get())  // Not enabled.
+    return;
+
+  ResourcePrefetchPredictor::Prediction prediction;
+  bool has_data = GetPrefetchData(url, &prediction);
+  DCHECK(has_data);
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&ResourcePrefetcherManager::MaybeAddPrefetch,
+                     prefetch_manager_, url, prediction.subresource_urls));
+
+  if (observer_)
+    observer_->OnPrefetchingStarted(url);
+}
+
+void ResourcePrefetchPredictor::StopPrefetching(const GURL& url) {
+  TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StopPrefetching", "url",
+               url.spec());
+  if (!prefetch_manager_.get())  // Not enabled.
+    return;
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&ResourcePrefetcherManager::MaybeRemovePrefetch,
+                     prefetch_manager_, url));
+
+  if (observer_)
+    observer_->OnPrefetchingStopped(url);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TestObserver.
 
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h
index f01b150..e8a88fb 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -24,6 +24,7 @@
 #include "chrome/browser/predictors/resource_prefetch_common.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
 #include "chrome/browser/predictors/resource_prefetcher.h"
+#include "chrome/browser/predictors/resource_prefetcher_manager.h"
 #include "components/history/core/browser/history_db_task.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/history/core/browser/history_types.h"
@@ -214,9 +215,9 @@
   ~ResourcePrefetchPredictor() override;
 
   // Starts initialization by posting a task to the DB thread to read the
-  // predictor database.
-  void StartInitialization();
-  void Shutdown();
+  // predictor database. Virtual for testing.
+  virtual void StartInitialization();
+  virtual void Shutdown();
 
   // Thread safe.
   static bool ShouldRecordRequest(net::URLRequest* request,
@@ -258,7 +259,7 @@
       std::unique_ptr<ResourcePrefetcher::PrefetcherStats> stats);
 
   // Returns true if prefetching data exists for the |main_frame_url|.
-  virtual bool IsUrlPrefetchable(const GURL& main_frame_url);
+  virtual bool IsUrlPrefetchable(const GURL& main_frame_url) const;
 
   // Returns true iff |resource| has sufficient confidence level and required
   // number of hits.
@@ -274,9 +275,9 @@
   void SetObserverForTesting(TestObserver* observer);
 
  private:
-  // Starts prefetching if it is enabled for |origin| and prefetching data
-  // exists for the |main_frame_url| either at the URL or at the host level.
-  void StartPrefetching(const GURL& main_frame_url, HintOrigin origin);
+  // Starts prefetching if it is enabled and prefetching data exists for the
+  // |main_frame_url| either at the URL or at the host level.
+  void StartPrefetching(const GURL& main_frame_url);
 
   // Stops prefetching that may be in progress corresponding to
   // |main_frame_url|.
@@ -360,7 +361,6 @@
   // Functions called on different network events pertaining to the loading of
   // main frame resource or sub resources.
   void OnMainFrameRequest(const URLRequestSummary& request);
-  void OnMainFrameResponse(const URLRequestSummary& response);
   void OnMainFrameRedirect(const URLRequestSummary& response);
   void OnSubresourceResponse(const URLRequestSummary& response);
   void OnSubresourceRedirect(const URLRequestSummary& response);
@@ -405,7 +405,7 @@
   // database has been read.
   void OnHistoryAndCacheLoaded();
 
-  // Cleanup inflight_navigations_, inflight_prefetches_, and prefetcher_stats_.
+  // Cleanup inflight_navigations_, and prefetcher_stats_.
   void CleanupAbandonedNavigations(const NavigationID& navigation_id);
 
   // Deletes all URLs from the predictor database, the caches and removes all
@@ -463,6 +463,12 @@
     tables_ = tables;
   }
 
+  // For testing.
+  void set_mock_resource_prefetcher_manager(
+      scoped_refptr<ResourcePrefetcherManager> prefetch_manager) {
+    prefetch_manager_ = prefetch_manager;
+  }
+
   Profile* const profile_;
   TestObserver* observer_;
   const LoadingPredictorConfig config_;
@@ -478,7 +484,6 @@
   std::unique_ptr<ManifestDataMap> manifest_data_;
   std::unique_ptr<OriginDataMap> origin_data_;
 
-  std::map<GURL, base::TimeTicks> inflight_prefetches_;
   NavigationMap inflight_navigations_;
 
   std::map<GURL, std::unique_ptr<ResourcePrefetcher::PrefetcherStats>>
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc b/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc
index 98af40f..a8a3762 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_test_util.cc
@@ -176,6 +176,22 @@
   return summary;
 }
 
+void PopulateTestConfig(LoadingPredictorConfig* config, bool small_db) {
+  if (small_db) {
+    config->max_urls_to_track = 3;
+    config->max_hosts_to_track = 2;
+    config->min_url_visit_count = 2;
+    config->max_resources_per_entry = 4;
+    config->max_consecutive_misses = 2;
+    config->max_redirect_consecutive_misses = 2;
+    config->min_resource_confidence_to_trigger_prefetch = 0.5;
+  }
+  config->is_url_learning_enabled = true;
+  config->is_manifests_enabled = true;
+  config->is_origin_learning_enabled = true;
+  config->mode = LoadingPredictorConfig::LEARNING;
+}
+
 std::ostream& operator<<(std::ostream& os, const PrefetchData& data) {
   os << "[" << data.primary_key() << "," << data.last_visit_time() << "]"
      << std::endl;
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_test_util.h b/chrome/browser/predictors/resource_prefetch_predictor_test_util.h
index e66fedd7..d11f783 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_test_util.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor_test_util.h
@@ -77,6 +77,8 @@
     bool has_validators = false,
     bool always_revalidate = false);
 
+void PopulateTestConfig(LoadingPredictorConfig* config, bool small_db = true);
+
 // For printing failures nicely.
 std::ostream& operator<<(std::ostream& stream, const PrefetchData& data);
 std::ostream& operator<<(std::ostream& stream, const ResourceData& resource);
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
index 00383da..e67236a7 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
@@ -14,8 +14,10 @@
 #include "base/test/histogram_tester.h"
 #include "base/time/time.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_test_util.h"
+#include "chrome/browser/predictors/resource_prefetcher_manager.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
@@ -259,7 +261,7 @@
   }
 
   void InitializePredictor() {
-    predictor_->StartInitialization();
+    loading_predictor_->StartInitialization();
     base::RunLoop loop;
     loop.RunUntilIdle();  // Runs the DB lookup.
     profile_->BlockUntilHistoryProcessesPendingRequests();
@@ -267,20 +269,10 @@
 
   void ResetPredictor(bool small_db = true) {
     LoadingPredictorConfig config;
-    if (small_db) {
-      config.max_urls_to_track = 3;
-      config.max_hosts_to_track = 2;
-      config.max_resources_per_entry = 4;
-      config.max_consecutive_misses = 2;
-      config.max_redirect_consecutive_misses = 2;
-      config.min_resource_confidence_to_trigger_prefetch = 0.5;
-    }
-    config.is_url_learning_enabled = true;
-    config.is_manifests_enabled = true;
-    config.is_origin_learning_enabled = true;
-
-    config.mode |= LoadingPredictorConfig::LEARNING;
-    predictor_.reset(new ResourcePrefetchPredictor(config, profile_.get()));
+    PopulateTestConfig(&config, small_db);
+    loading_predictor_ =
+        base::MakeUnique<LoadingPredictor>(config, profile_.get());
+    predictor_ = loading_predictor_->resource_prefetch_predictor();
     predictor_->set_mock_tables(mock_tables_);
   }
 
@@ -296,7 +288,8 @@
   std::unique_ptr<TestingProfile> profile_;
   net::TestURLRequestContext url_request_context_;
 
-  std::unique_ptr<ResourcePrefetchPredictor> predictor_;
+  std::unique_ptr<LoadingPredictor> loading_predictor_;
+  ResourcePrefetchPredictor* predictor_;
   scoped_refptr<StrictMock<MockResourcePrefetchPredictorTables>> mock_tables_;
 
   PrefetchDataMap test_url_data_;
@@ -355,7 +348,8 @@
             mock_tables_->manifest_table_.data_);
   EXPECT_EQ(*predictor_->origin_data_->data_cache_,
             mock_tables_->origin_table_.data_);
-  predictor_.reset(NULL);
+  loading_predictor_ = nullptr;
+  predictor_ = nullptr;
   profile_->DestroyHistoryService();
 }
 
@@ -624,8 +618,7 @@
       content::RESOURCE_TYPE_SCRIPT, net::MEDIUM, "text/javascript", false);
   predictor_->RecordURLResponse(resource3);
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(
       mock_observer,
       OnNavigationLearned(kVisitCount,
@@ -724,8 +717,7 @@
 
   predictor_->RecordURLResponse(redirected);
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(mock_observer,
               OnNavigationLearned(
                   kVisitCount, CreatePageRequestSummary("http://www.google.com",
@@ -839,8 +831,7 @@
   no_store.is_no_store = true;
   predictor_->RecordURLResponse(no_store);
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(mock_observer,
               OnNavigationLearned(
                   kVisitCount, CreatePageRequestSummary("http://www.google.com",
@@ -942,8 +933,7 @@
       content::RESOURCE_TYPE_IMAGE, net::MEDIUM, "image/png", false);
   predictor_->RecordURLResponse(resource2);
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(mock_observer,
               OnNavigationLearned(
                   kVisitCount, CreatePageRequestSummary(
@@ -1013,8 +1003,7 @@
   predictor_->RecordURLRedirect(fb3);
   NavigationID fb_end = CreateNavigationID(1, "https://facebook.com/google");
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(
       mock_observer,
       OnNavigationLearned(kVisitCount, CreatePageRequestSummary(
@@ -1064,8 +1053,7 @@
   predictor_->RecordURLRedirect(fb3);
   NavigationID fb_end = CreateNavigationID(1, "https://facebook.com/google");
 
-  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(
-      predictor_.get());
+  StrictMock<MockResourcePrefetchPredictorObserver> mock_observer(predictor_);
   EXPECT_CALL(
       mock_observer,
       OnNavigationLearned(kVisitCount, CreatePageRequestSummary(
@@ -2069,28 +2057,6 @@
       ResourcePrefetchPredictor::RedirectStatus::REDIRECT_CORRECTLY_PREDICTED);
 }
 
-TEST_F(ResourcePrefetchPredictorTest, TestPrefetchingDurationHistogram) {
-  // Prefetching duration for an url without resources in the database
-  // shouldn't be recorded.
-  const std::string main_frame_url = "http://google.com/?query=cats";
-  predictor_->StartPrefetching(GURL(main_frame_url), HintOrigin::EXTERNAL);
-  predictor_->StopPrefetching(GURL(main_frame_url));
-  histogram_tester_->ExpectTotalCount(
-      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 0);
-
-  // Fill the database to record a duration.
-  PrefetchData google = CreatePrefetchData("google.com", 1);
-  InitializeResourceData(
-      google.add_resources(), "https://cdn.google.com/script.js",
-      content::RESOURCE_TYPE_SCRIPT, 10, 0, 1, 2.1, net::MEDIUM, false, false);
-  predictor_->host_resource_data_->UpdateData(google.primary_key(), google);
-
-  predictor_->StartPrefetching(GURL(main_frame_url), HintOrigin::EXTERNAL);
-  predictor_->StopPrefetching(GURL(main_frame_url));
-  histogram_tester_->ExpectTotalCount(
-      internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 1);
-}
-
 TEST_F(ResourcePrefetchPredictorTest, TestRecordFirstContentfulPaint) {
   auto res1_time = base::TimeTicks::FromInternalValue(1);
   auto res2_time = base::TimeTicks::FromInternalValue(2);
diff --git a/chrome/browser/predictors/resource_prefetcher_manager.h b/chrome/browser/predictors/resource_prefetcher_manager.h
index 84a8ec52..52fb50273 100644
--- a/chrome/browser/predictors/resource_prefetcher_manager.h
+++ b/chrome/browser/predictors/resource_prefetcher_manager.h
@@ -63,7 +63,6 @@
  private:
   friend class base::RefCountedThreadSafe<ResourcePrefetcherManager>;
   friend class MockResourcePrefetcherManager;
-
   ~ResourcePrefetcherManager() override;
 
   ResourcePrefetchPredictor* predictor_;
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index fd34c47d..644fd76b 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -38,8 +38,8 @@
 #include "chrome/browser/net/chrome_http_user_agent_settings.h"
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/net/chrome_url_request_context_getter.h"
+#include "chrome/browser/net/loading_predictor_observer.h"
 #include "chrome/browser/net/proxy_service_factory.h"
-#include "chrome/browser/net/resource_prefetch_predictor_observer.h"
 #include "chrome/browser/policy/cloud/policy_header_service_factory.h"
 #include "chrome/browser/policy/policy_helpers.h"
 #include "chrome/browser/predictors/loading_predictor.h"
@@ -391,9 +391,9 @@
 
   if (auto* loading_predictor =
           predictors::LoadingPredictorFactory::GetForProfile(profile)) {
-    resource_prefetch_predictor_observer_.reset(
-        new chrome_browser_net::ResourcePrefetchPredictorObserver(
-            loading_predictor->resource_prefetch_predictor()));
+    loading_predictor_observer_ =
+        base::MakeUnique<chrome_browser_net::LoadingPredictorObserver>(
+            loading_predictor);
   }
 
   ProtocolHandlerRegistry* protocol_handler_registry =
@@ -1100,9 +1100,9 @@
   resource_context_->host_resolver_ = io_thread_globals->host_resolver.get();
   resource_context_->request_context_ = main_request_context_.get();
 
-  if (profile_params_->resource_prefetch_predictor_observer_) {
-    resource_prefetch_predictor_observer_.reset(
-        profile_params_->resource_prefetch_predictor_observer_.release());
+  if (profile_params_->loading_predictor_observer_) {
+    loading_predictor_observer_ =
+        std::move(profile_params_->loading_predictor_observer_);
   }
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 4b14494..895fa55 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -51,7 +51,7 @@
 }
 
 namespace chrome_browser_net {
-class ResourcePrefetchPredictorObserver;
+class LoadingPredictorObserver;
 }
 
 namespace certificate_transparency {
@@ -204,9 +204,9 @@
     return &incognito_availibility_pref_;
   }
 
-  chrome_browser_net::ResourcePrefetchPredictorObserver*
-      resource_prefetch_predictor_observer() const {
-    return resource_prefetch_predictor_observer_.get();
+  chrome_browser_net::LoadingPredictorObserver* loading_predictor_observer()
+      const {
+    return loading_predictor_observer_.get();
   }
 
   policy::PolicyHeaderIOHelper* policy_header_helper() const {
@@ -313,8 +313,8 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     scoped_refptr<extensions::InfoMap> extension_info_map;
 #endif
-    std::unique_ptr<chrome_browser_net::ResourcePrefetchPredictorObserver>
-        resource_prefetch_predictor_observer_;
+    std::unique_ptr<chrome_browser_net::LoadingPredictorObserver>
+        loading_predictor_observer_;
 
     // This pointer exists only as a means of conveying a url job factory
     // pointer from the protocol handler registry on the UI thread to the
@@ -600,8 +600,8 @@
 
   mutable scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
 
-  mutable std::unique_ptr<chrome_browser_net::ResourcePrefetchPredictorObserver>
-      resource_prefetch_predictor_observer_;
+  mutable std::unique_ptr<chrome_browser_net::LoadingPredictorObserver>
+      loading_predictor_observer_;
 
   mutable std::unique_ptr<ChromeHttpUserAgentSettings>
       chrome_http_user_agent_settings_;