Make first TouchStart and first TouchMove events on a flinging layer non-blocking.

Blocking touchstart and first touchMove handlers during fling can cause major hiccups during
fling boosting / flywheel fling.

We are doing a passive event listener during fling intervention experiment to see if we can
improve the fling performance by forcing touchstart and first touchmove to be passive when
there is an active fling animation.

BUG=595327

Review-Url: https://codereview.chromium.org/2233543002
Cr-Commit-Position: refs/heads/master@{#413189}
diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc
index 817815ac..701f4fde 100644
--- a/content/browser/renderer_host/input/touch_event_queue.cc
+++ b/content/browser/renderer_host/input/touch_event_queue.cc
@@ -792,10 +792,15 @@
   if (dispatching_touch_)
     return;
 
+  if (touch->event.type == WebInputEvent::TouchStart)
+    touch->event.touchStartOrFirstTouchMove = true;
+
   // For touchmove events, compare touch points position from current event
   // to last sent event and update touch points state.
   if (touch->event.type == WebInputEvent::TouchMove) {
     CHECK(last_sent_touchevent_);
+    if (last_sent_touchevent_->type == WebInputEvent::TouchStart)
+      touch->event.touchStartOrFirstTouchMove = true;
     for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
       const WebTouchPoint& last_touch_point =
           last_sent_touchevent_->touches[i];
diff --git a/content/browser/renderer_host/input/touch_event_queue_unittest.cc b/content/browser/renderer_host/input/touch_event_queue_unittest.cc
index d5dccf4..580e9a78b 100644
--- a/content/browser/renderer_host/input/touch_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/touch_event_queue_unittest.cc
@@ -2714,4 +2714,28 @@
   EXPECT_EQ(4U, GetAndResetSentEventCount());
 }
 
+// Tests that if touchStartOrFirstTouchMove is correctly set up for touch
+// events.
+TEST_F(TouchEventQueueTest, TouchStartOrFirstTouchMove) {
+  PressTouchPoint(1, 1);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+  EXPECT_TRUE(sent_event().touchStartOrFirstTouchMove);
+
+  MoveTouchPoint(0, 5, 5);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+  EXPECT_TRUE(sent_event().touchStartOrFirstTouchMove);
+
+  MoveTouchPoint(0, 15, 15);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type);
+  EXPECT_FALSE(sent_event().touchStartOrFirstTouchMove);
+
+  ReleaseTouchPoint(0);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type);
+  EXPECT_FALSE(sent_event().touchStartOrFirstTouchMove);
+}
+
 }  // namespace content
diff --git a/content/common/input/event_with_latency_info.cc b/content/common/input/event_with_latency_info.cc
index a2b306b..24ffd9ce 100644
--- a/content/common/input/event_with_latency_info.cc
+++ b/content/common/input/event_with_latency_info.cc
@@ -50,7 +50,7 @@
                 "Enum not ordered correctly");
   static_assert(
       WebInputEvent::DispatchType::ListenersNonBlockingPassive <
-          WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive,
+          WebInputEvent::DispatchType::ListenersForcedNonBlockingDueToFling,
       "Enum not ordered correctly");
   return static_cast<WebInputEvent::DispatchType>(
       std::min(static_cast<int>(type_1), static_cast<int>(type_2)));
diff --git a/content/common/input/event_with_latency_info_unittest.cc b/content/common/input/event_with_latency_info_unittest.cc
index 45907f7..23577f35 100644
--- a/content/common/input/event_with_latency_info_unittest.cc
+++ b/content/common/input/event_with_latency_info_unittest.cc
@@ -238,7 +238,7 @@
 
   touch0 = CreateTouch(WebInputEvent::TouchMove, 2);
   touch0.event.dispatchType =
-      WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive;
+      WebInputEvent::DispatchType::ListenersForcedNonBlockingDueToFling;
   touch1 = CreateTouch(WebInputEvent::TouchMove, 2);
   touch1.event.dispatchType =
       WebInputEvent::DispatchType::ListenersNonBlockingPassive;
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index 5221382..b3b4b89 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -43,6 +43,9 @@
     : routing_id_(routing_id),
       client_(client),
       is_flinging_(false),
+      last_touch_start_forced_nonblocking_due_to_fling_(false),
+      enable_fling_passive_listener_flag_(base::FeatureList::IsEnabled(
+          features::kPassiveEventListenersDueToFling)),
       main_task_runner_(main_task_runner) {}
 
 MainThreadEventQueue::~MainThreadEventQueue() {}
@@ -59,10 +62,6 @@
 
   bool non_blocking = original_dispatch_type == DISPATCH_TYPE_NON_BLOCKING ||
                       ack_result == INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING;
