Add runtime setting to force passive event listeners.

Add the ability to set the default value for the 'passive' field
in AddEventListenerOptions.

The value can take on 4 values; 'false', 'true', 'documentonlytrue',
'forcetrue'. It can be set from the command line or by adjusting
the chrome://flags.

BUG=599611

Review-Url: https://codereview.chromium.org/1965493002
Cr-Commit-Position: refs/heads/master@{#393565}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f421c6e..4346326b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5628,6 +5628,21 @@
       <message name="IDS_FLAGS_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK_DESCRIPTION" desc="Description for the flag for gesture requiment for media playback">
         User gesture requirement for playing media elements. Disabling this will allow autoplay to work.
       </message>
+      <message name="IDS_FLAGS_PASSIVE_EVENT_LISTENER_DOCUMENT_TRUE" desc="Choice for passive listeners to default to true on document level nodes.">
+        Document Level True (when unspecified)
+      </message>
+      <message name="IDS_FLAGS_PASSIVE_EVENT_LISTENER_TRUE" desc="Choice for passive listeners to default to true on all nodes not explicitly set.">
+        True (when unspecified)
+      </message>
+      <message name="IDS_FLAGS_PASSIVE_EVENT_LISTENER_FORCE_ALL_TRUE" desc="Choice for passive listeners to default to true on all nodes.">
+        Force All True
+      </message>
+     <message name="IDS_FLAGS_PASSIVE_EVENT_LISTENER_DEFAULT_NAME" desc="Name for the flag to adjust the default behaviour for passive event listeners.">
+        Passive Event Listener Override
+      </message>
+      <message name="IDS_FLAGS_PASSIVE_EVENT_LISTENER_DEFAULT_DESCRIPTION" desc="Description for the flag to adjust default behaviour for passive event listeners.">
+        Forces touchstart, touchmove, mousewheel and wheel event listeners (which haven't requested otherwise) to be treated as passive. This will break touch/wheel behavior on some websites but is useful for demonstrating the potential performance benefits of adopting passive event listeners.
+      </message>
       <if expr="is_android">
           <message name="IDS_FLAGS_MEDIA_STYLE_NOTIFICATION_NAME" desc="Title for the flag for using Android MediaStyle notification">
               Android MediaStyle notification
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0e2e88d3..539da28 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -185,6 +185,16 @@
 };
 #endif
 
+const FeatureEntry::Choice kPassiveListenersChoices[] = {
+    {IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""},
+    {IDS_FLAGS_PASSIVE_EVENT_LISTENER_DOCUMENT_TRUE,
+     switches::kPassiveListenersDefault, "documentonlytrue"},
+    {IDS_FLAGS_PASSIVE_EVENT_LISTENER_TRUE, switches::kPassiveListenersDefault,
+     "true"},
+    {IDS_FLAGS_PASSIVE_EVENT_LISTENER_FORCE_ALL_TRUE,
+     switches::kPassiveListenersDefault, "forcealltrue"},
+};
+
 const FeatureEntry::Choice kMarkNonSecureAsChoices[] = {
     {IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""},
     {IDS_MARK_NON_SECURE_AS_NEUTRAL, security_state::switches::kMarkNonSecureAs,
@@ -1866,6 +1876,11 @@
      IDS_FLAGS_EXPERIMENTAL_POINTER_EVENT_NAME,
      IDS_FLAGS_EXPERIMENTAL_POINTER_EVENT_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(features::kPointerEvents)},
+    {"passive-listener-default",  // FLAGS:RECORD_UMA
+     IDS_FLAGS_PASSIVE_EVENT_LISTENER_DEFAULT_NAME,
+     IDS_FLAGS_PASSIVE_EVENT_LISTENER_DEFAULT_DESCRIPTION, kOsAll,
+     MULTI_VALUE_TYPE(kPassiveListenersChoices)},
+
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
     // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 18c53677..557b67d3 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1437,6 +1437,7 @@
     switches::kNoReferrers,
     switches::kNoSandbox,
     switches::kOverridePluginPowerSaverForTesting,
+    switches::kPassiveListenersDefault,
     switches::kPpapiInProcess,
     switches::kProfilerTiming,
     switches::kReducedReferrerGranularity,
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index f95921d..244e371 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -675,6 +675,13 @@
 const char kOverscrollHistoryNavigation[] =
     "overscroll-history-navigation";
 
+// Override the default value for the 'passive' field in javascript
+// addEventListener calls. Values are defined as:
+//  'documentonlytrue' to set the default be true only for document level nodes.
+//  'true' to set the default to be true on all nodes (when not specified).
+//  'forcealltrue' to force the value on all nodes.
+const char kPassiveListenersDefault[] = "passive-listeners-default";
+
 // Argument to the process type that indicates a PPAPI broker process type.
 const char kPpapiBrokerProcess[]            = "ppapi-broker";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 2ebf9c8..be5cc9e 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -195,6 +195,7 @@
 CONTENT_EXPORT extern const char kNumRasterThreads[];
 CONTENT_EXPORT extern const char kOverridePluginPowerSaverForTesting[];
 CONTENT_EXPORT extern const char kOverscrollHistoryNavigation[];
+CONTENT_EXPORT extern const char kPassiveListenersDefault[];
 CONTENT_EXPORT extern const char kPpapiBrokerProcess[];
 CONTENT_EXPORT extern const char kPpapiFlashArgs[];
 CONTENT_EXPORT extern const char kPpapiInProcess[];
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 3990452..df3decf8 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -723,6 +723,20 @@
     selection_strategy = WebSettings::SelectionStrategyType::Direction;
   webview()->settings()->setSelectionStrategy(selection_strategy);
 
+  std::string passiveListenersDefault =
+      command_line.GetSwitchValueASCII(switches::kPassiveListenersDefault);
+  if (!passiveListenersDefault.empty()) {
+    WebSettings::PassiveEventListenerDefault passiveDefault =
+        WebSettings::PassiveEventListenerDefault::False;
+    if (passiveListenersDefault == "documentonlytrue")
+      passiveDefault = WebSettings::PassiveEventListenerDefault::DocumentTrue;
+    else if (passiveListenersDefault == "true")
+      passiveDefault = WebSettings::PassiveEventListenerDefault::True;
+    else if (passiveListenersDefault == "forcealltrue")
+      passiveDefault = WebSettings::PassiveEventListenerDefault::ForceAllTrue;
+    webview()->settings()->setPassiveEventListenerDefault(passiveDefault);
+  }
+
   ApplyBlinkSettings(command_line, webview()->settings());
 
   if (params.main_frame_routing_id != MSG_ROUTING_NONE) {
diff --git a/third_party/WebKit/Source/core/events/AddEventListenerOptionsDefaults.h b/third_party/WebKit/Source/core/events/AddEventListenerOptionsDefaults.h
new file mode 100644
index 0000000..8887c1e
--- /dev/null
+++ b/third_party/WebKit/Source/core/events/AddEventListenerOptionsDefaults.h
@@ -0,0 +1,25 @@
+// Copyright 2016 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 AddEventListenerOptionsDefaults_h
+#define AddEventListenerOptionsDefaults_h
+
+namespace blink {
+
+// Defines the default for 'passive' field used in the AddEventListenerOptions interface
+// when javascript calls addEventListener.
+// |False| is the default specified in
+// https://dom.spec.whatwg.org/#dictdef-addeventlisteneroptions. However
+// specifying a different default value is useful in demonstrating the
+// power of passive event listeners.
+enum class PassiveListenerDefault {
+    False, // Default of false.
+    True, // Default of true.
+    DocumentTrue, // Default of true for document level elements, false otherwise.
+    ForceAllTrue // Force all values to be true even when specified.
+};
+
+} // namespace blink
+
+#endif // AddEventListenerOptionsDefaults_h
diff --git a/third_party/WebKit/Source/core/events/EventTarget.cpp b/third_party/WebKit/Source/core/events/EventTarget.cpp
index c8caf6c..7781288b 100644
--- a/third_party/WebKit/Source/core/events/EventTarget.cpp
+++ b/third_party/WebKit/Source/core/events/EventTarget.cpp
@@ -54,21 +54,14 @@
 namespace blink {
 namespace {
 
-void setDefaultEventListenerOptionsLegacy(EventListenerOptions& options, bool useCapture)
+Settings* windowSettings(LocalDOMWindow* executingWindow)
 {
-    options.setCapture(useCapture);
-}
-
-void setDefaultAddEventListenerOptionsLegacy(AddEventListenerOptions& options, bool useCapture)
-{
-    setDefaultEventListenerOptionsLegacy(options, useCapture);
-    options.setPassive(false);
-}
-
-void setDefaultAddEventListenerOptions(AddEventListenerOptions& options)
-{
-    if (!options.hasPassive())
-        options.setPassive(false);
+    if (executingWindow) {
+        if (LocalFrame* frame = executingWindow->frame()) {
+            return frame->settings();
+        }
+    }
+    return nullptr;
 }
 
 double blockedEventsWarningThreshold(const ExecutionContext* context, const Event* event)
@@ -189,10 +182,44 @@
     return nullptr;
 }
 
+void EventTarget::setDefaultAddEventListenerOptions(AddEventListenerOptions& options)
+{
+    if (Settings* settings = windowSettings(executingWindow())) {
+        switch (settings->passiveListenerDefault()) {
+        case PassiveListenerDefault::False:
+            if (!options.hasPassive())
+                options.setPassive(false);
+            break;
+        case PassiveListenerDefault::True:
+            if (!options.hasPassive())
+                options.setPassive(true);
+            break;
+        case PassiveListenerDefault::ForceAllTrue:
+            options.setPassive(true);
+            break;
+        case PassiveListenerDefault::DocumentTrue:
+            if (!options.hasPassive()) {
+                if (Node* node = toNode()) {
+                    if (node->isDocumentNode() || node->document().documentElement() == node || node->document().body() == node) {
+                        options.setPassive(true);
+                    }
+                } else if (toLocalDOMWindow()) {
+                    options.setPassive(true);
+                }
+            }
+            break;
+        }
+    } else {
+        if (!options.hasPassive())
+            options.setPassive(false);
+    }
+}
+
 bool EventTarget::addEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
 {
     AddEventListenerOptions options;
-    setDefaultAddEventListenerOptionsLegacy(options, useCapture);
+    options.setCapture(useCapture);
+    setDefaultAddEventListenerOptions(options);
     return addEventListenerInternal(eventType, listener, options);
 }
 
@@ -245,7 +272,7 @@
 bool EventTarget::removeEventListener(const AtomicString& eventType, const EventListener* listener, bool useCapture)
 {
     EventListenerOptions options;
-    setDefaultEventListenerOptionsLegacy(options, useCapture);
+    options.setCapture(useCapture);
     return removeEventListenerInternal(eventType, listener, options);
 }
 
diff --git a/third_party/WebKit/Source/core/events/EventTarget.h b/third_party/WebKit/Source/core/events/EventTarget.h
index eba60f8..cac244aa 100644
--- a/third_party/WebKit/Source/core/events/EventTarget.h
+++ b/third_party/WebKit/Source/core/events/EventTarget.h
@@ -171,6 +171,7 @@
 
 private:
     LocalDOMWindow* executingWindow();
+    void setDefaultAddEventListenerOptions(AddEventListenerOptions&);
     void fireEventListeners(Event*, EventTargetData*, EventListenerVector&);
     void countLegacyEvents(const AtomicString& legacyTypeName, EventListenerVector*, EventListenerVector*);
 
diff --git a/third_party/WebKit/Source/core/frame/Settings.h b/third_party/WebKit/Source/core/frame/Settings.h
index 91dafef..6ab18c6 100644
--- a/third_party/WebKit/Source/core/frame/Settings.h
+++ b/third_party/WebKit/Source/core/frame/Settings.h
@@ -33,6 +33,7 @@
 #include "core/SettingsMacros.h"
 #include "core/editing/EditingBehaviorTypes.h"
 #include "core/editing/SelectionStrategy.h"
+#include "core/events/AddEventListenerOptionsDefaults.h"
 #include "core/frame/SettingsDelegate.h"
 #include "core/html/track/TextTrackKindUserPreference.h"
 #include "platform/Timer.h"
diff --git a/third_party/WebKit/Source/core/frame/Settings.in b/third_party/WebKit/Source/core/frame/Settings.in
index 09969f1..a21b4ff 100644
--- a/third_party/WebKit/Source/core/frame/Settings.in
+++ b/third_party/WebKit/Source/core/frame/Settings.in
@@ -408,3 +408,9 @@
 # event listener that caused an event to be dispatched to main thread and
 # delayed by more than the specified time (in seconds).
 blockedMainThreadEventsWarningThreshold type=double, initial=0
+
+# Ability to override the default 'passive' value in AddEventListenerOptions. This
+# is useful to demonstrate the power of passive event listeners. This can be removed
+# when there is greater adoption, interventions to force it on and associated devtools
+# to enable it have been shipped.
+passiveListenerDefault type=PassiveListenerDefault, initial=PassiveListenerDefault::False
diff --git a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
index 7479b00..d9212b9b 100644
--- a/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
+++ b/third_party/WebKit/Source/web/AssertMatchingEnums.cpp
@@ -535,6 +535,11 @@
 STATIC_ASSERT_ENUM(WebSettings::EditingBehaviorUnix, EditingUnixBehavior);
 STATIC_ASSERT_ENUM(WebSettings::EditingBehaviorAndroid, EditingAndroidBehavior);
 
+STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::False, PassiveListenerDefault::False);
+STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::True, PassiveListenerDefault::True);
+STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::DocumentTrue, PassiveListenerDefault::DocumentTrue);
+STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::ForceAllTrue, PassiveListenerDefault::ForceAllTrue);
+
 STATIC_ASSERT_ENUM(WebIDBDatabaseExceptionUnknownError, UnknownError);
 STATIC_ASSERT_ENUM(WebIDBDatabaseExceptionConstraintError, ConstraintError);
 STATIC_ASSERT_ENUM(WebIDBDatabaseExceptionDataError, DataError);
