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

Commit 71377ed

Browse files
committed
fix(event): should pass boolean to addEventListener if not support passive
1 parent c8c5990 commit 71377ed

File tree

2 files changed

+136
-53
lines changed

2 files changed

+136
-53
lines changed

lib/common/events.ts

Lines changed: 39 additions & 2 deletions
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
};
@@ -422,7 +455,11 @@ export function patchEventTarget(
422455
if (once) {
423456
options.once = true;
424457
}
425-
task.options = options;
458+
if (!(!passiveSupported && typeof task.options === 'boolean')) {
459+
// if not support passive, and we pass an option object
460+
// to addEventListener, we should save the options to task
461+
task.options = options;
462+
}
426463
task.target = target;
427464
task.capture = capture;
428465
task.eventName = eventName;

test/browser/browser.spec.ts

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ try {
4747
supportsPassive = true;
4848
}
4949
});
50-
window.addEventListener('test', null, opts);
51-
window.removeEventListener('test', null, opts);
50+
window.addEventListener('test', opts, opts);
51+
window.removeEventListener('test', opts, 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;
@@ -984,6 +992,46 @@ describe('Zone', function() {
984992
expect(logs).toEqual(['click']);
985993
}));
986994

995+
it('should change options to boolean if not support passive', () => {
996+
patchEventTarget(window, [TestEventListener.prototype]);
997+
const testEventListener = new TestEventListener();
998+
999+
const listener = function() {};
1000+
testEventListener.addEventListener('test', listener, {passive: true});
1001+
testEventListener.addEventListener('test1', listener, {once: true});
1002+
testEventListener.addEventListener('test2', listener, {capture: true});
1003+
testEventListener.addEventListener('test3', listener, {passive: false});
1004+
testEventListener.addEventListener('test4', listener, {once: false});
1005+
testEventListener.addEventListener('test5', listener, {capture: false});
1006+
if (!supportsPassive) {
1007+
expect(testEventListener.logs).toEqual([false, false, true, false, false, false]);
1008+
} else {
1009+
expect(testEventListener.logs).toEqual([
1010+
{passive: true}, {once: true}, {capture: true}, {passive: false}, {once: false},
1011+
{capture: false}
1012+
]);
1013+
}
1014+
});
1015+
1016+
it('should change options to boolean if not support passive on HTMLElement', () => {
1017+
const logs: string[] = [];
1018+
const listener = (e: Event) => {
1019+
logs.push('clicked');
1020+
};
1021+
1022+
(button as any).addEventListener('click', listener, {once: true});
1023+
button.dispatchEvent(clickEvent);
1024+
expect(logs).toEqual(['clicked']);
1025+
button.dispatchEvent(clickEvent);
1026+
if (supportsPassive) {
1027+
expect(logs).toEqual(['clicked']);
1028+
} else {
1029+
expect(logs).toEqual(['clicked', 'clicked']);
1030+
}
1031+
1032+
button.removeEventListener('click', listener);
1033+
});
1034+
9871035
it('should support addEventListener with AddEventListenerOptions passive setting',
9881036
ifEnvSupports(supportEventListenerOptions, function() {
9891037
const hookSpy = jasmine.createSpy('hook');
@@ -2472,61 +2520,59 @@ describe('Zone', function() {
24722520
});
24732521
}));
24742522

2475-
describe('ResizeObserver', ifEnvSupports('ResizeObserver', () => {
2476-
it('ResizeObserver callback should be in zone', (done) => {
2477-
const ResizeObserver = (window as any)['ResizeObserver'];
2478-
const div = document.createElement('div');
2479-
const zone = Zone.current.fork({
2480-
name: 'observer'
2481-
});
2482-
const observer = new ResizeObserver((entries: any, ob: any) => {
2483-
expect(Zone.current.name).toEqual(zone.name);
2484-
2485-
expect(entries.length).toBe(1);
2486-
expect(entries[0].target).toBe(div);
2487-
done();
2488-
});
2489-
2490-
zone.run(() => {
2491-
observer.observe(div);
2492-
});
2523+
describe(
2524+
'ResizeObserver', ifEnvSupports('ResizeObserver', () => {
2525+
it('ResizeObserver callback should be in zone', (done) => {
2526+
const ResizeObserver = (window as any)['ResizeObserver'];
2527+
const div = document.createElement('div');
2528+
const zone = Zone.current.fork({name: 'observer'});
2529+
const observer = new ResizeObserver((entries: any, ob: any) => {
2530+
expect(Zone.current.name).toEqual(zone.name);
24932531

2494-
document.body.appendChild(div);
2495-
});
2532+
expect(entries.length).toBe(1);
2533+
expect(entries[0].target).toBe(div);
2534+
done();
2535+
});
24962536

2497-
it('ResizeObserver callback should be able to in different zones which when they were observed', (done) => {
2498-
const ResizeObserver = (window as any)['ResizeObserver'];
2499-
const div1 = document.createElement('div');
2500-
const div2 = document.createElement('div');
2501-
const zone = Zone.current.fork({
2502-
name: 'observer'
2503-
});
2504-
let count = 0;
2505-
const observer = new ResizeObserver((entries: any, ob: any) => {
2506-
entries.forEach((entry: any) => {
2507-
if (entry.target === div1) {
2508-
expect(Zone.current.name).toEqual(zone.name);
2509-
} else {
2510-
expect(Zone.current.name).toEqual('<root>');
2511-
}
2537+
zone.run(() => {
2538+
observer.observe(div);
25122539
});
2513-
count ++;
2514-
if (count === 2) {
2515-
done();
2516-
}
2517-
});
25182540

2519-
zone.run(() => {
2520-
observer.observe(div1);
2521-
});
2522-
Zone.root.run(() => {
2523-
observer.observe(div2);
2541+
document.body.appendChild(div);
25242542
});
25252543

2526-
document.body.appendChild(div1);
2527-
document.body.appendChild(div2);
2528-
});
2529-
}));
2544+
it('ResizeObserver callback should be able to in different zones which when they were observed',
2545+
(done) => {
2546+
const ResizeObserver = (window as any)['ResizeObserver'];
2547+
const div1 = document.createElement('div');
2548+
const div2 = document.createElement('div');
2549+
const zone = Zone.current.fork({name: 'observer'});
2550+
let count = 0;
2551+
const observer = new ResizeObserver((entries: any, ob: any) => {
2552+
entries.forEach((entry: any) => {
2553+
if (entry.target === div1) {
2554+
expect(Zone.current.name).toEqual(zone.name);
2555+
} else {
2556+
expect(Zone.current.name).toEqual('<root>');
2557+
}
2558+
});
2559+
count++;
2560+
if (count === 2) {
2561+
done();
2562+
}
2563+
});
2564+
2565+
zone.run(() => {
2566+
observer.observe(div1);
2567+
});
2568+
Zone.root.run(() => {
2569+
observer.observe(div2);
2570+
});
2571+
2572+
document.body.appendChild(div1);
2573+
document.body.appendChild(div2);
2574+
});
2575+
}));
25302576

25312577
xdescribe('getUserMedia', () => {
25322578
it('navigator.mediaDevices.getUserMedia should in zone',

0 commit comments

Comments
 (0)