mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user