angular-cn/packages/zone.js/lib/browser/define-property.ts

133 lines
4.4 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google LLC 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
*/
/*
* This is necessary for Chrome and Chrome mobile, to enable
* things like redefining `createdCallback` on an element.
*/
let zoneSymbol: any;
let _defineProperty: any;
let _getOwnPropertyDescriptor: any;
let _create: any;
let unconfigurablesKey: any;
export function propertyPatch() {
zoneSymbol = Zone.__symbol__;
_defineProperty = (Object as any)[zoneSymbol('defineProperty')] = Object.defineProperty;
_getOwnPropertyDescriptor = (Object as any)[zoneSymbol('getOwnPropertyDescriptor')] =
Object.getOwnPropertyDescriptor;
_create = Object.create;
unconfigurablesKey = zoneSymbol('unconfigurables');
Object.defineProperty = function(obj: any, prop: string, desc: any) {
if (isUnconfigurable(obj, prop)) {
throw new TypeError('Cannot assign to read only property \'' + prop + '\' of ' + obj);
}
const originalConfigurableFlag = desc.configurable;
if (prop !== 'prototype') {
desc = rewriteDescriptor(obj, prop, desc);
}
return _tryDefineProperty(obj, prop, desc, originalConfigurableFlag);
};
Object.defineProperties = function(obj, props) {
Object.keys(props).forEach(function(prop) {
Object.defineProperty(obj, prop, props[prop]);
});
return obj;
};
Object.create = <any>function(obj: any, proto: any) {
if (typeof proto === 'object' && !Object.isFrozen(proto)) {
Object.keys(proto).forEach(function(prop) {
proto[prop] = rewriteDescriptor(obj, prop, proto[prop]);
});
}
return _create(obj, proto);
};
Object.getOwnPropertyDescriptor = function(obj, prop) {
const desc = _getOwnPropertyDescriptor(obj, prop);
if (desc && isUnconfigurable(obj, prop)) {
desc.configurable = false;
}
return desc;
};
}
export function _redefineProperty(obj: any, prop: string, desc: any) {
const originalConfigurableFlag = desc.configurable;
desc = rewriteDescriptor(obj, prop, desc);
return _tryDefineProperty(obj, prop, desc, originalConfigurableFlag);
}
function isUnconfigurable(obj: any, prop: any) {
return obj && obj[unconfigurablesKey] && obj[unconfigurablesKey][prop];
}
function rewriteDescriptor(obj: any, prop: string, desc: any) {
// issue-927, if the desc is frozen, don't try to change the desc
if (!Object.isFrozen(desc)) {
desc.configurable = true;
}
if (!desc.configurable) {
// issue-927, if the obj is frozen, don't try to set the desc to obj
if (!obj[unconfigurablesKey] && !Object.isFrozen(obj)) {
_defineProperty(obj, unconfigurablesKey, {writable: true, value: {}});
}
if (obj[unconfigurablesKey]) {
obj[unconfigurablesKey][prop] = true;
}
}
return desc;
}
function _tryDefineProperty(obj: any, prop: string, desc: any, originalConfigurableFlag: any) {
try {
return _defineProperty(obj, prop, desc);
} catch (error) {
if (desc.configurable) {
// In case of errors, when the configurable flag was likely set by rewriteDescriptor(), let's
// retry with the original flag value
if (typeof originalConfigurableFlag == 'undefined') {
delete desc.configurable;
} else {
desc.configurable = originalConfigurableFlag;
}
try {
return _defineProperty(obj, prop, desc);
} catch (error) {
fix(zone.js): defineProperty patch should not swallow error (#37582) Close #37432 zone.js monkey patches the `Object.defineProperty` API long time ago angular/zone.js@383b479 to resolve issues in very old version of Chrome web which override the property of `CustomElements`, and this is not an issue any longer, so we want to remove this monkey patch, since it may swallow the errors when the user want to define property on unconfigurable or frozen object properties. But currently there are several apps and tests depends on this patch, since it also change the `configurable` property to `true` by default, so in this PR we update the logic to not to swallow error any longer unless the property is the callbacks of `document.registerElements`. BREAKING CHANGE: ZoneJS no longer swallows errors produced by `Object.defineProperty` calls. Prior to this change, ZoneJS monkey patched `Object.defineProperty` and if there is an error (such as the property is not configurable or not writable) the patched logic swallowed it and only console.log was produced. This behavior used to hide real errors, so the logic is now updated to trigger original errors (if any). One exception where the patch remains in place is `document.registerElement` (to allow smooth transition for code/polyfills that rely on old behavior in legacy browsers). If your code relies on the old behavior (where errors were not thrown before), you may need to update the logic to handle the errors that are no longer masked by ZoneJS patch. PR Close #37582
2020-08-31 19:26:11 -04:00
let swallowError = false;
if (prop === 'createdCallback' || prop === 'attachedCallback' ||
prop === 'detachedCallback' || prop === 'attributeChangedCallback') {
fix(zone.js): defineProperty patch should not swallow error (#37582) Close #37432 zone.js monkey patches the `Object.defineProperty` API long time ago angular/zone.js@383b479 to resolve issues in very old version of Chrome web which override the property of `CustomElements`, and this is not an issue any longer, so we want to remove this monkey patch, since it may swallow the errors when the user want to define property on unconfigurable or frozen object properties. But currently there are several apps and tests depends on this patch, since it also change the `configurable` property to `true` by default, so in this PR we update the logic to not to swallow error any longer unless the property is the callbacks of `document.registerElements`. BREAKING CHANGE: ZoneJS no longer swallows errors produced by `Object.defineProperty` calls. Prior to this change, ZoneJS monkey patched `Object.defineProperty` and if there is an error (such as the property is not configurable or not writable) the patched logic swallowed it and only console.log was produced. This behavior used to hide real errors, so the logic is now updated to trigger original errors (if any). One exception where the patch remains in place is `document.registerElement` (to allow smooth transition for code/polyfills that rely on old behavior in legacy browsers). If your code relies on the old behavior (where errors were not thrown before), you may need to update the logic to handle the errors that are no longer masked by ZoneJS patch. PR Close #37582
2020-08-31 19:26:11 -04:00
// We only swallow the error in registerElement patch
// this is the work around since some applications
// fail if we throw the error
fix(zone.js): defineProperty patch should not swallow error (#37582) Close #37432 zone.js monkey patches the `Object.defineProperty` API long time ago angular/zone.js@383b479 to resolve issues in very old version of Chrome web which override the property of `CustomElements`, and this is not an issue any longer, so we want to remove this monkey patch, since it may swallow the errors when the user want to define property on unconfigurable or frozen object properties. But currently there are several apps and tests depends on this patch, since it also change the `configurable` property to `true` by default, so in this PR we update the logic to not to swallow error any longer unless the property is the callbacks of `document.registerElements`. BREAKING CHANGE: ZoneJS no longer swallows errors produced by `Object.defineProperty` calls. Prior to this change, ZoneJS monkey patched `Object.defineProperty` and if there is an error (such as the property is not configurable or not writable) the patched logic swallowed it and only console.log was produced. This behavior used to hide real errors, so the logic is now updated to trigger original errors (if any). One exception where the patch remains in place is `document.registerElement` (to allow smooth transition for code/polyfills that rely on old behavior in legacy browsers). If your code relies on the old behavior (where errors were not thrown before), you may need to update the logic to handle the errors that are no longer masked by ZoneJS patch. PR Close #37582
2020-08-31 19:26:11 -04:00
swallowError = true;
}
if (!swallowError) {
throw error;
}
// TODO: @JiaLiPassion, Some application such as `registerElement` patch
// still need to swallow the error, in the future after these applications
// are updated, the following logic can be removed.
let descJson: string|null = null;
try {
descJson = JSON.stringify(desc);
} catch (error) {
descJson = desc.toString();
}
console.log(`Attempting to configure '${prop}' with descriptor '${descJson}' on object '${
obj}' and got error, giving up: ${error}`);
}
} else {
throw error;
}
}
}