-
-  InputEventDispatchType dispatch_type =
-      non_blocking ? DISPATCH_TYPE_NON_BLOCKING : DISPATCH_TYPE_BLOCKING;
-
   bool is_wheel = event->type == blink::WebInputEvent::MouseWheel;
   bool is_touch = blink::WebInputEvent::isTouchEventType(event->type);
 
@@ -76,6 +75,21 @@
       touch_event->dispatchType =
           blink::WebInputEvent::ListenersNonBlockingPassive;
     }
+    if (touch_event->type == blink::WebInputEvent::TouchStart)
+      last_touch_start_forced_nonblocking_due_to_fling_ = false;
+
+    if (enable_fling_passive_listener_flag_ &&
+        touch_event->touchStartOrFirstTouchMove &&
+        touch_event->dispatchType == blink::WebInputEvent::Blocking) {
+      // If the touch start is forced to be passive due to fling, its following
+      // touch move should also be passive.
+      if (is_flinging_ || last_touch_start_forced_nonblocking_due_to_fling_) {
+        touch_event->dispatchType =
+            blink::WebInputEvent::ListenersForcedNonBlockingDueToFling;
+        non_blocking = true;
+        last_touch_start_forced_nonblocking_due_to_fling_ = true;
+      }
+    }
   }
   if (is_wheel && non_blocking) {
     // Adjust the |dispatchType| on the event since the compositor
@@ -84,6 +98,8 @@
         ->dispatchType = blink::WebInputEvent::ListenersNonBlockingPassive;
   }
 
+  InputEventDispatchType dispatch_type =
+      non_blocking ? DISPATCH_TYPE_NON_BLOCKING : DISPATCH_TYPE_BLOCKING;
   std::unique_ptr<EventWithDispatchType> event_with_dispatch_type(
       new EventWithDispatchType(std::move(event), latency, dispatch_type));
 
diff --git a/content/renderer/input/main_thread_event_queue.h b/content/renderer/input/main_thread_event_queue.h
index a7caf60..f848e15 100644
--- a/content/renderer/input/main_thread_event_queue.h
+++ b/content/renderer/input/main_thread_event_queue.h
@@ -6,11 +6,13 @@
 #define CONTENT_RENDERER_INPUT_MAIN_THREAD_EVENT_QUEUE_H_
 
 #include <deque>
+#include "base/feature_list.h"
 #include "content/common/content_export.h"
 #include "content/common/input/event_with_latency_info.h"
 #include "content/common/input/input_event_ack_state.h"
 #include "content/common/input/input_event_dispatch_type.h"
 #include "content/common/input/web_input_event_queue.h"
+#include "content/public/common/content_features.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "ui/events/blink/web_input_event_traits.h"
 #include "ui/events/latency_info.h"
@@ -131,6 +133,8 @@
   WebInputEventQueue<EventWithDispatchType> events_;
   std::unique_ptr<EventWithDispatchType> in_flight_event_;
   bool is_flinging_;
+  bool last_touch_start_forced_nonblocking_due_to_fling_;
+  bool enable_fling_passive_listener_flag_;
 
   base::Lock event_queue_lock_;
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
diff --git a/content/renderer/input/main_thread_event_queue_unittest.cc b/content/renderer/input/main_thread_event_queue_unittest.cc
index f2020e3..799325c 100644
--- a/content/renderer/input/main_thread_event_queue_unittest.cc
+++ b/content/renderer/input/main_thread_event_queue_unittest.cc
@@ -70,6 +70,18 @@
     return queue_->events_;
   }
 
+  void set_is_flinging(bool is_flinging) {
+    queue_->set_is_flinging(is_flinging);
+  }
+
+  bool last_touch_start_forced_nonblocking_due_to_fling() {
+    return queue_->last_touch_start_forced_nonblocking_due_to_fling_;
+  }
+
+  void set_enable_fling_passive_listener_flag(bool enable_flag) {
+    queue_->enable_fling_passive_listener_flag_ = enable_flag;
+  }
+
  protected:
   scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
   scoped_refptr<MainThreadEventQueue> queue_;
@@ -246,4 +258,142 @@
   }
 }
 
