As part of FW-1265, the `zone.js` package is made compatible with the TypeScript `--strict` flag. Read more about the strict flag [here](https://www.typescriptlang.org/docs/handbook/compiler-options.html) PR Close #30993
		
			
				
	
	
		
			510 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google Inc. 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
 | |
|  */
 | |
| /**
 | |
|  * Suppress closure compiler errors about unknown 'Zone' variable
 | |
|  * @fileoverview
 | |
|  * @suppress {undefinedVars,globalThis,missingRequire}
 | |
|  */
 | |
| 
 | |
| /// <reference types="node"/>
 | |
| 
 | |
| // issue #989, to reduce bundle size, use short name
 | |
| /** Object.getOwnPropertyDescriptor */
 | |
| export const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
 | |
| /** Object.defineProperty */
 | |
| export const ObjectDefineProperty = Object.defineProperty;
 | |
| /** Object.getPrototypeOf */
 | |
| export const ObjectGetPrototypeOf = Object.getPrototypeOf;
 | |
| /** Object.create */
 | |
| export const ObjectCreate = Object.create;
 | |
| /** Array.prototype.slice */
 | |
| export const ArraySlice = Array.prototype.slice;
 | |
| /** addEventListener string const */
 | |
| export const ADD_EVENT_LISTENER_STR = 'addEventListener';
 | |
| /** removeEventListener string const */
 | |
| export const REMOVE_EVENT_LISTENER_STR = 'removeEventListener';
 | |
| /** zoneSymbol addEventListener */
 | |
| export const ZONE_SYMBOL_ADD_EVENT_LISTENER = Zone.__symbol__(ADD_EVENT_LISTENER_STR);
 | |
| /** zoneSymbol removeEventListener */
 | |
| export const ZONE_SYMBOL_REMOVE_EVENT_LISTENER = Zone.__symbol__(REMOVE_EVENT_LISTENER_STR);
 | |
| /** true string const */
 | |
| export const TRUE_STR = 'true';
 | |
| /** false string const */
 | |
| export const FALSE_STR = 'false';
 | |
| /** Zone symbol prefix string const. */
 | |
| export const ZONE_SYMBOL_PREFIX = Zone.__symbol__('');
 | |
| 
 | |
| export function wrapWithCurrentZone<T extends Function>(callback: T, source: string): T {
 | |
|   return Zone.current.wrap(callback, source);
 | |
| }
 | |
| 
 | |
| export function scheduleMacroTaskWithCurrentZone(
 | |
|     source: string, callback: Function, data?: TaskData, customSchedule?: (task: Task) => void,
 | |
|     customCancel?: (task: Task) => void): MacroTask {
 | |
|   return Zone.current.scheduleMacroTask(source, callback, data, customSchedule, customCancel);
 | |
| }
 | |
| 
 | |
| // Hack since TypeScript isn't compiling this for a worker.
 | |
| declare const WorkerGlobalScope: any;
 | |
| 
 | |
| export const zoneSymbol = Zone.__symbol__;
 | |
| const isWindowExists = typeof window !== 'undefined';
 | |
| const internalWindow: any = isWindowExists ? window : undefined;
 | |
| const _global: any = isWindowExists && internalWindow || typeof self === 'object' && self || global;
 | |
| 
 | |
| const REMOVE_ATTRIBUTE = 'removeAttribute';
 | |
| const NULL_ON_PROP_VALUE: [any] = [null];
 | |
| 
 | |
| export function bindArguments(args: any[], source: string): any[] {
 | |
|   for (let i = args.length - 1; i >= 0; i--) {
 | |
|     if (typeof args[i] === 'function') {
 | |
|       args[i] = wrapWithCurrentZone(args[i], source + '_' + i);
 | |
|     }
 | |
|   }
 | |
|   return args;
 | |
| }
 | |
| 
 | |
| export function patchPrototype(prototype: any, fnNames: string[]) {
 | |
|   const source = prototype.constructor['name'];
 | |
|   for (let i = 0; i < fnNames.length; i++) {
 | |
|     const name = fnNames[i];
 | |
|     const delegate = prototype[name];
 | |
|     if (delegate) {
 | |
|       const prototypeDesc = ObjectGetOwnPropertyDescriptor(prototype, name);
 | |
|       if (!isPropertyWritable(prototypeDesc)) {
 | |
|         continue;
 | |
|       }
 | |
|       prototype[name] = ((delegate: Function) => {
 | |
|         const patched: any = function(this: unknown) {
 | |
|           return delegate.apply(this, bindArguments(<any>arguments, source + '.' + name));
 | |
|         };
 | |
|         attachOriginToPatched(patched, delegate);
 | |
|         return patched;
 | |
|       })(delegate);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| export function isPropertyWritable(propertyDesc: any) {
 | |
|   if (!propertyDesc) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (propertyDesc.writable === false) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return !(typeof propertyDesc.get === 'function' && typeof propertyDesc.set === 'undefined');
 | |
| }
 | |
| 
 | |
| export const isWebWorker: boolean =
 | |
|     (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope);
 | |
| 
 | |
| // Make sure to access `process` through `_global` so that WebPack does not accidentally browserify
 | |
| // this code.
 | |
| export const isNode: boolean =
 | |
|     (!('nw' in _global) && typeof _global.process !== 'undefined' &&
 | |
|      {}.toString.call(_global.process) === '[object process]');
 | |
| 
 | |
| export const isBrowser: boolean =
 | |
|     !isNode && !isWebWorker && !!(isWindowExists && internalWindow['HTMLElement']);
 | |
| 
 | |
| // we are in electron of nw, so we are both browser and nodejs
 | |
| // Make sure to access `process` through `_global` so that WebPack does not accidentally browserify
 | |
| // this code.
 | |
| export const isMix: boolean = typeof _global.process !== 'undefined' &&
 | |
|     {}.toString.call(_global.process) === '[object process]' && !isWebWorker &&
 | |
|     !!(isWindowExists && internalWindow['HTMLElement']);
 | |
| 
 | |
| const zoneSymbolEventNames: {[eventName: string]: string} = {};
 | |
| 
 | |
| const wrapFn = function(this: unknown, event: Event) {
 | |
|   // https://github.com/angular/zone.js/issues/911, in IE, sometimes
 | |
|   // event will be undefined, so we need to use window.event
 | |
|   event = event || _global.event;
 | |
|   if (!event) {
 | |
|     return;
 | |
|   }
 | |
|   let eventNameSymbol = zoneSymbolEventNames[event.type];
 | |
|   if (!eventNameSymbol) {
 | |
|     eventNameSymbol = zoneSymbolEventNames[event.type] = zoneSymbol('ON_PROPERTY' + event.type);
 | |
|   }
 | |
|   const target = this || event.target || _global;
 | |
|   const listener = target[eventNameSymbol];
 | |
|   let result;
 | |
|   if (isBrowser && target === internalWindow && event.type === 'error') {
 | |
|     // window.onerror have different signiture
 | |
|     // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror
 | |
|     // and onerror callback will prevent default when callback return true
 | |
|     const errorEvent: ErrorEvent = event as any;
 | |
|     result = listener &&
 | |
|         listener.call(
 | |
|             this, errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno,
 | |
|             errorEvent.error);
 | |
|     if (result === true) {
 | |
|       event.preventDefault();
 | |
|     }
 | |
|   } else {
 | |
|     result = listener && listener.apply(this, arguments);
 | |
|     if (result != undefined && !result) {
 | |
|       event.preventDefault();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| };
 | |
| 
 | |
| export function patchProperty(obj: any, prop: string, prototype?: any) {
 | |
|   let desc = ObjectGetOwnPropertyDescriptor(obj, prop);
 | |
|   if (!desc && prototype) {
 | |
|     // when patch window object, use prototype to check prop exist or not
 | |
|     const prototypeDesc = ObjectGetOwnPropertyDescriptor(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;
 | |
|   }
 | |
| 
 | |
|   const onPropPatchedSymbol = zoneSymbol('on' + prop + 'patched');
 | |
|   if (obj.hasOwnProperty(onPropPatchedSymbol) && obj[onPropPatchedSymbol]) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // A property descriptor cannot have getter/setter and be writable
 | |
|   // deleting the writable and value properties avoids this error:
 | |
|   //
 | |
|   // TypeError: property descriptors must not specify a value or be writable when a
 | |
|   // getter or setter has been specified
 | |
|   delete desc.writable;
 | |
|   delete desc.value;
 | |
|   const originalDescGet = desc.get;
 | |
|   const originalDescSet = desc.set;
 | |
| 
 | |
|   // substr(2) cuz 'onclick' -> 'click', etc
 | |
|   const eventName = prop.substr(2);
 | |
| 
 | |
|   let eventNameSymbol = zoneSymbolEventNames[eventName];
 | |
|   if (!eventNameSymbol) {
 | |
|     eventNameSymbol = zoneSymbolEventNames[eventName] = zoneSymbol('ON_PROPERTY' + eventName);
 | |
|   }
 | |
| 
 | |
|   desc.set = function(this: EventSource, newValue) {
 | |
|     // in some of windows's onproperty callback, this is undefined
 | |
|     // so we need to check it
 | |
|     let target = this;
 | |
|     if (!target && obj === _global) {
 | |
|       target = _global;
 | |
|     }
 | |
|     if (!target) {
 | |
|       return;
 | |
|     }
 | |
|     let previousValue = (target as any)[eventNameSymbol];
 | |
|     if (previousValue) {
 | |
|       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);
 | |
|     }
 | |
| 
 | |
|     if (typeof newValue === 'function') {
 | |
|       (target as any)[eventNameSymbol] = newValue;
 | |
|       target.addEventListener(eventName, wrapFn, false);
 | |
|     } else {
 | |
|       (target as any)[eventNameSymbol] = null;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // The getter would return undefined for unassigned properties but the default value of an
 | |
|   // unassigned property is null
 | |
|   desc.get = function() {
 | |
|     // in some of windows's onproperty callback, this is undefined
 | |
|     // so we need to check it
 | |
|     let target = this;
 | |
|     if (!target && obj === _global) {
 | |
|       target = _global;
 | |
|     }
 | |
|     if (!target) {
 | |
|       return null;
 | |
|     }
 | |
|     const listener = (target as any)[eventNameSymbol];
 | |
|     if (listener) {
 | |
|       return listener;
 | |
|     } else if (originalDescGet) {
 | |
|       // result will be null when use inline event attribute,
 | |
|       // such as <button onclick="func();">OK</button>
 | |
|       // because the onclick function is internal raw uncompiled handler
 | |
|       // the onclick will be evaluated when first time event was triggered or
 | |
|       // the property is accessed, https://github.com/angular/zone.js/issues/525
 | |
|       // so we should use original native get to retrieve the handler
 | |
|       let value = originalDescGet && originalDescGet.call(this);
 | |
|       if (value) {
 | |
|         desc !.set !.call(this, value);
 | |
|         if (typeof(target as any)[REMOVE_ATTRIBUTE] === 'function') {
 | |
|           (target as any).removeAttribute(prop);
 | |
|         }
 | |
|         return value;
 | |
|       }
 | |
|     }
 | |
|     return null;
 | |
|   };
 | |
| 
 | |
|   ObjectDefineProperty(obj, prop, desc);
 | |
| 
 | |
|   obj[onPropPatchedSymbol] = true;
 | |
| }
 | |
| 
 | |
| export function patchOnProperties(obj: any, properties: string[] | null, prototype?: any) {
 | |
|   if (properties) {
 | |
|     for (let i = 0; i < properties.length; i++) {
 | |
|       patchProperty(obj, 'on' + properties[i], prototype);
 | |
|     }
 | |
|   } else {
 | |
|     const onProperties = [];
 | |
|     for (const prop in obj) {
 | |
|       if (prop.substr(0, 2) == 'on') {
 | |
|         onProperties.push(prop);
 | |
|       }
 | |
|     }
 | |
|     for (let j = 0; j < onProperties.length; j++) {
 | |
|       patchProperty(obj, onProperties[j], prototype);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| const originalInstanceKey = zoneSymbol('originalInstance');
 | |
| 
 | |
| // wrap some native API on `window`
 | |
| export function patchClass(className: string) {
 | |
|   const OriginalClass = _global[className];
 | |
|   if (!OriginalClass) return;
 | |
|   // keep original class in global
 | |
|   _global[zoneSymbol(className)] = OriginalClass;
 | |
| 
 | |
|   _global[className] = function() {
 | |
|     const a = bindArguments(<any>arguments, className);
 | |
|     switch (a.length) {
 | |
|       case 0:
 | |
|         this[originalInstanceKey] = new OriginalClass();
 | |
|         break;
 | |
|       case 1:
 | |
|         this[originalInstanceKey] = new OriginalClass(a[0]);
 | |
|         break;
 | |
|       case 2:
 | |
|         this[originalInstanceKey] = new OriginalClass(a[0], a[1]);
 | |
|         break;
 | |
|       case 3:
 | |
|         this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2]);
 | |
|         break;
 | |
|       case 4:
 | |
|         this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2], a[3]);
 | |
|         break;
 | |
|       default:
 | |
|         throw new Error('Arg list too long.');
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // attach original delegate to patched function
 | |
|   attachOriginToPatched(_global[className], OriginalClass);
 | |
| 
 | |
|   const instance = new OriginalClass(function() {});
 | |
| 
 | |
|   let prop;
 | |
|   for (prop in instance) {
 | |
|     // https://bugs.webkit.org/show_bug.cgi?id=44721
 | |
|     if (className === 'XMLHttpRequest' && prop === 'responseBlob') continue;
 | |
|     (function(prop) {
 | |
|       if (typeof instance[prop] === 'function') {
 | |
|         _global[className].prototype[prop] = function() {
 | |
|           return this[originalInstanceKey][prop].apply(this[originalInstanceKey], arguments);
 | |
|         };
 | |
|       } else {
 | |
|         ObjectDefineProperty(_global[className].prototype, prop, {
 | |
|           set: function(fn) {
 | |
|             if (typeof fn === 'function') {
 | |
|               this[originalInstanceKey][prop] = wrapWithCurrentZone(fn, className + '.' + prop);
 | |
|               // keep callback in wrapped function so we can
 | |
|               // use it in Function.prototype.toString to return
 | |
|               // the native one.
 | |
|               attachOriginToPatched(this[originalInstanceKey][prop], fn);
 | |
|             } else {
 | |
|               this[originalInstanceKey][prop] = fn;
 | |
|             }
 | |
|           },
 | |
|           get: function() { return this[originalInstanceKey][prop]; }
 | |
|         });
 | |
|       }
 | |
|     }(prop));
 | |
|   }
 | |
| 
 | |
|   for (prop in OriginalClass) {
 | |
|     if (prop !== 'prototype' && OriginalClass.hasOwnProperty(prop)) {
 | |
|       _global[className][prop] = OriginalClass[prop];
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| export function copySymbolProperties(src: any, dest: any) {
 | |
|   if (typeof(Object as any).getOwnPropertySymbols !== 'function') {
 | |
|     return;
 | |
|   }
 | |
|   const symbols: any = (Object as any).getOwnPropertySymbols(src);
 | |
|   symbols.forEach((symbol: any) => {
 | |
|     const desc = Object.getOwnPropertyDescriptor(src, symbol);
 | |
|     Object.defineProperty(dest, symbol, {
 | |
|       get: function() { return src[symbol]; },
 | |
|       set: function(value: any) {
 | |
|         if (desc && (!desc.writable || typeof desc.set !== 'function')) {
 | |
|           // if src[symbol] is not writable or not have a setter, just return
 | |
|           return;
 | |
|         }
 | |
|         src[symbol] = value;
 | |
|       },
 | |
|       enumerable: desc ? desc.enumerable : true,
 | |
|       configurable: desc ? desc.configurable : true
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| let shouldCopySymbolProperties = false;
 | |
| 
 | |
| export function setShouldCopySymbolProperties(flag: boolean) {
 | |
|   shouldCopySymbolProperties = flag;
 | |
| }
 | |
| 
 | |
| export function patchMethod(
 | |
|     target: any, name: string, patchFn: (delegate: Function, delegateName: string, name: string) =>
 | |
|                                    (self: any, args: any[]) => any): Function|null {
 | |
|   let proto = target;
 | |
|   while (proto && !proto.hasOwnProperty(name)) {
 | |
|     proto = ObjectGetPrototypeOf(proto);
 | |
|   }
 | |
|   if (!proto && target[name]) {
 | |
|     // somehow we did not find it, but we can see it. This happens on IE for Window properties.
 | |
|     proto = target;
 | |
|   }
 | |
| 
 | |
|   const delegateName = zoneSymbol(name);
 | |
|   let delegate: Function|null = null;
 | |
|   if (proto && !(delegate = proto[delegateName])) {
 | |
|     delegate = proto[delegateName] = proto[name];
 | |
|     // check whether proto[name] is writable
 | |
|     // some property is readonly in safari, such as HtmlCanvasElement.prototype.toBlob
 | |
|     const desc = proto && ObjectGetOwnPropertyDescriptor(proto, name);
 | |
|     if (isPropertyWritable(desc)) {
 | |
|       const patchDelegate = patchFn(delegate !, delegateName, name);
 | |
|       proto[name] = function() { return patchDelegate(this, arguments as any); };
 | |
|       attachOriginToPatched(proto[name], delegate);
 | |
|       if (shouldCopySymbolProperties) {
 | |
|         copySymbolProperties(delegate, proto[name]);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return delegate;
 | |
| }
 | |
| 
 | |
| export interface MacroTaskMeta extends TaskData {
 | |
|   name: string;
 | |
|   target: any;
 | |
|   cbIdx: number;
 | |
|   args: any[];
 | |
| }
 | |
| 
 | |
| // TODO: @JiaLiPassion, support cancel task later if necessary
 | |
| export function patchMacroTask(
 | |
|     obj: any, funcName: string, metaCreator: (self: any, args: any[]) => MacroTaskMeta) {
 | |
|   let setNative: Function|null = null;
 | |
| 
 | |
|   function scheduleTask(task: Task) {
 | |
|     const data = <MacroTaskMeta>task.data;
 | |
|     data.args[data.cbIdx] = function() { task.invoke.apply(this, arguments); };
 | |
|     setNative !.apply(data.target, data.args);
 | |
|     return task;
 | |
|   }
 | |
| 
 | |
|   setNative = patchMethod(obj, funcName, (delegate: Function) => function(self: any, args: any[]) {
 | |
|     const meta = metaCreator(self, args);
 | |
|     if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === 'function') {
 | |
|       return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask);
 | |
|     } else {
 | |
|       // cause an error by calling it directly.
 | |
|       return delegate.apply(self, args);
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| export interface MicroTaskMeta extends TaskData {
 | |
|   name: string;
 | |
|   target: any;
 | |
|   cbIdx: number;
 | |
|   args: any[];
 | |
| }
 | |
| 
 | |
| export function patchMicroTask(
 | |
|     obj: any, funcName: string, metaCreator: (self: any, args: any[]) => MicroTaskMeta) {
 | |
|   let setNative: Function|null = null;
 | |
| 
 | |
|   function scheduleTask(task: Task) {
 | |
|     const data = <MacroTaskMeta>task.data;
 | |
|     data.args[data.cbIdx] = function() { task.invoke.apply(this, arguments); };
 | |
|     setNative !.apply(data.target, data.args);
 | |
|     return task;
 | |
|   }
 | |
| 
 | |
|   setNative = patchMethod(obj, funcName, (delegate: Function) => function(self: any, args: any[]) {
 | |
|     const meta = metaCreator(self, args);
 | |
|     if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === 'function') {
 | |
|       return Zone.current.scheduleMicroTask(meta.name, args[meta.cbIdx], meta, scheduleTask);
 | |
|     } else {
 | |
|       // cause an error by calling it directly.
 | |
|       return delegate.apply(self, args);
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| export function attachOriginToPatched(patched: Function, original: any) {
 | |
|   (patched as any)[zoneSymbol('OriginalDelegate')] = original;
 | |
| }
 | |
| 
 | |
| let isDetectedIEOrEdge = false;
 | |
| let ieOrEdge = false;
 | |
| 
 | |
| export function isIE() {
 | |
|   try {
 | |
|     const ua = internalWindow.navigator.userAgent;
 | |
|     if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1) {
 | |
|       return true;
 | |
|     }
 | |
|   } catch (error) {
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| export function isIEOrEdge() {
 | |
|   if (isDetectedIEOrEdge) {
 | |
|     return ieOrEdge;
 | |
|   }
 | |
| 
 | |
|   isDetectedIEOrEdge = true;
 | |
| 
 | |
|   try {
 | |
|     const ua = internalWindow.navigator.userAgent;
 | |
|     if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1 || ua.indexOf('Edge/') !== -1) {
 | |
|       ieOrEdge = true;
 | |
|     }
 | |
|   } catch (error) {
 | |
|   }
 | |
|   return ieOrEdge;
 | |
| }
 |