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

Commit e860d5f

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

File tree

2 files changed

+136
-52
lines changed

2 files changed

+136
-52
lines changed

lib/common/events.ts

Lines changed: 35 additions & 1 deletion
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,26 @@ export function patchEventTarget(
212231
proto[patchOptions.prepend];
213232
}
214233

215-
const customScheduleGlobal = function() {
234+
function checkIsPassive(task: Task) {
235+
if (typeof taskData.options !== 'boolean' && typeof taskData.options !== 'undefined' &&
236+
taskData.options !== null) {
237+
// options is a non-null non-undefined object
238+
if (!passiveSupported) {
239+
// passive is not supported
240+
// don't pass options as object
241+
// just pass capture as a boolean
242+
(task as any).options = !!taskData.options.capture;
243+
}
244+
}
245+
}
246+
247+
const customScheduleGlobal = function(task: Task) {
216248
// if there is already a task for the eventName + capture,
217249
// just return, because we use the shared globalZoneAwareCallback here.
218250
if (taskData.isExisting) {
219251
return;
220252
}
253+
checkIsPassive(task);
221254
return nativeAddEventListener.call(
222255
taskData.target, taskData.eventName,
223256
taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback,
@@ -265,6 +298,7 @@ export function patchEventTarget(
265298
};
266299

267300
const customScheduleNonGlobal = function(task: Task) {
301+
checkIsPassive(task);
268302
return nativeAddEventListener.call(
269303
taskData.target, taskData.eventName, task.invoke, taskData.options);
270304
};

test/browser/browser.spec.ts

Lines changed: 101 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,50 @@ 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+
console.log('testEventListener', testEventListener.logs);
1007+
if (!supportsPassive) {
1008+
expect(testEventListener.logs).toEqual([
1009+
'false', 'false', 'false', 'false', 'false', 'false'
1010+
]);
1011+
} else {
1012+
expect(testEventListener.logs).toEqual([
1013+
{passive: true}, {once: true}, {capture: true}, {passive: false}, {once: false},
1014+
{capture: false}
1015+
]);
1016+
}
1017+
});
1018+
1019+
it('should change options to boolean if not support passive on HTMLElement', () => {
1020+
const logs: string[] = [];
1021+
const listener = (e: Event) => {
1022+
logs.push('clicked');
1023+
};
1024+
1025+
(button as any).addEventListener('click', listener, {once: true});
1026+
1027+
button.dispatchEvent(clickEvent);
1028+
expect(logs).toEqual(['clicked']);
1029+
button.dispatchEvent(clickEvent);
1030+
if (supportsPassive) {
1031+
expect(logs).toEqual(['clicked']);
1032+
} else {
1033+
expect(logs).toEqual(['clicked', 'clicked']);
1034+
}
1035+
1036+
button.removeEventListener('click', listener);
1037+
});
1038+
9871039
it('should support addEventListener with AddEventListenerOptions passive setting',
9881040
ifEnvSupports(supportEventListenerOptions, function() {
9891041
const hookSpy = jasmine.createSpy('hook');
@@ -2472,61 +2524,59 @@ describe('Zone', function() {
24722524
});
24732525
}));
24742526

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-
});
2527+
describe(
2528+
'ResizeObserver', ifEnvSupports('ResizeObserver', () => {
2529+
it('ResizeObserver callback should be in zone', (done) => {
2530+
const ResizeObserver = (window as any)['ResizeObserver'];
2531+
const div = document.createElement('div');
2532+
const zone = Zone.current.fork({name: 'observer'});
2533+
const observer = new ResizeObserver((entries: any, ob: any) => {
2534+
expect(Zone.current.name).toEqual(zone.name);
24932535

2494-
document.body.appendChild(div);
2495-
});
2536+
expect(entries.length).toBe(1);
2537+
expect(entries[0].target).toBe(div);
2538+
done();
2539+
});
24962540

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-
}
2541+
zone.run(() => {
2542+
observer.observe(div);
25122543
});
2513-
count ++;
2514-
if (count === 2) {
2515-
done();
2516-
}
2517-
});
25182544

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

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

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

0 commit comments

Comments
 (0)