-}  // namespace content
+TEST_F(MainThreadEventQueueTest, BlockingTouchesDuringFling) {
+  SyntheticWebTouchEvent kEvents[1];
+  kEvents[0].PressPoint(10, 10);
+  kEvents[0].touchStartOrFirstTouchMove = true;
+  set_is_flinging(true);
+  set_enable_fling_passive_listener_flag(true);
+
+  EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(1u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(0)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(0)->type);
+  EXPECT_TRUE(last_touch_start_forced_nonblocking_due_to_fling());
+  const WebTouchEvent* last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(0).get());
+  kEvents[0].dispatchedDuringFling = true;
+  kEvents[0].dispatchType = WebInputEvent::ListenersForcedNonBlockingDueToFling;
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  kEvents[0].MovePoint(0, 30, 30);
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(2u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(1)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(1)->type);
+  EXPECT_TRUE(last_touch_start_forced_nonblocking_due_to_fling());
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(1).get());
+  kEvents[0].dispatchedDuringFling = true;
+  kEvents[0].dispatchType = WebInputEvent::ListenersForcedNonBlockingDueToFling;
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  kEvents[0].MovePoint(0, 50, 50);
+  kEvents[0].touchStartOrFirstTouchMove = false;
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(3u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(2)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(2)->type);
+  EXPECT_TRUE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(2).get());
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  kEvents[0].ReleasePoint(0);
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(4u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(3)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(3)->type);
+  EXPECT_TRUE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(3).get());
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+}
+
+TEST_F(MainThreadEventQueueTest, BlockingTouchesOutsideFling) {
+  SyntheticWebTouchEvent kEvents[1];
+  kEvents[0].PressPoint(10, 10);
+  kEvents[0].touchStartOrFirstTouchMove = true;
+  set_is_flinging(false);
+  set_enable_fling_passive_listener_flag(false);
+
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(1u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(0)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(0)->type);
+  EXPECT_FALSE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
+  const WebTouchEvent* last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(0).get());
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  set_is_flinging(true);
+  set_enable_fling_passive_listener_flag(false);
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(2u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(1)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(1)->type);
+  EXPECT_FALSE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(1).get());
+  kEvents[0].dispatchedDuringFling = true;
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  set_is_flinging(false);
+  set_enable_fling_passive_listener_flag(true);
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(3u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(2)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(2)->type);
+  EXPECT_TRUE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(2).get());
+  kEvents[0].dispatchedDuringFling = false;
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+
+  kEvents[0].MovePoint(0, 30, 30);
+  HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  main_task_runner_->RunUntilIdle();
+  EXPECT_FALSE(main_task_runner_->HasPendingTask());
+  EXPECT_EQ(0u, event_queue().size());
+  EXPECT_EQ(4u, handled_events_.size());
+  EXPECT_EQ(kEvents[0].size, handled_events_.at(3)->size);
+  EXPECT_EQ(kEvents[0].type, handled_events_.at(3)->type);
+  EXPECT_FALSE(kEvents[0].dispatchedDuringFling);
+  EXPECT_EQ(kEvents[0].dispatchType, WebInputEvent::Blocking);
+  EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
+  last_touch_event =
+      static_cast<const WebTouchEvent*>(handled_events_.at(3).get());
+  EXPECT_EQ(kEvents[0], *last_touch_event);
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index be87e45..3ea242a 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -128,14 +128,14 @@
     PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
     PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
     PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
-    PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING,
+    PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
     PASSIVE_LISTENER_UMA_ENUM_COUNT
   };
 
   int enum_value;
   switch (dispatch_type) {
-    case WebInputEvent::ListenersForcedNonBlockingPassive:
-      enum_value = PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING;
+    case WebInputEvent::ListenersForcedNonBlockingDueToFling:
+      enum_value = PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING;
       break;
     case WebInputEvent::ListenersNonBlockingPassive:
       enum_value = PASSIVE_LISTENER_UMA_ENUM_PASSIVE;
@@ -165,10 +165,11 @@
       UMA_HISTOGRAM_CUSTOM_COUNTS("Event.PassiveListeners.Latency",
                                   GetEventLatencyMicros(event_timestamp, now),
                                   1, 10000000, 100);
-    } else if (enum_value == PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING) {
+    } else if (enum_value ==
+               PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING) {
       base::TimeTicks now = base::TimeTicks::Now();
       UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "Event.PassiveListeners.ForcedNonBlockingLatency",
+          "Event.PassiveListeners.ForcedNonBlockingLatencyDueToFling",
           GetEventLatencyMicros(event_timestamp, now), 1, 10000000, 100);
     }
   }
@@ -335,21 +336,17 @@
     LogPassiveEventListenersUma(processed, touch.dispatchType,
                                 input_event.timeStampSeconds, latency_info);
 
