refactor(zone.js): drop passive supported check (#58010)

https://caniuse.com/passive-event-listener
Passive event listeners are supported in all major evergreen browsers.
Thus, we don't need to check whether passives are supported.

PR Close #58010
This commit is contained in:
arturovt
2024-09-30 00:31:30 +03:00
committed by Andrew Kushnir
parent 181cc17944
commit d4dc7cb70c
+102 -60
View File
@@ -69,29 +69,15 @@ interface InternalEventTask extends EventTask {
// its invocation if dispatched later.
isRemoved?: boolean;
allRemoved?: boolean;
}
// Note that passive event listeners are now supported by most modern browsers,
// including Chrome, Firefox, Safari, and Edge. There's a pending change that
// would remove support for legacy browsers by zone.js. Removing `passiveSupported`
// from the codebase will reduce the final code size for existing apps that still use zone.js.
let passiveSupported = false;
if (typeof window !== 'undefined') {
try {
const options = Object.defineProperty({}, 'passive', {
get: function () {
passiveSupported = true;
},
});
// Note: We pass the `options` object as the event handler too. This is not compatible with the
// signature of `addEventListener` or `removeEventListener` but enables us to remove the handler
// without an actual handler.
window.addEventListener('test', options as any, options);
window.removeEventListener('test', options as any, options);
} catch (err) {
passiveSupported = false;
}
// `originalDelegate` is the actual event listener object passed when
// calling `addEventListener()`, i.e., `{ handleEvent: event => ... }`.
// This object is used to compare event listeners when `addEventListener`
// is called again with the same event listener object reference.
// For example:
// const eventListenerObject = { handleEvent: console.log };
// document.addEventListener('click', eventListenerObject);
// document.addEventListener('click', eventListenerObject);
originalDelegate?: EventListenerObject;
}
// an identifier to tell ZoneTask do not create a new invoke closure
@@ -116,31 +102,92 @@ function prepareEventNames(eventName: string, eventNameToString?: (eventName: st
}
export interface PatchEventTargetOptions {
// validateHandler
/**
* Optional validator for the event handler before patching.
* If it returns false, the handler will not be patched.
*
* @param nativeDelegate The native method (e.g., original addEventListener).
* @param delegate The provided handler function.
* @param target The object being patched.
* @param args The arguments passed to the method.
* @returns Whether the handler is valid for patching.
*/
vh?: (nativeDelegate: any, delegate: any, target: any, args: any) => boolean;
// addEventListener function name
/**
* The property name for the method that adds an event listener.
* Typically `addEventListener`.
*/
add?: string;
// removeEventListener function name
/**
* The property name for the method that removes an event listener.
* Typically `removeEventListener`.
*/
rm?: string;
// prependEventListener function name
/**
* The property name for a method that prepends an event listener.
* Used in some Node.js-style APIs.
*/
prepend?: string;
// listeners function name
/**
* The property name for the method that returns the current listeners.
* `eventListeners` is the default.
*
* Example:
* ```js
* const element = document.querySelector(...);
* console.log(element.eventListeners());
* ```
*/
listeners?: string;
// removeAllListeners function name
/**
* The property name for the method that removes all listeners for an event.
* `removeAllListeners` is the default.
*/
rmAll?: string;
// useGlobalCallback flag
/**
* Indicates whether a shared global callback should be used for all events
* instead of individual per-event callbacks.
*/
useG?: boolean;
// check duplicate flag when addEventListener
/**
* If true, checks for duplicate listeners before adding a new one.
* Prevents multiple registrations of the same handler.
*/
chkDup?: boolean;
// return target flag when addEventListener
/**
* If true, the patched add method will return the target object
* (matching typical `addEventListener` behavior).
*/
rt?: boolean;
// event compare handler
/**
* Optional function to compare existing tasks with a given delegate.
* Used to match handlers when removing or managing listeners.
*
* @param task The internal Zone.js task object.
* @param delegate The original event handler function.
* @returns Whether the two refer to the same handler.
*/
diff?: (task: any, delegate: any) => boolean;
// support passive or not
supportPassive?: boolean;
// get string from eventName (in nodejs, eventName maybe Symbol)
/**
* Converts an event name to a string.
* Useful when event names are symbols (e.g., in Node.js).
*/
eventNameToString?: (eventName: any) => string;
// transfer eventName
/**
* Transforms or normalizes the event name before use.
* Allows remapping or renaming of event types.
*/
transferEventName?: (eventName: string) => string;
}
@@ -322,13 +369,7 @@ export function patchEventTarget(
* to handle all possible input from the user.
*/
function buildEventListenerOptions(options: any, passive: boolean) {
if (!passiveSupported && typeof options === 'object' && options) {
// doesn't support passive but user want to pass an object as options.
// this will not work on some old browser, so we just pass a boolean
// as useCapture parameter
return !!options.capture;
}
if (!passiveSupported || !passive) {
if (!passive) {
return options;
}
if (typeof options === 'boolean') {
@@ -443,8 +484,7 @@ export function patchEventTarget(
);
};
const compare =
patchOptions && patchOptions.diff ? patchOptions.diff : compareTaskCallbackVsDelegate;
const compare = patchOptions?.diff || compareTaskCallbackVsDelegate;
const unpatchedEvents: string[] = (Zone as any)[zoneSymbol('UNPATCHED_EVENTS')];
const passiveEvents: string[] = _global[zoneSymbol('PASSIVE_EVENTS')];
@@ -487,7 +527,7 @@ export function patchEventTarget(
if (patchOptions && patchOptions.transferEventName) {
eventName = patchOptions.transferEventName(eventName);
}
let delegate = arguments[1];
let delegate: EventListenerOrEventListenerObject = arguments[1];
if (!delegate) {
return nativeListener.apply(this, arguments);
}
@@ -496,23 +536,24 @@ export function patchEventTarget(
return nativeListener.apply(this, arguments);
}
// don't create the bind delegate function for handleEvent
// case here to improve addEventListener performance
// we will create the bind delegate when invoke
let isHandleEvent = false;
// To improve `addEventListener` performance, we will create the callback
// for the task later when the task is invoked.
let isEventListenerObject = false;
if (typeof delegate !== 'function') {
// This checks whether the provided listener argument is an object with
// a `handleEvent` method (since we can call `addEventListener` with a
// function `event => ...` or with an object `{ handleEvent: event => ... }`).
if (!delegate.handleEvent) {
return nativeListener.apply(this, arguments);
}
isHandleEvent = true;
isEventListenerObject = true;
}
if (validateHandler && !validateHandler(nativeListener, delegate, target, arguments)) {
return;
}
const passive =
passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1;
const passive = !!passiveEvents && passiveEvents.indexOf(eventName) !== -1;
const options = copyEventListenerOptions(buildEventListenerOptions(arguments[2], passive));
const signal: AbortSignal | undefined = options?.signal;
if (signal?.aborted) {
@@ -610,7 +651,7 @@ export function patchEventTarget(
// `taskData.options` to pass it to the native `addEventListener`.
const task: InternalEventTask = zone.scheduleEventTask(
source,
delegate,
<Function>delegate,
data,
customScheduleFn,
customCancelFn,
@@ -645,17 +686,18 @@ export function patchEventTarget(
if (once) {
taskData.options.once = true;
}
if (!(!passiveSupported && typeof task.options === 'boolean')) {
// if not support passive, and we pass an option object
// to addEventListener, we should save the options to task
if (typeof task.options !== 'boolean') {
// We should save the options on the task (if it's an object) because
// we'll be using `task.options` later when removing the event listener
// and passing it back to `removeEventListener`.
task.options = options;
}
task.target = target;
task.capture = capture;
task.eventName = eventName;
if (isHandleEvent) {
if (isEventListenerObject) {
// save original delegate for compare to check duplicate
(task as any).originalDelegate = delegate;
task.originalDelegate = <EventListenerObject>delegate;
}
if (!prepend) {
existingTasks.push(task);