Speculatively launch Service Workers on mouse/touch events. [3/5]

1/5: Introduce NavigationHintSender.
     https://codereview.chromium.org/2043863003/
2/5: Pipe NavigationHints from NavigationHintSender to ChromeRenderMessageFilter
     https://codereview.chromium.org/2043083002/
3/5: Call StartServiceWorkerForNavigationHint() from ChromeRenderMessageFilter
     This CL.
4/5: Measure the precision of the speculative launch of Service Workers for
NavigationHints
     https://codereview.chromium.org/2045153003/
5/5: Add flags to enable SupeculativeLaunchServiceWorker
     https://codereview.chromium.org/2053573002/

When ChromeRenderMessageFilter receives NavigationHint IPC message, it calls
ServiceWorkerContextWrapper::StartServiceWorkerForNavigationHint().
ServiceWorkerContextWrapper will try to start a Service Worker for the document
which is linked from the anchor element.

This CL depends on https://codereview.chromium.org/2181553003/

BUG=616502

Review-Url: https://codereview.chromium.org/2052613003
Cr-Commit-Position: refs/heads/master@{#407695}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e135887..423a8e1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -958,7 +958,8 @@
   net::URLRequestContextGetter* context =
       host->GetStoragePartition()->GetURLRequestContext();
 
-  host->AddFilter(new ChromeRenderMessageFilter(id, profile));
+  host->AddFilter(new ChromeRenderMessageFilter(
+      id, profile, host->GetStoragePartition()->GetServiceWorkerContext()));
 #if defined(ENABLE_EXTENSIONS)
   host->AddFilter(new cast::CastTransportHostFilter);
 #endif
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 0afbb94..b885387 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -31,6 +31,7 @@
 #include "components/web_cache/browser/web_cache_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/service_worker_context.h"
 
 #if defined(ENABLE_EXTENSIONS)
 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
@@ -46,17 +47,23 @@
     ChromeMsgStart, ContentSettingsMsgStart, NetworkHintsMsgStart,
 };
 
+void DidStartServiceWorkerForNavigationHint(bool success) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
 }  // namespace
 
-ChromeRenderMessageFilter::ChromeRenderMessageFilter(int render_process_id,
-                                                     Profile* profile)
+ChromeRenderMessageFilter::ChromeRenderMessageFilter(
+    int render_process_id,
+    Profile* profile,
+    content::ServiceWorkerContext* service_worker_context)
     : BrowserMessageFilter(kFilteredMessageClasses,
                            arraysize(kFilteredMessageClasses)),
       render_process_id_(render_process_id),
       profile_(profile),
       predictor_(profile_->GetNetworkPredictor()),
-      cookie_settings_(CookieSettingsFactory::GetForProfile(profile)) {
-}
+      cookie_settings_(CookieSettingsFactory::GetForProfile(profile)),
+      service_worker_context_(service_worker_context) {}
 
 ChromeRenderMessageFilter::~ChromeRenderMessageFilter() {
 }
@@ -94,6 +101,7 @@
 void ChromeRenderMessageFilter::OverrideThreadForMessage(
     const IPC::Message& message, BrowserThread::ID* thread) {
   switch (message.type()) {
+    case NetworkHintsMsg_NavigationHint::ID:
 #if defined(ENABLE_PLUGINS)
     case ChromeViewHostMsg_IsCrashReportingEnabled::ID:
 #endif
@@ -134,7 +142,10 @@
     blink::WebNavigationHintType type) {
   // TODO(horo): We don't need to have this method in //chrome. Move it to
   // //content while mojoifing network_hints. (crbug.com/610750)
-  // TODO(horo): Implement this.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  service_worker_context_->StartServiceWorkerForNavigationHint(
+      url, type, render_process_id_,
+      base::Bind(&DidStartServiceWorkerForNavigationHint));
 }
 
 void ChromeRenderMessageFilter::OnUpdatedCacheStats(
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 7b2f294..2ed27b0c 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -24,6 +24,10 @@
 class Predictor;
 }
 
+namespace content {
+class ServiceWorkerContext;
+}
+
 namespace content_settings {
 class CookieSettings;
 }
