diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 537190e3c..1f28bd08c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -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); })();