Add use counter when touch-action isn't used when preventDefault'd.

Keep track of the lack of usage of touch-action on pages.

BUG=639227
TBR=isherman@chromium.org

Review-Url: https://codereview.chromium.org/2475443004
Cr-Commit-Position: refs/heads/master@{#431618}
diff --git a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
index 28676fbd..8afeeeb 100644
--- a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
+++ b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.cpp
@@ -7,12 +7,13 @@
 namespace blink {
 
 AddEventListenerOptionsResolved::AddEventListenerOptionsResolved()
-    : m_passiveForcedForDocumentTarget(false) {}
+    : m_passiveForcedForDocumentTarget(false), m_passiveSpecified(false) {}
 
 AddEventListenerOptionsResolved::AddEventListenerOptionsResolved(
     const AddEventListenerOptions& options)
     : AddEventListenerOptions(options),
-      m_passiveForcedForDocumentTarget(false) {}
+      m_passiveForcedForDocumentTarget(false),
+      m_passiveSpecified(false) {}
 
 AddEventListenerOptionsResolved::~AddEventListenerOptionsResolved() {}
 
diff --git a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
index 26cbef2..fde5ae7 100644
--- a/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
+++ b/third_party/WebKit/Source/core/events/AddEventListenerOptionsResolved.h
@@ -29,10 +29,16 @@
     return m_passiveForcedForDocumentTarget;
   }
 
+  // Set whether passive was specified when the options were
+  // created by callee.
+  void setPassiveSpecified(bool specified) { m_passiveSpecified = specified; }
+  bool passiveSpecified() const { return m_passiveSpecified; }
+
   DECLARE_VIRTUAL_TRACE();
 
  private:
   bool m_passiveForcedForDocumentTarget;
+  bool m_passiveSpecified;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/Event.cpp b/third_party/WebKit/Source/core/events/Event.cpp
index 5adbdb9..68487d5 100644
--- a/third_party/WebKit/Source/core/events/Event.cpp
+++ b/third_party/WebKit/Source/core/events/Event.cpp
@@ -92,7 +92,7 @@
       m_wasInitialized(true),
       m_isTrusted(false),
       m_preventDefaultCalledOnUncancelableEvent(false),
-      m_handlingPassive(PassiveMode::NotPassive),
+      m_handlingPassive(PassiveMode::NotPassiveDefault),
       m_eventPhase(0),
       m_currentTarget(nullptr),
       m_platformTimeStamp(platformTimeStamp) {}
@@ -219,7 +219,8 @@
 }
 
 void Event::preventDefault() {
-  if (m_handlingPassive != PassiveMode::NotPassive) {
+  if (m_handlingPassive != PassiveMode::NotPassive &&
+      m_handlingPassive != PassiveMode::NotPassiveDefault) {
     m_preventDefaultCalledDuringPassive = true;
 
     const LocalDOMWindow* window =
@@ -228,9 +229,11 @@
       const char* devToolsMsg = nullptr;
       switch (m_handlingPassive) {
         case PassiveMode::NotPassive:
+        case PassiveMode::NotPassiveDefault:
           NOTREACHED();
           break;
         case PassiveMode::Passive:
+        case PassiveMode::PassiveDefault:
           devToolsMsg =
               "Unable to preventDefault inside passive event listener "
               "invocation.";
diff --git a/third_party/WebKit/Source/core/events/Event.h b/third_party/WebKit/Source/core/events/Event.h
index d1c8fdda..e2074dd 100644
--- a/third_party/WebKit/Source/core/events/Event.h
+++ b/third_party/WebKit/Source/core/events/Event.h
@@ -84,9 +84,17 @@
   };
 
   enum class PassiveMode {
+    // Not passive, default initialized.
+    NotPassiveDefault,
+    // Not passive, explicitly specified.
     NotPassive,
+    // Passive, explicitly specified.
     Passive,
+    // Passive, not explicitly specified and forced due to document level
+    // listener.
     PassiveForcedDocumentLevel,
+    // Passive, default initialized.
+    PassiveDefault,
   };
 
   static Event* create() { return new Event; }
diff --git a/third_party/WebKit/Source/core/events/EventTarget.cpp b/third_party/WebKit/Source/core/events/EventTarget.cpp
index 329f68d..13346d6 100644
--- a/third_party/WebKit/Source/core/events/EventTarget.cpp
+++ b/third_party/WebKit/Source/core/events/EventTarget.cpp
@@ -69,11 +69,16 @@
 
 Event::PassiveMode eventPassiveMode(
     const RegisteredEventListener& eventListener) {
-  if (!eventListener.passive())
-    return Event::PassiveMode::NotPassive;
+  if (!eventListener.passive()) {
+    if (eventListener.passiveSpecified())
+      return Event::PassiveMode::NotPassive;
+    return Event::PassiveMode::NotPassiveDefault;
+  }
   if (eventListener.passiveForcedForDocumentTarget())
     return Event::PassiveMode::PassiveForcedDocumentLevel;
-  return Event::PassiveMode::Passive;
+  if (eventListener.passiveSpecified())
+    return Event::PassiveMode::Passive;
+  return Event::PassiveMode::PassiveDefault;
 }
 
 Settings* windowSettings(LocalDOMWindow* executingWindow) {
@@ -192,6 +197,8 @@
 void EventTarget::setDefaultAddEventListenerOptions(
     const AtomicString& eventType,
     AddEventListenerOptionsResolved& options) {
+  options.setPassiveSpecified(options.hasPassive());
+
   if (!isScrollBlockingEvent(eventType)) {
     if (!options.hasPassive())
       options.setPassive(false);
diff --git a/third_party/WebKit/Source/core/events/RegisteredEventListener.h b/third_party/WebKit/Source/core/events/RegisteredEventListener.h
index 948f5850..0d73bf8 100644
--- a/third_party/WebKit/Source/core/events/RegisteredEventListener.h
+++ b/third_party/WebKit/Source/core/events/RegisteredEventListener.h
@@ -40,7 +40,8 @@
         m_passive(false),
         m_once(false),
         m_blockedEventWarningEmitted(false),
-        m_passiveForcedForDocumentTarget(false) {}
+        m_passiveForcedForDocumentTarget(false),
+        m_passiveSpecified(false) {}
 
   RegisteredEventListener(EventListener* listener,
                           const AddEventListenerOptionsResolved& options)
@@ -50,7 +51,8 @@
         m_once(options.once()),
         m_blockedEventWarningEmitted(false),
         m_passiveForcedForDocumentTarget(
-            options.passiveForcedForDocumentTarget()) {}
+            options.passiveForcedForDocumentTarget()),
+        m_passiveSpecified(options.passiveSpecified()) {}
 
   DEFINE_INLINE_TRACE() { visitor->trace(m_listener); }
 
@@ -60,6 +62,7 @@
     result.setPassive(m_passive);
     result.setPassiveForcedForDocumentTarget(m_passiveForcedForDocumentTarget);
     result.setOnce(m_once);
+    result.setPassiveSpecified(m_passiveSpecified);
     return result;
   }
 
@@ -81,6 +84,8 @@
     return m_passiveForcedForDocumentTarget;
   }
 
+  bool passiveSpecified() const { return m_passiveSpecified; }
+
   void setBlockedEventWarningEmitted() { m_blockedEventWarningEmitted = true; }
 
   bool matches(const EventListener* listener,
@@ -107,6 +112,7 @@
   unsigned m_once : 1;
   unsigned m_blockedEventWarningEmitted : 1;
   unsigned m_passiveForcedForDocumentTarget : 1;
+  unsigned m_passiveSpecified : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/events/TouchEvent.cpp b/third_party/WebKit/Source/core/events/TouchEvent.cpp
index 9821312..b4df5135 100644
--- a/third_party/WebKit/Source/core/events/TouchEvent.cpp
+++ b/third_party/WebKit/Source/core/events/TouchEvent.cpp
@@ -210,7 +210,8 @@
                        bool cancelable,
                        bool causesScrollingIfUncanceled,
                        bool firstTouchMoveOrStart,
-                       double platformTimeStamp)
+                       double platformTimeStamp,
+                       TouchAction currentTouchAction)
     // Pass a sourceCapabilities including the ability to fire touchevents when
     // creating this touchevent, which is always created from input device
     // capabilities from EventHandler.
@@ -228,7 +229,8 @@
       m_changedTouches(changedTouches),
       m_causesScrollingIfUncanceled(causesScrollingIfUncanceled),
       m_firstTouchMoveOrStart(firstTouchMoveOrStart),
-      m_defaultPreventedBeforeCurrentTarget(false) {}
+      m_defaultPreventedBeforeCurrentTarget(false),
+      m_currentTouchAction(currentTouchAction) {}
 
 TouchEvent::TouchEvent(const AtomicString& type,
                        const TouchEventInit& initializer)
@@ -265,6 +267,24 @@
                                    "because scrolling is in progress and "
                                    "cannot be interrupted."));
   }