-    if (input_event.type == WebInputEvent::TouchStart &&
-        touch.dispatchType == WebInputEvent::Blocking &&
+    // TODO(lanwei): Remove this metric for event latency outside fling in M56,
+    // once we've gathered enough data to decide if we want to ship the passive
+    // event listener for fling, see https://crbug.com/638661.
+    if (touch.dispatchType == WebInputEvent::Blocking &&
+        touch.touchStartOrFirstTouchMove &&
         base::TimeTicks::IsHighResolution()) {
       base::TimeTicks now = base::TimeTicks::Now();
-      if (touch.dispatchedDuringFling) {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Event.Touch.TouchStartLatencyDuringFling",
-            GetEventLatencyMicros(input_event.timeStampSeconds, now), 1,
-            100000000, 50);
-      } else {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Event.Touch.TouchStartLatencyOutsideFling",
-            GetEventLatencyMicros(input_event.timeStampSeconds, now), 1,
-            100000000, 50);
-      }
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Event.Touch.TouchLatencyOutsideFling",
+          GetEventLatencyMicros(input_event.timeStampSeconds, now), 1,
+          100000000, 50);
     }
   } else if (input_event.type == WebInputEvent::MouseWheel) {
     LogPassiveEventListenersUma(
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 60370ae9..4095fa5 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -35,7 +35,7 @@
   PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
   PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
   PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
-  PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING,
+  PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
   PASSIVE_LISTENER_UMA_ENUM_COUNT
 };
 
@@ -281,9 +281,10 @@
 TEST_F(RenderWidgetUnittest, RenderWidgetInputEventUmaMetrics) {
   SyntheticWebTouchEvent touch;
   touch.PressPoint(10, 10);
+  touch.touchStartOrFirstTouchMove = true;
 
   EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_))
-      .Times(4)
+      .Times(5)
       .WillRepeatedly(
           ::testing::Return(blink::WebInputEventResult::NotHandled));
 
@@ -304,11 +305,20 @@
                                        PASSIVE_LISTENER_UMA_ENUM_PASSIVE, 1);
 
   touch.dispatchType =
-      blink::WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive;
+      blink::WebInputEvent::DispatchType::ListenersForcedNonBlockingDueToFling;
   widget()->SendInputEvent(touch);
   histogram_tester().ExpectBucketCount(
       EVENT_LISTENER_RESULT_HISTOGRAM,
-      PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING, 1);
+      PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 1);
+
+  touch.MovePoint(0, 10, 10);
+  touch.touchStartOrFirstTouchMove = true;
+  touch.dispatchType =
+      blink::WebInputEvent::DispatchType::ListenersForcedNonBlockingDueToFling;
+  widget()->SendInputEvent(touch);
+  histogram_tester().ExpectBucketCount(
+      EVENT_LISTENER_RESULT_HISTOGRAM,
+      PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING, 2);
 
   EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_))
       .WillOnce(
@@ -328,24 +338,32 @@
       PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED, 1);
 }
 
-TEST_F(RenderWidgetUnittest, TouchStartDuringOrOutsideFlingUmaMetrics) {
+TEST_F(RenderWidgetUnittest, TouchDuringOrOutsideFlingUmaMetrics) {
   EXPECT_CALL(*widget()->mock_webwidget(), handleInputEvent(_))
-      .Times(2)
+      .Times(3)
       .WillRepeatedly(
           ::testing::Return(blink::WebInputEventResult::NotHandled));
 
   SyntheticWebTouchEvent touch;
   touch.PressPoint(10, 10);
   touch.dispatchType = blink::WebInputEvent::DispatchType::Blocking;
-  touch.dispatchedDuringFling = true;
-  widget()->SendInputEvent(touch);
-  histogram_tester().ExpectTotalCount(
-      "Event.Touch.TouchStartLatencyDuringFling", 1);
-
   touch.dispatchedDuringFling = false;
+  touch.touchStartOrFirstTouchMove = true;
   widget()->SendInputEvent(touch);
-  histogram_tester().ExpectTotalCount(
-      "Event.Touch.TouchStartLatencyOutsideFling", 1);
+  histogram_tester().ExpectTotalCount("Event.Touch.TouchLatencyOutsideFling",
+                                      1);
+
+  touch.MovePoint(0, 10, 10);
+  touch.touchStartOrFirstTouchMove = true;
+  widget()->SendInputEvent(touch);
+  histogram_tester().ExpectTotalCount("Event.Touch.TouchLatencyOutsideFling",
+                                      2);
+
+  touch.MovePoint(0, 30, 30);
+  touch.touchStartOrFirstTouchMove = false;
+  widget()->SendInputEvent(touch);
+  histogram_tester().ExpectTotalCount("Event.Touch.TouchLatencyOutsideFling",
+                                      2);
 }
 
 }  // namespace content
diff --git a/third_party/WebKit/Source/core/events/Event.cpp b/third_party/WebKit/Source/core/events/Event.cpp
index 5e661a6..77fe19a 100644
--- a/third_party/WebKit/Source/core/events/Event.cpp
+++ b/third_party/WebKit/Source/core/events/Event.cpp
@@ -81,6 +81,7 @@
     , m_wasInitialized(true)
     , m_isTrusted(false)
     , m_handlingPassive(false)
+    , m_preventDefaultCalledOnUncancelableEvent(false)
     , m_eventPhase(0)
     , m_currentTarget(nullptr)
     , m_platformTimeStamp(platformTimeStamp)
