feat(core): Add an option '__Zone_symbol_prefix' to set symbol prefix used in Zone.__symbol__().#1219
Conversation
… 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__`.
|
@mleibman, thanks for the PR, could you explain the use case why we need to load multiple |
|
@JiaLiPassion I have an scenario where a separate Angular application with its own copy of Zone.js will be loaded in an IFrame on the same domain and will access the DOM elements (as well as document and window) in the parent window directly, potentially adding event listeners to them. When that happens, the two separate instances of Zone.js interfere with each other since they store the data using the same hard-coded symbol names. |
|
@mleibman, thanks for the explanation, so with this PR, the application will load like this,
is that correct? If this is correct, we can do like this without have this
But I totally agree the code of |
|
That is one of the scenarios, namely specifying options for Zone.js prior to loading. However the bigger issue is that Zone.js stores the state data like event handlers, original unpatched properties, configuration data, etc. on various objects at runtime. (And other code like Angular also reads from there for some optimizations around event listeners.) Consider this scenario: // loads zone.js
document.onclick = () => console.log('global click handler');
const iframe = document.body.appendChild(
document.createElement('iframe'));
iframe.contentWindow.eval(`
// load zone.js
window.parent.document.onclick = () => console.log('iframe click handler');
`);Clicking on the document will result in the iframe click handler executing twice, and the main click handler not executing at all, all because the two instances of Zone.js interfere with each other. With this change, the issue can be avoided by having the iframe code set a different |
|
@mleibman, currently, if |
|
@JiaLiPassion In my example above, the iframe loads its own copy of zone.js. |
|
@mleibman, got it, maybe |
|
@JiaLiPassion Thanks! It has done that several times... Do you think I'm missing something in the test configs? The local runs of |
|
@JiaLiPassion As for Zone.js automatically doing it, as I mentioned, ultimately the symbol prefix should be unique (auto-generated? ES6 symbols?), but a lot of code assumes the hard-coded one, so this would be a breaking change. |
|
@mleibman, yeah, the
You are right, |
|
@mleibman, the
You will see the error is Line 1300 in 82a68f3 __symbol__ is called, the symbolPrefix in Line 1299 in 82a68f3 line 1299 to the top Line 675 in 82a68f3 |
|
@JiaLiPassion Thanks! Not sure why that didn't fail the other tests though... I'm still getting a bunch of errors though:
|
|
@JiaLiPassion I guess that's just my environment. The remaining error on Travis is
which could be a fluke. |
|
@mleibman, thanks, the |
|
@mleibman, there are some conflicts due to the other merges, could you rebase your PR? thanks! |
|
@JiaLiPassion intermittent CI error? |
|
@mleibman, yeah, you need to update the |
| ]); | ||
|
|
||
| function nodeTest(specFiles, cb) { | ||
| require('./build/test/node-env-setup'); |
There was a problem hiding this comment.
this seems unrelated. Can you explain the reason for this?
There was a problem hiding this comment.
In order to test the changes and protect against regressions, the tests are run with the symbol prefix changed to __zone_symbol_test__. This adds the environment setup that sets the __Zone_symbol_prefix optin.
| module.exports = function(config) { | ||
| require('./karma-base.conf.js')(config); | ||
| config.files.push('node_modules/core-js-bundle/index.js'); | ||
| config.files.push('build/test/browser-env-setup.js'); |
There was a problem hiding this comment.
this seems unrelated. Can you explain the reason for this?
There was a problem hiding this comment.
In order to test the changes and protect against regressions, the tests are run with the symbol prefix changed to __zone_symbol_test__. This adds the environment setup that sets the __Zone_symbol_prefix optin.
|
|
||
| const EVENT_NAME_SYMBOL_REGX = /^__zone_symbol__(\w+)(true|false)$/; | ||
| const IMMEDIATE_PROPAGATION_SYMBOL = ('__zone_symbol__propagationStopped'); | ||
| const EVENT_NAME_SYMBOL_REGX = new RegExp('^' + ZONE_SYMBOL_PREFIX + '(\\w+)(true|false)$'); |
There was a problem hiding this comment.
| const EVENT_NAME_SYMBOL_REGX = new RegExp('^' + ZONE_SYMBOL_PREFIX + '(\\w+)(true|false)$'); | |
| const EVENT_NAME_SYMBOL_REGX = new RegExp('^' + zoneSymbol('') + '(\\w+)(true|false)$'); |
There was a problem hiding this comment.
I guess this file is a bit inconsistent. In some places it uses the ZONE_SYMBOL_PREFIX and string concatenation, while in other places it calls zoneSymbo()... In this case, IMHO the intent is clearer with the ZONE_SYMBOL_PREFIX.
| // Initialize before it's accessed below. | ||
| // __Zone_symbol_prefix global can be used to override the default zone | ||
| // symbol prefix with a custom one if needed. | ||
| const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__'; |
There was a problem hiding this comment.
Not a fan of this now becoming part of the public API which needs to be documented etc...
One way to solve this would be:
| const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__'; | |
| const symbolPrefix = '__zone_symbol__' + new Date().getTime() + '__; |
But my guess is that this would be very breaking because even if it is private API people are probably relying on it.
There was a problem hiding this comment.
That's my concern as well. Having a unique symbol prefix is exactly what I need. The customizable symbol prefix is a way to do this w/o introducing a breaking change.
|
This has been already merged into |
Add a global environment option
__Zone_symbol_prefixto 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 identifier 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' 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__.