diff --git a/third_party/WebKit/Source/web/WebSettingsImpl.cpp b/third_party/WebKit/Source/web/WebSettingsImpl.cpp
index da9e03d..54d06cb 100644
--- a/third_party/WebKit/Source/web/WebSettingsImpl.cpp
+++ b/third_party/WebKit/Source/web/WebSettingsImpl.cpp
@@ -627,6 +627,11 @@
     m_settings->setStrictlyBlockBlockableMixedContent(enabled);
 }
 
+void WebSettingsImpl::setPassiveEventListenerDefault(PassiveEventListenerDefault defaultValue)
+{
+    m_settings->setPassiveListenerDefault(static_cast<PassiveListenerDefault>(defaultValue));
+}
+
 void WebSettingsImpl::setPasswordEchoEnabled(bool flag)
 {
     m_settings->setPasswordEchoEnabled(flag);
diff --git a/third_party/WebKit/Source/web/WebSettingsImpl.h b/third_party/WebKit/Source/web/WebSettingsImpl.h
index f2190939..3ba0a70 100644
--- a/third_party/WebKit/Source/web/WebSettingsImpl.h
+++ b/third_party/WebKit/Source/web/WebSettingsImpl.h
@@ -123,6 +123,7 @@
     void setMinimumLogicalFontSize(int) override;
     void setMockScrollbarsEnabled(bool) override;
     void setOfflineWebApplicationCacheEnabled(bool) override;
+    void setPassiveEventListenerDefault(PassiveEventListenerDefault) override;
     void setPasswordEchoDurationInSeconds(double) override;
     void setPasswordEchoEnabled(bool) override;
     void setPerTilePaintingEnabled(bool) override;
diff --git a/third_party/WebKit/public/web/WebSettings.h b/third_party/WebKit/public/web/WebSettings.h
index f215571..f905fd0 100644
--- a/third_party/WebKit/public/web/WebSettings.h
+++ b/third_party/WebKit/public/web/WebSettings.h
@@ -95,6 +95,15 @@
         Subtitles
     };
 
