Chromium Code Reviews| 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 = |
|
jkwang
2017/07/10 23:49:39
Why not call "CreateWebTouchPointFromWebPointerEve
Navid Zolghadr
2017/07/11 02:21:28
I cannot think of any reason not to. Calling "Crea
|
| + 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 |