Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 22b9937

Browse files
mleibmanmhevery
authored andcommittedMay 14, 2019
feat(core): Add an option '__Zone_symbol_prefix' to set symbol prefix used in Zone.__symbol__(). (#1219)
* feat(core): Add an option '__Zone_symbol_prefix' to set symbol prefix used in Zone.__symbol__(). Add a global environment option `__Zone_symbol_prefix` to allow configuring the symbol prefix Zone.js uses to store its state data on objects. This is needed in situations where multiple instances of Zone.js may 'touch' the same object or DOM node, accessing and clobbering the other instance's data stored there, and causing unpredictable behavior. Ultimately, it would be nice if Zone.js used proper ES6 symbols or some unique idetifier as a prefix, but many products rely on being able to access the data stored by it. This change adds the env option and cleans up all hard-coded references to '__zone_symbol_XXX' to go through `Zone.__symbol__()`. In order to test the changes and protect against regressions, the tests are run with the symbol prefix changed to `__zone_symbol_test__`. While the primary purpose of this change is to be able to isolate data stored on the objects & DOM nodes, some global environment options are also specified with the `__zone_symbol__` prefix and not the usual `__Zone_` one. The current API for providing env options isn't very consistent. For example, disabling 'on<X>' property patching is done via `__Zone_ignore_on_properties`, w/o using the symbol prefix, while disabling event patching is done via `__zone_symbol__UNPATCHED_EVENTS`, using the symbol prefix. This change only affects the options that are prefixed with `__zone_symbol__`. * Fix failing karma test. * Remove trailing whitespace. * Bump up the file size limiit.
1 parent c555b08 commit 22b9937

24 files changed

+76
-43
lines changed
 

‎file-size-limit.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{
99
"path": "dist/zone.min.js",
1010
"checkTarget": true,
11-
"limit": 44500
11+
"limit": 45000
1212
}
1313
]
1414
}

‎gulpfile.js

+2
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ gulp.task('build', [
466466
]);
467467