@@ -40,7 +44,10 @@
 // process on the IPC thread.
 class ChromeRenderMessageFilter : public content::BrowserMessageFilter {
  public:
-  ChromeRenderMessageFilter(int render_process_id, Profile* profile);
+  ChromeRenderMessageFilter(
+      int render_process_id,
+      Profile* profile,
+      content::ServiceWorkerContext* service_worker_context);
 
   // content::BrowserMessageFilter methods:
   bool OnMessageReceived(const IPC::Message& message) override;
@@ -137,6 +144,9 @@
   // Used to look up permissions at database creation time.
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
 
+  // Used to start Service Workers for navigation hints.
+  content::ServiceWorkerContext* service_worker_context_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeRenderMessageFilter);
 };
 
diff --git a/content/browser/DEPS b/content/browser/DEPS
index e181fae..e572690 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -58,6 +58,7 @@
   "+third_party/WebKit/public/platform/WebGamepad.h",
   "+third_party/WebKit/public/platform/WebGamepads.h",
   "+third_party/WebKit/public/platform/WebInsecureRequestPolicy.h",
+  "+third_party/WebKit/public/platform/WebNavigationHintType.h",
   "+third_party/WebKit/public/platform/WebReferrerPolicy.h",
   "+third_party/WebKit/public/platform/WebScreenInfo.h",
   "+third_party/WebKit/public/platform/WebString.h",
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index bb5d2de..230f143 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -20,6 +20,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_observer.h"
 #include "content/browser/service_worker/service_worker_process_manager.h"
@@ -29,9 +30,11 @@
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
 #include "net/base/url_util.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/special_storage_policy.h"
+#include "third_party/WebKit/public/platform/WebNavigationHintType.h"
 
 namespace content {
 
@@ -81,6 +84,21 @@
   registration->ActivateWaitingVersionWhenReady();
 }
 
+ServiceWorkerMetrics::EventType GetNavigationHintEventType(
+    blink::WebNavigationHintType type) {
+  switch (type) {
+    case blink::WebNavigationHintType::LinkMouseDown:
+      return ServiceWorkerMetrics::EventType::NAVIGATION_HINT_LINK_MOUSE_DOWN;
+    case blink::WebNavigationHintType::LinkTapUnconfirmed:
+      return ServiceWorkerMetrics::EventType::
+          NAVIGATION_HINT_LINK_TAP_UNCONFIRMED;
+    case blink::WebNavigationHintType::LinkTapDown:
+      return ServiceWorkerMetrics::EventType::NAVIGATION_HINT_LINK_TAP_DOWN;
+  }
+  NOTREACHED() << "Unexpected navigation hint" << static_cast<int>(type);
+  return ServiceWorkerMetrics::EventType::UNKNOWN;
+}
+
 }  // namespace
 
 void ServiceWorkerContext::AddExcludedHeadersForFetchEvent(
@@ -282,6 +300,84 @@
                  this));
 }
 
