diff --git a/dist/zone.js b/dist/zone.js index e15c127cf..b9c45aa1c 100644 --- a/dist/zone.js +++ b/dist/zone.js @@ -18,8 +18,42 @@ * 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 */ +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (undefined && undefined.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; var Zone$1 = (function (global) { - var FUNCTION = 'function'; var performance = global['performance']; function mark(name) { performance && performance['mark'] && performance['mark'](name); @@ -29,11 +63,33 @@ var Zone$1 = (function (global) { } mark('Zone'); if (global['Zone']) { - throw new Error('Zone already loaded.'); + // if global['Zone'] already exists (maybe zone.js was already loaded or + // some other lib also registered a global object named Zone), we may need + // to throw an error, but sometimes user may not want this error. + // For example, + // we have two web pages, page1 includes zone.js, page2 doesn't. + // and the 1st time user load page1 and page2, everything work fine, + // but when user load page2 again, error occurs because global['Zone'] already exists. + // so we add a flag to let user choose whether to throw this error or not. + // By default, if existing Zone is from zone.js, we will not throw the error. + if (global[('__zone_symbol__forceDuplicateZoneCheck')] === true || + typeof global['Zone'].__symbol__ !== 'function') { + throw new Error('Zone already loaded.'); + } + else { + return global['Zone']; + } } + var detectAsyncFunction = function () { + return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { + return [2 /*return*/]; + }); }); + }; + var AsyncFunction = detectAsyncFunction.constructor.name === 'AsyncFunction' ? + detectAsyncFunction.constructor : + null; var Zone = /** @class */ (function () { function Zone(parent, zoneSpec) { - this._properties = null; this._parent = parent; this._name = zoneSpec ? zoneSpec.name || 'unnamed' : ''; this._properties = zoneSpec && zoneSpec.properties || {}; @@ -74,6 +130,9 @@ var Zone$1 = (function (global) { enumerable: true, configurable: true }); + Zone.setAsyncFrame = function () { + _currentZoneFrame = _asyncZoneFrame; + }; Zone.__load_patch = function (name, fn) { if (patches.hasOwnProperty(name)) { throw Error('Already loaded patch: ' + name); @@ -120,7 +179,7 @@ var Zone$1 = (function (global) { return this._zoneDelegate.fork(this, zoneSpec); }; Zone.prototype.wrap = function (callback, source) { - if (typeof callback !== FUNCTION) { + if (typeof callback !== 'function') { throw new Error('Expecting function got: ' + callback); } var _callback = this._zoneDelegate.intercept(this, callback, source); @@ -130,21 +189,34 @@ var Zone$1 = (function (global) { }; }; Zone.prototype.run = function (callback, applyThis, applyArgs, source) { - if (applyThis === void 0) { applyThis = undefined; } - if (applyArgs === void 0) { applyArgs = null; } - if (source === void 0) { source = null; } _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { + if (callback && callback.constructor === AsyncFunction) { + var r = this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); + if (r && typeof r.then === 'function') { + _asyncZoneFrame = _currentZoneFrame; + return r.then(function (result) { + _currentZoneFrame = _asyncZoneFrame.parent; + _isAsyncSet = true; + _asyncZoneFrame = null; + return result; + }); + } + return r; + } return this._zoneDelegate.invoke(this, callback, applyThis, applyArgs, source); } finally { - _currentZoneFrame = _currentZoneFrame.parent; + if (!_isAsyncSet) { + _currentZoneFrame = _currentZoneFrame.parent; + } + else { + _isAsyncSet = false; + } } }; Zone.prototype.runGuarded = function (callback, applyThis, applyArgs, source) { if (applyThis === void 0) { applyThis = null; } - if (applyArgs === void 0) { applyArgs = null; } - if (source === void 0) { source = null; } _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { try { @@ -168,10 +240,7 @@ var Zone$1 = (function (global) { // https://github.com/angular/zone.js/issues/778, sometimes eventTask // will run in notScheduled(canceled) state, we should not try to // run such kind of task but just return - // we have to define an variable here, if not - // typescript compiler will complain below - var isNotScheduled = task.state === notScheduled; - if (isNotScheduled && task.type === eventTask) { + if (task.state === notScheduled && task.type === eventTask) { return; } var reEntryGuard = task.state != running; @@ -182,7 +251,7 @@ var Zone$1 = (function (global) { _currentZoneFrame = { parent: _currentZoneFrame, zone: this }; try { if (task.type == macroTask && task.data && !task.data.isPeriodic) { - task.cancelFn = null; + task.cancelFn = undefined; } try { return this._zoneDelegate.invokeTask(this, task, applyThis, applyArgs); @@ -218,8 +287,7 @@ var Zone$1 = (function (global) { var newZone = this; while (newZone) { if (newZone === task.zone) { - throw Error("can not reschedule task to " + this - .name + " which is descendants of the original zone " + task.zone.name); + throw Error("can not reschedule task to " + this.name + " which is descendants of the original zone " + task.zone.name); } newZone = newZone.parent; } @@ -249,7 +317,7 @@ var Zone$1 = (function (global) { return task; }; Zone.prototype.scheduleMicroTask = function (source, callback, data, customSchedule) { - return this.scheduleTask(new ZoneTask(microTask, source, callback, data, customSchedule, null)); + return this.scheduleTask(new ZoneTask(microTask, source, callback, data, customSchedule, undefined)); }; Zone.prototype.scheduleMacroTask = function (source, callback, data, customSchedule, customCancel) { return this.scheduleTask(new ZoneTask(macroTask, source, callback, data, customSchedule, customCancel)); @@ -290,16 +358,14 @@ var Zone$1 = (function (global) { }()); var DELEGATE_ZS = { name: '', - onHasTask: function (delegate, _, target, hasTaskState) { - return delegate.hasTask(target, hasTaskState); - }, + onHasTask: function (delegate, _, target, hasTaskState) { return delegate.hasTask(target, hasTaskState); }, onScheduleTask: function (delegate, _, target, task) { return delegate.scheduleTask(target, task); }, - onInvokeTask: function (delegate, _, target, task, applyThis, applyArgs) { return delegate.invokeTask(target, task, applyThis, applyArgs); }, - onCancelTask: function (delegate, _, target, task) { - return delegate.cancelTask(target, task); - } + onInvokeTask: function (delegate, _, target, task, applyThis, applyArgs) { + return delegate.invokeTask(target, task, applyThis, applyArgs); + }, + onCancelTask: function (delegate, _, target, task) { return delegate.cancelTask(target, task); } }; var ZoneDelegate = /** @class */ (function () { function ZoneDelegate(zone, parentDelegate, zoneSpec) { @@ -327,8 +393,8 @@ var Zone$1 = (function (global) { zoneSpec && (zoneSpec.onHandleError ? this.zone : parentDelegate.zone); this._scheduleTaskZS = zoneSpec && (zoneSpec.onScheduleTask ? zoneSpec : parentDelegate._scheduleTaskZS); - this._scheduleTaskDlgt = - zoneSpec && (zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt); + this._scheduleTaskDlgt = zoneSpec && + (zoneSpec.onScheduleTask ? parentDelegate : parentDelegate._scheduleTaskDlgt); this._scheduleTaskCurrZone = zoneSpec && (zoneSpec.onScheduleTask ? this.zone : parentDelegate.zone); this._invokeTaskZS = @@ -383,8 +449,7 @@ var Zone$1 = (function (global) { callback; }; ZoneDelegate.prototype.invoke = function (targetZone, callback, applyThis, applyArgs, source) { - return this._invokeZS ? - this._invokeZS.onInvoke(this._invokeDlgt, this._invokeCurrZone, targetZone, callback, applyThis, applyArgs, source) : + return this._invokeZS ? this._invokeZS.onInvoke(this._invokeDlgt, this._invokeCurrZone, targetZone, callback, applyThis, applyArgs, source) : callback.apply(applyThis, applyArgs); }; ZoneDelegate.prototype.handleError = function (targetZone, error) { @@ -416,8 +481,7 @@ var Zone$1 = (function (global) { return returnTask; }; ZoneDelegate.prototype.invokeTask = function (targetZone, task, applyThis, applyArgs) { - return this._invokeTaskZS ? - this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt, this._invokeTaskCurrZone, targetZone, task, applyThis, applyArgs) : + return this._invokeTaskZS ? this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt, this._invokeTaskCurrZone, targetZone, task, applyThis, applyArgs) : task.callback.apply(applyThis, applyArgs); }; ZoneDelegate.prototype.cancelTask = function (targetZone, task) { @@ -437,7 +501,7 @@ var Zone$1 = (function (global) { // hasTask should not throw error so other ZoneDelegate // can still trigger hasTask callback try { - return this._hasTaskZS && + this._hasTaskZS && this._hasTaskZS.onHasTask(this._hasTaskDlgt, this._hasTaskCurrZone, targetZone, isEmpty); } catch (err) { @@ -527,9 +591,7 @@ var Zone$1 = (function (global) { } } else { - throw new Error(this.type + " '" + this.source + "': can not transition to '" + toState + "', expecting state '" + fromState1 + "'" + (fromState2 ? - ' or \'' + fromState2 + '\'' : - '') + ", was '" + this._state + "'."); + throw new Error(this.type + " '" + this.source + "': can not transition to '" + toState + "', expecting state '" + fromState1 + "'" + (fromState2 ? ' or \'' + fromState2 + '\'' : '') + ", was '" + this._state + "'."); } }; ZoneTask.prototype.toString = function () { @@ -575,7 +637,13 @@ var Zone$1 = (function (global) { } } if (nativeMicroTaskQueuePromise) { - nativeMicroTaskQueuePromise[symbolThen](drainMicroTaskQueue); + var nativeThen = nativeMicroTaskQueuePromise[symbolThen]; + if (!nativeThen) { + // native Promise is not patchable, we need to use `then` directly + // issue 1078 + nativeThen = nativeMicroTaskQueuePromise['then']; + } + nativeThen.call(nativeMicroTaskQueuePromise, drainMicroTaskQueue); } else { global[symbolSetTimeout](drainMicroTaskQueue, 0); @@ -622,17 +690,20 @@ var Zone$1 = (function (global) { patchEventTarget: function () { return []; }, patchOnProperties: noop, patchMethod: function () { return noop; }, - bindArguments: function () { return null; }, + bindArguments: function () { return []; }, + patchThen: function () { return noop; }, setNativePromise: function (NativePromise) { // sometimes NativePromise.resolve static function // is not ready yet, (such as core-js/es6.promise) // so we need to check here. - if (NativePromise && typeof NativePromise.resolve === FUNCTION) { + if (NativePromise && typeof NativePromise.resolve === 'function') { nativeMicroTaskQueuePromise = NativePromise.resolve(0); } }, }; var _currentZoneFrame = { parent: null, zone: new Zone(null, null) }; + var _asyncZoneFrame = null; + var _isAsyncSet = false; var _currentTask = null; var _numberOfNestedTaskFrames = 0; function noop() { } @@ -643,6 +714,16 @@ var Zone$1 = (function (global) { return global['Zone'] = Zone; })(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global); +var __values = (undefined && undefined.__values) || function (o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +}; Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { var ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var ObjectDefineProperty = Object.defineProperty; @@ -785,7 +866,7 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { var queue = promise[symbolValue]; promise[symbolValue] = value; if (promise[symbolFinally] === symbolFinally) { - // the promise is generated by Promise.prototype.finally + // the promise is generated by Promise.prototype.finally if (state === RESOLVED) { // the state is resolved, should ignore the value // and use parent promise value @@ -824,6 +905,9 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { api.scheduleMicroTask(); // to make sure that it is running } } + if (promise['__zone_symbol__isAsync'] === true) { + Zone.setAsyncFrame(); + } } } // Resolving an already resolved promise is a noop. @@ -869,7 +953,9 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { chainPromise[symbolParentPromiseState] = promiseState; } // should not pass value to finally callback - var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? [] : [parentPromiseValue]); + var value = zone.run(delegate, undefined, isFinallyPromise && delegate !== forwardRejection && delegate !== forwardResolution ? + [] : + [parentPromiseValue]); resolvePromise(chainPromise, true, value); } catch (error) { @@ -904,6 +990,7 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { return resolvePromise(new this(null), REJECTED, error); }; ZoneAwarePromise.race = function (values) { + var e_1, _a; var resolve; var reject; var promise = new this(function (res, rej) { @@ -916,16 +1003,26 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { function onReject(error) { promise && (promise = null || reject(error)); } - for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { - var value = values_1[_i]; - if (!isThenable(value)) { - value = this.resolve(value); + try { + for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { + var value = values_1_1.value; + if (!isThenable(value)) { + value = this.resolve(value); + } + value.then(onResolve, onReject); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1); } - value.then(onResolve, onReject); + finally { if (e_1) throw e_1.error; } } return promise; }; ZoneAwarePromise.all = function (values) { + var e_2, _a; var resolve; var reject; var promise = new this(function (res, rej) { @@ -934,19 +1031,28 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { }); var count = 0; var resolvedValues = []; - for (var _i = 0, values_2 = values; _i < values_2.length; _i++) { - var value = values_2[_i]; - if (!isThenable(value)) { - value = this.resolve(value); - } - value.then((function (index) { return function (value) { - resolvedValues[index] = value; - count--; - if (!count) { - resolve(resolvedValues); + try { + for (var values_2 = __values(values), values_2_1 = values_2.next(); !values_2_1.done; values_2_1 = values_2.next()) { + var value = values_2_1.value; + if (!isThenable(value)) { + value = this.resolve(value); } - }; })(count), reject); - count++; + value.then((function (index) { return function (value) { + resolvedValues[index] = value; + count--; + if (!count) { + resolve(resolvedValues); + } + }; })(count), reject); + count++; + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (values_2_1 && !values_2_1.done && (_a = values_2.return)) _a.call(values_2); + } + finally { if (e_2) throw e_2.error; } } if (!count) resolve(resolvedValues); @@ -1038,38 +1144,126 @@ Zone.__load_patch('ZoneAwarePromise', function (global, Zone, api) { proto[symbolThen] = originalThen; Ctor.prototype.then = function (onResolve, onReject) { var _this = this; + var isNativePromise = false; + if (this && !(this instanceof ZoneAwarePromise)) { + isNativePromise = true; + } var wrapped = new ZoneAwarePromise(function (resolve, reject) { originalThen.call(_this, resolve, reject); }); + if (isNativePromise) { + wrapped['__zone_symbol__isAsync'] = true; + } return wrapped.then(onResolve, onReject); }; Ctor[symbolThenPatched] = true; } - function zoneify(fn) { - return function () { - var resultPromise = fn.apply(this, arguments); - if (resultPromise instanceof ZoneAwarePromise) { - return resultPromise; - } - var ctor = resultPromise.constructor; - if (!ctor[symbolThenPatched]) { - patchThen(ctor); - } - return resultPromise; - }; - } + api.patchThen = patchThen; if (NativePromise) { patchThen(NativePromise); - var fetch_1 = global['fetch']; - if (typeof fetch_1 == 'function') { - global['fetch'] = zoneify(fetch_1); - } } // This is not part of public API, but it is useful for tests, so we expose it. Promise[Zone.__symbol__('uncaughtPromiseErrors')] = _uncaughtPromiseErrors; return ZoneAwarePromise; }); +/** + * @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 + */ +Zone.__load_patch('fetch', function (global, Zone, api) { + var fetch = global['fetch']; + var ZoneAwarePromise = global.Promise; + var symbolThenPatched = api.symbol('thenPatched'); + var fetchTaskScheduling = api.symbol('fetchTaskScheduling'); + var fetchTaskAborting = api.symbol('fetchTaskAborting'); + if (typeof fetch !== 'function') { + return; + } + var OriginalAbortController = global['AbortController']; + var supportAbort = typeof OriginalAbortController === 'function'; + var abortNative = null; + if (supportAbort) { + global['AbortController'] = function () { + var abortController = new OriginalAbortController(); + var signal = abortController.signal; + signal.abortController = abortController; + return abortController; + }; + abortNative = api.patchMethod(OriginalAbortController.prototype, 'abort', function (delegate) { return function (self, args) { + if (self.task) { + return self.task.zone.cancelTask(self.task); + } + return delegate.apply(self, args); + }; }); + } + var placeholder = function () { }; + global['fetch'] = function () { + var _this = this; + var args = Array.prototype.slice.call(arguments); + var options = args.length > 1 ? args[1] : null; + var signal = options && options.signal; + return new Promise(function (res, rej) { + var task = Zone.current.scheduleMacroTask('fetch', placeholder, args, function () { + var fetchPromise; + var zone = Zone.current; + try { + zone[fetchTaskScheduling] = true; + fetchPromise = fetch.apply(_this, args); + } + catch (error) { + rej(error); + return; + } + finally { + zone[fetchTaskScheduling] = false; + } + if (!(fetchPromise instanceof ZoneAwarePromise)) { + var ctor = fetchPromise.constructor; + if (!ctor[symbolThenPatched]) { + api.patchThen(ctor); + } + } + fetchPromise.then(function (resource) { + if (task.state !== 'notScheduled') { + task.invoke(); + } + res(resource); + }, function (error) { + if (task.state !== 'notScheduled') { + task.invoke(); + } + rej(error); + }); + }, function () { + if (!supportAbort) { + rej('No AbortController supported, can not cancel fetch'); + return; + } + if (signal && signal.abortController && !signal.aborted && + typeof signal.abortController.abort === 'function' && abortNative) { + try { + Zone.current[fetchTaskAborting] = true; + abortNative.call(signal.abortController); + } + finally { + Zone.current[fetchTaskAborting] = false; + } + } + else { + rej('cancel fetch need a AbortController.signal'); + } + }); + if (signal && signal.abortController) { + signal.abortController.task = task; + } + }); + }; +}); + /** * @license * Copyright Google Inc. All Rights Reserved. @@ -1185,9 +1379,23 @@ var wrapFn = function (event) { } var target = this || event.target || _global; var listener = target[eventNameSymbol]; - var result = listener && listener.apply(this, arguments); - if (result != undefined && !result) { - event.preventDefault(); + var 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 + var errorEvent = event; + 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; }; @@ -1205,6 +1413,10 @@ function patchProperty(obj, prop, prototype) { if (!desc || !desc.configurable) { return; } + var 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: // @@ -1282,6 +1494,7 @@ function patchProperty(obj, prop, prototype) { return null; }; ObjectDefineProperty(obj, prop, desc); + obj[onPropPatchedSymbol] = true; } function patchOnProperties(obj, properties, prototype) { if (properties) { @@ -1372,6 +1585,31 @@ function patchClass(className) { } } } +function copySymbolProperties(src, dest) { + if (typeof Object.getOwnPropertySymbols !== 'function') { + return; + } + var symbols = Object.getOwnPropertySymbols(src); + symbols.forEach(function (symbol) { + var desc = Object.getOwnPropertyDescriptor(src, symbol); + Object.defineProperty(dest, symbol, { + get: function () { + return src[symbol]; + }, + set: function (value) { + 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 + }); + }); +} +var shouldCopySymbolProperties = false; + function patchMethod(target, name, patchFn) { var proto = target; while (proto && !proto.hasOwnProperty(name)) { @@ -1382,7 +1620,7 @@ function patchMethod(target, name, patchFn) { proto = target; } var delegateName = zoneSymbol(name); - var delegate; + var delegate = null; if (proto && !(delegate = proto[delegateName])) { delegate = proto[delegateName] = proto[name]; // check whether proto[name] is writable @@ -1394,6 +1632,9 @@ function patchMethod(target, name, patchFn) { return patchDelegate_1(this, arguments); }; attachOriginToPatched(proto[name], delegate); + if (shouldCopySymbolProperties) { + copySymbolProperties(delegate, proto[name]); + } } } return delegate; @@ -1412,7 +1653,7 @@ function patchMacroTask(obj, funcName, metaCreator) { setNative = patchMethod(obj, funcName, function (delegate) { return function (self, args) { var meta = metaCreator(self, args); if (meta.cbIdx >= 0 && typeof args[meta.cbIdx] === 'function') { - return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask, null); + return scheduleMacroTaskWithCurrentZone(meta.name, args[meta.cbIdx], meta, scheduleTask); } else { // cause an error by calling it directly. @@ -1426,6 +1667,17 @@ function attachOriginToPatched(patched, original) { } var isDetectedIEOrEdge = false; var ieOrEdge = false; +function isIE() { + try { + var ua = internalWindow.navigator.userAgent; + if (ua.indexOf('MSIE ') !== -1 || ua.indexOf('Trident/') !== -1) { + return true; + } + } + catch (error) { + } + return false; +} function isIEOrEdge() { if (isDetectedIEOrEdge) { return ieOrEdge; @@ -1507,6 +1759,21 @@ Zone.__load_patch('toString', function (global) { * @fileoverview * @suppress {missingRequire} */ +var passiveSupported = false; +if (typeof window !== 'undefined') { + try { + var 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 var OPTIMIZED_ZONE_EVENT_TASK_DATA = { useG: true @@ -1657,12 +1924,24 @@ function patchEventTarget(_global, apis, patchOptions) { nativePrependEventListener = proto[zoneSymbol(patchOptions.prepend)] = proto[patchOptions.prepend]; } - var customScheduleGlobal = function () { + function checkIsPassive(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.options = !!taskData.options.capture; + taskData.options = task.options; + } + } + var customScheduleGlobal = function (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, taskData.options); }; var customCancelGlobal = function (task) { @@ -1703,6 +1982,7 @@ function patchEventTarget(_global, apis, patchOptions) { return nativeRemoveEventListener.call(task.target, task.eventName, task.capture ? globalZoneAwareCaptureCallback : globalZoneAwareCallback, task.options); }; var customScheduleNonGlobal = function (task) { + checkIsPassive(task); return nativeAddEventListener.call(taskData.target, taskData.eventName, task.invoke, taskData.options); }; var customSchedulePrepend = function (task) { @@ -1823,7 +2103,7 @@ function patchEventTarget(_global, apis, patchOptions) { taskData.capture = capture; taskData.eventName = eventName; taskData.isExisting = isExisting; - var data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : null; + var data = useGlobalCallback ? OPTIMIZED_ZONE_EVENT_TASK_DATA : undefined; // keep taskData into data to allow onScheduleEventTask to access the task information if (data) { data.taskData = taskData; @@ -1841,7 +2121,11 @@ function patchEventTarget(_global, apis, patchOptions) { 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; @@ -2082,9 +2366,9 @@ function patchTimer(window, setName, cancelName, nameSuffix) { patchMethod(window, setName, function (delegate) { return function (self, args) { if (typeof args[0] === 'function') { var options = { - handleId: null, isPeriodic: nameSuffix === 'Interval', - delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : null, + delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 : + undefined, args: args }; var task = scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask); @@ -2199,7 +2483,7 @@ function propertyPatch() { }; Object.getOwnPropertyDescriptor = function (obj, prop) { var desc = _getOwnPropertyDescriptor(obj, prop); - if (isUnconfigurable(obj, prop)) { + if (desc && isUnconfigurable(obj, prop)) { desc.configurable = false; } return desc; @@ -2542,7 +2826,7 @@ var websocketEventNames = ['close', 'error', 'open', 'message']; var workerEventNames = ['error', 'message']; var eventNames = globalEventHandlersEventNames.concat(webglEventNames, formEventNames, detailEventNames, documentEventNames, windowEventNames, htmlElementEventNames, ieElementEventNames); function filterProperties(target, onProperties, ignoreProperties) { - if (!ignoreProperties) { + if (!ignoreProperties || ignoreProperties.length === 0) { return onProperties; } var tip = ignoreProperties.filter(function (ip) { return ip.target === target; }); @@ -2571,9 +2855,10 @@ function propertyDescriptorPatch(api, _global) { // for browsers that we can patch the descriptor: Chrome & Firefox if (isBrowser) { var internalWindow = window; + var 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']), ignoreProperties, ObjectGetPrototypeOf(internalWindow)); + patchFilteredProperties(internalWindow, eventNames.concat(['messageerror']), ignoreProperties ? ignoreProperties.concat(ignoreErrorProperties) : ignoreProperties, ObjectGetPrototypeOf(internalWindow)); patchFilteredProperties(Document.prototype, eventNames, ignoreProperties); if (typeof internalWindow['SVGElement'] !== 'undefined') { patchFilteredProperties(internalWindow['SVGElement'].prototype, eventNames, ignoreProperties); @@ -2922,6 +3207,7 @@ Zone.__load_patch('XHR', function (global, Zone) { var XHR_LISTENER = zoneSymbol('xhrListener'); var XHR_SCHEDULED = zoneSymbol('xhrScheduled'); var XHR_URL = zoneSymbol('xhrURL'); + var XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol('xhrErrorBeforeScheduled'); function patchXHR(window) { var XMLHttpRequestPrototype = XMLHttpRequest.prototype; function findPendingTask(target) { @@ -2940,9 +3226,10 @@ Zone.__load_patch('XHR', function (global, Zone) { var READY_STATE_CHANGE = 'readystatechange'; var SCHEDULED = 'scheduled'; function scheduleTask(task) { - XMLHttpRequest[XHR_SCHEDULED] = false; var data = task.data; var target = data.target; + target[XHR_SCHEDULED] = false; + target[XHR_ERROR_BEFORE_SCHEDULED] = false; // remove existing event listener var listener = target[XHR_LISTENER]; if (!oriAddListener) { @@ -2956,8 +3243,35 @@ Zone.__load_patch('XHR', function (global, Zone) { if (target.readyState === target.DONE) { // sometimes on some browsers XMLHttpRequest will fire onreadystatechange with // readyState=4 multiple times, so we need to check task state here - if (!data.aborted && XMLHttpRequest[XHR_SCHEDULED] && task.state === SCHEDULED) { - task.invoke(); + if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) { + // check whether the xhr has registered onload listener + // if that is the case, the task should invoke after all + // onload listeners finish. + var loadTasks = target['__zone_symbol__loadfalse']; + if (loadTasks && loadTasks.length > 0) { + var oriInvoke_1 = task.invoke; + task.invoke = function () { + // need to load the tasks again, because in other + // load listener, they may remove themselves + var loadTasks = target['__zone_symbol__loadfalse']; + for (var i = 0; i < loadTasks.length; i++) { + if (loadTasks[i] === task) { + loadTasks.splice(i, 1); + } + } + if (!data.aborted && task.state === SCHEDULED) { + oriInvoke_1.call(task); + } + }; + loadTasks.push(task); + } + else { + task.invoke(); + } + } + else if (!data.aborted && target[XHR_SCHEDULED] === false) { + // error occurs when xhr.send() + target[XHR_ERROR_BEFORE_SCHEDULED] = true; } } }; @@ -2967,7 +3281,7 @@ Zone.__load_patch('XHR', function (global, Zone) { target[XHR_TASK] = task; } sendNative.apply(target, data.args); - XMLHttpRequest[XHR_SCHEDULED] = true; + target[XHR_SCHEDULED] = true; return task; } function placeholderCallback() { } @@ -2984,24 +3298,32 @@ Zone.__load_patch('XHR', function (global, Zone) { return openNative.apply(self, args); }; }); var XMLHTTPREQUEST_SOURCE = 'XMLHttpRequest.send'; + var fetchTaskAborting = zoneSymbol('fetchTaskAborting'); + var fetchTaskScheduling = zoneSymbol('fetchTaskScheduling'); var sendNative = patchMethod(XMLHttpRequestPrototype, 'send', function () { return function (self, args) { + if (Zone.current[fetchTaskScheduling] === true) { + // a fetch is scheduling, so we are using xhr to polyfill fetch + // and because we already schedule macroTask for fetch, we should + // not schedule a macroTask for xhr again + return sendNative.apply(self, args); + } if (self[XHR_SYNC]) { // if the XHR is sync there is no task to schedule, just execute the code. return sendNative.apply(self, args); } else { - var options = { - target: self, - url: self[XHR_URL], - isPeriodic: false, - delay: null, - args: args, - aborted: false - }; - return scheduleMacroTaskWithCurrentZone(XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask); + var options = { target: self, url: self[XHR_URL], isPeriodic: false, args: args, aborted: false }; + var task = scheduleMacroTaskWithCurrentZone(XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask); + if (self && self[XHR_ERROR_BEFORE_SCHEDULED] === true && !options.aborted && + task.state === SCHEDULED) { + // xhr request throw error when send + // we should invoke task instead of leaving a scheduled + // pending macroTask + task.invoke(); + } } }; }); - var abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', function () { return function (self) { + var abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', function () { return function (self, args) { var task = findPendingTask(self); if (task && typeof task.type == 'string') { // If the XHR has already completed, do nothing. @@ -3013,6 +3335,10 @@ Zone.__load_patch('XHR', function (global, Zone) { } task.zone.cancelTask(task); } + else if (Zone.current[fetchTaskAborting] === true) { + // the abort is called from fetch polyfill, we need to call native abort of XHR. + return abortNative.apply(self, args); + } // Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no // task // to cancel. Do nothing.