/** * @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 */ class ProxyZoneSpec implements ZoneSpec { name: string = 'ProxyZone'; private _delegateSpec: ZoneSpec|null = null; properties: {[k: string]: any} = {'ProxyZoneSpec': this}; propertyKeys: string[]|null = null; lastTaskState: HasTaskState|null = null; isNeedToTriggerHasTask = false; private tasks: Task[] = []; static get(): ProxyZoneSpec { return Zone.current.get('ProxyZoneSpec'); } static isLoaded(): boolean { return ProxyZoneSpec.get() instanceof ProxyZoneSpec; } static assertPresent(): ProxyZoneSpec { if (!ProxyZoneSpec.isLoaded()) { throw new Error(`Expected to be running in 'ProxyZone', but it was not found.`); } return ProxyZoneSpec.get(); } constructor(private defaultSpecDelegate: ZoneSpec|null = null) { this.setDelegate(defaultSpecDelegate); } setDelegate(delegateSpec: ZoneSpec|null) { const isNewDelegate = this._delegateSpec !== delegateSpec; this._delegateSpec = delegateSpec; this.propertyKeys && this.propertyKeys.forEach((key) => delete this.properties[key]); this.propertyKeys = null; if (delegateSpec && delegateSpec.properties) { this.propertyKeys = Object.keys(delegateSpec.properties); this.propertyKeys.forEach((k) => this.properties[k] = delegateSpec.properties ![k]); } // if set a new delegateSpec, shoulde check whether need to // trigger hasTask or not if (isNewDelegate && this.lastTaskState && (this.lastTaskState.macroTask || this.lastTaskState.microTask)) { this.isNeedToTriggerHasTask = true; } } getDelegate() { return this._delegateSpec; } resetDelegate() { const delegateSpec = this.getDelegate(); this.setDelegate(this.defaultSpecDelegate); } tryTriggerHasTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone) { if (this.isNeedToTriggerHasTask && this.lastTaskState) { // last delegateSpec has microTask or macroTask // should call onHasTask in current delegateSpec this.isNeedToTriggerHasTask = false; this.onHasTask(parentZoneDelegate, currentZone, targetZone, this.lastTaskState); } } removeFromTasks(task: Task) { if (!this.tasks) { return; } for (let i = 0; i < this.tasks.length; i++) { if (this.tasks[i] === task) { this.tasks.splice(i, 1); return; } } } getAndClearPendingTasksInfo() { if (this.tasks.length === 0) { return ''; } const taskInfo = this.tasks.map((task: Task) => { const dataInfo = task.data && Object.keys(task.data) .map((key: string) => { return key + ':' + (task.data as any)[key]; }) .join(','); return `type: ${task.type}, source: ${task.source}, args: {${dataInfo}}`; }); const pendingTasksInfo = '--Pendng async tasks are: [' + taskInfo + ']'; // clear tasks this.tasks = []; return pendingTasksInfo; } onFork(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, zoneSpec: ZoneSpec): Zone { if (this._delegateSpec && this._delegateSpec.onFork) { return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec); } else { return parentZoneDelegate.fork(targetZone, zoneSpec); } } onIntercept( parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function, source: string): Function { if (this._delegateSpec && this._delegateSpec.onIntercept) { return this._delegateSpec.onIntercept( parentZoneDelegate, currentZone, targetZone, delegate, source); } else { return parentZoneDelegate.intercept(targetZone, delegate, source); } } onInvoke( parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function, applyThis: any, applyArgs?: any[], source?: string): any { this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onInvoke) { return this._delegateSpec.onInvoke( parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source); } else { return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source); } } onHandleError(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any): boolean { if (this._delegateSpec && this._delegateSpec.onHandleError) { return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error); } else { return parentZoneDelegate.handleError(targetZone, error); } } onScheduleTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): Task { if (task.type !== 'eventTask') { this.tasks.push(task); } if (this._delegateSpec && this._delegateSpec.onScheduleTask) { return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task); } else { return parentZoneDelegate.scheduleTask(targetZone, task); } } onInvokeTask( parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task, applyThis: any, applyArgs: any): any { if (task.type !== 'eventTask') { this.removeFromTasks(task); } this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onInvokeTask) { return this._delegateSpec.onInvokeTask( parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs); } else { return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs); } } onCancelTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): any { if (task.type !== 'eventTask') { this.removeFromTasks(task); } this.tryTriggerHasTask(parentZoneDelegate, currentZone, targetZone); if (this._delegateSpec && this._delegateSpec.onCancelTask) { return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task); } else { return parentZoneDelegate.cancelTask(targetZone, task); } } onHasTask(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState): void { this.lastTaskState = hasTaskState; if (this._delegateSpec && this._delegateSpec.onHasTask) { this._delegateSpec.onHasTask(delegate, current, target, hasTaskState); } else { delegate.hasTask(target, hasTaskState); } } } // Export the class so that new instances can be created with proper // constructor params. (Zone as any)['ProxyZoneSpec'] = ProxyZoneSpec;