| Index: third_party/WebKit/Source/core/input/TouchEventManager.cpp
|
| diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
|
| index 6f6476f60c4d100c5cb68e7b6be1f0963f8a3882..b46e3cfd5e340b4e1063f69e9641978d37e19dd3 100644
|
| --- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
|
| +++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
|
| @@ -37,19 +37,17 @@ bool HasTouchHandlers(const EventHandlerRegistry& registry) {
|
| EventHandlerRegistry::kTouchEndOrCancelEventPassive);
|
| }
|
|
|
| -const AtomicString& TouchEventNameForTouchPointState(
|
| - WebTouchPoint::State state) {
|
| - switch (state) {
|
| - case WebTouchPoint::kStateReleased:
|
| +const AtomicString& TouchEventNameForPointerEventType(
|
| + WebInputEvent::Type type) {
|
| + switch (type) {
|
| + case WebInputEvent::kPointerUp:
|
| return EventTypeNames::touchend;
|
| - case WebTouchPoint::kStateCancelled:
|
| + case WebInputEvent::kPointerCancel:
|
| return EventTypeNames::touchcancel;
|
| - case WebTouchPoint::kStatePressed:
|
| + case WebInputEvent::kPointerDown:
|
| return EventTypeNames::touchstart;
|
| - case WebTouchPoint::kStateMoved:
|
| + case WebInputEvent::kPointerMove:
|
| return EventTypeNames::touchmove;
|
| - case WebTouchPoint::kStateStationary:
|
| - // Fall through to default
|
| default:
|
| NOTREACHED();
|
| return g_empty_atom;
|
| @@ -62,19 +60,55 @@ enum TouchEventDispatchResultType {
|
| kTouchEventDispatchResultTypeMax,
|
| };
|
|
|
| -bool IsTouchSequenceStart(const WebTouchEvent& event) {
|
| - if (!event.touches_length)
|
| - return false;
|
| - if (event.GetType() != WebInputEvent::kTouchStart)
|
| - return false;
|
| - for (size_t i = 0; i < event.touches_length; ++i) {
|
| - if (event.touches[i].state != blink::WebTouchPoint::kStatePressed)
|
| - return false;
|
| +WebTouchPoint::State TouchPointStateFromPointerEventType(
|
| + WebInputEvent::Type type,
|
| + bool stale) {
|
| + if (stale)
|
| + return WebTouchPoint::kStateStationary;
|
| + switch (type) {
|
| + case WebInputEvent::Type::kPointerUp:
|
| + return WebTouchPoint::kStateReleased;
|
| + case WebInputEvent::Type::kPointerCancel:
|
| + return WebTouchPoint::kStateCancelled;
|
| + case WebInputEvent::Type::kPointerDown:
|
| + return WebTouchPoint::kStatePressed;
|
| + case WebInputEvent::Type::kPointerMove:
|
| + return WebTouchPoint::kStateMoved;
|
| + default:
|
| + NOTREACHED();
|
| + return WebTouchPoint::kStateUndefined;
|
| }
|
| - return true;
|
| }
|
|
|
| -// Defining this class type local to dispatchTouchEvents() and annotating
|
| +WebTouchPoint CreateWebTouchPointFromWebPointerEvent(
|
| + const WebPointerEvent& web_pointer_event,
|
| + bool stale) {
|
| + WebTouchPoint web_touch_point(web_pointer_event);
|
| + web_touch_point.state =
|
| + TouchPointStateFromPointerEventType(web_pointer_event.GetType(), stale);
|
| + // TODO(crbug.com/731725): This mapping needs a division by 2.
|
| + web_touch_point.radius_x = web_pointer_event.width;
|
| + web_touch_point.radius_y = web_pointer_event.height;
|
| + web_touch_point.rotation_angle = web_pointer_event.rotation_angle;
|
| + return web_touch_point;
|
| +}
|
| +
|
| +void SetWebTouchEventAttributesFromWebPointerEvent(
|
| + WebTouchEvent* web_touch_event,
|
| + const WebPointerEvent& web_pointer_event) {
|
| + web_touch_event->dispatch_type = web_pointer_event.dispatch_type;
|
| + web_touch_event->touch_start_or_first_touch_move =
|
| + web_pointer_event.touch_start_or_first_touch_move;
|
| + web_touch_event->moved_beyond_slop_region =
|
| + web_pointer_event.moved_beyond_slop_region;
|
| + web_touch_event->SetFrameScale(web_pointer_event.FrameScale());
|
| + web_touch_event->SetFrameTranslate(web_pointer_event.FrameTranslate());
|
| + web_touch_event->SetTimeStampSeconds(web_pointer_event.TimeStampSeconds());
|
| + web_touch_event->SetModifiers(web_pointer_event.GetModifiers());
|
| +}
|
| +
|
| +// Defining this class type local to
|
| +// DispatchTouchEventFromAccumulatdTouchPoints() and annotating
|
| // it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning
|
| // that the local class doesn't provide a local definition for 'operator new'.
|
| // Which it intentionally doesn't and shouldn't.
|
| @@ -92,10 +126,70 @@ class ChangedTouches final {
|
| using EventTargetSet = HeapHashSet<Member<EventTarget>>;
|
| // Set of targets involved in m_touches.
|
| EventTargetSet targets_;
|
| -
|
| - WebPointerProperties::PointerType pointer_type_;
|
| };
|
|
|
| +void ReportMetricsForTouch(const WebPointerEvent& event,
|
| + DispatchEventResult dom_dispatch_result,
|
| + bool prevent_default_called_on_uncancelable_event,
|
| + bool is_frame_loaded) {
|
| + int64_t latency_in_micros =
|
| + (TimeTicks::Now() - TimeTicks::FromSeconds(event.TimeStampSeconds()))
|
| + .InMicroseconds();
|
| + if (event.IsCancelable()) {
|
| + if (is_frame_loaded) {
|
| + DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| + touch_dispositions_after_page_load_histogram,
|
| + ("Event.Touch.TouchDispositionsAfterPageLoad",
|
| + kTouchEventDispatchResultTypeMax));
|
| + touch_dispositions_after_page_load_histogram.Count(
|
| + (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| + ? kHandledTouches
|
| + : kUnhandledTouches);
|
| +
|
| + DEFINE_STATIC_LOCAL(
|
| + CustomCountHistogram, event_latency_after_page_load_histogram,
|
| + ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50));
|
| + event_latency_after_page_load_histogram.Count(latency_in_micros);
|
| + } else {
|
| + DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| + touch_dispositions_before_page_load_histogram,
|
| + ("Event.Touch.TouchDispositionsBeforePageLoad",
|
| + kTouchEventDispatchResultTypeMax));
|
| + touch_dispositions_before_page_load_histogram.Count(
|
| + (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| + ? kHandledTouches
|
| + : kUnhandledTouches);
|
| +
|
| + DEFINE_STATIC_LOCAL(
|
| + CustomCountHistogram, event_latency_before_page_load_histogram,
|
| + ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50));
|
| + event_latency_before_page_load_histogram.Count(latency_in_micros);
|
| + }
|
| + // Report the touch disposition there is no active fling animation.
|
| + DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| + touch_dispositions_outside_fling_histogram,
|
| + ("Event.Touch.TouchDispositionsOutsideFling2",
|
| + kTouchEventDispatchResultTypeMax));
|
| + touch_dispositions_outside_fling_histogram.Count(
|
| + (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| + ? kHandledTouches
|
| + : kUnhandledTouches);
|
| + }
|
| +
|
| + // Report the touch disposition when there is an active fling
|
| + // animation.
|
| + if (event.dispatch_type ==
|
| + WebInputEvent::kListenersForcedNonBlockingDueToFling) {
|
| + DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| + touch_dispositions_during_fling_histogram,
|
| + ("Event.Touch.TouchDispositionsDuringFling2",
|
| + kTouchEventDispatchResultTypeMax));
|
| + touch_dispositions_during_fling_histogram.Count(
|
| + prevent_default_called_on_uncancelable_event ? kHandledTouches
|
| + : kUnhandledTouches);
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| TouchEventManager::TouchEventManager(LocalFrame& frame) : frame_(frame) {
|
| @@ -104,9 +198,8 @@ TouchEventManager::TouchEventManager(LocalFrame& frame) : frame_(frame) {
|
|
|
| void TouchEventManager::Clear() {
|
| touch_sequence_document_.Clear();
|
| - target_for_touch_id_.clear();
|
| - region_for_touch_id_.clear();
|
| - touch_pressed_ = false;
|
| + touch_attribute_map_.clear();
|
| + last_coalesced_touch_event_ = WebTouchEvent();
|
| suppressing_touchmoves_within_slop_ = false;
|
| current_touch_action_ = TouchAction::kTouchActionAuto;
|
| }
|
| @@ -114,31 +207,18 @@ void TouchEventManager::Clear() {
|
| DEFINE_TRACE(TouchEventManager) {
|
| visitor->Trace(frame_);
|
| visitor->Trace(touch_sequence_document_);
|
| - visitor->Trace(target_for_touch_id_);
|
| + visitor->Trace(touch_attribute_map_);
|
| }
|
|
|
| -Touch* TouchEventManager::CreateDomTouch(const WebTouchPoint& point,
|
| - bool* known_target) {
|
| - Node* touch_node = nullptr;
|
| - String region_id;
|
| +Touch* TouchEventManager::CreateDomTouch(
|
| + const TouchEventManager::TouchPointAttributes* point_attr,
|
| + bool* known_target) {
|
| + Node* touch_node = point_attr->target_;
|
| + String region_id = point_attr->region_;
|
| *known_target = false;
|
| FloatPoint content_point;
|
| FloatSize adjusted_radius;
|
|
|
| - if (point.state == WebTouchPoint::kStateReleased ||
|
| - point.state == WebTouchPoint::kStateCancelled) {
|
| - // The target should be the original target for this touch, so get
|
| - // it from the hashmap. As it's a release or cancel we also remove
|
| - // it from the map.
|
| - touch_node = target_for_touch_id_.Take(point.id);
|
| - region_id = region_for_touch_id_.Take(point.id);
|
| - } else {
|
| - // No hittest is performed on move or stationary, since the target
|
| - // is not allowed to change anyway.
|
| - touch_node = target_for_touch_id_.at(point.id);
|
| - region_id = region_for_touch_id_.at(point.id);
|
| - }
|
| -
|
| LocalFrame* target_frame = nullptr;
|
| if (touch_node) {
|
| Document& doc = touch_node->GetDocument();
|
| @@ -169,40 +249,193 @@ Touch* TouchEventManager::CreateDomTouch(const WebTouchPoint& point,
|
| }
|
| DCHECK(target_frame);
|
|
|
| + WebPointerEvent transformed_event =
|
| + point_attr->event_.WebPointerEventInRootFrame();
|
| // pagePoint should always be in the target element's document coordinates.
|
| - FloatPoint page_point =
|
| - target_frame->View()->RootFrameToContents(point.PositionInWidget());
|
| + FloatPoint page_point = target_frame->View()->RootFrameToContents(
|
| + transformed_event.PositionInWidget());
|
| float scale_factor = 1.0f / target_frame->PageZoomFactor();
|
|
|
| content_point = page_point.ScaledBy(scale_factor);
|
| - adjusted_radius =
|
| - FloatSize(point.radius_x, point.radius_y).ScaledBy(scale_factor);
|
| + adjusted_radius = FloatSize(transformed_event.width, transformed_event.height)
|
| + .ScaledBy(scale_factor);
|
| +
|
| + return Touch::Create(target_frame, touch_node, point_attr->event_.id,
|
| + transformed_event.PositionInScreen(), content_point,
|
| + adjusted_radius, transformed_event.rotation_angle,
|
| + transformed_event.force, region_id);
|
| +}
|
| +
|
| +WebCoalescedInputEvent TouchEventManager::GenerateWebCoalescedInputEvent() {
|
| + DCHECK(!touch_attribute_map_.IsEmpty());
|
| +
|
| + WebTouchEvent event;
|
| +
|
| + const auto& first_touch_pointer_event =
|
| + touch_attribute_map_.begin()->value->event_;
|
| +
|
| + SetWebTouchEventAttributesFromWebPointerEvent(&event,
|
| + first_touch_pointer_event);
|
| + SetWebTouchEventAttributesFromWebPointerEvent(&last_coalesced_touch_event_,
|
| + first_touch_pointer_event);
|
| + WebInputEvent::Type touch_event_type = WebInputEvent::kTouchMove;
|
| + Vector<WebPointerEvent> all_coalesced_events;
|
| + Vector<int> available_ids;
|
| + for (const auto& id : touch_attribute_map_.Keys())
|
| + available_ids.push_back(id);
|
| + std::sort(available_ids.begin(), available_ids.end());
|
| + for (const int& touch_point_id : available_ids) {
|
| + const auto& touch_point_attribute = touch_attribute_map_.at(touch_point_id);
|
| + const WebPointerEvent& touch_pointer_event = touch_point_attribute->event_;
|
| + event.touches[event.touches_length++] =
|
| + CreateWebTouchPointFromWebPointerEvent(touch_pointer_event,
|
| + touch_point_attribute->stale_);
|
| +
|
| + // Only change the touch event type from move. So if we have two pointers
|
| + // in up and down state we just set the touch event type to the first one
|
| + // we see.
|
| + // TODO(crbug.com/732842): Note that event sender API allows sending any
|
| + // mix of input and as long as we don't crash or anything we should be good
|
| + // for now.
|
| + if (touch_event_type == WebInputEvent::kTouchMove) {
|
| + if (touch_pointer_event.GetType() == WebInputEvent::kPointerDown)
|
| + touch_event_type = WebInputEvent::kTouchStart;
|
| + else if (touch_pointer_event.GetType() == WebInputEvent::kPointerCancel)
|
| + touch_event_type = WebInputEvent::kTouchCancel;
|
| + else if (touch_pointer_event.GetType() == WebInputEvent::kPointerUp)
|
| + touch_event_type = WebInputEvent::kTouchEnd;
|
| + }
|
| +
|
| + for (const WebPointerEvent& coalesced_event :
|
| + touch_point_attribute->coalesced_events_) {
|
| + all_coalesced_events.push_back(coalesced_event);
|
| + }
|
| + }
|
| + event.SetType(touch_event_type);
|
| + last_coalesced_touch_event_.SetType(touch_event_type);
|
|
|
| - return Touch::Create(target_frame, touch_node, point.id,
|
| - point.PositionInScreen(), content_point, adjusted_radius,
|
| - point.rotation_angle, point.force, region_id);
|
| + // Create all coalesced touch events based on pointerevents
|
| + struct {
|
| + bool operator()(const WebPointerEvent& a, const WebPointerEvent& b) {
|
| + return a.TimeStampSeconds() < b.TimeStampSeconds();
|
| + }
|
| + } timestamp_based_event_comparison;
|
| + std::sort(all_coalesced_events.begin(), all_coalesced_events.end(),
|
| + timestamp_based_event_comparison);
|
| + WebCoalescedInputEvent result(event, std::vector<const WebInputEvent*>());
|
| + for (const auto& web_pointer_event : all_coalesced_events) {
|
| + if (web_pointer_event.GetType() == WebInputEvent::kPointerDown) {
|
| + // TODO(crbug.com/732842): Technically we should never receive the
|
| + // pointerdown twice for the same touch point. But event sender API allows
|
| + // that. So we should handle it gracefully.
|
| + WebTouchPoint web_touch_point(web_pointer_event);
|
| + bool found_existing_id = false;
|
| + for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length;
|
| + ++i) {
|
| + if (last_coalesced_touch_event_.touches[i].id == web_pointer_event.id) {
|
| + last_coalesced_touch_event_.touches[i] =
|
| + CreateWebTouchPointFromWebPointerEvent(web_pointer_event, false);
|
| + found_existing_id = true;
|
| + break;
|
| + }
|
| + }
|
| + // If the pointerdown point didn't exist add a new point to the array.
|
| + if (!found_existing_id) {
|
| + last_coalesced_touch_event_
|
| + .touches[last_coalesced_touch_event_.touches_length++] =
|
| + CreateWebTouchPointFromWebPointerEvent(web_pointer_event, false);
|
| + }
|
| + struct {
|
| + bool operator()(const WebTouchPoint& a, const WebTouchPoint& b) {
|
| + return a.id < b.id;
|
| + }
|
| + } id_based_event_comparison;
|
| + std::sort(last_coalesced_touch_event_.touches,
|
| + last_coalesced_touch_event_.touches +
|
| + last_coalesced_touch_event_.touches_length,
|
| + id_based_event_comparison);
|
| + result.AddCoalescedEvent(last_coalesced_touch_event_);
|
| + } else {
|
| + for (unsigned i = 0; i < last_coalesced_touch_event_.touches_length;
|
| + ++i) {
|
| + if (last_coalesced_touch_event_.touches[i].id == web_pointer_event.id) {
|
| + last_coalesced_touch_event_.touches[i].movement_x =
|
| + web_pointer_event.movement_x;
|
| + last_coalesced_touch_event_.touches[i].movement_y =
|
| + web_pointer_event.movement_y;
|
| + last_coalesced_touch_event_.SetTimeStampSeconds(
|
| + web_pointer_event.TimeStampSeconds());
|
| + last_coalesced_touch_event_.touches[i].state =
|
| + TouchPointStateFromPointerEventType(web_pointer_event.GetType(),
|
| + false);
|
| + result.AddCoalescedEvent(last_coalesced_touch_event_);
|
| +
|
| + // Remove up and canceled points.
|
| + unsigned result_size = 0;
|
| + for (unsigned j = 0; j < last_coalesced_touch_event_.touches_length;
|
| + j++) {
|
| + if (last_coalesced_touch_event_.touches[j].state !=
|
| + WebTouchPoint::kStateCancelled &&
|
| + last_coalesced_touch_event_.touches[j].state !=
|
| + WebTouchPoint::kStateReleased) {
|
| + last_coalesced_touch_event_.touches[result_size++] =
|
| + last_coalesced_touch_event_.touches[j];
|
| + }
|
| + }
|
| + last_coalesced_touch_event_.touches_length = result_size;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (unsigned i = 0; i < event.touches_length; ++i) {
|
| + event.touches[i].state = blink::WebTouchPoint::kStateStationary;
|
| + event.touches[i].movement_x = 0;
|
| + event.touches[i].movement_y = 0;
|
| + }
|
| + }
|
| +
|
| + return result;
|
| }
|
|
|
| -WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| - const WebTouchEvent& event,
|
| - const Vector<WebTouchEvent>& coalesced_events,
|
| - bool all_touches_released) {
|
| +WebInputEventResult
|
| +TouchEventManager::DispatchTouchEventFromAccumulatdTouchPoints() {
|
| // 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
|
| // lists fit together.
|
|
|
| - if (event.GetType() == WebInputEvent::kTouchEnd ||
|
| - event.GetType() == WebInputEvent::kTouchCancel ||
|
| - event.touches_length > 1) {
|
| - suppressing_touchmoves_within_slop_ = false;
|
| + bool new_touch_point_since_last_dispatch = false;
|
| + bool any_touch_canceled_or_ended = false;
|
| + bool all_touch_points_pressed = true;
|
| +
|
| + for (const auto& attr : touch_attribute_map_.Values()) {
|
| + if (!attr->stale_)
|
| + new_touch_point_since_last_dispatch = true;
|
| + if (attr->event_.GetType() == WebInputEvent::kPointerUp ||
|
| + attr->event_.GetType() == WebInputEvent::kPointerCancel)
|
| + any_touch_canceled_or_ended = true;
|
| + if (attr->event_.GetType() != WebInputEvent::kPointerDown)
|
| + all_touch_points_pressed = false;
|
| }
|
|
|
| - if (suppressing_touchmoves_within_slop_ &&
|
| - event.GetType() == WebInputEvent::kTouchMove) {
|
| - if (!event.moved_beyond_slop_region)
|
| - return WebInputEventResult::kHandledSuppressed;
|
| + if (!new_touch_point_since_last_dispatch)
|
| + return WebInputEventResult::kNotHandled;
|
| +
|
| + if (any_touch_canceled_or_ended || touch_attribute_map_.size() > 1)
|
| suppressing_touchmoves_within_slop_ = false;
|
| +
|
| + if (suppressing_touchmoves_within_slop_) {
|
| + // There is exactly one touch point here otherwise
|
| + // |suppressing_touchmoves_within_slop_| would have been false.
|
| + DCHECK_EQ(1U, touch_attribute_map_.size());
|
| + const auto& touch_point_attribute = touch_attribute_map_.begin()->value;
|
| + if (touch_point_attribute->event_.GetType() ==
|
| + WebInputEvent::kPointerMove) {
|
| + if (!touch_point_attribute->event_.moved_beyond_slop_region)
|
| + return WebInputEventResult::kHandledSuppressed;
|
| + suppressing_touchmoves_within_slop_ = false;
|
| + }
|
| }
|
|
|
| // Holds the complete set of touches on the screen.
|
| @@ -214,15 +447,19 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| TargetTouchesHeapMap touches_by_target;
|
|
|
| // Array of touches per state, used to assemble the |changedTouches| list.
|
| - ChangedTouches changed_touches[WebTouchPoint::kStateMax + 1];
|
| -
|
| - for (unsigned touch_point_idx = 0; touch_point_idx < event.touches_length;
|
| - touch_point_idx++) {
|
| - const WebTouchPoint& point = event.TouchPointInRootFrame(touch_point_idx);
|
| - WebTouchPoint::State point_state = point.state;
|
| + ChangedTouches changed_touches[WebInputEvent::kPointerTypeLast -
|
| + WebInputEvent::kPointerTypeFirst + 1];
|
| +
|
| + Vector<int> available_ids;
|
| + for (const auto& id : touch_attribute_map_.Keys())
|
| + available_ids.push_back(id);
|
| + std::sort(available_ids.begin(), available_ids.end());
|
| + for (const int& touch_point_id : available_ids) {
|
| + const auto& touch_point_attribute = touch_attribute_map_.at(touch_point_id);
|
| + WebInputEvent::Type event_type = touch_point_attribute->event_.GetType();
|
| bool known_target;
|
|
|
| - Touch* touch = CreateDomTouch(point, &known_target);
|
| + Touch* touch = CreateDomTouch(touch_point_attribute, &known_target);
|
| EventTarget* touch_target = touch->target();
|
|
|
| // Ensure this target's touch list exists, even if it ends up empty, so
|
| @@ -237,8 +474,8 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| // |touches| and |targetTouches| should only contain information about
|
| // touches still on the screen, so if this point is released or
|
| // cancelled it will only appear in the |changedTouches| list.
|
| - if (point_state != WebTouchPoint::kStateReleased &&
|
| - point_state != WebTouchPoint::kStateCancelled) {
|
| + if (event_type != WebInputEvent::kPointerUp &&
|
| + event_type != WebInputEvent::kPointerCancel) {
|
| touches->Append(touch);
|
| target_touches_iterator->value->Append(touch);
|
| }
|
| @@ -249,43 +486,37 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| // never be in the |changedTouches| list so we do not handle them
|
| // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609
|
| // for further discussion about the TouchStationary state.
|
| - if (point_state != WebTouchPoint::kStateStationary && known_target) {
|
| - DCHECK_LE(point_state, WebTouchPoint::kStateMax);
|
| - if (!changed_touches[point_state].touches_)
|
| - changed_touches[point_state].touches_ = TouchList::Create();
|
| - changed_touches[point_state].touches_->Append(touch);
|
| - changed_touches[point_state].targets_.insert(touch_target);
|
| - changed_touches[point_state].pointer_type_ = point.pointer_type;
|
| + if (!touch_point_attribute->stale_ && known_target) {
|
| + size_t event_type_idx = event_type - WebInputEvent::kPointerTypeFirst;
|
| + if (!changed_touches[event_type_idx].touches_)
|
| + changed_touches[event_type_idx].touches_ = TouchList::Create();
|
| + changed_touches[event_type_idx].touches_->Append(touch);
|
| + changed_touches[event_type_idx].targets_.insert(touch_target);
|
| }
|
| }
|
|
|
| - if (all_touches_released) {
|
| - touch_sequence_document_.Clear();
|
| - current_touch_action_ = TouchAction::kTouchActionAuto;
|
| - }
|
| -
|
| WebInputEventResult event_result = WebInputEventResult::kNotHandled;
|
| - // First we construct the webcoalescedinputevent contains all the coalesced
|
| +
|
| + // First we construct the webcoalescedinputevent containing all the coalesced
|
| // touch event.
|
| - std::vector<const WebInputEvent*> coalesced_touches;
|
| - for (size_t i = 0; i < coalesced_events.size(); ++i) {
|
| - coalesced_touches.push_back(&coalesced_events[i]);
|
| - }
|
| - WebCoalescedInputEvent coalesced_event(event, coalesced_touches);
|
| + WebCoalescedInputEvent coalesced_event = GenerateWebCoalescedInputEvent();
|
|
|
| // Now iterate through the |changedTouches| list and |m_targets| within it,
|
| // sending TouchEvents to the targets as required.
|
| - for (unsigned state = 0; state <= WebTouchPoint::kStateMax; ++state) {
|
| - if (!changed_touches[state].touches_)
|
| + for (unsigned action = WebInputEvent::kPointerTypeFirst;
|
| + action <= WebInputEvent::kPointerTypeLast; ++action) {
|
| + size_t action_idx = action - WebInputEvent::kPointerTypeFirst;
|
| + if (!changed_touches[action_idx].touches_)
|
| continue;
|
|
|
| - const AtomicString& event_name(TouchEventNameForTouchPointState(
|
| - static_cast<WebTouchPoint::State>(state)));
|
| - for (const auto& event_target : changed_touches[state].targets_) {
|
| + const AtomicString& event_name(TouchEventNameForPointerEventType(
|
| + static_cast<WebInputEvent::Type>(action)));
|
| +
|
| + for (const auto& event_target : changed_touches[action_idx].targets_) {
|
| EventTarget* touch_event_target = event_target;
|
| TouchEvent* touch_event = TouchEvent::Create(
|
| coalesced_event, touches, touches_by_target.at(touch_event_target),
|
| - changed_touches[state].touches_.Get(), event_name,
|
| + changed_touches[action_idx].touches_.Get(), event_name,
|
| touch_event_target->ToNode()->GetDocument().domWindow(),
|
| current_touch_action_);
|
|
|
| @@ -294,66 +525,15 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
|
|
| // Only report for top level documents with a single touch on
|
| // touch-start or the first touch-move.
|
| - if (event.touch_start_or_first_touch_move && event.touches_length == 1 &&
|
| - 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 latency_in_micros =
|
| - (TimeTicks::Now() -
|
| - TimeTicks::FromSeconds(event.TimeStampSeconds()))
|
| - .InMicroseconds();
|
| - if (event.IsCancelable()) {
|
| - if (frame_->GetDocument()->IsLoadCompleted()) {
|
| - DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| - touch_dispositions_after_page_load_histogram,
|
| - ("Event.Touch.TouchDispositionsAfterPageLoad",
|
| - kTouchEventDispatchResultTypeMax));
|
| - touch_dispositions_after_page_load_histogram.Count(
|
| - (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| - ? kHandledTouches
|
| - : kUnhandledTouches);
|
| -
|
| - DEFINE_STATIC_LOCAL(
|
| - CustomCountHistogram, event_latency_after_page_load_histogram,
|
| - ("Event.Touch.TouchLatencyAfterPageLoad", 1, 100000000, 50));
|
| - event_latency_after_page_load_histogram.Count(latency_in_micros);
|
| - } else {
|
| - DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| - touch_dispositions_before_page_load_histogram,
|
| - ("Event.Touch.TouchDispositionsBeforePageLoad",
|
| - kTouchEventDispatchResultTypeMax));
|
| - touch_dispositions_before_page_load_histogram.Count(
|
| - (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| - ? kHandledTouches
|
| - : kUnhandledTouches);
|
| -
|
| - DEFINE_STATIC_LOCAL(
|
| - CustomCountHistogram, event_latency_before_page_load_histogram,
|
| - ("Event.Touch.TouchLatencyBeforePageLoad", 1, 100000000, 50));
|
| - event_latency_before_page_load_histogram.Count(latency_in_micros);
|
| - }
|
| - // Report the touch disposition there is no active fling animation.
|
| - DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| - touch_dispositions_outside_fling_histogram,
|
| - ("Event.Touch.TouchDispositionsOutsideFling2",
|
| - kTouchEventDispatchResultTypeMax));
|
| - touch_dispositions_outside_fling_histogram.Count(
|
| - (dom_dispatch_result != DispatchEventResult::kNotCanceled)
|
| - ? kHandledTouches
|
| - : kUnhandledTouches);
|
| - }
|
| -
|
| - // Report the touch disposition when there is an active fling animation.
|
| - if (event.dispatch_type ==
|
| - WebInputEvent::kListenersForcedNonBlockingDueToFling) {
|
| - DEFINE_STATIC_LOCAL(EnumerationHistogram,
|
| - touch_dispositions_during_fling_histogram,
|
| - ("Event.Touch.TouchDispositionsDuringFling2",
|
| - kTouchEventDispatchResultTypeMax));
|
| - touch_dispositions_during_fling_histogram.Count(
|
| - touch_event->PreventDefaultCalledOnUncancelableEvent()
|
| - ? kHandledTouches
|
| - : kUnhandledTouches);
|
| + if (touch_attribute_map_.size() == 1 && frame_->IsMainFrame()) {
|
| + const auto& event = touch_attribute_map_.begin()->value->event_;
|
| + if (event.touch_start_or_first_touch_move) {
|
| + // Record the disposition and latency of touch starts and first touch
|
| + // moves before and after the page is fully loaded respectively.
|
| + ReportMetricsForTouch(
|
| + event, dom_dispatch_result,
|
| + touch_event->PreventDefaultCalledOnUncancelableEvent(),
|
| + frame_->GetDocument()->IsLoadCompleted());
|
| }
|
| }
|
| event_result = EventHandlingUtil::MergeEventResult(
|
| @@ -362,8 +542,9 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| }
|
| }
|
|
|
| - // Do not suppress any touchmoves if the touchstart is consumed.
|
| - if (IsTouchSequenceStart(event) &&
|
| + // Suppress following touchmoves within the slop region if the touchstart is
|
| + // not consumed.
|
| + if (all_touch_points_pressed &&
|
| event_result == WebInputEventResult::kNotHandled) {
|
| suppressing_touchmoves_within_slop_ = true;
|
| }
|
| @@ -371,14 +552,22 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents(
|
| return event_result;
|
| }
|
|
|
| -void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart(
|
| - const WebTouchPoint& touch_point,
|
| +void TouchEventManager::UpdateTouchAttributeMapsForPointerDown(
|
| + const WebPointerEvent& event,
|
| const EventHandlingUtil::PointerEventTarget& pointer_event_target) {
|
| // Touch events implicitly capture to the touched node, and don't change
|
| // active/hover states themselves (Gesture events do). So we only need
|
| // to hit-test on touchstart and when the target could be different than
|
| // the corresponding pointer event target.
|
| - DCHECK(touch_point.state == WebTouchPoint::kStatePressed);
|
| + DCHECK(event.GetType() == WebInputEvent::kPointerDown);
|
| + // Ideally we'd DCHECK(!touch_attribute_map_.Contains(event.id))
|
| + // since we shouldn't get a touchstart for a touch that's already
|
| + // down. However EventSender allows this to be violated and there's
|
| + // some tests that take advantage of it. There may also be edge
|
| + // cases in the browser where this happens.
|
| + // See http://crbug.com/345372.
|
| + touch_attribute_map_.Set(event.id, new TouchPointAttributes(event));
|
| +
|
| Node* touch_node = pointer_event_target.target_node;
|
| String region = pointer_event_target.region;
|
|
|
| @@ -398,7 +587,7 @@ void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart(
|
| if (touch_sequence_document_->GetFrame()) {
|
| LayoutPoint frame_point = LayoutPoint(
|
| touch_sequence_document_->GetFrame()->View()->RootFrameToContents(
|
| - touch_point.PositionInWidget()));
|
| + event.PositionInWidget()));
|
| result = EventHandlingUtil::HitTestResultInFrame(
|
| touch_sequence_document_->GetFrame(), frame_point, hit_type);
|
| Node* node = result.InnerNode();
|
| @@ -430,15 +619,9 @@ void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart(
|
| DCHECK(touch_sequence_document_->GetFrame()->View());
|
| }
|
|
|
| - // Ideally we'd DCHECK(!m_targetForTouchID.contains(point.id())
|
| - // since we shouldn't get a touchstart for a touch that's already
|
| - // down. However EventSender allows this to be violated and there's
|
| - // some tests that take advantage of it. There may also be edge
|
| - // cases in the browser where this happens.
|
| - // See http://crbug.com/345372.
|
| - target_for_touch_id_.Set(touch_point.id, touch_node);
|
| -
|
| - region_for_touch_id_.Set(touch_point.id, region);
|
| + TouchPointAttributes* attributes = touch_attribute_map_.at(event.id);
|
| + attributes->target_ = touch_node;
|
| + attributes->region_ = region;
|
|
|
| TouchAction effective_touch_action =
|
| TouchActionUtil::ComputeEffectiveTouchAction(*touch_node);
|
| @@ -452,28 +635,20 @@ void TouchEventManager::UpdateTargetAndRegionMapsForTouchStart(
|
| }
|
| }
|
|
|
| -bool TouchEventManager::HitTestTouchPointsIfNeeded(
|
| - const WebTouchEvent& event,
|
| - const HeapVector<EventHandlingUtil::PointerEventTarget>&
|
| - pointer_event_targets) {
|
| - bool new_touch_sequence = true;
|
| - bool all_touches_released = true;
|
| -
|
| - for (unsigned i = 0; i < event.touches_length; ++i) {
|
| - WebTouchPoint::State state = event.touches[i].state;
|
| - if (state != WebTouchPoint::kStatePressed)
|
| - new_touch_sequence = false;
|
| - if (state != WebTouchPoint::kStateReleased &&
|
| - state != WebTouchPoint::kStateCancelled)
|
| - all_touches_released = false;
|
| - }
|
| - if (new_touch_sequence) {
|
| +void TouchEventManager::HandleTouchPoint(
|
| + const WebPointerEvent& event,
|
| + const Vector<WebPointerEvent>& coalesced_events,
|
| + const EventHandlingUtil::PointerEventTarget& pointer_event_target) {
|
| + DCHECK_GE(event.GetType(), WebInputEvent::kPointerTypeFirst);
|
| + DCHECK_LE(event.GetType(), WebInputEvent::kPointerTypeLast);
|
| +
|
| + if (touch_attribute_map_.IsEmpty()) {
|
| // Ideally we'd DCHECK(!m_touchSequenceDocument) here since we should
|
| // have cleared the active document when we saw the last release. But we
|
| // have some tests that violate this, ClusterFuzz could trigger it, and
|
| // there may be cases where the browser doesn't reliably release all
|
| // touches. http://crbug.com/345372 tracks this.
|
| - touch_sequence_document_.Clear();
|
| + AllTouchesReleasedCleanup();
|
| }
|
|
|
| DCHECK(frame_->View());
|
| @@ -482,59 +657,74 @@ bool TouchEventManager::HitTestTouchPointsIfNeeded(
|
| !touch_sequence_document_->GetFrame()->View())) {
|
| // If the active touch document has no frame or view, it's probably being
|
| // destroyed so we can't dispatch events.
|
| - return false;
|
| + return;
|
| }
|
|
|
| - for (unsigned i = 0; i < event.touches_length; ++i) {
|
| - // In touch event model only touch starts can set the target and after that
|
| - // the touch event always goes to that target.
|
| - if (event.touches[i].state == WebTouchPoint::kStatePressed) {
|
| - UpdateTargetAndRegionMapsForTouchStart(event.TouchPointInRootFrame(i),
|
| - pointer_event_targets[i]);
|
| - }
|
| + // In touch event model only touch starts can set the target and after that
|
| + // the touch event always goes to that target.
|
| + if (event.GetType() == WebInputEvent::kPointerDown) {
|
| + UpdateTouchAttributeMapsForPointerDown(event, pointer_event_target);
|
| }
|
|
|
| - touch_pressed_ = !all_touches_released;
|
| -
|
| - // If there's no document receiving touch events, or no handlers on the
|
| - // document set to receive the events, then we can skip all the rest of
|
| - // this work.
|
| - if (!touch_sequence_document_ || !touch_sequence_document_->GetPage() ||
|
| - !HasTouchHandlers(
|
| - touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) ||
|
| - !touch_sequence_document_->GetFrame()) {
|
| - if (all_touches_released) {
|
| - touch_sequence_document_.Clear();
|
| - }
|
| - return false;
|
| + // We might not receive the down action for a touch point. In that case we
|
| + // would have never added them to |touch_attribute_map_| or hit-tested
|
| + // them. For those just keep them in the map with a null target. Later they
|
| + // will be targeted at the |touch_sequence_document_|.
|
| + if (!touch_attribute_map_.Contains(event.id)) {
|
| + touch_attribute_map_.insert(event.id, new TouchPointAttributes(event));
|
| }
|
|
|
| - return true;
|
| + TouchPointAttributes* attributes = touch_attribute_map_.at(event.id);
|
| + attributes->event_ = event;
|
| + attributes->coalesced_events_ = coalesced_events;
|
| + attributes->stale_ = false;
|
| }
|
|
|
| -WebInputEventResult TouchEventManager::HandleTouchEvent(
|
| - const WebTouchEvent& event,
|
| - const Vector<WebTouchEvent>& coalesced_events,
|
| - const HeapVector<EventHandlingUtil::PointerEventTarget>&
|
| - pointer_event_targets) {
|
| - DCHECK(event.touches_length == pointer_event_targets.size());
|
| +WebInputEventResult TouchEventManager::FlushEvents() {
|
| + WebInputEventResult result = WebInputEventResult::kNotHandled;
|
|
|
| - if (!HitTestTouchPointsIfNeeded(event, pointer_event_targets))
|
| - return WebInputEventResult::kNotHandled;
|
| + // If there's no document receiving touch events, or no handlers on the
|
| + // document set to receive the events, then we can skip all the rest of
|
| + // sending the event.
|
| + if (touch_sequence_document_ && touch_sequence_document_->GetPage() &&
|
| + HasTouchHandlers(
|
| + touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) &&
|
| + touch_sequence_document_->GetFrame() &&
|
| + touch_sequence_document_->GetFrame()->View()) {
|
| + result = DispatchTouchEventFromAccumulatdTouchPoints();
|
| + }
|
| +
|
| + // Cleanup the |touch_attribute_map_| map from released and canceled
|
| + // touch points.
|
| + Vector<int> released_canceled_points;
|
| + for (auto& attributes : touch_attribute_map_.Values()) {
|
| + if (attributes->event_.GetType() == WebInputEvent::kPointerUp ||
|
| + attributes->event_.GetType() == WebInputEvent::kPointerCancel) {
|
| + released_canceled_points.push_back(attributes->event_.id);
|
| + } else {
|
| + attributes->stale_ = true;
|
| + attributes->event_.movement_x = 0;
|
| + attributes->event_.movement_y = 0;
|
| + attributes->coalesced_events_.clear();
|
| + }
|
| + }
|
| + touch_attribute_map_.RemoveAll(released_canceled_points);
|
|
|
| - bool all_touches_released = true;
|
| - for (unsigned i = 0; i < event.touches_length; ++i) {
|
| - WebTouchPoint::State state = event.touches[i].state;
|
| - if (state != WebTouchPoint::kStateReleased &&
|
| - state != WebTouchPoint::kStateCancelled)
|
| - all_touches_released = false;
|
| + if (touch_attribute_map_.IsEmpty()) {
|
| + AllTouchesReleasedCleanup();
|
| }
|
|
|
| - return DispatchTouchEvents(event, coalesced_events, all_touches_released);
|
| + return result;
|
| +}
|
| +
|
| +void TouchEventManager::AllTouchesReleasedCleanup() {
|
| + touch_sequence_document_.Clear();
|
| + current_touch_action_ = TouchAction::kTouchActionAuto;
|
| + last_coalesced_touch_event_ = WebTouchEvent();
|
| }
|
|
|
| bool TouchEventManager::IsAnyTouchActive() const {
|
| - return touch_pressed_;
|
| + return !touch_attribute_map_.IsEmpty();
|
| }
|
|
|
| } // namespace blink
|
|
|