From 2c402d5c99d9191f0532d740b5c315e5f13e5752 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sun, 21 Jul 2019 22:53:20 +0900 Subject: [PATCH] fix(zone.js): handle MSPointer event correctly (#31722) Close #31699 PR Close #31722 --- .../lib/browser/event-target-legacy.ts | 20 ++- packages/zone.js/lib/common/events.ts | 22 ++- packages/zone.js/test/browser/browser.spec.ts | 127 ++++++++++++++++++ 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/packages/zone.js/lib/browser/event-target-legacy.ts b/packages/zone.js/lib/browser/event-target-legacy.ts index c409968b24..79c3863eeb 100644 --- a/packages/zone.js/lib/browser/event-target-legacy.ts +++ b/packages/zone.js/lib/browser/event-target-legacy.ts @@ -39,6 +39,18 @@ export function eventTargetLegacyPatch(_global: any, api: _ZonePrivate) { const FUNCTION_WRAPPER = '[object FunctionWrapper]'; const BROWSER_TOOLS = 'function __BROWSERTOOLS_CONSOLE_SAFEFUNC() { [native code] }'; + const pointerEventsMap: {[key: string]: string} = { + 'MSPointerCancel': 'pointercancel', + 'MSPointerDown': 'pointerdown', + 'MSPointerEnter': 'pointerenter', + 'MSPointerHover': 'pointerhover', + 'MSPointerLeave': 'pointerleave', + 'MSPointerMove': 'pointermove', + 'MSPointerOut': 'pointerout', + 'MSPointerOver': 'pointerover', + 'MSPointerUp': 'pointerup' + }; + // predefine all __zone_symbol__ + eventName + true/false string for (let i = 0; i < eventNames.length; i++) { const eventName = eventNames[i]; @@ -100,7 +112,13 @@ export function eventTargetLegacyPatch(_global: any, api: _ZonePrivate) { } // vh is validateHandler to check event handler // is valid or not(for security check) - api.patchEventTarget(_global, apiTypes, {vh: checkIEAndCrossContext}); + api.patchEventTarget(_global, apiTypes, { + vh: checkIEAndCrossContext, + transferEventName: (eventName: string) => { + const pointerEventName = pointerEventsMap[eventName]; + return pointerEventName || eventName; + } + }); (Zone as any)[api.symbol('patchEventTarget')] = !!_global[EVENT_TARGET]; return true; } diff --git a/packages/zone.js/lib/common/events.ts b/packages/zone.js/lib/common/events.ts index d3a97f5557..084f126457 100644 --- a/packages/zone.js/lib/common/events.ts +++ b/packages/zone.js/lib/common/events.ts @@ -69,6 +69,8 @@ export interface PatchEventTargetOptions { supportPassive?: boolean; // get string from eventName (in nodejs, eventName maybe Symbol) eventNameToString?: (eventName: any) => string; + // transfer eventName + transferEventName?: (eventName: string) => string; } export function patchEventTarget( @@ -332,7 +334,10 @@ export function patchEventTarget( returnTarget = false, prepend = false) { return function(this: unknown) { const target = this || _global; - const eventName = arguments[0]; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } let delegate = arguments[1]; if (!delegate) { return nativeListener.apply(this, arguments); @@ -498,7 +503,10 @@ export function patchEventTarget( proto[REMOVE_EVENT_LISTENER] = function() { const target = this || _global; - const eventName = arguments[0]; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } const options = arguments[2]; let capture; @@ -565,7 +573,10 @@ export function patchEventTarget( proto[LISTENERS_EVENT_LISTENER] = function() { const target = this || _global; - const eventName = arguments[0]; + let eventName = arguments[0]; + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } const listeners: any[] = []; const tasks = @@ -582,7 +593,7 @@ export function patchEventTarget( proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function() { const target = this || _global; - const eventName = arguments[0]; + let eventName = arguments[0]; if (!eventName) { const keys = Object.keys(target); for (let i = 0; i < keys.length; i++) { @@ -600,6 +611,9 @@ export function patchEventTarget( // remove removeListener listener finally this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener'); } else { + if (patchOptions && patchOptions.transferEventName) { + eventName = patchOptions.transferEventName(eventName); + } const symbolEventNames = zoneSymbolEventNames[eventName]; if (symbolEventNames) { const symbolEventName = symbolEventNames[FALSE_STR]; diff --git a/packages/zone.js/test/browser/browser.spec.ts b/packages/zone.js/test/browser/browser.spec.ts index 21e0bc9183..71263adbbd 100644 --- a/packages/zone.js/test/browser/browser.spec.ts +++ b/packages/zone.js/test/browser/browser.spec.ts @@ -2381,4 +2381,131 @@ describe('Zone', function() { })); }); }); + + + describe( + 'pointer event in IE', + ifEnvSupports( + () => { return getIEVersion() === 11; }, + () => { + const pointerEventsMap: {[key: string]: string} = { + 'MSPointerCancel': 'pointercancel', + 'MSPointerDown': 'pointerdown', + 'MSPointerEnter': 'pointerenter', + 'MSPointerHover': 'pointerhover', + 'MSPointerLeave': 'pointerleave', + 'MSPointerMove': 'pointermove', + 'MSPointerOut': 'pointerout', + 'MSPointerOver': 'pointerover', + 'MSPointerUp': 'pointerup' + }; + + let div: HTMLDivElement; + beforeEach(() => { + div = document.createElement('div'); + document.body.appendChild(div); + }); + afterEach(() => { document.body.removeChild(div); }); + Object.keys(pointerEventsMap).forEach(key => { + it(`${key} and ${pointerEventsMap[key]} should both be triggered`, (done: DoneFn) => { + const logs: string[] = []; + div.addEventListener(key, (event: any) => { + expect(event.type).toEqual(pointerEventsMap[key]); + logs.push(`${key} triggered`); + }); + div.addEventListener(pointerEventsMap[key], (event: any) => { + expect(event.type).toEqual(pointerEventsMap[key]); + logs.push(`${pointerEventsMap[key]} triggered`); + }); + const evt1 = document.createEvent('Event'); + evt1.initEvent(key, true, true); + div.dispatchEvent(evt1); + + setTimeout(() => { + expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]); + }); + + const evt2 = document.createEvent('Event'); + evt2.initEvent(pointerEventsMap[key], true, true); + div.dispatchEvent(evt2); + + setTimeout(() => { + expect(logs).toEqual([`${key} triggered`, `${pointerEventsMap[key]} triggered`]); + }); + + setTimeout(done); + }); + + it(`${key} and ${ + pointerEventsMap[key]} with same listener should not be triggered twice`, + (done: DoneFn) => { + const logs: string[] = []; + const listener = function(event: any) { + expect(event.type).toEqual(pointerEventsMap[key]); + logs.push(`${key} triggered`); + }; + div.addEventListener(key, listener); + div.addEventListener(pointerEventsMap[key], listener); + + const evt1 = document.createEvent('Event'); + evt1.initEvent(key, true, true); + div.dispatchEvent(evt1); + + setTimeout(() => { expect(logs).toEqual([`${key} triggered`]); }); + + const evt2 = document.createEvent('Event'); + evt2.initEvent(pointerEventsMap[key], true, true); + div.dispatchEvent(evt2); + + setTimeout( + () => { expect(logs).toEqual([`${pointerEventsMap[key]} triggered`]); }); + + setTimeout(done); + }); + + it(`${key} and ${ + pointerEventsMap + [key]} should be able to be removed with removeEventListener`, + (done: DoneFn) => { + const logs: string[] = []; + const listener1 = function(event: any) { logs.push(`${key} triggered`); }; + const listener2 = function(event: any) { + logs.push(`${pointerEventsMap[key]} triggered`); + }; + div.addEventListener(key, listener1); + div.addEventListener(pointerEventsMap[key], listener2); + + div.removeEventListener(key, listener1); + div.removeEventListener(key, listener2); + + const evt1 = document.createEvent('Event'); + evt1.initEvent(key, true, true); + div.dispatchEvent(evt1); + + setTimeout(() => { expect(logs).toEqual([]); }); + + const evt2 = document.createEvent('Event'); + evt2.initEvent(pointerEventsMap[key], true, true); + div.dispatchEvent(evt2); + + setTimeout(() => { expect(logs).toEqual([]); }); + + div.addEventListener(key, listener1); + div.addEventListener(pointerEventsMap[key], listener2); + + div.removeEventListener(pointerEventsMap[key], listener1); + div.removeEventListener(pointerEventsMap[key], listener2); + + div.dispatchEvent(evt1); + + setTimeout(() => { expect(logs).toEqual([]); }); + + div.dispatchEvent(evt2); + + setTimeout(() => { expect(logs).toEqual([]); }); + + setTimeout(done); + }); + }); + })); });