mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
f307e95459
Migrate formatting to prettier for zone.js from clang-format PR Close #55427
390 lines
9.0 KiB
TypeScript
390 lines
9.0 KiB
TypeScript
/**
|
|
* @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
|
|
*/
|
|
/**
|
|
* @fileoverview
|
|
* @suppress {globalThis}
|
|
*/
|
|
|
|
import * as webSocketPatch from './websocket';
|
|
|
|
export function propertyDescriptorLegacyPatch(api: _ZonePrivate, _global: any) {
|
|
const {isNode, isMix} = api.getGlobalObjects()!;
|
|
if (isNode && !isMix) {
|
|
return;
|
|
}
|
|
|
|
if (!canPatchViaPropertyDescriptor(api, _global)) {
|
|
const supportsWebSocket = typeof WebSocket !== 'undefined';
|
|
// Safari, Android browsers (Jelly Bean)
|
|
patchViaCapturingAllTheEvents(api);
|
|
api.patchClass('XMLHttpRequest');
|
|
if (supportsWebSocket) {
|
|
webSocketPatch.apply(api, _global);
|
|
}
|
|
(Zone as any)[api.symbol('patchEvents')] = true;
|
|
}
|
|
}
|
|
|
|
function canPatchViaPropertyDescriptor(api: _ZonePrivate, _global: any) {
|
|
const {isBrowser, isMix} = api.getGlobalObjects()!;
|
|
if (
|
|
(isBrowser || isMix) &&
|
|
!api.ObjectGetOwnPropertyDescriptor(HTMLElement.prototype, 'onclick') &&
|
|
typeof Element !== 'undefined'
|
|
) {
|
|
// WebKit https://bugs.webkit.org/show_bug.cgi?id=134364
|
|
// IDL interface attributes are not configurable
|
|
const desc = api.ObjectGetOwnPropertyDescriptor(Element.prototype, 'onclick');
|
|
if (desc && !desc.configurable) return false;
|
|
// try to use onclick to detect whether we can patch via propertyDescriptor
|
|
// because XMLHttpRequest is not available in service worker
|
|
if (desc) {
|
|
api.ObjectDefineProperty(Element.prototype, 'onclick', {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function () {
|
|
return true;
|
|
},
|
|
});
|
|
const div = document.createElement('div');
|
|
const result = !!div.onclick;
|
|
api.ObjectDefineProperty(Element.prototype, 'onclick', desc);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
const XMLHttpRequest = _global['XMLHttpRequest'];
|
|
if (!XMLHttpRequest) {
|
|
// XMLHttpRequest is not available in service worker
|
|
return false;
|
|
}
|
|
const ON_READY_STATE_CHANGE = 'onreadystatechange';
|
|
const XMLHttpRequestPrototype = XMLHttpRequest.prototype;
|
|
|
|
const xhrDesc = api.ObjectGetOwnPropertyDescriptor(
|
|
XMLHttpRequestPrototype,
|
|
ON_READY_STATE_CHANGE,
|
|
);
|
|
|
|
// add enumerable and configurable here because in opera
|
|
// by default XMLHttpRequest.prototype.onreadystatechange is undefined
|
|
// without adding enumerable and configurable will cause onreadystatechange
|
|
// non-configurable
|
|
// and if XMLHttpRequest.prototype.onreadystatechange is undefined,
|
|
// we should set a real desc instead a fake one
|
|
if (xhrDesc) {
|
|
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function () {
|
|
return true;
|
|
},
|
|
});
|
|
const req = new XMLHttpRequest();
|
|
const result = !!req.onreadystatechange;
|
|
// restore original desc
|
|
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, xhrDesc || {});
|
|
return result;
|
|
} else {
|
|
const SYMBOL_FAKE_ONREADYSTATECHANGE = api.symbol('fake');
|
|
api.ObjectDefineProperty(XMLHttpRequestPrototype, ON_READY_STATE_CHANGE, {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function () {
|
|
return this[SYMBOL_FAKE_ONREADYSTATECHANGE];
|
|
},
|
|
set: function (value) {
|
|
this[SYMBOL_FAKE_ONREADYSTATECHANGE] = value;
|
|
},
|
|
});
|
|
const req = new XMLHttpRequest();
|
|
const detectFunc = () => {};
|
|
req.onreadystatechange = detectFunc;
|
|
const result = (req as any)[SYMBOL_FAKE_ONREADYSTATECHANGE] === detectFunc;
|
|
req.onreadystatechange = null as any;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
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 eventNames = [
|
|
...globalEventHandlersEventNames,
|
|
...webglEventNames,
|
|
...formEventNames,
|
|
...detailEventNames,
|
|
...documentEventNames,
|
|
...windowEventNames,
|
|
...htmlElementEventNames,
|
|
...ieElementEventNames,
|
|
];
|
|
|
|
// Whenever any eventListener fires, we check the eventListener target and all parents
|
|
// for `onwhatever` properties and replace them with zone-bound functions
|
|
// - Chrome (for now)
|
|
function patchViaCapturingAllTheEvents(api: _ZonePrivate) {
|
|
const unboundKey = api.symbol('unbound');
|
|
for (let i = 0; i < eventNames.length; i++) {
|
|
const property = eventNames[i];
|
|
const onproperty = 'on' + property;
|
|
self.addEventListener(
|
|
property,
|
|
function (event) {
|
|
let elt: any = <Node>event.target,
|
|
bound,
|
|
source;
|
|
if (elt) {
|
|
source = elt.constructor['name'] + '.' + onproperty;
|
|
} else {
|
|
source = 'unknown.' + onproperty;
|
|
}
|
|
while (elt) {
|
|
if (elt[onproperty] && !elt[onproperty][unboundKey]) {
|
|
bound = api.wrapWithCurrentZone(elt[onproperty], source);
|
|
bound[unboundKey] = elt[onproperty];
|
|
elt[onproperty] = bound;
|
|
}
|
|
elt = elt.parentElement;
|
|
}
|
|
},
|
|
true,
|
|
);
|
|
}
|
|
}
|