Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit e9536ec

Browse files
JiaLiPassionmhevery
authored andcommitted
fix(event): should pass boolean to addEventListener if not support passive (#1053)
1 parent 0720d79 commit e9536ec

File tree

2 files changed

+89
-4
lines changed

2 files changed

+89
-4
lines changed

Diff for: lib/common/events.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ interface EventTaskData extends TaskData {
1818
readonly useG?: boolean;
1919
}
2020

21+
let passiveSupported = false;
22+
23+
if (typeof window !== 'undefined') {
24+
try {
25+
const options = Object.defineProperty({}, 'passive', {
26+
get: function() {
27+
passiveSupported = true;
28+
}
29+
});
30+
31+
window.addEventListener('test', options, options);
32+
window.removeEventListener('test', options, options);
33+
} catch (err) {
34+
passiveSupported = false;
35+
}
36+
}
37+
2138
// an identifier to tell ZoneTask do not create a new invoke closure
2239
const OPTIMIZED_ZONE_EVENT_TASK_DATA: EventTaskData = {
2340
useG: true
@@ -50,6 +67,8 @@ export interface PatchEventTargetOptions {
5067
rt?: boolean;
5168
// event compare handler
5269
diff?: (task: any, delegate: any) => boolean;
70+
// support passive or not
71+
supportPassive?: boolean;
5372
}
5473

5574
export function patchEventTarget(
@@ -212,12 +231,25 @@ export function patchEventTarget(
212231
proto[patchOptions.prepend];
213232
}
214233

215-
const customScheduleGlobal = function() {
234+
function checkIsPassive(task: Task) {
235+
if (!passiveSupported && typeof taskData.options !== 'boolean' &&
236+
typeof taskData.options !== 'undefined' && taskData.options !== null) {
237+
// options is a non-null non-undefined object
238+
// passive is not supported
239+
// don't pass options as object
240+
// just pass capture as a boolean
241+
(task as any).options = !!taskData.options.capture;
242+
taskData.options = (task as any).options;
243+
}
244+
}
245+
246+
const customScheduleGlobal = function(task: Task) {
216247
// if there is already a task for the eventName + capture,
217248
// just return, because we use the shared globalZoneAwareCallback here.
218249
if (taskData.isExisting) {
219250
return;
220251
}
252+
checkIsPassive(task);
221253
return nativeAddEventListener.call(
222254
taskData.target, taskData.eventName,
223255
taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback,
@@ -265,6 +297,7 @@ export function patchEventTarget(
265297
};
266298

267299
const customScheduleNonGlobal = function(task: Task) {
300+
checkIsPassive(task);
268301
return nativeAddEventListener.call(
269302
taskData.target, taskData.eventName, task.invoke, taskData.options);
270303
};
@@ -421,7 +454,11 @@ export function patchEventTarget(
421454
if (once) {
422455
options.once = true;
423456
}
424-
task.options = options;
457+
if (!(!passiveSupported && typeof task.options === 'boolean')) {
458+
// if not support passive, and we pass an option object
459+
// to addEventListener, we should save the options to task
460+
task.options = options;
461+
}
425462
task.target = target;
426463
task.capture = capture;
427464
task.eventName = eventName;

Diff for: test/browser/browser.spec.ts

+50-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ try {
4747
supportsPassive = true;
4848
}
4949
});
50-
window.addEventListener('test', null as any, opts);
51-
window.removeEventListener('test', null as any, opts);
50+
window.addEventListener('test', opts as any, opts);
51+
window.removeEventListener('test', opts as any, opts);
5252
} catch (e) {
5353
}
5454

@@ -75,6 +75,14 @@ function ieOrEdge() {
7575

7676
(ieOrEdge as any).message = 'IE/Edge Test';
7777

78+
class TestEventListener {
79+
logs: string[] = [];
80+
addEventListener(eventName: string, listener: any, options: any) {
81+
this.logs.push(options);
82+
}
83+
removeEventListener(eventName: string, listener: any, options: any) {}
84+
}
85+
7886
describe('Zone', function() {
7987
const rootZone = Zone.current;
8088
(Zone as any)[zoneSymbol('ignoreConsoleErrorUncaughtError')] = true;
@@ -996,6 +1004,46 @@ describe('Zone', function() {
9961004
expect(logs).toEqual(['click']);
9971005
}));
9981006

1007+
it('should change options to boolean if not support passive', () => {
1008+
patchEventTarget(window, [TestEventListener.prototype]);
1009+
const testEventListener = new TestEventListener();
1010+
1011+
const listener = function() {};
1012+
testEventListener.addEventListener('test', listener, {passive: true});
1013+
testEventListener.addEventListener('test1', listener, {once: true});
1014+
testEventListener.addEventListener('test2', listener, {capture: true});
1015+
testEventListener.addEventListener('test3', listener, {passive: false});
1016+
testEventListener.addEventListener('test4', listener, {once: false});
1017+
testEventListener.addEventListener('test5', listener, {capture: false});
1018+
if (!supportsPassive) {
1019+
expect(testEventListener.logs).toEqual([false, false, true, false, false, false]);
1020+
} else {
1021+
expect(testEventListener.logs).toEqual([
1022+
{passive: true}, {once: true}, {capture: true}, {passive: false}, {once: false},
1023+
{capture: false}
1024+
]);
1025+
}
1026+
});
1027+
1028+
it('should change options to boolean if not support passive on HTMLElement', () => {
1029+
const logs: string[] = [];
1030+
const listener = (e: Event) => {
1031+
logs.push('clicked');
1032+
};
1033+
1034+
(button as any).addEventListener('click', listener, {once: true});
1035+
button.dispatchEvent(clickEvent);
1036+
expect(logs).toEqual(['clicked']);
1037+
button.dispatchEvent(clickEvent);
1038+
if (supportsPassive) {
1039+
expect(logs).toEqual(['clicked']);
1040+
} else {
1041+
expect(logs).toEqual(['clicked', 'clicked']);
1042+
}
1043+
1044+
button.removeEventListener('click', listener);
1045+
});
1046+
9991047
it('should support addEventListener with AddEventListenerOptions passive setting',
10001048
ifEnvSupports(supportEventListenerOptions, function() {
10011049
const hookSpy = jasmine.createSpy('hook');

0 commit comments

Comments
 (0)