@@ -116,6 +117,7 @@
     m_immediatePropagationStopped = false;
     m_defaultPrevented = false;
     m_isTrusted = false;
+    m_preventDefaultCalledOnUncancelableEvent = false;
 
     m_type = eventTypeArg;
     m_canBubble = canBubbleArg;
@@ -233,6 +235,8 @@
 
     if (m_cancelable)
         m_defaultPrevented = true;
+    else
+        m_preventDefaultCalledOnUncancelableEvent = true;
 }
 
 void Event::setTarget(EventTarget* target)
diff --git a/third_party/WebKit/Source/core/events/Event.h b/third_party/WebKit/Source/core/events/Event.h
index 7becfcd5..686d0a9 100644
--- a/third_party/WebKit/Source/core/events/Event.h
+++ b/third_party/WebKit/Source/core/events/Event.h
@@ -215,6 +215,8 @@
 
     bool preventDefaultCalledDuringPassive() const { return m_preventDefaultCalledDuringPassive; }
 
+    bool preventDefaultCalledOnUncancelableEvent() const { return m_preventDefaultCalledOnUncancelableEvent; }
+
     DECLARE_VIRTUAL_TRACE();
 
 protected:
@@ -255,6 +257,8 @@
     // Whether preventDefault was called when |m_handlingPassive| is
     // true. This field is reset on each call to setHandlingPassive.
     unsigned m_preventDefaultCalledDuringPassive : 1;
+    // Whether preventDefault was called on uncancelable event.
+    unsigned m_preventDefaultCalledOnUncancelableEvent : 1;
 
     unsigned short m_eventPhase;
     Member<EventTarget> m_currentTarget;
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
index 5971003..9fca18a 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
@@ -95,15 +95,6 @@
     const HeapVector<TouchInfo>& touchInfos,
     bool allTouchesReleased)
 {
-    bool touchStartOrFirstTouchMove = false;
-    if (event.type() == PlatformEvent::TouchStart) {
-        m_waitingForFirstTouchMove = true;
-        touchStartOrFirstTouchMove = true;
-    } else if (event.type() == PlatformEvent::TouchMove) {
-        touchStartOrFirstTouchMove = m_waitingForFirstTouchMove;
-        m_waitingForFirstTouchMove = false;
-    }
-
     // Build up the lists to use for the |touches|, |targetTouches| and
     // |changedTouches| attributes in the JS event. See
     // http://www.w3.org/TR/touch-events/#touchevent-interface for how these
@@ -186,39 +177,39 @@
             TouchEvent* touchEvent = TouchEvent::create(
                 touches, touchesByTarget.get(touchEventTarget), changedTouches[state].m_touches.get(),
                 eventName, touchEventTarget->toNode()->document().domWindow(),
-                event.getModifiers(), event.cancelable(), event.causesScrollingIfUncanceled(), touchStartOrFirstTouchMove, event.timestamp());
+                event.getModifiers(), event.cancelable(), event.causesScrollingIfUncanceled(), event.touchStartOrFirstTouchMove(), event.timestamp());
 
             DispatchEventResult domDispatchResult = touchEventTarget->dispatchEvent(touchEvent);
 
             // Only report for top level documents with a single touch on
             // touch-start or the first touch-move.
-            if (touchStartOrFirstTouchMove && touchInfos.size() == 1 && event.cancelable() && m_frame->isMainFrame()) {
+            if (event.touchStartOrFirstTouchMove() && touchInfos.size() == 1 && m_frame->isMainFrame()) {
 
                 // Record the disposition and latency of touch starts and first touch moves before and after the page is fully loaded respectively.
                 int64_t latencyInMicros = static_cast<int64_t>((monotonicallyIncreasingTime() - event.timestamp()) * 1000000.0);
-                if (m_frame->document()->isLoadCompleted()) {
-                    DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsAfterPageLoadHistogram, ("Event.Touch.TouchDispositionsAfterPageLoad", TouchEventDispatchResultTypeMax));
-                    touchDispositionsAfterPageLoadHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
+                if (event.cancelable()) {
+                    if (m_frame->document()->isLoadCompleted()) {
+                        DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsAfterPageLoadHistogram, ("Event.Touch.TouchDispositionsAfterPageLoad", TouchEventDispatchResultTypeMax));
+                        touchDispositionsAfterPageLoadHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
 
-                    DEFINE_STATIC_LOCAL(CustomCountHistogram, eventLatencyAfterPageLoadHistogram, ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50));
-                    eventLatencyAfterPageLoadHistogram.count(latencyInMicros);
-                } else {
-                    DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsBeforePageLoadHistogram, ("Event.Touch.TouchDispositionsBeforePageLoad", TouchEventDispatchResultTypeMax));
-                    touchDispositionsBeforePageLoadHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
+                        DEFINE_STATIC_LOCAL(CustomCountHistogram, eventLatencyAfterPageLoadHistogram, ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50));
+                        eventLatencyAfterPageLoadHistogram.count(latencyInMicros);
+                    } else {
+                        DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsBeforePageLoadHistogram, ("Event.Touch.TouchDispositionsBeforePageLoad", TouchEventDispatchResultTypeMax));
+                        touchDispositionsBeforePageLoadHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
 
