refactor(zone.js): remove onProp eventNames array to reduce the bundle size (#40962)

Zone.js supports the google closure compiler in the advance optimization mode,
to prevent closure compiler modify the `onProperty` name such as `Element.prototype.onclick`,
Zone.js implements the `onProperty` patch logic by declaring all the
event names in the source code, this increases the bundle size and also require
updating the event names array to keep the information updated.

Now google closure compiler has the required event names defined in the built-in
externs files, so zone.js can use more simple implementation and decrease the bundle size
of zone.js (about 4k).

PR Close #40962
This commit is contained in:
JiaLiPassion
2021-02-23 21:45:03 +08:00
committed by Jessica Janiuk
parent c9679760b2
commit 0f298a13db
7 changed files with 460 additions and 322 deletions
+4 -1
View File
@@ -10,9 +10,12 @@ import {globalSources, patchEventPrototype, patchEventTarget, zoneSymbolEventNam
import {ADD_EVENT_LISTENER_STR, ArraySlice, attachOriginToPatched, bindArguments, FALSE_STR, isBrowser, isIEOrEdge, isMix, isNode, ObjectCreate, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, patchClass, patchMacroTask, patchMethod, patchOnProperties, REMOVE_EVENT_LISTENER_STR, TRUE_STR, wrapWithCurrentZone, ZONE_SYMBOL_PREFIX} from '../common/utils';
import {patchCallbacks} from './browser-util';
import {eventNames, filterProperties} from './property-descriptor';
import {filterProperties, getOnEventNames} from './property-descriptor';
Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
// Collect native event names by looking at properties
// on the global namespace, e.g. 'onclick'.
const eventNames: string[] = getOnEventNames(global);
api.patchOnProperties = patchOnProperties;
api.patchMethod = patchMethod;
api.bindArguments = bindArguments;
@@ -12,226 +12,6 @@
import {isBrowser, isIE, isMix, isNode, ObjectGetPrototypeOf, patchOnProperties} from '../common/utils';
const globalEventHandlersEventNames = [
'abort',
'animationcancel',
'animationend',
'animationiteration',
'auxclick',
'beforeinput',
'blur',
'cancel',
'canplay',
'canplaythrough',
'change',
'compositionstart',
'compositionupdate',
'compositionend',
'cuechange',
'click',
'close',
'contextmenu',
'curechange',
'dblclick',
'drag',
'dragend',
'dragenter',
'dragexit',
'dragleave',
'dragover',
'drop',
'durationchange',
'emptied',
'ended',
'error',
'focus',
'focusin',
'focusout',
'gotpointercapture',
'input',
'invalid',
'keydown',
'keypress',
'keyup',
'load',
'loadstart',
'loadeddata',
'loadedmetadata',
'lostpointercapture',
'mousedown',
'mouseenter',
'mouseleave',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'mousewheel',
'orientationchange',
'pause',
'play',
'playing',
'pointercancel',
'pointerdown',
'pointerenter',
'pointerleave',
'pointerlockchange',
'mozpointerlockchange',
'webkitpointerlockerchange',
'pointerlockerror',
'mozpointerlockerror',
'webkitpointerlockerror',
'pointermove',
'pointout',
'pointerover',
'pointerup',
'progress',
'ratechange',
'reset',
'resize',
'scroll',
'seeked',
'seeking',
'select',
'selectionchange',
'selectstart',
'show',
'sort',
'stalled',
'submit',
'suspend',
'timeupdate',
'volumechange',
'touchcancel',
'touchmove',
'touchstart',
'touchend',
'transitioncancel',
'transitionend',
'waiting',
'wheel'
];
const documentEventNames = [
'afterscriptexecute', 'beforescriptexecute', 'DOMContentLoaded', 'freeze', 'fullscreenchange',
'mozfullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange', 'fullscreenerror',
'mozfullscreenerror', 'webkitfullscreenerror', 'msfullscreenerror', 'readystatechange',
'visibilitychange', 'resume'
];
const windowEventNames = [
'absolutedeviceorientation',
'afterinput',
'afterprint',
'appinstalled',
'beforeinstallprompt',
'beforeprint',
'beforeunload',
'devicelight',
'devicemotion',
'deviceorientation',
'deviceorientationabsolute',
'deviceproximity',
'hashchange',
'languagechange',
'message',
'mozbeforepaint',
'offline',
'online',
'paint',
'pageshow',
'pagehide',
'popstate',
'rejectionhandled',
'storage',
'unhandledrejection',
'unload',
'userproximity',
'vrdisplayconnected',
'vrdisplaydisconnected',
'vrdisplaypresentchange'
];
const htmlElementEventNames = [
'beforecopy', 'beforecut', 'beforepaste', 'copy', 'cut', 'paste', 'dragstart', 'loadend',
'animationstart', 'search', 'transitionrun', 'transitionstart', 'webkitanimationend',
'webkitanimationiteration', 'webkitanimationstart', 'webkittransitionend'
];
const mediaElementEventNames =
['encrypted', 'waitingforkey', 'msneedkey', 'mozinterruptbegin', 'mozinterruptend'];
const ieElementEventNames = [
'activate',
'afterupdate',
'ariarequest',
'beforeactivate',
'beforedeactivate',
'beforeeditfocus',
'beforeupdate',
'cellchange',
'controlselect',
'dataavailable',
'datasetchanged',
'datasetcomplete',
'errorupdate',
'filterchange',
'layoutcomplete',
'losecapture',
'move',
'moveend',
'movestart',
'propertychange',
'resizeend',
'resizestart',
'rowenter',
'rowexit',
'rowsdelete',
'rowsinserted',
'command',
'compassneedscalibration',
'deactivate',
'help',
'mscontentzoom',
'msmanipulationstatechanged',
'msgesturechange',
'msgesturedoubletap',
'msgestureend',
'msgesturehold',
'msgesturestart',
'msgesturetap',
'msgotpointercapture',
'msinertiastart',
'mslostpointercapture',
'mspointercancel',
'mspointerdown',
'mspointerenter',
'mspointerhover',
'mspointerleave',
'mspointermove',
'mspointerout',
'mspointerover',
'mspointerup',
'pointerout',
'mssitemodejumplistitemremoved',
'msthumbnailclick',
'stop',
'storagecommit'
];
const webglEventNames = ['webglcontextrestored', 'webglcontextlost', 'webglcontextcreationerror'];
const formEventNames = ['autocomplete', 'autocompleteerror'];
const detailEventNames = ['toggle'];
const frameEventNames = ['load'];
const frameSetEventNames = ['blur', 'error', 'focus', 'load', 'resize', 'scroll', 'messageerror'];
const marqueeEventNames = ['bounce', 'finish', 'start'];
const XMLHttpRequestEventNames = [
'loadstart', 'progress', 'abort', 'error', 'load', 'progress', 'timeout', 'loadend',
'readystatechange'
];
const IDBIndexEventNames =
['upgradeneeded', 'complete', 'abort', 'success', 'error', 'blocked', 'versionchange', 'close'];
const websocketEventNames = ['close', 'error', 'open', 'message'];
const workerEventNames = ['error', 'message'];
export const eventNames = globalEventHandlersEventNames.concat(
webglEventNames, formEventNames, detailEventNames, documentEventNames, windowEventNames,
htmlElementEventNames, ieElementEventNames);
export interface IgnoreProperty {
target: any;
ignoreProperties: string[];
@@ -263,6 +43,16 @@ export function patchFilteredProperties(
patchOnProperties(target, filteredProperties, prototype);
}
/**
* Get all event name properties which the event name startsWith `on`
* from the target object itself, inherited properties are not considered.
*/
export function getOnEventNames(target: Object) {
return Object.getOwnPropertyNames(target)
.filter(name => name.startsWith('on') && name.length > 2)
.map(name => name.substring(2));
}
export function propertyDescriptorPatch(api: _ZonePrivate, _global: any) {
if (isNode && !isMix) {
return;
@@ -271,64 +61,32 @@ export function propertyDescriptorPatch(api: _ZonePrivate, _global: any) {
// events are already been patched by legacy patch.
return;
}
const supportsWebSocket = typeof WebSocket !== 'undefined';
const ignoreProperties: IgnoreProperty[] = _global['__Zone_ignore_on_properties'];
// for browsers that we can patch the descriptor: Chrome & Firefox
let patchTargets: string[] = [];
if (isBrowser) {
const internalWindow: any = window;
patchTargets = patchTargets.concat([
'Document', 'SVGElement', 'Element', 'HTMLElement', 'HTMLBodyElement', 'HTMLMediaElement',
'HTMLFrameSetElement', 'HTMLFrameElement', 'HTMLIFrameElement', 'HTMLMarqueeElement', 'Worker'
]);
const ignoreErrorProperties =
isIE() ? [{target: internalWindow, ignoreProperties: ['error']}] : [];
// in IE/Edge, onProp not exist in window object, but in WindowPrototype
// so we need to pass WindowPrototype to check onProp exist or not
patchFilteredProperties(
internalWindow, eventNames.concat(['messageerror']),
internalWindow, getOnEventNames(internalWindow),
ignoreProperties ? ignoreProperties.concat(ignoreErrorProperties) : ignoreProperties,
ObjectGetPrototypeOf(internalWindow));
patchFilteredProperties(Document.prototype, eventNames, ignoreProperties);
if (typeof internalWindow['SVGElement'] !== 'undefined') {
patchFilteredProperties(internalWindow['SVGElement'].prototype, eventNames, ignoreProperties);
}
patchFilteredProperties(Element.prototype, eventNames, ignoreProperties);
patchFilteredProperties(HTMLElement.prototype, eventNames, ignoreProperties);
patchFilteredProperties(HTMLMediaElement.prototype, mediaElementEventNames, ignoreProperties);
patchFilteredProperties(
HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames),
ignoreProperties);
patchFilteredProperties(
HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames), ignoreProperties);
patchFilteredProperties(HTMLFrameElement.prototype, frameEventNames, ignoreProperties);
patchFilteredProperties(HTMLIFrameElement.prototype, frameEventNames, ignoreProperties);
const HTMLMarqueeElement = internalWindow['HTMLMarqueeElement'];
if (HTMLMarqueeElement) {
patchFilteredProperties(HTMLMarqueeElement.prototype, marqueeEventNames, ignoreProperties);
}
const Worker = internalWindow['Worker'];
if (Worker) {
patchFilteredProperties(Worker.prototype, workerEventNames, ignoreProperties);
}
}
const XMLHttpRequest = _global['XMLHttpRequest'];
if (XMLHttpRequest) {
// XMLHttpRequest is not available in ServiceWorker, so we need to check here
patchFilteredProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames, ignoreProperties);
}
const XMLHttpRequestEventTarget = _global['XMLHttpRequestEventTarget'];
if (XMLHttpRequestEventTarget) {
patchFilteredProperties(
XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype, XMLHttpRequestEventNames,
ignoreProperties);
}
if (typeof IDBIndex !== 'undefined') {
patchFilteredProperties(IDBIndex.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBOpenDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBDatabase.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBTransaction.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBCursor.prototype, IDBIndexEventNames, ignoreProperties);
}
if (supportsWebSocket) {
patchFilteredProperties(WebSocket.prototype, websocketEventNames, ignoreProperties);
patchTargets = patchTargets.concat([
'XMLHttpRequest', 'XMLHttpRequestEventTarget', 'IDBIndex', 'IDBRequest', 'IDBOpenDBRequest',
'IDBDatabase', 'IDBTransaction', 'IDBCursor', 'WebSocket'
]);
for (let i = 0; i < patchTargets.length; i++) {
const target = _global[patchTargets[i]];
target && target.prototype &&
patchFilteredProperties(
target.prototype, getOnEventNames(target.prototype), ignoreProperties);
}
}
+9 -11
View File
@@ -207,22 +207,20 @@ export function patchProperty(obj: any, prop: string, prototype?: any) {
if (!target) {
return;
}
let previousValue = (target as any)[eventNameSymbol];
if (previousValue) {
const previousValue = (target as any)[eventNameSymbol];
if (previousValue && typeof previousValue === 'function') {
target.removeEventListener(eventName, wrapFn);
}
// issue #978, when onload handler was added before loading zone.js
// we should remove it with originalDescSet
if (originalDescSet) {
originalDescSet.apply(target, NULL_ON_PROP_VALUE);
}
(target as any)[eventNameSymbol] = newValue;
if (typeof newValue === 'function') {
(target as any)[eventNameSymbol] = newValue;
// issue #978, when onload handler was added before loading zone.js
// we should remove it with originalDescSet
if (originalDescSet) {
originalDescSet.apply(target, NULL_ON_PROP_VALUE);
}
target.addEventListener(eventName, wrapFn, false);
} else {
(target as any)[eventNameSymbol] = null;
}
};
@@ -0,0 +1,7 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
+390 -18
View File
@@ -138,17 +138,265 @@ describe('Zone', function() {
hookSpy = jasmine.createSpy('hook');
eventListenerSpy = jasmine.createSpy('eventListener');
});
const globalEventHandlersEventNames = [
'abort',
'animationcancel',
'animationend',
'animationiteration',
'auxclick',
'beforeinput',
'blur',
'cancel',
'canplay',
'canplaythrough',
'change',
'compositionstart',
'compositionupdate',
'compositionend',
'cuechange',
'click',
'close',
'contextmenu',
'curechange',
'dblclick',
'drag',
'dragend',
'dragenter',
'dragexit',
'dragleave',
'dragover',
'drop',
'durationchange',
'emptied',
'ended',
'error',
'focus',
'focusin',
'focusout',
'gotpointercapture',
'input',
'invalid',
'keydown',
'keypress',
'keyup',
'load',
'loadstart',
'loadeddata',
'loadedmetadata',
'lostpointercapture',
'mousedown',
'mouseenter',
'mouseleave',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'mousewheel',
'orientationchange',
'pause',
'play',
'playing',
'pointercancel',
'pointerdown',
'pointerenter',
'pointerleave',
'pointerlockchange',
'mozpointerlockchange',
'webkitpointerlockerchange',
'pointerlockerror',
'mozpointerlockerror',
'webkitpointerlockerror',
'pointermove',
'pointout',
'pointerover',
'pointerup',
'progress',
'ratechange',
'reset',
'resize',
'scroll',
'seeked',
'seeking',
'select',
'selectionchange',
'selectstart',
'show',
'sort',
'stalled',
'submit',
'suspend',
'timeupdate',
'volumechange',
'touchcancel',
'touchmove',
'touchstart',
'touchend',
'transitioncancel',
'transitionend',
'waiting',
'wheel'
];
const documentEventNames = [
'afterscriptexecute', 'beforescriptexecute', 'DOMContentLoaded', 'freeze',
'fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange',
'msfullscreenchange', 'fullscreenerror', 'mozfullscreenerror', 'webkitfullscreenerror',
'msfullscreenerror', 'readystatechange', 'visibilitychange', 'resume'
];
const windowEventNames = [
'absolutedeviceorientation',
'afterinput',
'afterprint',
'appinstalled',
'beforeinstallprompt',
'beforeprint',
'beforeunload',
'devicelight',
'devicemotion',
'deviceorientation',
'deviceorientationabsolute',
'deviceproximity',
'hashchange',
'languagechange',
'message',
'mozbeforepaint',
'offline',
'online',
'paint',
'pageshow',
'pagehide',
'popstate',
'rejectionhandled',
'storage',
'unhandledrejection',
'unload',
'userproximity',
'vrdisplayconnected',
'vrdisplaydisconnected',
'vrdisplaypresentchange'
];
const htmlElementEventNames = [
'beforecopy', 'beforecut', 'beforepaste', 'copy', 'cut', 'paste', 'dragstart',
'loadend', 'animationstart', 'search', 'transitionrun', 'transitionstart',
'webkitanimationend', 'webkitanimationiteration', 'webkitanimationstart',
'webkittransitionend'
];
const mediaElementEventNames =
['encrypted', 'waitingforkey', 'msneedkey', 'mozinterruptbegin', 'mozinterruptend'];
const ieElementEventNames = [
'activate',
'afterupdate',
'ariarequest',
'beforeactivate',
'beforedeactivate',
'beforeeditfocus',
'beforeupdate',
'cellchange',
'controlselect',
'dataavailable',
'datasetchanged',
'datasetcomplete',
'errorupdate',
'filterchange',
'layoutcomplete',
'losecapture',
'move',
'moveend',
'movestart',
'propertychange',
'resizeend',
'resizestart',
'rowenter',
'rowexit',
'rowsdelete',
'rowsinserted',
'command',
'compassneedscalibration',
'deactivate',
'help',
'mscontentzoom',
'msmanipulationstatechanged',
'msgesturechange',
'msgesturedoubletap',
'msgestureend',
'msgesturehold',
'msgesturestart',
'msgesturetap',
'msgotpointercapture',
'msinertiastart',
'mslostpointercapture',
'mspointercancel',
'mspointerdown',
'mspointerenter',
'mspointerhover',
'mspointerleave',
'mspointermove',
'mspointerout',
'mspointerover',
'mspointerup',
'pointerout',
'mssitemodejumplistitemremoved',
'msthumbnailclick',
'stop',
'storagecommit'
];
const webglEventNames =
['webglcontextrestored', 'webglcontextlost', 'webglcontextcreationerror'];
const formEventNames = ['autocomplete', 'autocompleteerror'];
const detailEventNames = ['toggle'];
const frameEventNames = ['load'];
const frameSetEventNames =
['blur', 'error', 'focus', 'load', 'resize', 'scroll', 'messageerror'];
const marqueeEventNames = ['bounce', 'finish', 'start'];
function checkIsOnPropertiesPatched(target: any, ignoredProperties?: string[]) {
for (let prop in target) {
const XMLHttpRequestEventNames = [
'loadstart', 'progress', 'abort', 'error', 'load', 'progress', 'timeout', 'loadend',
'readystatechange'
];
const IDBIndexEventNames = [
'upgradeneeded', 'complete', 'abort', 'success', 'error', 'blocked', 'versionchange',
'close'
];
const websocketEventNames = ['close', 'error', 'open', 'message'];
const workerEventNames = ['error', 'message'];
const eventNames = globalEventHandlersEventNames.concat(
webglEventNames, formEventNames, detailEventNames, documentEventNames,
windowEventNames, htmlElementEventNames, ieElementEventNames);
function checkIsOnPropertiesPatched(
target: any, shouldPatchedProperties?: string[], ignoredProperties?: string[]) {
let checkTargetProps =
shouldPatchedProperties && shouldPatchedProperties.map(p => `on${p}`);
if (!checkTargetProps) {
checkTargetProps = [];
for (let prop in target) {
checkTargetProps.push(prop);
}
}
for (let i = 0; i < checkTargetProps.length; i++) {
const prop = checkTargetProps[i];
if (ignoredProperties &&
ignoredProperties.filter(ignoreProp => ignoreProp === prop).length > 0) {
continue;
}
if (prop.substr(0, 2) === 'on' && prop.length > 2) {
let propExistsOnTarget = false;
let checkTarget = target;
while (checkTarget && checkTarget !== Object) {
const desc = Object.getOwnPropertyDescriptor(checkTarget, prop);
if (desc && desc.configurable) {
propExistsOnTarget = true;
break;
}
checkTarget = Object.getPrototypeOf(checkTarget);
}
if (!propExistsOnTarget) {
continue;
}
target[prop] = noop;
if (!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]) {
console.log('onProp is null:', prop);
fail(`${prop} of ${target} is not patched`);
} else {
target[prop] = null;
expect(!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]).toBeTruthy();
@@ -157,7 +405,90 @@ describe('Zone', function() {
}
}
it('should patch all possbile on properties on element', function() {
it('should patch all possible on properties on native prototype', function() {
function isPropertyPatched(obj: any, prop: string, prototype?: any) {
let desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc && prototype) {
// when patch window object, use prototype to check prop exist or not
const prototypeDesc = Object.getOwnPropertyDescriptor(prototype, prop);
if (prototypeDesc) {
desc = {enumerable: true, configurable: true};
}
}
// if the descriptor not exists or is not configurable
// just return
if (!desc || !desc.configurable) {
return true;
}
const onPropPatchedSymbol = zoneSymbol('on' + prop + 'patched');
if (obj.hasOwnProperty(onPropPatchedSymbol) && obj[onPropPatchedSymbol]) {
return true;
}
return false;
}
function isPropertiesPatched(obj: any, properties: string[]|null, prototype?: any) {
if (!properties) {
return [];
}
for (let i = 0; i < properties.length; i++) {
if (!isPropertyPatched(obj, 'on' + properties[i], prototype)) {
fail(`${properties[i]} is not patched on ${obj}`);
}
}
}
isPropertiesPatched(
window, eventNames.concat(['messageerror']), Object.getPrototypeOf(window));
isPropertiesPatched(Document.prototype, eventNames);
if (typeof window['SVGElement'] !== 'undefined') {
isPropertiesPatched(window['SVGElement'].prototype, eventNames);
}
isPropertiesPatched(Element.prototype, eventNames);
isPropertiesPatched(HTMLElement.prototype, eventNames);
isPropertiesPatched(HTMLMediaElement.prototype, mediaElementEventNames);
isPropertiesPatched(
HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames));
isPropertiesPatched(
HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames));
isPropertiesPatched(HTMLFrameElement.prototype, frameEventNames);
isPropertiesPatched(HTMLIFrameElement.prototype, frameEventNames);
const HTMLMarqueeElement = window['HTMLMarqueeElement'];
if (HTMLMarqueeElement) {
isPropertiesPatched(HTMLMarqueeElement.prototype, marqueeEventNames);
}
const Worker = window['Worker'];
if (Worker) {
isPropertiesPatched(Worker.prototype, workerEventNames);
}
const XMLHttpRequest = window['XMLHttpRequest'];
if (XMLHttpRequest) {
// XMLHttpRequest is not available in ServiceWorker, so we need to check here
isPropertiesPatched(XMLHttpRequest.prototype, XMLHttpRequestEventNames);
}
const XMLHttpRequestEventTarget = window['XMLHttpRequestEventTarget'];
if (XMLHttpRequestEventTarget) {
isPropertiesPatched(
XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype,
XMLHttpRequestEventNames);
}
if (typeof IDBIndex !== 'undefined') {
isPropertiesPatched(IDBIndex.prototype, IDBIndexEventNames);
isPropertiesPatched(IDBRequest.prototype, IDBIndexEventNames);
isPropertiesPatched(IDBOpenDBRequest.prototype, IDBIndexEventNames);
isPropertiesPatched(IDBDatabase.prototype, IDBIndexEventNames);
isPropertiesPatched(IDBTransaction.prototype, IDBIndexEventNames);
isPropertiesPatched(IDBCursor.prototype, IDBIndexEventNames);
}
const WebSocket = window['WebSocket'];
if (WebSocket) {
isPropertiesPatched(WebSocket.prototype, websocketEventNames);
}
});
it('should patch all possible on properties on element', function() {
const htmlElementTagNames: string[] = [
'a', 'area', 'audio', 'base', 'basefont', 'blockquote', 'br',
'button', 'canvas', 'caption', 'col', 'colgroup', 'data', 'datalist',
@@ -173,29 +504,60 @@ describe('Zone', function() {
'video'
];
htmlElementTagNames.forEach(tagName => {
checkIsOnPropertiesPatched(document.createElement(tagName), ['onorientationchange']);
checkIsOnPropertiesPatched(
document.createElement(tagName), eventNames, ['onorientationchange']);
});
});
it('should patch all possbile on properties on body', function() {
checkIsOnPropertiesPatched(document.body, ['onorientationchange']);
it('should patch all possible on properties on svg element', function() {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
checkIsOnPropertiesPatched(svg, eventNames);
});
it('should patch all possbile on properties on Document', function() {
checkIsOnPropertiesPatched(document, ['onorientationchange']);
it('should patch all possible on properties on media element', function() {
const audio = document.createElement('audio');
checkIsOnPropertiesPatched(audio, mediaElementEventNames);
});
it('should patch all possbile on properties on Window', function() {
checkIsOnPropertiesPatched(window, [
'onvrdisplayactivate', 'onvrdisplayblur', 'onvrdisplayconnect',
'onvrdisplaydeactivate', 'onvrdisplaydisconnect', 'onvrdisplayfocus',
'onvrdisplaypointerrestricted', 'onvrdisplaypointerunrestricted',
'onorientationchange', 'onerror'
]);
it('should patch all possible on properties on frameset element', function() {
const frameset = document.createElement('frameset');
checkIsOnPropertiesPatched(frameset, windowEventNames.concat(frameSetEventNames));
});
it('should patch all possbile on properties on xhr', function() {
checkIsOnPropertiesPatched(new XMLHttpRequest());
it('should patch all possible on properties on body', function() {
checkIsOnPropertiesPatched(document.body, windowEventNames.concat(frameSetEventNames));
});
it('should patch all possible on properties on Frame', function() {
const frame = document.createElement('frame');
checkIsOnPropertiesPatched(frame, frameEventNames);
});
it('should patch all possible on properties on marquee element', function() {
const marquee = document.createElement('marquee');
checkIsOnPropertiesPatched(marquee, marqueeEventNames);
});
it('should patch all possible on properties on Window', function() {
checkIsOnPropertiesPatched(window, eventNames.concat(['messageerror']));
});
it('should patch all possible on properties on xhr', function() {
checkIsOnPropertiesPatched(new XMLHttpRequest(), XMLHttpRequestEventNames);
});
it('should patch all possible on properties on worker', function() {
checkIsOnPropertiesPatched(
new Worker('/base/angular/packages/zone.js/test/assets/worker.js'),
workerEventNames);
});
it('should patch all possible on properties on websocket', function() {
try {
checkIsOnPropertiesPatched(new WebSocket('ws://localhost:8001'), websocketEventNames);
} catch (e) {
console.log('error when creating websocket', e);
}
});
it('should not patch ignored on properties', function() {
@@ -242,6 +604,16 @@ describe('Zone', function() {
document.body.removeChild(div);
});
it('property startsWith on but not event listener should still work as expected', () => {
(window as any).one_two_three = {foo: 'bar'};
expect((window as any).one_two_three).toEqual({foo: 'bar'});
(window as any).one_two_three = {bar: 'foo'};
expect((window as any).one_two_three).toEqual({bar: 'foo'});
(window as any).one_two_three = null;
expect((window as any).one_two_three).toBeNull();
});
it('should be able to clear on handler added before load zone.js', function() {
const TestTarget: any = (window as any)['TestTarget'];
patchFilteredProperties(