PointerEvents: Fix dispatch optimization

Summary:
Changelog: [Internal] Fixing a recent optimization to prevent event dispatches for events that are not listened for. An incorrect hitpath was passed in for `leave` events.

Refactored the PointerEvent optimization such that `filterByShouldDispatch` determines what views should dispatch a PointerEvent, and `dispatchEventForViewTargets` to actually dispatch them. We are separating this because the order of dispatch differs between `enter` and `leave` events.

Reviewed By: vincentriemer

Differential Revision: D37348726

fbshipit-source-id: a09a04df3ae027cce95e0d93a4163c2015fe3fe3
This commit is contained in:
Luna Wei 2022-06-23 11:01:04 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 15d9aa0cdb
Коммит fa814d4875
1 изменённых файлов: 85 добавлений и 64 удалений

Просмотреть файл

@ -20,6 +20,7 @@ import com.facebook.react.uimanager.events.PointerEventHelper;
import com.facebook.react.uimanager.events.PointerEventHelper.EVENT; import com.facebook.react.uimanager.events.PointerEventHelper.EVENT;
import com.facebook.react.uimanager.events.TouchEvent; import com.facebook.react.uimanager.events.TouchEvent;
import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper; import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -110,14 +111,17 @@ public class JSPointerDispatcher {
mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime); mTouchEventCoalescingKeyHelper.addCoalescingKey(mDownStartTime);
if (!supportsHover) { if (!supportsHover) {
dispatchNonBubblingEventForPathWhenListened( List<ViewTarget> enterViewTargets =
EVENT.ENTER, filterByShouldDispatch(hitPath, EVENT.ENTER, EVENT.ENTER_CAPTURE, false);
EVENT.ENTER_CAPTURE,
hitPath, // Dispatch root -> target, we need to reverse order of enterViewTargets
Collections.reverse(enterViewTargets);
dispatchEventForViewTargets(
PointerEventHelper.POINTER_ENTER,
enterViewTargets,
eventDispatcher, eventDispatcher,
surfaceId, surfaceId,
motionEvent, motionEvent);
false);
} }
boolean listeningForDown = boolean listeningForDown =
@ -201,14 +205,16 @@ public class JSPointerDispatcher {
} }
if (!supportsHover) { if (!supportsHover) {
dispatchNonBubblingEventForPathWhenListened( List<ViewTarget> leaveViewTargets =
EVENT.LEAVE, filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
EVENT.LEAVE_CAPTURE,
hitPath, // target -> root
dispatchEventForViewTargets(
PointerEventHelper.POINTER_LEAVE,
leaveViewTargets,
eventDispatcher, eventDispatcher,
surfaceId, surfaceId,
motionEvent, motionEvent);
false);
} }
return; return;
} }
@ -240,43 +246,47 @@ public class JSPointerDispatcher {
} }
/** /**
* Dispatch event only if ancestor is listening to relevant capture event. This should only be * Returns list of view targets that we should be dispatching events from
* relevant for ENTER/LEAVE events that need to be dispatched along every relevant view in the hit
* path.
* *
* @param pointerEventType - Should only be ENTER/LEAVE events * @param viewTargets, ordered from target -> root
* @param hitPath - ViewTargets ordered from target -> root * @param bubble, name of event that bubbles
* @param dispatcher * @param capture, name of event that captures
* @param surfaceId * @param forceDispatch, if true, all viewTargets should dispatch
* @param motionEvent * @return list of viewTargets filtered from target -> root
* @param forceDispatch - Ignore if ancestor is listening and force the event to be dispatched
*/ */
private static void dispatchNonBubblingEventForPathWhenListened( private static List<ViewTarget> filterByShouldDispatch(
EVENT event, List<ViewTarget> viewTargets, EVENT bubble, EVENT capture, boolean forceDispatch) {
EVENT captureEvent, if (forceDispatch) {
List<ViewTarget> hitPath, return viewTargets;
EventDispatcher dispatcher,
int surfaceId,
MotionEvent motionEvent,
boolean forceDispatch) {
boolean ancestorListening = forceDispatch;
String eventName = PointerEventHelper.getDispatchableEventName(event);
if (eventName == null) {
return;
} }
// iterate through hitPath from ancestor -> target boolean ancestorListening = false;
for (int i = hitPath.size() - 1; i >= 0; i--) { List<ViewTarget> dispatchableViewTargets = new ArrayList<ViewTarget>(viewTargets);
View view = hitPath.get(i).getView(); for (int i = viewTargets.size() - 1; i >= 0; i--) {
int viewId = hitPath.get(i).getViewId(); ViewTarget viewTarget = viewTargets.get(i);
if (ancestorListening View view = viewTarget.getView();
|| (i == 0 && PointerEventHelper.isListening(view, event)) if (!ancestorListening
|| PointerEventHelper.isListening(view, captureEvent)) { && (PointerEventHelper.isListening(view, capture)
dispatcher.dispatchEvent(PointerEvent.obtain(eventName, surfaceId, viewId, motionEvent)); || (i == 0 && PointerEventHelper.isListening(view, bubble)))) {
ancestorListening = true; ancestorListening = true;
} else if (!ancestorListening) {
dispatchableViewTargets.remove(i);
} }
} }
return dispatchableViewTargets;
}
private static void dispatchEventForViewTargets(
String eventName,
List<ViewTarget> viewTargets,
EventDispatcher dispatcher,
int surfaceId,
MotionEvent motionEvent) {
for (ViewTarget viewTarget : viewTargets) {
int viewId = viewTarget.getViewId();
dispatcher.dispatchEvent(PointerEvent.obtain(eventName, surfaceId, viewId, motionEvent));
}
} }
// called on hover_move motion events only // called on hover_move motion events only
@ -360,31 +370,40 @@ public class JSPointerDispatcher {
// If something has changed in either enter/exit, let's start a new coalescing key // If something has changed in either enter/exit, let's start a new coalescing key
mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey); mTouchEventCoalescingKeyHelper.incrementCoalescingKey(mHoverInteractionKey);
// target -> root
List<ViewTarget> enterViewTargets = List<ViewTarget> enterViewTargets =
hitPath.subList(0, hitPath.size() - firstDivergentIndexFromBack); filterByShouldDispatch(
hitPath.subList(0, hitPath.size() - firstDivergentIndexFromBack),
EVENT.ENTER,
EVENT.ENTER_CAPTURE,
nonDivergentListeningToEnter);
if (enterViewTargets.size() > 0) { if (enterViewTargets.size() > 0) {
dispatchNonBubblingEventForPathWhenListened( // We want to iterate these from root -> target so we need to reverse
EVENT.ENTER, Collections.reverse(enterViewTargets);
EVENT.ENTER_CAPTURE, dispatchEventForViewTargets(
PointerEventHelper.POINTER_ENTER,
enterViewTargets, enterViewTargets,
eventDispatcher, eventDispatcher,
surfaceId, surfaceId,
motionEvent, motionEvent);
nonDivergentListeningToEnter);
} }
List<ViewTarget> exitViewTargets = // target -> root
mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack); List<ViewTarget> leaveViewTargets =
if (exitViewTargets.size() > 0) { filterByShouldDispatch(
// child -> root mLastHitPath.subList(0, mLastHitPath.size() - firstDivergentIndexFromBack),
dispatchNonBubblingEventForPathWhenListened( EVENT.LEAVE,
EVENT.LEAVE, EVENT.LEAVE_CAPTURE,
EVENT.LEAVE_CAPTURE, nonDivergentListeningToLeave);
enterViewTargets, if (leaveViewTargets.size() > 0) {
// We want to dispatch from target -> root, so no need to reverse
dispatchEventForViewTargets(
PointerEventHelper.POINTER_LEAVE,
leaveViewTargets,
eventDispatcher, eventDispatcher,
surfaceId, surfaceId,
motionEvent, motionEvent);
nonDivergentListeningToLeave);
} }
} }
@ -424,14 +443,16 @@ public class JSPointerDispatcher {
PointerEventHelper.POINTER_CANCEL, surfaceId, targetTag, motionEvent)); PointerEventHelper.POINTER_CANCEL, surfaceId, targetTag, motionEvent));
} }
dispatchNonBubblingEventForPathWhenListened( List<ViewTarget> leaveViewTargets =
EVENT.LEAVE, filterByShouldDispatch(hitPath, EVENT.LEAVE, EVENT.LEAVE_CAPTURE, false);
EVENT.LEAVE_CAPTURE,
hitPath, // dispatch from target -> root
dispatchEventForViewTargets(
PointerEventHelper.POINTER_LEAVE,
leaveViewTargets,
eventDispatcher, eventDispatcher,
surfaceId, surfaceId,
motionEvent, motionEvent);
false);
mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime); mTouchEventCoalescingKeyHelper.removeCoalescingKey(mDownStartTime);
mDownStartTime = TouchEvent.UNSET; mDownStartTime = TouchEvent.UNSET;