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..970d58c096739eff0266fee82871377da685bb5b 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,16 +60,24 @@ 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 |
| @@ -92,8 +98,6 @@ class ChangedTouches final { |
| using EventTargetSet = HeapHashSet<Member<EventTarget>>; |
| // Set of targets involved in m_touches. |
| EventTargetSet targets_; |
| - |
| - WebPointerProperties::PointerType pointer_type_; |
| }; |
| } // namespace |
| @@ -104,9 +108,7 @@ 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_points_attributes_.clear(); |
| suppressing_touchmoves_within_slop_ = false; |
| current_touch_action_ = TouchAction::kTouchActionAuto; |
| } |
| @@ -114,31 +116,18 @@ void TouchEventManager::Clear() { |
| DEFINE_TRACE(TouchEventManager) { |
| visitor->Trace(frame_); |
| visitor->Trace(touch_sequence_document_); |
| - visitor->Trace(target_for_touch_id_); |
| + visitor->Trace(touch_points_attributes_); |
| } |
| -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 +158,148 @@ 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_points_attributes_.IsEmpty()); |
| + |
| + WebTouchEvent event; |
| + |
| + const auto& first_touch_pointer_event = |
| + touch_points_attributes_.begin()->value->event_; |
| + event.dispatch_type = first_touch_pointer_event.dispatch_type; |
| + event.touch_start_or_first_touch_move = |
| + first_touch_pointer_event.touch_start_or_first_touch_move; |
| + event.moved_beyond_slop_region = |
| + first_touch_pointer_event.moved_beyond_slop_region; |
| + event.dispatch_type = first_touch_pointer_event.dispatch_type; |
| + event.dispatch_type = first_touch_pointer_event.dispatch_type; |
| + event.SetFrameScale(first_touch_pointer_event.FrameScale()); |
| + event.SetFrameTranslate(first_touch_pointer_event.FrameTranslate()); |
| + event.SetTimeStampSeconds(first_touch_pointer_event.TimeStampSeconds()); |
| + event.SetModifiers(first_touch_pointer_event.GetModifiers()); |
| + |
| + WebInputEvent::Type touch_event_type = WebInputEvent::kTouchMove; |
| + Vector<WebPointerEvent> all_coalesced_events; |
| + Vector<int> available_ids; |
| + for (const auto& id : touch_points_attributes_.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_points_attributes_.at(touch_point_id); |
| + const WebPointerEvent& touch_pointer_event = touch_point_attribute->event_; |
| + WebTouchPoint web_touch_point(touch_pointer_event); |
| + web_touch_point.state = TouchPointStateFromPointerEventType( |
| + touch_pointer_event.GetType(), touch_point_attribute->stale_); |
| + web_touch_point.radius_x = touch_pointer_event.width; |
|
mustaq
2017/06/09 16:12:27
Repeat the TODO here for radius/width problem.
Navid Zolghadr
2017/06/12 16:17:31
I didn't have TODO anywhere else. I added a TODO h
|
| + web_touch_point.radius_y = touch_pointer_event.height; |
| + web_touch_point.rotation_angle = touch_pointer_event.rotation_angle; |
| + event.touches[event.touches_length++] = web_touch_point; |
| + |
| + // 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. |
| + if (touch_event_type == WebInputEvent::kTouchMove) { |
|
mustaq
2017/06/09 16:12:27
You already hardcoded the event type in Line 197 a
Navid Zolghadr
2017/06/12 16:17:31
This is in a for loop. So the value of the touch_e
|
| + 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; |
| + } |
| + |
| + event.SetType(touch_event_type); |
| + |
| + for (const WebPointerEvent& coalesced_event : |
| + touch_point_attribute->coalesced_events_) |
| + all_coalesced_events.push_back(coalesced_event); |
| + } |
| + |
| + // 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); |
| + // This assumes that we only get move events as coalesced events. |
|
mustaq
2017/06/09 16:12:27
This comment also applies to the above hardcoded k
Navid Zolghadr
2017/06/12 16:17:31
I added a DCHECK. However, I also changed the code
|
| + for (const auto& web_pointer_event : all_coalesced_events) { |
| + for (unsigned i = 0; i < event.touches_length; ++i) { |
| + if (event.touches[i].id == web_pointer_event.id && |
| + event.touches[i].state == blink::WebTouchPoint::kStateMoved && |
| + web_pointer_event.GetType() == WebInputEvent::kPointerMove) { |
| + event.touches[i].movement_x = web_pointer_event.movement_x; |
| + event.touches[i].movement_y = web_pointer_event.movement_y; |
| + event.SetTimeStampSeconds(web_pointer_event.TimeStampSeconds()); |
| + result.AddCoalescedEvent(event); |
| + break; |
| + } |
| + } |
| + for (unsigned i = 0; i < event.touches_length; ++i) { |
|
mustaq
2017/06/09 16:12:27
After adding to the coalesced events, |event| is n
Navid Zolghadr
2017/06/12 16:17:31
It's used for the next coalesced event (i.e. next
|
| + event.touches[i].state = blink::WebTouchPoint::kStateStationary; |
| + event.touches[i].movement_x = 0; |
| + event.touches[i].movement_y = 0; |
| + } |
| + } |
| - return Touch::Create(target_frame, touch_node, point.id, |
| - point.PositionInScreen(), content_point, adjusted_radius, |
| - point.rotation_angle, point.force, region_id); |
| + return result; |
| } |
| -WebInputEventResult TouchEventManager::DispatchTouchEvents( |
| - const WebTouchEvent& event, |
| - const Vector<WebTouchEvent>& coalesced_events, |
| - bool all_touches_released) { |
| +WebInputEventResult TouchEventManager::DispatchTouchEvents() { |
| // 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_raf = false; |
|
mustaq
2017/06/09 16:12:27
Let's not use "raf" here since this method will ev
Navid Zolghadr
2017/06/12 16:17:31
Done.
|
| + bool any_touch_canceled_ended = false; |
|
mustaq
2017/06/09 16:12:27
..._canceled_or_ended?
Navid Zolghadr
2017/06/12 16:17:32
Done.
|
| + bool all_touch_points_pressed = true; |
| + |
| + for (const auto& attr : touch_points_attributes_.Values()) { |
| + if (!attr->stale_) |
| + new_touch_point_since_last_raf = true; |
| + if (attr->event_.GetType() == WebInputEvent::kPointerUp || |
| + attr->event_.GetType() == WebInputEvent::kPointerCancel) |
| + any_touch_canceled_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_raf) |
| + return WebInputEventResult::kNotHandled; |
| + |
| + if (any_touch_canceled_ended || touch_points_attributes_.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(touch_points_attributes_.size(), 1); |
| + const auto& touch_point_attribute = touch_points_attributes_.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 +311,23 @@ 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_points_attributes_.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_points_attributes_.at(touch_point_id); |
| + WebInputEvent::Type pointer_action = |
|
mustaq
2017/06/09 16:12:27
s/pointer_action/pointer_event_type/
Navid Zolghadr
2017/06/12 16:17:31
Done.
|
| + touch_point_attribute->event_.GetType(); |
| + size_t pointer_action_idx = |
|
mustaq
2017/06/09 16:12:27
event_type_idx?
Navid Zolghadr
2017/06/12 16:17:31
Done.
|
| + pointer_action - WebInputEvent::kPointerTypeFirst; |
| 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 +342,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 (pointer_action != WebInputEvent::kPointerUp && |
| + pointer_action != WebInputEvent::kPointerCancel) { |
| touches->Append(touch); |
| target_touches_iterator->value->Append(touch); |
| } |
| @@ -249,43 +354,36 @@ 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) { |
| + if (!changed_touches[pointer_action_idx].touches_) |
| + changed_touches[pointer_action_idx].touches_ = TouchList::Create(); |
| + changed_touches[pointer_action_idx].touches_->Append(touch); |
| + changed_touches[pointer_action_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 |
|
mustaq
2017/06/09 16:12:27
/containing/
Navid Zolghadr
2017/06/12 16:17:31
Done.
|
| // 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 +392,72 @@ WebInputEventResult TouchEventManager::DispatchTouchEvents( |
| // Only report for top level documents with a single touch on |
|
mustaq
2017/06/09 16:12:27
The UMA block is too long and a bit distracting he
Navid Zolghadr
2017/06/12 16:17:31
Done.
|
| // 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()) { |
| + if (touch_points_attributes_.size() == 1 && frame_->IsMainFrame()) { |
| + const auto& event = touch_points_attributes_.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. |
| + 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_after_page_load_histogram, |
| - ("Event.Touch.TouchDispositionsAfterPageLoad", |
| + touch_dispositions_outside_fling_histogram, |
| + ("Event.Touch.TouchDispositionsOutsideFling2", |
| kTouchEventDispatchResultTypeMax)); |
| - touch_dispositions_after_page_load_histogram.Count( |
| + touch_dispositions_outside_fling_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 { |
| + // Report the touch disposition when there is an active fling |
| + // animation. |
| + // if (event[0].first.dispatch_type == |
| + if (event.dispatch_type == |
| + WebInputEvent::kListenersForcedNonBlockingDueToFling) { |
| DEFINE_STATIC_LOCAL(EnumerationHistogram, |
| - touch_dispositions_before_page_load_histogram, |
| - ("Event.Touch.TouchDispositionsBeforePageLoad", |
| + touch_dispositions_during_fling_histogram, |
| + ("Event.Touch.TouchDispositionsDuringFling2", |
| kTouchEventDispatchResultTypeMax)); |
| - touch_dispositions_before_page_load_histogram.Count( |
| - (dom_dispatch_result != DispatchEventResult::kNotCanceled) |
| + touch_dispositions_during_fling_histogram.Count( |
| + touch_event->PreventDefaultCalledOnUncancelableEvent() |
| ? 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); |
| } |
| } |
| event_result = EventHandlingUtil::MergeEventResult( |
| @@ -362,8 +466,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 +476,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_points_attributes_.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_points_attributes_.Set(event.id, new TouchPointAttributes(event)); |
| + |
| Node* touch_node = pointer_event_target.target_node; |
| String region = pointer_event_target.region; |
| @@ -398,7 +511,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 +543,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_points_attributes_.at(event.id); |
| + attributes->target_ = touch_node; |
| + attributes->region_ = region; |
| TouchAction effective_touch_action = |
| TouchActionUtil::ComputeEffectiveTouchAction(*touch_node); |
| @@ -452,28 +559,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_points_attributes_.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 +581,73 @@ 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_points_attributes_| 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_points_attributes_.Contains(event.id)) { |
| + touch_points_attributes_.insert(event.id, new TouchPointAttributes(event)); |
| } |
| - return true; |
| + TouchPointAttributes* attributes = touch_points_attributes_.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::HandleVSyncSignal() { |
| + // 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. |
| + WebInputEventResult result = WebInputEventResult::kNotHandled; |
| + if (touch_sequence_document_ && touch_sequence_document_->GetPage() && |
| + HasTouchHandlers( |
| + touch_sequence_document_->GetPage()->GetEventHandlerRegistry()) && |
| + touch_sequence_document_->GetFrame() && |
| + touch_sequence_document_->GetFrame()->View()) { |
| + result = DispatchTouchEvents(); |
| + } |
| - if (!HitTestTouchPointsIfNeeded(event, pointer_event_targets)) |
| - return WebInputEventResult::kNotHandled; |
| + // Cleanup the |touch_points_attributes_| map from released and canceled |
| + // touch points. |
| + Vector<int> releasedCanceledPoints; |
| + for (auto& attributes : touch_points_attributes_.Values()) { |
| + if (attributes->event_.GetType() == WebInputEvent::kPointerUp || |
| + attributes->event_.GetType() == WebInputEvent::kPointerCancel) { |
| + releasedCanceledPoints.push_back(attributes->event_.id); |
| + } else { |
| + attributes->stale_ = true; |
| + attributes->event_.movement_x = 0; |
| + attributes->event_.movement_y = 0; |
| + } |
| + } |
| + for (int id : releasedCanceledPoints) { |
| + touch_points_attributes_.erase(id); |
|
mustaq
2017/06/09 16:12:27
Can use .erase(releasedCanceledPoints) instead?
Navid Zolghadr
2017/06/12 16:17:31
Not quite. But there seems to be a RemoveAll funct
|
| + } |
| - 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_points_attributes_.IsEmpty()) { |
| + allTouchesReleasedCleanup(); |
| } |
| - return DispatchTouchEvents(event, coalesced_events, all_touches_released); |
| + return result; |
| +} |
| + |
| +void TouchEventManager::allTouchesReleasedCleanup() { |
| + touch_sequence_document_.Clear(); |
| + current_touch_action_ = TouchAction::kTouchActionAuto; |
| } |
| bool TouchEventManager::IsAnyTouchActive() const { |
| - return touch_pressed_; |
| + return !touch_points_attributes_.IsEmpty(); |
| } |
| } // namespace blink |