Speculatively launch Service Workers on mouse/touch events. [5/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
     https://codereview.chromium.org/2052613003/
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
     This CL.

This CL introduces a new feature named "SpeculativeLaunchServiceWorker".
When the feature is enabled, Service Workers are speculatively started on the
user's mousedown/touch events.
Demo:
https://drive.google.com/file/d/0B6skYAFVnosEMWdzRm4yMEtIMkk/view?usp=sharing

BUG=616502

Review-Url: https://codereview.chromium.org/2053573002
Cr-Commit-Position: refs/heads/master@{#410295}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7129b381..61bbab3 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6311,6 +6311,12 @@
       <message name="IDS_FLAGS_V8_CACHE_STRATEGIES_FOR_CACHE_STORAGE_AGGRESSIVE" desc="The V8 cache for CacheStorage supports three strategies: disabled; caching same as if the script is in HTTPCache; force caching on the first load. This option describes the 3rd of these, force caching on the first load.">
         Aggressive
       </message>
+      <message name="IDS_FLAGS_SPECULATIVE_LAUNCH_SERVICE_WORKER_NAME" desc="Name of the about::flags setting for speculative launch of service workers.">
+        Speculative launch of service workers.
+      </message>
+      <message name="IDS_FLAGS_SPECULATIVE_LAUNCH_SERVICE_WORKER_DESCRIPTION" desc="Description of the about::flags setting for speculative launch of service workers.">
+        Speculatively launch service workers using touch and mouse events.
+      </message>
 
       <!-- Data Reduction Proxy -->
       <message name="IDS_FLAGS_DATA_REDUCTION_PROXY_LO_FI_NAME" desc="An about::flags experiment title to enable/disable Data Saver Lo-Fi" translateable="false">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 74b692c..08e1cde0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1119,6 +1119,10 @@
      IDS_FLAGS_SUGGESTIONS_WITH_SUB_STRING_MATCH_DESCRIPTION, kOsAll,
      SINGLE_VALUE_TYPE(
          autofill::switches::kEnableSuggestionsWithSubstringMatch)},
+    {"enable-speculative-launch-service-worker",
+     IDS_FLAGS_SPECULATIVE_LAUNCH_SERVICE_WORKER_NAME,
+     IDS_FLAGS_SPECULATIVE_LAUNCH_SERVICE_WORKER_DESCRIPTION, kOsAll,
+     FEATURE_VALUE_TYPE(features::kSpeculativeLaunchServiceWorker)},
     {"enable-supervised-user-managed-bookmarks-folder",
      IDS_FLAGS_SUPERVISED_USER_MANAGED_BOOKMARKS_FOLDER_NAME,
      IDS_FLAGS_SUPERVISED_USER_MANAGED_BOOKMARKS_FOLDER_DESCRIPTION, kOsAll,
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index ac5f1c4d..834b3d97 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -26,6 +27,8 @@
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ppapi/shared_impl/ppapi_switches.h"
@@ -516,4 +519,86 @@
             ExecutePNACLUrlLoaderTest("OtherCORSCredentials"));
 }
 
+class ServiceWorkerSpeculativeLaunchTest : public ChromeServiceWorkerTest {
+ protected:
+  ServiceWorkerSpeculativeLaunchTest() {}
+  ~ServiceWorkerSpeculativeLaunchTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(
+        switches::kEnableFeatures,
+        features::kSpeculativeLaunchServiceWorker.name);
+  }
+
+  base::HistogramTester histogram_tester_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerSpeculativeLaunchTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerSpeculativeLaunchTest, MouseDown) {
+  WriteFile(
+      FILE_PATH_LITERAL("sw.js"),
+      "self.onfetch = function(e) {"
+      "  e.respondWith(new Response('<title>Done</title>',"
+      "                             {headers: {'Content-Type': 'text/html'}}));"
+      "};");
+  WriteFile(
+      FILE_PATH_LITERAL("test.html"),
+      "<script>"
+      "navigator.serviceWorker.register('./sw.js', {scope: './scope/'})"
+      "  .then(function(reg) {"
+      "      reg.addEventListener('updatefound', function() {"
+      "          var worker = reg.installing;"
+      "          worker.addEventListener('statechange', function() {"
+      "              if (worker.state == 'activated')"
+      "                document.title = 'READY';"
+      "            });"
+      "        });"
+      "    });"
+      "</script>"
+      "<body style='margin:0; padding:0;'>"
+      "<a href='./scope/' style='position:fixed; width:1px; height:1px;'></a>"
+      "</body>");
+
+  embedded_test_server()->ServeFilesFromDirectory(service_worker_dir_.path());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  content::ServiceWorkerContext* sw_context =
+      content::BrowserContext::GetDefaultStoragePartition(browser()->profile())
+          ->GetServiceWorkerContext();
+
+  const base::string16 expected_title1 = base::ASCIIToUTF16("READY");
+  content::TitleWatcher title_watcher1(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title1);
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL("/test.html"));
+  EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle());
+
+  histogram_tester_.ExpectBucketCount("ServiceWorker.StartNewWorker.Status",
+                                      0 /* SERVICE_WORKER_OK */, 1);
+
+  sw_context->StopAllServiceWorkersForOrigin(
+      embedded_test_server()->base_url());
+
+  const base::string16 expected_title2 = base::ASCIIToUTF16("Done");
+  content::TitleWatcher title_watcher2(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title2);
+
+  histogram_tester_.ExpectTotalCount(
+      "ServiceWorker.StartWorker.StatusByPurpose_NAVIGATION_HINT_LINK_MOUSE_"
+      "DOWN",
+      0);
+  content::SimulateMouseClickAt(
+      browser()->tab_strip_model()->GetActiveWebContents(), 0,
+      blink::WebMouseEvent::ButtonLeft, gfx::Point(0, 0));
+  EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
+
+  // The service worker must be started by a navigation hint.
+  histogram_tester_.ExpectBucketCount(
+      "ServiceWorker.StartWorker.StatusByPurpose_NAVIGATION_HINT_LINK_MOUSE_"
+      "DOWN",
+      0 /* SERVICE_WORKER_OK */, 1);
+}
+
 }  // namespace
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 6c23bd38..873f2c9d 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -261,6 +261,9 @@
       base::FeatureList::IsEnabled(features::kWebPayments));
 #endif
 