+void ServiceWorkerContextWrapper::StartServiceWorkerForNavigationHint(
+    const GURL& document_url,
+    blink::WebNavigationHintType type,
+    int render_process_id,
+    const ResultCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
+  if (!host ||
+      !RenderProcessHostImpl::IsSuitableHost(host, host->GetBrowserContext(),
+                                             document_url)) {
+    callback.Run(false);
+    return;
+  }
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(
+          &ServiceWorkerContextWrapper::DidCheckRenderProcessForNavigationHint,
+          this, document_url, type, render_process_id, callback));
+}
+
+void ServiceWorkerContextWrapper::DidCheckRenderProcessForNavigationHint(
+    const GURL& document_url,
+    blink::WebNavigationHintType type,
+    int render_process_id,
+    const ResultCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!context_core_) {
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                            base::Bind(callback, false));
+    return;
+  }
+  FindReadyRegistrationForDocument(
+      document_url,
+      base::Bind(
+          &ServiceWorkerContextWrapper::DidFindRegistrationForNavigationHint,
+          this, type, render_process_id, callback));
+}
+
+void ServiceWorkerContextWrapper::DidFindRegistrationForNavigationHint(
+    blink::WebNavigationHintType type,
+    int render_process_id,
+    const ResultCallback& callback,
+    ServiceWorkerStatusCode status,
+    scoped_refptr<ServiceWorkerRegistration> registration) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (status != SERVICE_WORKER_OK || !registration->active_version()) {
+    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                            base::Bind(callback, false));
+    return;
+  }
+
+  // Add the process reference of |render_process_id| not to launch a new
+  // renderer process for the service worker.
+  context_core_->process_manager()->AddProcessReferenceToPattern(
+      registration->pattern(), render_process_id);
+
+  registration->active_version()->StartWorker(
+      GetNavigationHintEventType(type),
+      base::Bind(
+          &ServiceWorkerContextWrapper::DidStartServiceWorkerForNavigationHint,
+          this, registration->pattern(), render_process_id, callback));
+}
+
+void ServiceWorkerContextWrapper::DidStartServiceWorkerForNavigationHint(
+    const GURL& pattern,
+    int render_process_id,
+    const ResultCallback& callback,
+    ServiceWorkerStatusCode code) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Remove the process reference added in DidFindRegistrationForNavigationHint.
+  context_core_->process_manager()->RemoveProcessReferenceFromPattern(
+      pattern, render_process_id);
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                          base::Bind(callback, code == SERVICE_WORKER_OK));
+}
+
 void ServiceWorkerContextWrapper::StartServiceWorker(
     const GURL& pattern,
     const StatusCallback& callback) {
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index ab4c900..415f025 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -25,6 +25,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace blink {
+enum class WebNavigationHintType;
+}
+
 namespace storage {
 class QuotaManagerProxy;
 class SpecialStoragePolicy;
@@ -110,6 +114,11 @@
       const CheckHasServiceWorkerCallback& callback) override;
   void StopAllServiceWorkersForOrigin(const GURL& origin) override;
   void ClearAllServiceWorkersForTest(const base::Closure& callback) override;
+  void StartServiceWorkerForNavigationHint(
+      const GURL& document_url,
+      blink::WebNavigationHintType type,
+      int render_process_id,
+      const ResultCallback& callback) override;
 
   // These methods must only be called from the IO thread.
   ServiceWorkerRegistration* GetLiveRegistration(int64_t registration_id);
@@ -231,6 +240,23 @@
       ServiceWorkerStatusCode status,
       scoped_refptr<content::ServiceWorkerRegistration> registration);
 
+  void DidCheckRenderProcessForNavigationHint(const GURL& document_url,
+                                              blink::WebNavigationHintType type,
+                                              int render_process_id,
+                                              const ResultCallback& callback);
+
+  void DidFindRegistrationForNavigationHint(
+      blink::WebNavigationHintType type,
+      int render_process_id,
+      const ResultCallback& callback,
+      ServiceWorkerStatusCode status,
+      scoped_refptr<ServiceWorkerRegistration> registration);
+
+  void DidStartServiceWorkerForNavigationHint(const GURL& pattern,
+                                              int render_process_id,
+                                              const ResultCallback& callback,
+                                              ServiceWorkerStatusCode code);
+
   // The core context is only for use on the IO thread.
   // Can be null before/during init, during/after shutdown, and after
   // DeleteAndStartOver fails.
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index 0758556..0c1cfca0 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -71,6 +71,12 @@
       return "_FETCH_WAITUNTIL";
     case ServiceWorkerMetrics::EventType::FOREIGN_FETCH_WAITUNTIL:
       return "_FOREIGN_FETCH_WAITUNTIL";
+    case ServiceWorkerMetrics::EventType::NAVIGATION_HINT_LINK_MOUSE_DOWN:
+      return "_NAVIGATION_HINT_LINK_MOUSE_DOWN";
+    case ServiceWorkerMetrics::EventType::NAVIGATION_HINT_LINK_TAP_UNCONFIRMED:
+      return "_NAVIGATION_HINT_LINK_TAP_UNCONFIRMED";
+    case ServiceWorkerMetrics::EventType::NAVIGATION_HINT_LINK_TAP_DOWN:
+      return "_NAVIGATION_HINT_LINK_TAP_DOWN";
     case ServiceWorkerMetrics::EventType::NUM_TYPES:
       NOTREACHED() << static_cast<int>(event_type);
   }
