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

`+

/******************************************************************************/

`