468468
function nodeTest(specFiles, cb) {
469+
require('./build/test/node-env-setup');
470+
469471
// load zone-node here to let jasmine be able to use jasmine.clock().install()
470472
// without throw error
471473
require('./build/lib/node/rollup-main');

‎karma-build.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
module.exports = function(config) {
1010
require('./karma-base.conf.js')(config);
1111
config.files.push('node_modules/core-js-bundle/index.js');
12+
config.files.push('build/test/browser-env-setup.js');
1213
config.files.push('build/test/wtf_mock.js');
1314
config.files.push('build/test/test_fake_polyfill.js');
1415
config.files.push('build/lib/zone.js');

‎karma-dist.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
module.exports = function(config) {
1010
require('./karma-base.conf.js')(config);
1111
config.files.push('node_modules/core-js-bundle/index.js');
12+
config.files.push('build/test/browser-env-setup.js');
1213
config.files.push('build/test/wtf_mock.js');
1314
config.files.push('build/test/test_fake_polyfill.js');
1415
config.files.push('build/test/custom_error.js');

‎karma-evergreen-dist.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = function(config) {
1616
}
1717
}
1818

19+
config.files.push('build/test/browser-env-setup.js');
1920
config.files.push('build/test/wtf_mock.js');
2021
config.files.push('build/test/test_fake_polyfill.js');
2122
config.files.push('build/test/custom_error.js');

‎lib/browser/browser-legacy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {propertyDescriptorLegacyPatch} from './property-descriptor-legacy';
1515
import {registerElementPatch} from './register-element';
1616

1717
(function(_global: any) {
18-
_global['__zone_symbol__legacyPatch'] = function() {
18+
_global[Zone.__symbol__('legacyPatch')] = function() {
1919
const Zone = _global['Zone'];
2020
Zone.__load_patch('registerElement', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
2121
registerElementPatch(global, api);

‎lib/browser/browser.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,13 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
142142
// check whether the xhr has registered onload listener
143143
// if that is the case, the task should invoke after all
144144
// onload listeners finish.
145-
const loadTasks = target['__zone_symbol__loadfalse'];
145+
const loadTasks = target[Zone.__symbol__('loadfalse')];
146146
if (loadTasks && loadTasks.length > 0) {
147147
const oriInvoke = task.invoke;
148148
task.invoke = function() {
149149
// need to load the tasks again, because in other
150150
// load listener, they may remove themselves
151-
const loadTasks = target['__zone_symbol__loadfalse'];
151+
const loadTasks = target[Zone.__symbol__('loadfalse')];
152152
for (let i = 0; i < loadTasks.length; i++) {
153153
if (loadTasks[i] === task) {
154154
loadTasks.splice(i, 1);

‎lib/common/events.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ const OPTIMIZED_ZONE_EVENT_TASK_DATA: EventTaskData = {
4343
export const zoneSymbolEventNames: any = {};
4444
export const globalSources: any = {};
4545

46-
const EVENT_NAME_SYMBOL_REGX = /^__zone_symbol__(\w+)(true|false)$/;
47-
const IMMEDIATE_PROPAGATION_SYMBOL = ('__zone_symbol__propagationStopped');
46+
const EVENT_NAME_SYMBOL_REGX = new RegExp('^' + ZONE_SYMBOL_PREFIX + '(\\w+)(true|false)$');
47+
const IMMEDIATE_PROPAGATION_SYMBOL = zoneSymbol('propagationStopped');
4848

4949
export interface PatchEventTargetOptions {
5050
// validateHandler
@@ -327,7 +327,7 @@ export function patchEventTarget(
327327
const compare =
328328
(patchOptions && patchOptions.diff) ? patchOptions.diff : compareTaskCallbackVsDelegate;
329329

330-
const blackListedEvents: string[] = (Zone as any)[Zone.__symbol__('BLACK_LISTED_EVENTS')];
330+
const blackListedEvents: string[] = (Zone as any)[zoneSymbol('BLACK_LISTED_EVENTS')];
331331

332332
const makeAddListener = function(
333333
nativeListener: any, addSource: string, customScheduleFn: any, customCancelFn: any,

‎lib/common/utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ export const ZONE_SYMBOL_REMOVE_EVENT_LISTENER = Zone.__symbol__(REMOVE_EVENT_LI
3434
export const TRUE_STR = 'true';
3535
/** false string const */
3636
export const FALSE_STR = 'false';
37-
/** __zone_symbol__ string const */
38-
export const ZONE_SYMBOL_PREFIX = '__zone_symbol__';
37+
/** Zone symbol prefix string const. */
38+
export const ZONE_SYMBOL_PREFIX = Zone.__symbol__('');
3939

4040
export function wrapWithCurrentZone<T extends Function>(callback: T, source: string): T {
4141
return Zone.current.wrap(callback, source);

‎lib/jasmine/jasmine.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@
217217
ambientZone.scheduleMicroTask('jasmine.onComplete', fn);
218218
})(attrs.onComplete);
219219

220-
const nativeSetTimeout = _global['__zone_symbol__setTimeout'];
221-
const nativeClearTimeout = _global['__zone_symbol__clearTimeout'];
220+
const nativeSetTimeout = _global[Zone.__symbol__('setTimeout')];
221+
const nativeClearTimeout = _global[Zone.__symbol__('clearTimeout')];
222222
if (nativeSetTimeout) {
223223
// should run setTimeout inside jasmine outside of zone
224224
attrs.timeout = {

‎lib/zone.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ interface Zone {
154154
* @returns {any} The value for the key, or `undefined` if not found.
155155
*/
156156
get(key: string): any;
157+
157158
/**
158159
* Returns a Zone which defines a `key`.
159160
*
@@ -163,13 +164,15 @@ interface Zone {
163164
* @returns {Zone} The Zone which defines the `key`, `null` if not found.
164165
*/
165166
getZoneWith(key: string): Zone|null;
167+
166168
/**
167169
* Used to create a child zone.
168170
*
169171
* @param zoneSpec A set of rules which the child zone should follow.
170172
* @returns {Zone} A new child zone.
171173
*/
172174
fork(zoneSpec: ZoneSpec): Zone;
175+
173176
/**
174177
* Wraps a callback function in a new function which will properly restore the current zone upon
175178
* invocation.
@@ -184,6 +187,7 @@ interface Zone {
184187
* @returns {function(): *} A function which will invoke the `callback` through [Zone.runGuarded].
185188
*/
186189
wrap<F extends Function>(callback: F, source: string): F;
190+
187191
/**
188192
* Invokes a function in a given zone.
189193
*
@@ -196,6 +200,7 @@ interface Zone {
196200
* @returns {any} Value from the `callback` function.
197201
*/
198202
run<T>(callback: Function, applyThis?: any, applyArgs?: any[], source?: string): T;
203+
199204
/**
200205
* Invokes a function in a given zone and catches any exceptions.
201206
*
@@ -211,6 +216,7 @@ interface Zone {
211216
* @returns {any} Value from the `callback` function.
212217
*/
213218
runGuarded<T>(callback: Function, applyThis?: any, applyArgs?: any[], source?: string): T;
219+
214220
/**
215221
* Execute the Task by restoring the [Zone.currentTask] in the Task's zone.
216222
*
@@ -287,6 +293,7 @@ interface ZoneType {
287293
* duration of the run method callback.
288294
*/
289295
current: Zone;
296+
290297
/**
291298
* @returns {Task} The task associated with the current execution.
292299
*/
@@ -666,7 +673,17 @@ const Zone: ZoneType = (function(global: any) {
666673
performance && performance['measure'] && performance['measure'](name, label);
667674
}
668675
mark('Zone');
669-
const checkDuplicate = global[('__zone_symbol__forceDuplicateZoneCheck')] === true;
676+
677+
// Initialize before it's accessed below.
678+
// __Zone_symbol_prefix global can be used to override the default zone
679+
// symbol prefix with a custom one if needed.
680+
const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__';
681+
682+
function __symbol__(name: string) {
683+
return symbolPrefix + name;
684+
}
685+
686+
const checkDuplicate = global[__symbol__('forceDuplicateZoneCheck')] === true;
670687
if (global['Zone']) {
671688
// if global['Zone'] already exists (maybe zone.js was already loaded or
672689
// some other lib also registered a global object named Zone), we may need
@@ -1284,6 +1301,7 @@ const Zone: ZoneType = (function(global: any) {
12841301
}
12851302
}
12861303

1304+
12871305
//////////////////////////////////////////////////////
12881306
//////////////////////////////////////////////////////
12891307
/// MICROTASK QUEUE
@@ -1397,10 +1415,6 @@ const Zone: ZoneType = (function(global: any) {
13971415

13981416
function noop() {}
13991417

1400-
function __symbol__(name: string) {
1401-
return '__zone_symbol__' + name;
1402-
}
1403-
14041418
performanceMeasure('Zone', 'Zone');
14051419
return global['Zone'] = Zone;
14061420
})(typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global);

‎promise-adapter.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
require('./dist/zone-node.js');
2-
Zone[('__zone_symbol__ignoreConsoleErrorUncaughtError')] = true;
2+
Zone[Zone.__symbol__('ignoreConsoleErrorUncaughtError')] = true;
33
module.exports.deferred = function() {
44
const p = {};
55
p.promise = new Promise((resolve, reject) => {

‎promise.finally.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
var assert = require('assert');
44
var adapter = require('./promise-adapter');
5-
var P = global['__zone_symbol__Promise'];
5+
var P = global[Zone.__symbol__('Promise')];
66

77
var someRejectionReason = {message: 'some rejection reason'};
88
var anotherReason = {message: 'another rejection reason'};

‎test/browser-env-setup.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Change default symbol prefix for testing to ensure no hard-coded references.
2+
(window as any)['__Zone_symbol_prefix'] = '_test__';

‎test/browser-zone-setup.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
if (typeof window !== 'undefined') {
9+
const zoneSymbol = (window as any).Zone.__symbol__;
910
(window as any)['__Zone_enable_cross_context_check'] = true;
10-
(window as any)['__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched'] = true;
11+
(window as any)[zoneSymbol('fakeAsyncAutoFakeAsyncWhenClockPatched')] = true;
1112
}
1213
import '../lib/common/to-string';
1314
import '../lib/browser/api-util';

‎test/browser/XMLHttpRequest.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {ifEnvSupports, ifEnvSupportsWithDone, supportPatchXHROnProperty} from '../test-util';
9+
import {ifEnvSupports, ifEnvSupportsWithDone, supportPatchXHROnProperty, zoneSymbol} from '../test-util';
1010

1111
describe('XMLHttpRequest', function() {
1212
let testZone: Zone;
@@ -45,7 +45,7 @@ describe('XMLHttpRequest', function() {
4545
.toMatch(/\> Zone\:invokeTask.*addEventListener\:load/);
4646
}
4747
// if browser can patch onload
48-
if ((req as any)['__zone_symbol__loadfalse']) {
48+
if ((req as any)[zoneSymbol('loadfalse')]) {
4949
expect(logs).toEqual(['onload']);
5050
}
5151
done();
@@ -278,8 +278,8 @@ describe('XMLHttpRequest', function() {
278278
}
279279
req.addEventListener('readystatechange', function(ev) {
280280
if (req.readyState === 4) {
281-
const xhrScheduled = (req as any)['__zone_symbol__xhrScheduled'];
282-
const task = (req as any)['__zone_symbol__xhrTask'];
281+
const xhrScheduled = (req as any)[zoneSymbol('xhrScheduled')];
282+
const task = (req as any)[zoneSymbol('xhrTask')];
283283
if (xhrScheduled === false) {
284284
expect(task.state).toEqual('scheduling');
285285
setTimeout(() => {
@@ -323,7 +323,7 @@ describe('XMLHttpRequest', function() {
323323
let isError = false;
324324
let timerId = null;
325325
try {
326-
timerId = (window as any)['__zone_symbol__setTimeout'](() => {
326+
timerId = (window as any)[zoneSymbol('setTimeout')](() => {
327327
expect(logs).toEqual([
328328
`{"microTask":false,"macroTask":true,"eventTask":false,"change":"macroTask"}`,
329329
`{"microTask":false,"macroTask":false,"eventTask":false,"change":"macroTask"}`
@@ -333,7 +333,7 @@ describe('XMLHttpRequest', function() {
333333
req.send();
334334
} catch (error) {
335335
isError = true;
336-
(window as any)['__zone_symbol__clearTimeout'](timerId);
336+
(window as any)[zoneSymbol('clearTimeout')](timerId);
337337
done();
338338
}
339339
});

‎test/browser/browser.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {patchFilteredProperties} from '../../lib/browser/property-descriptor';
1010
import {patchEventTarget} from '../../lib/common/events';
11-
import {isBrowser, isIEOrEdge, isMix, zoneSymbol} from '../../lib/common/utils';
11+
import {isIEOrEdge, zoneSymbol} from '../../lib/common/utils';
1212
import {getEdgeVersion, getIEVersion, ifEnvSupports, ifEnvSupportsWithDone, isEdge} from '../test-util';
1313

1414
import Spy = jasmine.Spy;
@@ -306,8 +306,8 @@ describe('Zone', function() {
306306
const logs: string[] = [];
307307
const EventTarget = (window as any)['EventTarget'];
308308
let oriAddEventListener = EventTarget && EventTarget.prototype ?
309-
(EventTarget.prototype as any)['__zone_symbol__addEventListener'] :
310-
(HTMLSpanElement.prototype as any)['__zone_symbol__addEventListener'];
309+
(EventTarget.prototype as any)[zoneSymbol('addEventListener')] :
310+
(HTMLSpanElement.prototype as any)[zoneSymbol('addEventListener')];
311311

312312
if (!oriAddEventListener) {
313313
// no patched addEventListener found
@@ -334,7 +334,7 @@ describe('Zone', function() {
334334
return oriAddEventListener.apply(this, arguments);
335335
};
336336

337-
(HTMLSpanElement.prototype as any)['__zone_symbol__addEventListener'] = null;
337+
(HTMLSpanElement.prototype as any)[zoneSymbol('addEventListener')] = null;
338338

339339
patchEventTarget(window, [HTMLSpanElement.prototype]);
340340

@@ -356,10 +356,10 @@ describe('Zone', function() {
356356
expect(logs).toEqual(['listener1', 'listener2']);
357357
document.body.removeChild(span);
358358
if (EventTarget) {
359-
(EventTarget.prototype as any)['__zone_symbol__addEventListener'] =
359+
(EventTarget.prototype as any)[zoneSymbol('addEventListener')] =
360360
oriAddEventListener;
361361
} else {
362-
(HTMLSpanElement.prototype as any)['__zone_symbol__addEventListener'] =
362+
(HTMLSpanElement.prototype as any)[zoneSymbol('addEventListener')] =
363363
oriAddEventListener;
364364
}
365365
}));

‎test/common/Error.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {isBrowser} from '../../lib/common/utils';
9-
import {isSafari} from '../test-util';
9+
import {isSafari, zoneSymbol} from '../test-util';
1010

1111
// simulate @angular/facade/src/error.ts
1212
class BaseError extends Error {
@@ -439,7 +439,7 @@ describe('ZoneAwareError', () => {
439439
it('should be able to generate zone free stack even NativeError stack is readonly', function() {
440440
const _global: any =
441441
typeof window === 'object' && window || typeof self === 'object' && self || global;
442-
const NativeError = _global['__zone_symbol__Error'];
442+
const NativeError = _global[zoneSymbol('Error')];
443443
const desc = Object.getOwnPropertyDescriptor(NativeError.prototype, 'stack');
444444
if (desc) {
445445
const originalSet: ((value: any) => void)|undefined = desc.set;

‎test/node-env-setup.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Change default symbol prefix for testing to ensure no hard-coded references.
2+
(global as any)['__Zone_symbol_prefix'] = '__zone_symbol_test__';

‎test/node_entry_point.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
*/
88

99
// Must be loaded before zone loads, so that zone can detect WTF.
10-
if (typeof global !== 'undefined' &&
11-
(global as any)['__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched'] !== false) {
12-
(global as any)['__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched'] = true;
13-
}
1410
import './wtf_mock';
1511
import './test_fake_polyfill';
1612

13+
// Zone symbol prefix is set to '__zone_symbol2__' in node-env-setup.ts.
14+
if (typeof global !== 'undefined' &&
15+
(global as any)['__zone_symbol_test__fakeAsyncAutoFakeAsyncWhenClockPatched'] !== false) {
16+
(global as any)['__zone_symbol_test__fakeAsyncAutoFakeAsyncWhenClockPatched'] = true;
17+
}
18+
1719
// Setup tests for Zone without microtask support
1820
import '../lib/testing/zone-testing';
1921
import '../lib/zone-spec/task-tracking';

‎test/test-env-setup-jasmine-no-patch-clock.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
(global as any)['__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched'] = false;
8+
(global as any)[(global as any).Zone.__symbol__('fakeAsyncAutoFakeAsyncWhenClockPatched')] = false;

‎test/test-util.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
*
2222
* ifEnvSupports(supportsOnClick, function() { ... });
2323
*/
24-
import {isNode} from '../lib/common/utils';
24+
import {isNode, zoneSymbol} from '../lib/common/utils';
25+
26+
// Re-export for convenience.
27+
export {zoneSymbol};
2528

2629
declare const global: any;
2730
export function ifEnvSupports(test: any, block: Function): () => void {

‎test/test_fake_polyfill.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ Object.defineProperties(TestTarget.prototype, {
7272
}
7373
});
7474

75+
// Zone symbol prefix may be set in *-env-setup.ts (browser & node),
76+
// but this file is used in multiple scenarios, and Zone isn't loaded at this point yet.
77+
const zoneSymbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__';
78+
7579
global['__Zone_ignore_on_properties'] =
7680
[{target: TestTarget.prototype, ignoreProperties: ['prop1']}];
77-
global['__zone_symbol__FakeAsyncTestMacroTask'] = [{source: 'TestClass.myTimeout'}];
78-
global['__zone_symbol__UNPATCHED_EVENTS'] = ['scroll'];
81+
global[zoneSymbolPrefix + 'FakeAsyncTestMacroTask'] = [{source: 'TestClass.myTimeout'}];
82+
global[zoneSymbolPrefix + 'UNPATCHED_EVENTS'] = ['scroll'];
7983
})(typeof window === 'object' && window || typeof self === 'object' && self || global);

‎test/zone-spec/fake-async-test.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import '../../lib/rxjs/rxjs-fake-async';
1111
import {Observable} from 'rxjs';
1212
import {delay} from 'rxjs/operators';
1313

14-
import {isNode, patchMacroTask} from '../../lib/common/utils';
14+
import {isNode, patchMacroTask, zoneSymbol} from '../../lib/common/utils';
1515
import {ifEnvSupports} from '../test-util';
1616

1717
function supportNode() {
@@ -23,7 +23,7 @@ function supportNode() {
2323
function supportClock() {
2424
const _global: any = typeof window === 'undefined' ? global : window;
2525
return typeof jasmine.clock === 'function' &&
26-
_global['__zone_symbol__fakeAsyncAutoFakeAsyncWhenClockPatched'];
26+
_global[zoneSymbol('fakeAsyncAutoFakeAsyncWhenClockPatched')];
2727
}
2828

2929
(supportClock as any).message = 'support patch clock';

0 commit comments

Comments
 (0)
This repository has been archived.