-                    DEFINE_STATIC_LOCAL(CustomCountHistogram, eventLatencyBeforePageLoadHistogram, ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50));
-                    eventLatencyBeforePageLoadHistogram.count(latencyInMicros);
+                        DEFINE_STATIC_LOCAL(CustomCountHistogram, eventLatencyBeforePageLoadHistogram, ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50));
+                        eventLatencyBeforePageLoadHistogram.count(latencyInMicros);
+                    }
+                    // Report the touch disposition there is no active fling animation.
+                    DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsOutsideFlingHistogram, ("Event.Touch.TouchDispositionsOutsideFling2", TouchEventDispatchResultTypeMax));
+                    touchDispositionsOutsideFlingHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
                 }
 
-                // Report the touch disposition, split by whether there is an active fling animation.
-                if (event.type() == PlatformEvent::TouchStart) {
-                    if (event.dispatchedDuringFling()) {
-                        DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsDuringFlingHistogram, ("Event.Touch.TouchDispositionsDuringFling", TouchEventDispatchResultTypeMax));
-                        touchDispositionsDuringFlingHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
-                    } else {
-                        DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsOutsideFlingHistogram, ("Event.Touch.TouchDispositionsOutsideFling", TouchEventDispatchResultTypeMax));
-                        touchDispositionsOutsideFlingHistogram.count((domDispatchResult != DispatchEventResult::NotCanceled) ? HandledTouches : UnhandledTouches);
-                    }
+                // Report the touch disposition when there is an active fling animation.
+                if (event.dispatchType() == PlatformEvent::ListenersForcedNonBlockingDueToFling) {
+                    DEFINE_STATIC_LOCAL(EnumerationHistogram, touchDispositionsDuringFlingHistogram, ("Event.Touch.TouchDispositionsDuringFling2", TouchEventDispatchResultTypeMax));
+                    touchDispositionsDuringFlingHistogram.count(touchEvent->preventDefaultCalledOnUncancelableEvent() ? HandledTouches : UnhandledTouches);
                 }
             }
             eventResult = EventHandler::mergeEventResult(eventResult,
@@ -498,7 +489,6 @@
     m_targetForTouchID.clear();
     m_regionForTouchID.clear();
     m_touchPressed = false;
-    m_waitingForFirstTouchMove = false;
     m_touchScrollStarted = false;
     m_currentEvent = PlatformEvent::NoType;
 }
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.h b/third_party/WebKit/Source/core/input/TouchEventManager.h
index 9a1dc64..501d9465 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.h
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.h
@@ -99,8 +99,6 @@
 
     RefPtr<UserGestureToken> m_touchSequenceUserGestureToken;
     bool m_touchPressed;
-    // True if waiting on first touch move after a touch start.
-    bool m_waitingForFirstTouchMove;
     // True if a touch is active but scrolling/zooming has started.
     bool m_touchScrollStarted;
     // The touch event currently being handled or NoType if none.
diff --git a/third_party/WebKit/Source/platform/PlatformEvent.h b/third_party/WebKit/Source/platform/PlatformEvent.h
index ad3883f..05f6b2c5 100644
--- a/third_party/WebKit/Source/platform/PlatformEvent.h
+++ b/third_party/WebKit/Source/platform/PlatformEvent.h
@@ -130,8 +130,8 @@
         // All listeners are passive.
         ListenersNonBlockingPassive,
         // This value represents a state which would have normally blocking
-        // but was forced to be non-blocking.
-        ListenersForcedNonBlockingPassive,
+        // but was forced to be non-blocking during fling; not cancelable.
+        ListenersForcedNonBlockingDueToFling,
     };
 
     EventType type() const { return static_cast<EventType>(m_type); }
diff --git a/third_party/WebKit/Source/platform/PlatformTouchEvent.h b/third_party/WebKit/Source/platform/PlatformTouchEvent.h
index 48fabfd..f0e383b 100644
--- a/third_party/WebKit/Source/platform/PlatformTouchEvent.h
+++ b/third_party/WebKit/Source/platform/PlatformTouchEvent.h
@@ -33,6 +33,7 @@
         , m_dispatchType(PlatformEvent::Blocking)
         , m_causesScrollingIfUncanceled(false)
         , m_dispatchedDuringFling(false)
+        , m_touchStartOrFirstTouchMove(false)
     {
     }
 
