diff --git a/lib/browser/browser.ts b/lib/browser/browser.ts index bf01626d7..7ec291cf8 100644 --- a/lib/browser/browser.ts +++ b/lib/browser/browser.ts @@ -230,7 +230,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => { task.invoke(); } } - }); + }, true); const abortNative = patchMethod(XMLHttpRequestPrototype, 'abort', () => function(self: any, args: any[]) { @@ -244,13 +244,12 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => { return; } task.zone.cancelTask(task); - } else if ((Zone.current as any)[fetchTaskAborting] === true) { - // the abort is called from fetch polyfill, we need to call native abort of XHR. - return abortNative!.apply(self, args); + return; } // Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no - // task - // to cancel. Do nothing. + // task to cancel. But we need to use abortNative to abort the XHR in case the send + // is called outside of Zone. + return abortNative!.apply(self, args); }); } }); diff --git a/lib/browser/webapis-media-query.ts b/lib/browser/webapis-media-query.ts index 7ca46e171..0b96cb670 100644 --- a/lib/browser/webapis-media-query.ts +++ b/lib/browser/webapis-media-query.ts @@ -16,7 +16,7 @@ Zone.__load_patch('mediaQuery', (global: any, Zone: ZoneType, api: _ZonePrivate) } else { return delegate.apply(self, args); } - }); + }, true); } function patchRemoveListener(proto: any) { diff --git a/lib/browser/webapis-resize-observer.ts b/lib/browser/webapis-resize-observer.ts index 8e7f73643..e22c83319 100644 --- a/lib/browser/webapis-resize-observer.ts +++ b/lib/browser/webapis-resize-observer.ts @@ -58,7 +58,7 @@ Zone.__load_patch('ResizeObserver', (global: any, Zone: any, api: _ZonePrivate) targets.push(target); target[resizeObserverSymbol] = Zone.current; return delegate.apply(self, args); - }); + }, true); api.patchMethod( ResizeObserver.prototype, 'unobserve', (delegate: Function) => (self: any, args: any[]) => { diff --git a/lib/common/events.ts b/lib/common/events.ts index 4d404b427..6f524d1a4 100644 --- a/lib/common/events.ts +++ b/lib/common/events.ts @@ -331,7 +331,9 @@ export function patchEventTarget( return function() { const target = this || _global; let delegate = arguments[1]; - if (!delegate) { + // if handler is not available or we are in root zone + // use nativeListener + if (!delegate || Zone.current === Zone.root) { return nativeListener.apply(this, arguments); } @@ -488,6 +490,10 @@ export function patchEventTarget( } proto[REMOVE_EVENT_LISTENER] = function() { + const delegate = arguments[1]; + if (!delegate) { + return nativeRemoveEventListener.apply(this, arguments); + } const target = this || _global; const eventName = arguments[0]; const options = arguments[2]; @@ -503,11 +509,6 @@ export function patchEventTarget( capture = options ? !!options.capture : false; } - const delegate = arguments[1]; - if (!delegate) { - return nativeRemoveEventListener.apply(this, arguments); - } - if (validateHandler && !validateHandler(nativeRemoveEventListener, delegate, target, arguments)) { return; @@ -553,11 +554,32 @@ export function patchEventTarget( const listeners: any[] = []; const tasks = findEventTasks(target, eventName); + const invokes: any[]|null = nativeListeners ? [] : null; for (let i = 0; i < tasks.length; i++) { const task: any = tasks[i]; let delegate = task.originalDelegate ? task.originalDelegate : task.callback; listeners.push(delegate); + if (invokes) { + invokes.push(task.invoke); + } + } + if (nativeListeners) { + const natives = nativeListeners.apply(this, arguments); + if (natives && invokes) { + natives.forEach((n: any) => { + let found = false; + for (let i = 0; i < invokes.length; i++) { + if (invokes[i] === n) { + found = true; + break; + } + } + if (!found) { + listeners.push(n); + } + }); + } } return listeners; }; @@ -580,8 +602,12 @@ export function patchEventTarget( this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName); } } - // remove removeListener listener finally + // call native again in case some event handler was not + // use patched version of addListener this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener'); + if (nativeRemoveAllListeners) { + nativeRemoveAllListeners.call(this); + } } else { const symbolEventNames = zoneSymbolEventNames[eventName]; if (symbolEventNames) { @@ -609,6 +635,10 @@ export function patchEventTarget( } } } + + if (nativeRemoveAllListeners) { + nativeRemoveAllListeners.call(this, eventName); + } } if (returnTarget) { diff --git a/lib/common/fetch.ts b/lib/common/fetch.ts index 5fda7a5ab..9edaa6b69 100644 --- a/lib/common/fetch.ts +++ b/lib/common/fetch.ts @@ -36,6 +36,10 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } const placeholder = function() {}; global['fetch'] = function() { + // if in root zone, just use native fetch directly. + if (Zone.current === Zone.root) { + return fetch.apply(this, arguments); + } const args = Array.prototype.slice.call(arguments); const options = args.length > 1 ? args[1] : null; const signal = options && options.signal; @@ -97,4 +101,4 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } }); }; -}); \ No newline at end of file +}); diff --git a/lib/common/timers.ts b/lib/common/timers.ts index ccf53aaba..da24bb047 100644 --- a/lib/common/timers.ts +++ b/lib/common/timers.ts @@ -99,7 +99,7 @@ export function patchTimer(window: any, setName: string, cancelName: string, nam // cause an error by calling it directly. return delegate.apply(window, args); } - }); + }, true); clearNative = patchMethod(window, cancelName, (delegate: Function) => function(self: any, args: any[]) { diff --git a/lib/common/utils.ts b/lib/common/utils.ts index 77554e524..a23177bf0 100644 --- a/lib/common/utils.ts +++ b/lib/common/utils.ts @@ -389,7 +389,8 @@ export function setShouldCopySymbolProperties(flag: boolean) { export function patchMethod( target: any, name: string, patchFn: (delegate: Function, delegateName: string, name: string) => (self: any, args: any[]) => - any): Function|null { + any, + checkInZone = false): Function|null { let proto = target; while (proto && !proto.hasOwnProperty(name)) { proto = ObjectGetPrototypeOf(proto); @@ -409,6 +410,10 @@ export function patchMethod( if (isPropertyWritable(desc)) { const patchDelegate = patchFn(delegate!, delegateName, name); proto[name] = function() { + // if we are in root zone, just use native delegate. + if (checkInZone && Zone.current === Zone.root && delegate) { + return delegate.apply(this, arguments); + } return patchDelegate(this, arguments as any); }; attachOriginToPatched(proto[name], delegate); @@ -449,7 +454,7 @@ export function patchMacroTask( // cause an error by calling it directly. return delegate.apply(self, args); } - }); + }, true); } export interface MicroTaskMeta extends TaskData { @@ -480,7 +485,7 @@ export function patchMicroTask( // cause an error by calling it directly. return delegate.apply(self, args); } - }); + }, true); } export function attachOriginToPatched(patched: Function, original: any) { diff --git a/lib/zone.ts b/lib/zone.ts index 4de4e077d..5ce0986ec 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -327,7 +327,8 @@ interface _ZonePrivate { patchMethod: (target: any, name: string, patchFn: (delegate: Function, delegateName: string, name: string) => - (self: any, args: any[]) => any) => Function | null; + (self: any, args: any[]) => any, + checkInZone?: boolean) => Function | null; bindArguments: (args: any[], source: string) => any[]; } @@ -710,7 +711,6 @@ const Zone: ZoneType = (function(global: any) { return this._name; } - private _parent: Zone|null; private _name: string; private _properties: {[key: string]: any}; @@ -752,6 +752,9 @@ const Zone: ZoneType = (function(global: any) { const _callback = this._zoneDelegate.intercept(this, callback, source); const zone: Zone = this; return function() { + if (zone === rootZone) { + return _callback.apply(this, arguments); + } return zone.runGuarded(_callback, (this as any), arguments, source); } as any as T; } @@ -1352,7 +1355,8 @@ const Zone: ZoneType = (function(global: any) { } }, }; - let _currentZoneFrame: _ZoneFrame = {parent: null, zone: new Zone(null, null)}; + const rootZone = new Zone(null, null); + let _currentZoneFrame: _ZoneFrame = {parent: null, zone: rootZone}; let _currentTask: Task|null = null; let _numberOfNestedTaskFrames = 0;