@@ -194,6 +200,12 @@
       return "Fetch WaitUntil";
     case EventType::FOREIGN_FETCH_WAITUNTIL:
       return "Foreign Fetch WaitUntil";
+    case EventType::NAVIGATION_HINT_LINK_MOUSE_DOWN:
+      return "Navigation Hint Link Mouse Down";
+    case EventType::NAVIGATION_HINT_LINK_TAP_UNCONFIRMED:
+      return "Navigation Hint Link Tap Unconfirmed";
+    case EventType::NAVIGATION_HINT_LINK_TAP_DOWN:
+      return "Navigation Hint Link Tap Down";
     case EventType::NUM_TYPES:
       break;
   }
@@ -461,6 +473,10 @@
       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ExtendableMessageEvent.Time",
                                  time);
       break;
+    // Those navigation hints should not be sent as request events.
+    case EventType::NAVIGATION_HINT_LINK_MOUSE_DOWN:
+    case EventType::NAVIGATION_HINT_LINK_TAP_UNCONFIRMED:
+    case EventType::NAVIGATION_HINT_LINK_TAP_DOWN:
 
     case EventType::UNKNOWN:
     case EventType::NUM_TYPES:
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index d4cc56e..1506fcb 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -106,6 +106,9 @@
     FOREIGN_FETCH = 15,
     FETCH_WAITUNTIL = 16,
     FOREIGN_FETCH_WAITUNTIL = 17,
+    NAVIGATION_HINT_LINK_MOUSE_DOWN = 18,
+    NAVIGATION_HINT_LINK_TAP_UNCONFIRMED = 19,
+    NAVIGATION_HINT_LINK_TAP_DOWN = 20,
     // Add new events to record here.
     NUM_TYPES
   };
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index 7265015..47f355c 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -12,6 +12,10 @@
 #include "content/public/browser/service_worker_usage_info.h"
 #include "url/gurl.h"
 
+namespace blink {
+enum class WebNavigationHintType;
+}
+
 namespace content {
 
 // Represents the per-StoragePartition ServiceWorker data.
@@ -105,6 +109,18 @@
   // be called on the UI thread.
   virtual void ClearAllServiceWorkersForTest(const base::Closure& callback) = 0;
 
+  // Starts a Service Worker for |document_url| for a navigation hint in the
+  // specified render process |render_process_id|. Must be called from the UI
+  // thread. The |callback| will always be called on the UI thread.
+  // This method can fail if:
+  //  * No Service Worker was registered for |document_url|.
+  //  * The specified render process is not suitable for loading |document_url|.
+  virtual void StartServiceWorkerForNavigationHint(
+      const GURL& document_url,
+      blink::WebNavigationHintType type,
+      int render_process_id,
+      const ResultCallback& callback) = 0;
+
  protected:
   ServiceWorkerContext() {}
   virtual ~ServiceWorkerContext() {}
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 93ddce9..5203279b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -100799,6 +100799,12 @@
   <suffix name="FETCH_SUB_RESOURCE" label="FETCH_SUB_RESOURCE"/>
   <suffix name="UNKNOWN" label="UNKNOWN"/>
   <suffix name="FOREIGN_FETCH" label="FOREIGN_FETCH"/>
+  <suffix name="NAVIGATION_HINT_LINK_MOUSE_DOWN"
+      label="NAVIGATION_HINT_LINK_MOUSE_DOWN"/>
+  <suffix name="NAVIGATION_HINT_LINK_TAP_UNCONFIRMED"
+      label="NAVIGATION_HINT_LINK_TAP_UNCONFIRMED"/>
+  <suffix name="NAVIGATION_HINT_LINK_TAP_DOWN"
+      label="NAVIGATION_HINT_LINK_TAP_DOWN"/>
   <affected-histogram name="ServiceWorker.StartWorker.StatusByPurpose"/>
   <affected-histogram name="ServiceWorker.StartWorker.Time_DuringStartup"/>
   <affected-histogram name="ServiceWorker.StartWorker.Time_ExistingProcess"/>