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
/// alias set.js
(function() {
const thisScript = document.currentScript;
const chain = '{{1}}';
let cValue = '{{2}}';
const thisScript = document.currentScript;
if ( cValue === 'undefined' ) {
cValue = undefined;
} else if ( cValue === 'false' ) {
@ -622,53 +623,83 @@
(typeof v !== typeof cValue);
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('.');
if ( pos === -1 ) {
const original = owner[chain];
if ( mustAbort(original) ) { return; }
const desc = Object.getOwnPropertyDescriptor(owner, chain);
if ( desc === undefined || desc.get === undefined ) {
Object.defineProperty(owner, chain, {
get: function() {
return document.currentScript === thisScript
? original
: cValue;
},
set: function(a) {
if ( mustAbort(a) ) {
cValue = a;
}
}
});
}
trapProp(owner, chain, {
v: undefined,
init: function(v) {
if ( mustAbort(v) ) { return false; }
this.v = v;
return true;
},
getter: function() {
return document.currentScript === thisScript
? this.v
: cValue;
},
setter: function(a) {
if ( mustAbort(a) === false ) { return; }
cValue = a;
}
});
return;
}
const prop = chain.slice(0, pos);
let v = owner[prop];
const v = owner[prop];
chain = chain.slice(pos + 1);
if (
(v instanceof Object) ||
(typeof v === 'object' && v !== null)
) {
makeProxy(v, chain);
if ( v instanceof Object || typeof v === 'object' && v !== null ) {
trapChain(v, chain);
return;
}
const desc = Object.getOwnPropertyDescriptor(owner, prop);
if ( desc && desc.set !== undefined ) { return; }
Object.defineProperty(owner, prop, {
get: function() {
return v;
trapProp(owner, prop, {
v: undefined,
init: function(v) {
this.v = v;
return true;
},
set: function(a) {
v = a;
getter: function() {
return this.v;
},
setter: function(a) {
this.v = a;
if ( a instanceof Object ) {
makeProxy(a, chain);
trapChain(a, chain);
}
}
});
};
makeProxy(window, '{{1}}');
trapChain(window, chain);
})();