@@ -42,7 +43,7 @@
     bool cancelable() const { return m_dispatchType == PlatformEvent::Blocking; }
     bool causesScrollingIfUncanceled() const { return m_causesScrollingIfUncanceled; }
     bool dispatchedDuringFling() const { return m_dispatchedDuringFling; }
-
+    bool touchStartOrFirstTouchMove() const { return m_touchStartOrFirstTouchMove; }
     uint32_t uniqueTouchEventId() const { return m_uniqueTouchEventId; }
 
 protected:
@@ -50,7 +51,7 @@
     DispatchType m_dispatchType;
     bool m_causesScrollingIfUncanceled;
     bool m_dispatchedDuringFling;
-
+    bool m_touchStartOrFirstTouchMove;
     uint32_t m_uniqueTouchEventId;
 };
 
diff --git a/third_party/WebKit/Source/web/WebInputEventConversion.cpp b/third_party/WebKit/Source/web/WebInputEventConversion.cpp
index ca0bbbe..203d818 100644
--- a/third_party/WebKit/Source/web/WebInputEventConversion.cpp
+++ b/third_party/WebKit/Source/web/WebInputEventConversion.cpp
@@ -98,7 +98,7 @@
         "Dispatch Types not equal");
     static_assert(PlatformEvent::DispatchType::ListenersNonBlockingPassive == static_cast<PlatformEvent::DispatchType>(WebInputEvent::DispatchType::ListenersNonBlockingPassive),
         "Dispatch Types not equal");
-    static_assert(PlatformEvent::DispatchType::ListenersForcedNonBlockingPassive == static_cast<PlatformEvent::DispatchType>(WebInputEvent::DispatchType::ListenersForcedNonBlockingPassive),
+    static_assert(PlatformEvent::DispatchType::ListenersForcedNonBlockingDueToFling == static_cast<PlatformEvent::DispatchType>(WebInputEvent::DispatchType::ListenersForcedNonBlockingDueToFling),
         "Dispatch Types not equal");
 
     return static_cast<PlatformEvent::DispatchType>(type);
@@ -483,6 +483,7 @@
     m_timestamp = event.timeStampSeconds;
     m_causesScrollingIfUncanceled = event.movedBeyondSlopRegion;
     m_dispatchedDuringFling = event.dispatchedDuringFling;
+    m_touchStartOrFirstTouchMove = event.touchStartOrFirstTouchMove;
 
     for (unsigned i = 0; i < event.touchesLength; ++i)
         m_touchPoints.append(PlatformTouchPointBuilder(widget, event.touches[i]));
diff --git a/third_party/WebKit/public/platform/WebInputEvent.h b/third_party/WebKit/public/platform/WebInputEvent.h
index c7ccbf8f..e22529c 100644
--- a/third_party/WebKit/public/platform/WebInputEvent.h
+++ b/third_party/WebKit/public/platform/WebInputEvent.h
@@ -202,8 +202,8 @@
         // All listeners are passive; not cancelable.
         ListenersNonBlockingPassive,
         // This value represents a state which would have normally blocking
-        // but was forced to be non-blocking; not cancelable.
-        ListenersForcedNonBlockingPassive,
+        // but was forced to be non-blocking during fling; not cancelable.
+        ListenersForcedNonBlockingDueToFling,
     };
 
     // The rail mode for a wheel event specifies the axis on which scrolling is
@@ -624,6 +624,9 @@
     // dispatched.
     bool dispatchedDuringFling;
 
+    // Whether this touch event is a touchstart or a first touchmove event per scroll.
+    bool touchStartOrFirstTouchMove;
+
     // A unique identifier for the touch event. Valid ids start at one and
     // increase monotonically. Zero means an unknown id.
     uint32_t uniqueTouchEventId;
@@ -634,6 +637,7 @@
         , dispatchType(Blocking)
         , movedBeyondSlopRegion(false)
         , dispatchedDuringFling(false)
+        , touchStartOrFirstTouchMove(false)
         , uniqueTouchEventId(0)
     {
     }
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index eebc0af..cc984f5 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13346,6 +13346,10 @@
 
 <histogram name="Event.PassiveListeners.ForcedNonBlockingLatency"
     units="microseconds">
+  <obsolete>
+    Deprecated 08/2016 in Issue 595327, and replaced by
+    Event.PassiveListeners.ForcedNonBlockingLatencyDueToFling.
+  </obsolete>
   <owner>dtapuska@chromium.org</owner>
   <summary>
     Time between when a forced non-blocking event was generated and the event
@@ -13354,6 +13358,17 @@
   </summary>
 </histogram>
 