+    // Defines the default for 'passive' field used in the AddEventListenerOptions interface
+    // when javascript calls addEventListener.
+    enum class PassiveEventListenerDefault {
+        False, // Default of false.
+        True, // Default of true.
+        DocumentTrue, // Default of true for document level elements, false otherwise.
+        ForceAllTrue // Force all values to be true even when specified.
+    };
+
     // Sets value of a setting by its string identifier from Settings.in and
     // string representation of value. An enum's string representation is the
     // string representation of the integer value of the enum.
@@ -184,6 +193,7 @@
     virtual void setMinimumLogicalFontSize(int) = 0;
     virtual void setMockScrollbarsEnabled(bool) = 0;
     virtual void setOfflineWebApplicationCacheEnabled(bool) = 0;
+    virtual void setPassiveEventListenerDefault(PassiveEventListenerDefault) = 0;
     virtual void setPasswordEchoDurationInSeconds(double) = 0;
     virtual void setPasswordEchoEnabled(bool) = 0;
     virtual void setPerTilePaintingEnabled(bool) = 0;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 989a968d..f99682b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -77488,6 +77488,7 @@
   <int value="157217034" label="enable-tab-for-desktop-share"/>
   <int value="178337215" label="enable-md-history"/>
   <int value="180074362" label="memory-pressure-thresholds"/>
+  <int value="194895489" label="passive-listeners-default"/>
   <int value="203776499" label="enable-virtual-keyboard-overscroll"/>
   <int value="244697230" label="enable-theme-color-in-tabbed-mode"/>
   <int value="266702296" label="disable-plugin-power-saver"/>