+
+  if ((type() == EventTypeNames::touchstart ||
+       type() == EventTypeNames::touchmove) &&
+      view() && view()->frame() && m_currentTouchAction == TouchActionAuto) {
+    switch (handlingPassive()) {
+      case PassiveMode::NotPassiveDefault:
+        UseCounter::count(view()->frame(),
+                          UseCounter::TouchEventPreventedNoTouchAction);
+        break;
+      case PassiveMode::PassiveForcedDocumentLevel:
+        UseCounter::count(
+            view()->frame(),
+            UseCounter::TouchEventPreventedForcedDocumentPassiveNoTouchAction);
+        break;
+      default:
+        break;
+    }
+  }
 }
 
 void TouchEvent::doneDispatchingEventAtCurrentTarget() {
diff --git a/third_party/WebKit/Source/core/events/TouchEvent.h b/third_party/WebKit/Source/core/events/TouchEvent.h
index 530ae237..307663c0 100644
--- a/third_party/WebKit/Source/core/events/TouchEvent.h
+++ b/third_party/WebKit/Source/core/events/TouchEvent.h
@@ -53,10 +53,12 @@
                             bool cancelable,
                             bool causesScrollingIfUncanceled,
                             bool firstTouchMoveOrStart,
-                            double platformTimeStamp) {
+                            double platformTimeStamp,
+                            TouchAction currentTouchAction) {
     return new TouchEvent(touches, targetTouches, changedTouches, type, view,
                           modifiers, cancelable, causesScrollingIfUncanceled,
-                          firstTouchMoveOrStart, platformTimeStamp);
+                          firstTouchMoveOrStart, platformTimeStamp,
+                          currentTouchAction);
   }
 
   static TouchEvent* create(const AtomicString& type,
@@ -103,7 +105,8 @@
              bool cancelable,
              bool causesScrollingIfUncanceled,
              bool firstTouchMoveOrStart,
-             double platformTimeStamp);
+             double platformTimeStamp,
+             TouchAction currentTouchAction);
   TouchEvent(const AtomicString&, const TouchEventInit&);
 
   Member<TouchList> m_touches;
