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

Commit

Permalink
fix(event): should pass boolean to addEventListener if not support pa…
Browse files Browse the repository at this point in the history
…ssive (#1053)
  • Loading branch information
JiaLiPassion authored and mhevery committed Jun 18, 2018
1 parent 0720d79 commit e9536ec
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 4 deletions.
41 changes: 39 additions & 2 deletions lib/common/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ interface EventTaskData extends TaskData {
readonly useG?: boolean;
}

let passiveSupported = false;

if (typeof window !== 'undefined') {
try {
const options = Object.defineProperty({}, 'passive', {
get: function() {
passiveSupported = true;
}
});

window.addEventListener('test', options, options);
window.removeEventListener('test', options, options);
} catch (err) {
passiveSupported = false;
}
}

// an identifier to tell ZoneTask do not create a new invoke closure
const OPTIMIZED_ZONE_EVENT_TASK_DATA: EventTaskData = {
useG: true
Expand Down Expand Up @@ -50,6 +67,8 @@ export interface PatchEventTargetOptions {
rt?: boolean;
// event compare handler
diff?: (task: any, delegate: any) => boolean;
// support passive or not
supportPassive?: boolean;
}

export function patchEventTarget(
Expand Down Expand Up @@ -212,12 +231,25 @@ export function patchEventTarget(
proto[patchOptions.prepend];
}

const customScheduleGlobal = function() {
function checkIsPassive(task: Task) {
if (!passiveSupported && typeof taskData.options !== 'boolean' &&
typeof taskData.options !== 'undefined' && taskData.options !== null) {
// options is a non-null non-undefined object
// passive is not supported
// don't pass options as object
// just pass capture as a boolean
(task as any).options = !!taskData.options.capture;
taskData.options = (task as any).options;
}
}

const customScheduleGlobal = function(task: Task) {
// if there is already a task for the eventName + capture,
// just return, because we use the shared globalZoneAwareCallback here.
if (taskData.isExisting) {
return;
}
checkIsPassive(task);
return nativeAddEventListener.call(
taskData.target, taskData.eventName,
taskData.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback,
Expand Down Expand Up @@ -265,6 +297,7 @@ export function patchEventTarget(
};

const customScheduleNonGlobal = function(task: Task) {
checkIsPassive(task);
return nativeAddEventListener.call(
taskData.target, taskData.eventName, task.invoke, taskData.options);
};
Expand Down Expand Up @@ -421,7 +454,11 @@ export function patchEventTarget(
if (once) {
options.once = true;
}
task.options = options;
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
task.options = options;
}
task.target = target;
task.capture = capture;
task.eventName = eventName;
Expand Down
52 changes: 50 additions & 2 deletions test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ try {
supportsPassive = true;
}
});
window.addEventListener('test', null as any, opts);
window.removeEventListener('test', null as any, opts);
window.addEventListener('test', opts as any, opts);
window.removeEventListener('test', opts as any, opts);
} catch (e) {
}

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

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

class TestEventListener {
logs: string[] = [];
addEventListener(eventName: string, listener: any, options: any) {
this.logs.push(options);
}
removeEventListener(eventName: string, listener: any, options: any) {}
}

describe('Zone', function() {
const rootZone = Zone.current;
(Zone as any)[zoneSymbol('ignoreConsoleErrorUncaughtError')] = true;
Expand Down Expand Up @@ -996,6 +1004,46 @@ describe('Zone', function() {
expect(logs).toEqual(['click']);
}));

it('should change options to boolean if not support passive', () => {
patchEventTarget(window, [TestEventListener.prototype]);
const testEventListener = new TestEventListener();

const listener = function() {};
testEventListener.addEventListener('test', listener, {passive: true});
testEventListener.addEventListener('test1', listener, {once: true});
testEventListener.addEventListener('test2', listener, {capture: true});
testEventListener.addEventListener('test3', listener, {passive: false});
testEventListener.addEventListener('test4', listener, {once: false});
testEventListener.addEventListener('test5', listener, {capture: false});
if (!supportsPassive) {
expect(testEventListener.logs).toEqual([false, false, true, false, false, false]);
} else {
expect(testEventListener.logs).toEqual([
{passive: true}, {once: true}, {capture: true}, {passive: false}, {once: false},
{capture: false}
]);
}
});

it('should change options to boolean if not support passive on HTMLElement', () => {
const logs: string[] = [];
const listener = (e: Event) => {
logs.push('clicked');
};

(button as any).addEventListener('click', listener, {once: true});
button.dispatchEvent(clickEvent);
expect(logs).toEqual(['clicked']);
button.dispatchEvent(clickEvent);
if (supportsPassive) {
expect(logs).toEqual(['clicked']);
} else {
expect(logs).toEqual(['clicked', 'clicked']);
}

button.removeEventListener('click', listener);
});

it('should support addEventListener with AddEventListenerOptions passive setting',
ifEnvSupports(supportEventListenerOptions, function() {
const hookSpy = jasmine.createSpy('hook');
Expand Down

0 comments on commit e9536ec

Please sign in to comment.