Add trusted-source support for privileged scriptlets · gorhill/uBlock@4187633 (original) (raw)
`@@ -68,14 +68,13 @@ builtinScriptlets.push({
`
68
68
`name: 'pattern-to-regex.fn',
`
69
69
`fn: patternToRegex,
`
70
70
`});
`
71
``
`-
function patternToRegex(pattern) {
`
72
``
`-
if ( pattern === '' ) {
`
73
``
`-
return /^/;
`
``
71
`+
function patternToRegex(pattern, flags = undefined) {
`
``
72
`+
if ( pattern === '' ) { return /^/; }
`
``
73
`+
const match = /^/(.+)/([gimsu]*)$/.exec(pattern);
`
``
74
`+
if ( match !== null ) {
`
``
75
`+
return new RegExp(match[1], match[2] || flags);
`
74
76
`}
`
75
``
`-
if ( pattern.startsWith('/') && pattern.endsWith('/') ) {
`
76
``
`-
return new RegExp(pattern.slice(1, -1));
`
77
``
`-
}
`
78
``
`-
return new RegExp(pattern.replace(/[.*+?^${}()|[]\]/g, '\$&'));
`
``
77
`+
return new RegExp(pattern.replace(/[.*+?^${}()|[]\]/g, '\$&'), flags);
`
79
78
`}
`
80
79
``
81
80
`/******************************************************************************/
`
`@@ -394,7 +393,7 @@ function abortOnStackTrace(
`
394
393
`for ( let line of err.stack.split(/[\n\r]+/) ) {
`
395
394
`if ( line.includes(exceptionToken) ) { continue; }
`
396
395
`line = line.trim();
`
397
``
`-
let match = safe.RegExp_exec.call(reLine, line);
`
``
396
`+
const match = safe.RegExp_exec.call(reLine, line);
`
398
397
`if ( match === null ) { continue; }
`
399
398
`let url = match[2];
`
400
399
`if ( url.startsWith('(') ) { url = url.slice(1); }
`
`@@ -2122,6 +2121,9 @@ function callNothrow(
`
2122
2121
`builtinScriptlets.push({
`
2123
2122
`name: 'spoof-css.js',
`
2124
2123
`fn: spoofCSS,
`
``
2124
`+
dependencies: [
`
``
2125
`+
'safe-self.fn',
`
``
2126
`+
],
`
2125
2127
`});
`
2126
2128
`function spoofCSS(
`
2127
2129
`selector,
`
`@@ -2137,19 +2139,30 @@ function spoofCSS(
`
2137
2139
`if ( typeof args[i+1] !== 'string' ) { break; }
`
2138
2140
`propToValueMap.set(toCamelCase(args[i+0]), args[i+1]);
`
2139
2141
`}
`
``
2142
`+
const safe = safeSelf();
`
``
2143
`+
const canDebug = scriptletGlobals.has('canDebug');
`
``
2144
`+
const shouldDebug = canDebug && propToValueMap.get('debug') || 0;
`
``
2145
`+
const shouldLog = canDebug && propToValueMap.has('log') || 0;
`
``
2146
`+
const proxiedStyles = new WeakSet();
`
``
2147
`+
const spoofStyle = (prop, real) => {
`
``
2148
`+
const normalProp = toCamelCase(prop);
`
``
2149
`+
const shouldSpoof = propToValueMap.has(normalProp);
`
``
2150
`+
const value = shouldSpoof ? propToValueMap.get(normalProp) : real;
`
``
2151
`+
if ( shouldLog === 2 || shouldSpoof && shouldLog === 1 ) {
`
``
2152
`+
safe.uboLog(prop, value);
`
``
2153
`+
}
`
``
2154
`+
return value;
`
``
2155
`+
};
`
2140
2156
`self.getComputedStyle = new Proxy(self.getComputedStyle, {
`
2141
2157
`apply: function(target, thisArg, args) {
`
2142
``
`-
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
`
``
2158
`+
if ( shouldDebug !== 0 ) { debugger; } // jshint ignore: line
`
2143
2159
`const style = Reflect.apply(target, thisArg, args);
`
2144
2160
`const targetElements = new WeakSet(document.querySelectorAll(selector));
`
2145
2161
`if ( targetElements.has(args[0]) === false ) { return style; }
`
``
2162
`+
proxiedStyles.add(target);
`
2146
2163
`const proxiedStyle = new Proxy(style, {
`
2147
2164
`get(target, prop, receiver) {
`
2148
``
`-
const normalProp = toCamelCase(prop);
`
2149
``
`-
const value = propToValueMap.has(normalProp)
`
2150
``
`-
? propToValueMap.get(normalProp)
`
2151
``
`-
: Reflect.get(target, prop, receiver);
`
2152
``
`-
return value;
`
``
2165
`+
return spoofStyle(prop, Reflect.get(target, prop, receiver));
`
2153
2166
`},
`
2154
2167
`});
`
2155
2168
`return proxiedStyle;
`
`@@ -2161,9 +2174,23 @@ function spoofCSS(
`
2161
2174
`return Reflect.get(target, prop, receiver);
`
2162
2175
`},
`
2163
2176
`});
`
``
2177
`+
CSSStyleDeclaration.prototype.getPropertyValue = new Proxy(CSSStyleDeclaration.prototype.getPropertyValue, {
`
``
2178
`+
apply: function(target, thisArg, args) {
`
``
2179
`+
if ( shouldDebug !== 0 ) { debugger; } // jshint ignore: line
`
``
2180
`+
const value = Reflect.apply(target, thisArg, args);
`
``
2181
`+
if ( proxiedStyles.has(thisArg) === false ) { return value; }
`
``
2182
`+
return spoofStyle(args[0], value);
`
``
2183
`+
},
`
``
2184
`+
get(target, prop, receiver) {
`
``
2185
`+
if ( prop === 'toString' ) {
`
``
2186
`+
return target.toString.bind(target);
`
``
2187
`+
}
`
``
2188
`+
return Reflect.get(target, prop, receiver);
`
``
2189
`+
},
`
``
2190
`+
});
`
2164
2191
`Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, {
`
2165
2192
`apply: function(target, thisArg, args) {
`
2166
``
`-
if ( propToValueMap.has('debug') ) { debugger; } // jshint ignore: line
`
``
2193
`+
if ( shouldDebug !== 0 ) { debugger; } // jshint ignore: line
`
2167
2194
`const rect = Reflect.apply(target, thisArg, args);
`
2168
2195
`const targetElements = new WeakSet(document.querySelectorAll(selector));
`
2169
2196
`if ( targetElements.has(thisArg) === false ) { return rect; }
`
`@@ -2186,3 +2213,62 @@ function spoofCSS(
`
2186
2213
`}
`
2187
2214
``
2188
2215
`/******************************************************************************/
`
``
2216
+
``
2217
`+
builtinScriptlets.push({
`
``
2218
`+
name: 'sed.js',
`
``
2219
`+
requiresTrust: true,
`
``
2220
`+
fn: sed,
`
``
2221
`+
dependencies: [
`
``
2222
`+
'pattern-to-regex.fn',
`
``
2223
`+
'safe-self.fn',
`
``
2224
`+
],
`
``
2225
`+
});
`
``
2226
`+
function sed(
`
``
2227
`+
nodeName = '',
`
``
2228
`+
pattern = '',
`
``
2229
`+
replacement = ''
`
``
2230
`+
) {
`
``
2231
`+
const reNodeName = patternToRegex(nodeName, 'i');
`
``
2232
`+
const rePattern = patternToRegex(pattern, 'gms');
`
``
2233
`+
const extraArgs = new Map(
`
``
2234
`+
Array.from(arguments).slice(3).reduce((out, v, i, a) => {
`
``
2235
`+
if ( (i & 1) === 0 ) { out.push([ a[i], a[i+1] || undefined ]); }
`
``
2236
`+
return out;
`
``
2237
`+
}, [])
`
``
2238
`+
);
`
``
2239
`+
const shouldLog = scriptletGlobals.has('canDebug') && extraArgs.get('log') || 0;
`
``
2240
`+
const reCondition = patternToRegex(extraArgs.get('condition') || '', 'gms');
`
``
2241
`+
let sedCount = extraArgs.has('sedCount') ? parseInt(extraArgs.get('sedCount')) : 0;
`
``
2242
`+
let tryCount = extraArgs.has('tryCount') ? parseInt(extraArgs.get('tryCount')) : 0;
`
``
2243
`+
const safe = safeSelf();
`
``
2244
`+
const handler = mutations => {
`
``
2245
`+
for ( const mutation of mutations ) {
`
``
2246
`+
for ( const node of mutation.addedNodes ) {
`
``
2247
`+
if ( reNodeName.test(node.nodeName) === false ) { continue; }
`
``
2248
`+
const before = node.textContent;
`
``
2249
`+
if ( safe.RegExp_test.call(rePattern, before) === false ) { continue; }
`
``
2250
`+
if ( safe.RegExp_test.call(reCondition, before) === false ) { continue; }
`
``
2251
`+
if ( shouldLog !== 0 ) { safe.uboLog('sed.js before:\n', before); }
`
``
2252
`+
const after = before.replace(rePattern, replacement);
`
``
2253
`+
if ( shouldLog !== 0 ) { safe.uboLog('sed.js after:\n', after); }
`
``
2254
`+
node.textContent = after;
`
``
2255
`+
if ( sedCount !== 0 && (sedCount -= 1) === 0 ) {
`
``
2256
`+
observer.disconnect();
`
``
2257
`+
if ( shouldLog !== 0 ) { safe.uboLog('sed.js: quitting'); }
`
``
2258
`+
return;
`
``
2259
`+
}
`
``
2260
`+
}
`
``
2261
`+
}
`
``
2262
`+
if ( tryCount !== 0 && (tryCount -= 1) === 0 ) {
`
``
2263
`+
observer.disconnect();
`
``
2264
`+
if ( shouldLog !== 0 ) { safe.uboLog('sed.js: quitting'); }
`
``
2265
`+
}
`
``
2266
`+
};
`
``
2267
`+
const observer = new MutationObserver(handler);
`
``
2268
`+
observer.observe(document.documentElement, {
`
``
2269
`+
childList: true,
`
``
2270
`+
subtree: true,
`
``
2271
`+
});
`
``
2272
`+
}
`
``
2273
+
``
2274
`+
/******************************************************************************/
`