+<histogram name="Event.PassiveListeners.ForcedNonBlockingLatencyDueToFling"
+    units="microseconds">
+  <owner>dtapuska@chromium.org</owner>
+  <summary>
+    Time between when a forced non-blocking touchstart or first touchmove event
+    per scroll was generated and the event processed during fling. This
+    histogram tracks the benefit of forcing non-blocking events listeners during
+    fling.
+  </summary>
+</histogram>
+
 <histogram name="Event.PassiveListeners.Latency" units="microseconds">
   <owner>dtapuska@chromium.org</owner>
   <summary>
@@ -13435,7 +13450,7 @@
     enum="TouchEventDispatchResultType">
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Records the disposition (handled or not handled) of touch start events and
+    Records the disposition (handled or not handled) of touchstart events and
     the first touchmove events per scroll. Only recorded after the page is fully
     loaded.
   </summary>
@@ -13445,7 +13460,7 @@
     enum="TouchEventDispatchResultType">
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Records the disposition (handled or not handled) of touch start events and
+    Records the disposition (handled or not handled) of touchstart events and
     the first touchmove events per scroll. Only recorded before the page is
     fully loaded.
   </summary>
@@ -13453,27 +13468,55 @@
 
 <histogram name="Event.Touch.TouchDispositionsDuringFling"
     enum="TouchEventDispatchResultType">
+  <obsolete>
+    Deprecated 08/2016 in Issue 595327, and replaced by
+    Event.Touch.TouchDispositionsDuringFling2.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Records the disposition (handled or not handled) of touch start events. Only
+    Records the disposition (handled or not handled) of touchstart events. Only
     recorded while there is an active fling animation.
   </summary>
 </histogram>
 
+<histogram name="Event.Touch.TouchDispositionsDuringFling2"
+    enum="TouchEventDispatchResultType">
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    Records the disposition (handled or not handled) of touchstart and first
+    touchmove events per scroll. Only recorded while there is an active fling
+    animation.
+  </summary>
+</histogram>
+
 <histogram name="Event.Touch.TouchDispositionsOutsideFling"
     enum="TouchEventDispatchResultType">
+  <obsolete>
+    Deprecated 08/2016 in Issue 595327, and replaced by
+    Event.Touch.TouchDispositionsOutsideFling2.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Records the disposition (handled or not handled) of touch start events. Only
+    Records the disposition (handled or not handled) of touchstart events. Only
     recorded while there is no active fling animation.
   </summary>
 </histogram>
 
+<histogram name="Event.Touch.TouchDispositionsOutsideFling2"
+    enum="TouchEventDispatchResultType">
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    Records the disposition (handled or not handled) of touchstart and first
+    touchmove events per scroll events. Only recorded while there is no active
+    fling animation.
+  </summary>
+</histogram>
+
 <histogram name="Event.Touch.TouchLatencyAfterPageLoad" units="microseconds">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between when a touch event was generated and the event was processed.
-    Recorded only for touch start events and the first touchmove events per
+    Recorded only for touchstart events and the first touchmove events per
     scroll that occur after the page is fully loaded.
   </summary>
 </histogram>
@@ -13482,16 +13525,29 @@
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between when a touch event was generated and the event was processed.
-    Recorded only for touch start events and the first touchmove events per
+    Recorded only for touchstart events and the first touchmove events per
     scroll that occur before the page is fully loaded. This histogram tracks the
     benefit of forcing passive event listeners before the page is fully loaded.
   </summary>
 </histogram>
 
-<histogram name="Event.Touch.TouchStartLatencyDuringFling" units="microseconds">
+<histogram name="Event.Touch.TouchLatencyOutsideFling" units="microseconds">
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Time between when a touch start event was generated and the event was
+    Time between when a touch event was generated and the event was processed.
+    Recorded only for touchstart events and the first touchmove events per
+    scroll when there was no active fling animation.
+  </summary>
+</histogram>
+
+<histogram name="Event.Touch.TouchStartLatencyDuringFling" units="microseconds">
+  <obsolete>
+    Deprecated 08/2016 in Issue 595327, and replaced by
+    Event.PassiveListeners.ForcedNonBlockingLatencyDueToFling.
+  </obsolete>
+  <owner>tdresser@chromium.org</owner>
+  <summary>
+    Time between when a touchstart event was generated and the event was
     processed. Recorded only when there was an active fling animation. This
     histogram tracks the benefit of forcing passive event listeners during
     fling.
@@ -13500,9 +13556,13 @@
 
 <histogram name="Event.Touch.TouchStartLatencyOutsideFling"
     units="microseconds">
+  <obsolete>
+    Deprecated 08/2016 in Issue 595327, and replaced by
+    Event.Touch.TouchLatencyOutsideFling.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
-    Time between when a touch start event was generated and the event was
+    Time between when a touchstart event was generated and the event was
     processed. Recorded only when there was no active fling animation.
   </summary>
 </histogram>