+  if (base::FeatureList::IsEnabled(features::kSpeculativeLaunchServiceWorker))
+    WebRuntimeFeatures::enableSpeculativeLaunchServiceWorker(true);
+
   // Enable explicitly enabled features, and then disable explicitly disabled
   // ones.
   if (command_line.HasSwitch(switches::kEnableBlinkFeatures)) {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 300b692..d2a1f78 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -112,6 +112,10 @@
 const base::Feature kScrollAnchoring{"ScrollAnchoring",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Speculatively launches Service Workers on mouse/touch events.
+const base::Feature kSpeculativeLaunchServiceWorker{
+    "SpeculativeLaunchServiceWorker", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables implementation of the Cache-Control: stale-while-revalidate directive
 // which permits servers to allow the use of stale resources while revalidation
 // proceeds in the background. See http://crbug.com/348877
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 7a1d388..6f87268 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -37,6 +37,7 @@
 CONTENT_EXPORT extern const base::Feature kPointerEvents;
 CONTENT_EXPORT extern const base::Feature kRenderingPipelineThrottling;
 CONTENT_EXPORT extern const base::Feature kScrollAnchoring;
+CONTENT_EXPORT extern const base::Feature kSpeculativeLaunchServiceWorker;
 CONTENT_EXPORT extern const base::Feature kStaleWhileRevalidate;
 CONTENT_EXPORT extern const base::Feature kTokenBinding;
 CONTENT_EXPORT extern const base::Feature kWeakMemoryCache;
diff --git a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
index f3d3de83..6e6be67 100644
--- a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
@@ -238,9 +238,8 @@
             return;
         }
 
-        // TODO(horo): Call NavigationHintSender::handleEvent() when
-        // SpeculativeLaunchServiceWorker feature is enabled.
-        // ensureNavigationHintSender()->handleEvent(event);
+        if (RuntimeEnabledFeatures::speculativeLaunchServiceWorkerEnabled())
+            ensureNavigationHintSender()->handleEvent(event);
 
         if (isLinkClick(event) && isLiveLink()) {
             handleClick(event);
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 583ded6..a40997d 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -206,6 +206,7 @@
 // be shipped would be enabled. Instead, remove the flag from the shipping
 // interface.
 ExperimentalStream status=experimental
+SpeculativeLaunchServiceWorker
 StorageEstimate status=experimental
 Suborigins status=test
 // Many websites disable mouse support when touch APIs are available.  We'd
diff --git a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
index 881e5b4c..6b3569d4 100644
--- a/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
+++ b/third_party/WebKit/Source/web/WebRuntimeFeatures.cpp
@@ -230,6 +230,11 @@
     RuntimeEnabledFeatures::setSlimmingPaintV2Enabled(enable);
 }
 
+void WebRuntimeFeatures::enableSpeculativeLaunchServiceWorker(bool enable)
+{
+    RuntimeEnabledFeatures::setSpeculativeLaunchServiceWorkerEnabled(enable);
+}
+
 void WebRuntimeFeatures::enableTouch(bool enable)
 {
     RuntimeEnabledFeatures::setTouchEnabled(enable);
diff --git a/third_party/WebKit/public/web/WebRuntimeFeatures.h b/third_party/WebKit/public/web/WebRuntimeFeatures.h
index 75fc545..2abe67c 100644
--- a/third_party/WebKit/public/web/WebRuntimeFeatures.h
+++ b/third_party/WebKit/public/web/WebRuntimeFeatures.h
@@ -103,6 +103,7 @@
     BLINK_EXPORT static void enableScrollAnchoring(bool);
     BLINK_EXPORT static void enableSharedWorker(bool);
     BLINK_EXPORT static void enableSlimmingPaintV2(bool);
+    BLINK_EXPORT static void enableSpeculativeLaunchServiceWorker(bool);
     BLINK_EXPORT static void enableTouch(bool);
     BLINK_EXPORT static void enableUnsafeES3APIs(bool);
     BLINK_EXPORT static void enableV8IdleTasks(bool);
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7dd4a384..29bd28575 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -82818,6 +82818,7 @@
   <int value="-667517406" label="overscroll-history-navigation"/>
   <int value="-661978438" label="enable-data-reduction-proxy-lo-fi"/>
   <int value="-660160292" label="enable-apps-show-on-first-paint"/>
+  <int value="-650504533" label="enable-speculative-launch-service-worker"/>
   <int value="-649956990" label="enable-harfbuzz-rendertext"/>
   <int value="-641719457" label="disable-compositor-touch-hit-testing"/>
   <int value="-631740127" label="inert-visual-viewport"/>