mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 01:02:08 +01:00
Add spoof-css
scriptlet
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/2618 Usage: example.com##+js(spoof-css, selector, property-name, property-value, ...) - selector: a valid CSS selector which matches the elements for which the spoofing must apply. - property-name: a CSS property name (can be dashed- or camel-cased) - property-value: the value to return regardless of the currently computed value. There can be any number of property-name/property-value pairs, all separated by commas. A special property-name/property-value pair `debug/1` can be used to force the browser to break when `getComputedStyle()` or `getBoundingClientrect()` is called, useful to help pinpoint usage of those calls in the page's source code: example.com##+js(spoof-css, .ad, debug, 1)
This commit is contained in:
parent
f0fba0d640
commit
d405460584
1 changed files with 79 additions and 3 deletions
|
@ -52,9 +52,10 @@ function safeSelf() {
|
||||||
'addEventListener': self.EventTarget.prototype.addEventListener,
|
'addEventListener': self.EventTarget.prototype.addEventListener,
|
||||||
'removeEventListener': self.EventTarget.prototype.removeEventListener,
|
'removeEventListener': self.EventTarget.prototype.removeEventListener,
|
||||||
'log': console.log.bind(console),
|
'log': console.log.bind(console),
|
||||||
'uboLog': function(msg) {
|
'uboLog': function(...args) {
|
||||||
if ( msg === '' ) { return; }
|
if ( args.length === 0 ) { return; }
|
||||||
this.log(`[uBO] ${msg}`);
|
if ( `${args[0]}` === '' ) { return; }
|
||||||
|
this.log('[uBO]', ...args);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
scriptletGlobals.set('safeSelf', safe);
|
scriptletGlobals.set('safeSelf', safe);
|
||||||
|
@ -1589,6 +1590,12 @@ function alertBuster() {
|
||||||
apply: function(a) {
|
apply: function(a) {
|
||||||
console.info(a);
|
console.info(a);
|
||||||
},
|
},
|
||||||
|
get(target, prop, receiver) {
|
||||||
|
if ( prop === 'toString' ) {
|
||||||
|
return target.toString.bind(target);
|
||||||
|
}
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2109,4 +2116,73 @@ function callNothrow(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'spoof-css.js',
|
||||||
|
fn: spoofCSS,
|
||||||
|
});
|
||||||
|
function spoofCSS(
|
||||||
|
selector,
|
||||||
|
...args
|
||||||
|
) {
|
||||||
|
if ( typeof selector !== 'string' ) { return; }
|
||||||
|
if ( selector === '' ) { return; }
|
||||||
|
const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase());
|
||||||
|
const propToValueMap = new Map();
|
||||||
|
for ( let i = 0; i < args.length; i += 2 ) {
|
||||||
|
if ( typeof args[i+0] !== 'string' ) { break; }
|
||||||
|
if ( args[i+0] === '' ) { break; }
|
||||||
|
if ( typeof args[i+1] !== 'string' ) { break; }
|
||||||
|
propToValueMap.set(toCamelCase(args[i+0]), args[i+1]);
|
||||||
|
}
|
||||||
|
self.getComputedStyle = new Proxy(self.getComputedStyle, {
|
||||||
|
apply: function(target, thisArg, args) {
|
||||||
|
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
|
||||||
|
const style = Reflect.apply(target, thisArg, args);
|
||||||
|
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
||||||
|
if ( targetElements.has(args[0]) === false ) { return style; }
|
||||||
|
const proxiedStyle = new Proxy(style, {
|
||||||
|
get(target, prop, receiver) {
|
||||||
|
const normalProp = toCamelCase(prop);
|
||||||
|
const value = propToValueMap.has(normalProp)
|
||||||
|
? propToValueMap.get(normalProp)
|
||||||
|
: Reflect.get(target, prop, receiver);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return proxiedStyle;
|
||||||
|
},
|
||||||
|
get(target, prop, receiver) {
|
||||||
|
if ( prop === 'toString' ) {
|
||||||
|
return target.toString.bind(target);
|
||||||
|
}
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, {
|
||||||
|
apply: function(target, thisArg, args) {
|
||||||
|
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
|
||||||
|
const rect = Reflect.apply(target, thisArg, args);
|
||||||
|
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
||||||
|
if ( targetElements.has(thisArg) === false ) { return rect; }
|
||||||
|
let { height, width } = rect;
|
||||||
|
if ( propToValueMap.has('width') ) {
|
||||||
|
width = parseFloat(propToValueMap.get('width'));
|
||||||
|
}
|
||||||
|
if ( propToValueMap.has('height') ) {
|
||||||
|
height = parseFloat(propToValueMap.get('height'));
|
||||||
|
}
|
||||||
|
return new self.DOMRect(rect.x, rect.y, width, height);
|
||||||
|
},
|
||||||
|
get(target, prop, receiver) {
|
||||||
|
if ( prop === 'toString' ) {
|
||||||
|
return target.toString.bind(target);
|
||||||
|
}
|
||||||
|
return Reflect.get(target, prop, receiver);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
Loading…
Reference in a new issue