@@ -112,6 +115,10 @@
   bool m_causesScrollingIfUncanceled;
   bool m_firstTouchMoveOrStart;
   bool m_defaultPreventedBeforeCurrentTarget;
+
+  // The current effective touch action computed before each
+  // touchstart event is generated. It is used for UMA histograms.
+  TouchAction m_currentTouchAction;
 };
 
 class TouchEventDispatchMediator final : public EventDispatchMediator {
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 31dce80a..589fff3 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1387,6 +1387,8 @@
     HTMLMediaElementPreloadForcedMetadata = 1679,
     GenericSensorStart = 1680,
     GenericSensorStop = 1681,
+    TouchEventPreventedNoTouchAction = 1682,
+    TouchEventPreventedForcedDocumentPassiveNoTouchAction = 1683,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
index 69c4190..6880e9f 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
@@ -94,6 +94,7 @@
   m_regionForTouchID.clear();
   m_touchPressed = false;
   m_currentEvent = PlatformEvent::NoType;
+  m_currentTouchAction = TouchActionAuto;
 }
 
 DEFINE_TRACE(TouchEventManager) {
@@ -167,6 +168,7 @@
 
   if (allTouchesReleased) {
     m_touchSequenceDocument.clear();
+    m_currentTouchAction = TouchActionAuto;
   }
 
   WebInputEventResult eventResult = WebInputEventResult::NotHandled;
@@ -182,13 +184,14 @@
         static_cast<PlatformTouchPoint::TouchState>(state)));
     for (const auto& eventTarget : changedTouches[state].m_targets) {
       EventTarget* touchEventTarget = eventTarget;
-      TouchEvent* touchEvent = TouchEvent::create(
-          touches, touchesByTarget.get(touchEventTarget),
-          changedTouches[state].m_touches.get(), eventName,
-          touchEventTarget->toNode()->document().domWindow(),
-          event.getModifiers(), event.cancelable(),
-          event.causesScrollingIfUncanceled(),
-          event.touchStartOrFirstTouchMove(), event.timestamp());
+      TouchEvent* touchEvent =
+          TouchEvent::create(touches, touchesByTarget.get(touchEventTarget),
+                             changedTouches[state].m_touches.get(), eventName,
+                             touchEventTarget->toNode()->document().domWindow(),
+                             event.getModifiers(), event.cancelable(),
+                             event.causesScrollingIfUncanceled(),
+                             event.touchStartOrFirstTouchMove(),
+                             event.timestamp(), m_currentTouchAction);
 
       DispatchEventResult domDispatchResult =
           touchEventTarget->dispatchEvent(touchEvent);
@@ -333,8 +336,13 @@
 
       TouchAction effectiveTouchAction =
           TouchActionUtil::computeEffectiveTouchAction(*touchInfo.touchNode);
-      if (effectiveTouchAction != TouchActionAuto)
+      if (effectiveTouchAction != TouchActionAuto) {
         m_frame->page()->chromeClient().setTouchAction(effectiveTouchAction);
+
+        // Combine the current touch action sequence with the touch action
+        // for the current finger press.
+        m_currentTouchAction &= effectiveTouchAction;
+      }
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.h b/third_party/WebKit/Source/core/input/TouchEventManager.h
index e6c7d5e8..7504490 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.h
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.h
@@ -96,6 +96,10 @@
   bool m_touchPressed;
   // The touch event currently being handled or NoType if none.
   PlatformEvent::EventType m_currentEvent;
+
+  // The current touch action, computed on each touch start and is
+  // a union of all touches. Reset when all touches are released.
+  TouchAction m_currentTouchAction;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp b/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
index 85a2341..b2b2af8 100644
--- a/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebInputEventConversionTest.cpp
@@ -149,7 +149,7 @@
     touchList->append(touch0);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(1u, webTouchBuilder.touchesLength);
@@ -174,7 +174,7 @@
     touchList->append(touch0);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, true, false, true, 0);
