Support multiple trappers to same property in set-constant

Related issues:
- https://github.com/uBlockOrigin/uBlock-issues/issues/156
- https://github.com/uBlockOrigin/uBlock-issues/issues/1162

Take into account that a trapped property may have been
already trapped, and if so honour previous trapper
getter/setter.
This commit is contained in:
Raymond Hill 2020-07-19 08:16:40 -04:00
parent 9880b9c18f
commit c33de41660
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 65 additions and 34 deletions

View File

@ -588,8 +588,9 @@
/// set-constant.js /// set-constant.js
/// alias set.js /// alias set.js
(function() { (function() {
const thisScript = document.currentScript; const chain = '{{1}}';
let cValue = '{{2}}'; let cValue = '{{2}}';
const thisScript = document.currentScript;
if ( cValue === 'undefined' ) { if ( cValue === 'undefined' ) {
cValue = undefined; cValue = undefined;
} else if ( cValue === 'false' ) { } else if ( cValue === 'false' ) {
@ -622,53 +623,83 @@
(typeof v !== typeof cValue); (typeof v !== typeof cValue);
return aborted; return aborted;
}; };
const makeProxy = function(owner, chain) { // https://github.com/uBlockOrigin/uBlock-issues/issues/156
// Support multiple trappers for the same property.
const trapProp = function(owner, prop, handler) {
if ( handler.init(owner[prop]) === false ) { return; }
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
let prevGetter, prevSetter;
if ( odesc instanceof Object ) {
if ( odesc.get instanceof Function ) {
prevGetter = odesc.get;
}
if ( odesc.set instanceof Function ) {
prevSetter = odesc.set;
}
}
Object.defineProperty(owner, prop, {
configurable: true,
get() {
if ( prevGetter !== undefined ) {
prevGetter();
}
return handler.getter();
},
set(a) {
if ( prevSetter !== undefined ) {
prevSetter(a);
}
handler.setter(a);
}
});
};
const trapChain = function(owner, chain) {
const pos = chain.indexOf('.'); const pos = chain.indexOf('.');
if ( pos === -1 ) { if ( pos === -1 ) {
const original = owner[chain]; trapProp(owner, chain, {
if ( mustAbort(original) ) { return; } v: undefined,
const desc = Object.getOwnPropertyDescriptor(owner, chain); init: function(v) {
if ( desc === undefined || desc.get === undefined ) { if ( mustAbort(v) ) { return false; }
Object.defineProperty(owner, chain, { this.v = v;
get: function() { return true;
return document.currentScript === thisScript },
? original getter: function() {
: cValue; return document.currentScript === thisScript
}, ? this.v
set: function(a) { : cValue;
if ( mustAbort(a) ) { },
cValue = a; setter: function(a) {
} if ( mustAbort(a) === false ) { return; }
} cValue = a;
}); }
} });
return; return;
} }
const prop = chain.slice(0, pos); const prop = chain.slice(0, pos);
let v = owner[prop]; const v = owner[prop];
chain = chain.slice(pos + 1); chain = chain.slice(pos + 1);
if ( if ( v instanceof Object || typeof v === 'object' && v !== null ) {
(v instanceof Object) || trapChain(v, chain);
(typeof v === 'object' && v !== null)
) {
makeProxy(v, chain);
return; return;
} }
const desc = Object.getOwnPropertyDescriptor(owner, prop); trapProp(owner, prop, {
if ( desc && desc.set !== undefined ) { return; } v: undefined,
Object.defineProperty(owner, prop, { init: function(v) {
get: function() { this.v = v;
return v; return true;
}, },
set: function(a) { getter: function() {
v = a; return this.v;
},
setter: function(a) {
this.v = a;
if ( a instanceof Object ) { if ( a instanceof Object ) {
makeProxy(a, chain); trapChain(a, chain);
} }
} }
}); });
}; };
makeProxy(window, '{{1}}'); trapChain(window, chain);
})(); })();