+        PlatformEvent::NoModifiers, true, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     EXPECT_EQ(WebInputEvent::Blocking, webTouchBuilder.dispatchType);
@@ -187,10 +187,10 @@
     activeTouchList->append(touch0);
     activeTouchList->append(touch1);
     movedTouchList->append(touch0);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, movedTouchList,
-                           EventTypeNames::touchmove, domWindow,
-                           PlatformEvent::NoModifiers, false, false, true, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, movedTouchList,
+        EventTypeNames::touchmove, domWindow, PlatformEvent::NoModifiers, false,
+        false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -209,10 +209,10 @@
     activeTouchList->append(touch0);
     activeTouchList->append(touch1);
     movedTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, movedTouchList,
-                           EventTypeNames::touchmove, domWindow,
-                           PlatformEvent::NoModifiers, false, false, true, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, movedTouchList,
+        EventTypeNames::touchmove, domWindow, PlatformEvent::NoModifiers, false,
+        false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -230,10 +230,10 @@
     TouchList* releasedTouchList = TouchList::create();
     activeTouchList->append(touch0);
     releasedTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, releasedTouchList,
-                           EventTypeNames::touchend, domWindow,
-                           PlatformEvent::NoModifiers, false, false, false, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, releasedTouchList,
+        EventTypeNames::touchend, domWindow, PlatformEvent::NoModifiers, false,
+        false, false, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -251,10 +251,10 @@
     TouchList* cancelledTouchList = TouchList::create();
     cancelledTouchList->append(touch0);
     cancelledTouchList->append(touch1);
-    TouchEvent* touchEvent =
-        TouchEvent::create(activeTouchList, activeTouchList, cancelledTouchList,
-                           EventTypeNames::touchcancel, domWindow,
-                           PlatformEvent::NoModifiers, false, false, false, 0);
+    TouchEvent* touchEvent = TouchEvent::create(
+        activeTouchList, activeTouchList, cancelledTouchList,
+        EventTypeNames::touchcancel, domWindow, PlatformEvent::NoModifiers,
+        false, false, false, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(2u, webTouchBuilder.touchesLength);
@@ -281,7 +281,7 @@
     }
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchstart, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(static_cast<unsigned>(WebTouchEvent::kTouchesLengthCap),
@@ -559,7 +559,7 @@
     touchList->append(touch);
     TouchEvent* touchEvent = TouchEvent::create(
         touchList, touchList, touchList, EventTypeNames::touchmove, domWindow,
-        PlatformEvent::NoModifiers, false, false, true, 0);
+        PlatformEvent::NoModifiers, false, false, true, 0, TouchActionAuto);
 
     WebTouchEventBuilder webTouchBuilder(documentLayoutView, *touchEvent);
     ASSERT_EQ(1u, webTouchBuilder.touchesLength);
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ac05428..271542f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -85706,6 +85706,9 @@
   <int value="1679" label="HTMLMediaElementPreloadForcedMetadata"/>
   <int value="1680" label="GenericSensorStart"/>
   <int value="1681" label="GenericSensorStop"/>
+  <int value="1682" label="TouchEventPreventedNoTouchAction"/>
+  <int value="1683"
+      label="TouchEventPreventedForcedDocumentPassiveNoTouchAction"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">