Skip to content

Commit be3bb05

Browse files
committed
Improve proxy-apply utility scriptlet
Allow to configure behavior of `proxy-apply.fn` scriptlet through `proxy-apply-config` scriptlet, i.e.: ..##+js(proxy-apply-config, {"skipToString":true}) The above filter will ensure the `proxy-apply` utility scriptlet (which is used by many other user-facing scriptlet filters internally) will not trap `toString`, which can be an issue on some sites.
1 parent 1e26209 commit be3bb05

4 files changed

Lines changed: 57 additions & 36 deletions

File tree

src/js/redirect-engine.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class RedirectEngine {
215215
js: entry.toContent(),
216216
world: entry.world,
217217
dependencies: entry.dependencies.slice(),
218+
priority: entry.priority ?? 0,
218219
};
219220
}
220221

src/js/resources/prevent-xhr.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ registerScriptlet(preventXhrFn, {
230230
* */
231231

232232
function preventXhr(...args) {
233-
return preventXhrFn(false, ...args);
233+
preventXhrFn(false, ...args);
234234
}
235235
registerScriptlet(preventXhr, {
236236
name: 'prevent-xhr.js',
@@ -260,7 +260,7 @@ registerScriptlet(preventXhr, {
260260
* */
261261

262262
function trustedPreventXhr(...args) {
263-
return preventXhrFn(true, ...args);
263+
preventXhrFn(true, ...args);
264264
}
265265
registerScriptlet(trustedPreventXhr, {
266266
name: 'trusted-prevent-xhr.js',

src/js/resources/proxy-apply.js

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,22 @@ export function proxyApplyFn(
8787
};
8888
proxyApplyFn.isCtor = new Map();
8989
proxyApplyFn.proxies = new WeakMap();
90-
proxyApplyFn.nativeToString = Function.prototype.toString;
91-
const proxiedToString = new Proxy(Function.prototype.toString, {
92-
apply(target, thisArg) {
93-
let proxied = thisArg;
94-
for(;;) {
95-
const fn = proxyApplyFn.proxies.get(proxied);
96-
if ( fn === undefined ) { break; }
97-
proxied = fn;
90+
if ( proxyApplyFn.skipToString !== true ) {
91+
proxyApplyFn.nativeToString = Function.prototype.toString;
92+
const proxiedToString = new Proxy(Function.prototype.toString, {
93+
apply(target, thisArg) {
94+
let proxied = thisArg;
95+
for(;;) {
96+
const fn = proxyApplyFn.proxies.get(proxied);
97+
if ( fn === undefined ) { break; }
98+
proxied = fn;
99+
}
100+
return proxyApplyFn.nativeToString.call(proxied);
98101
}
99-
return proxyApplyFn.nativeToString.call(proxied);
100-
}
101-
});
102-
proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString);
103-
Function.prototype.toString = proxiedToString;
102+
});
103+
proxyApplyFn.proxies.set(proxiedToString, proxyApplyFn.nativeToString);
104+
Function.prototype.toString = proxiedToString;
105+
}
104106
}
105107
if ( proxyApplyFn.isCtor.has(target) === false ) {
106108
proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn);
@@ -122,3 +124,19 @@ export function proxyApplyFn(
122124
registerScriptlet(proxyApplyFn, {
123125
name: 'proxy-apply.fn',
124126
});
127+
128+
/******************************************************************************/
129+
130+
export function proxyApplyConfig(config = '') {
131+
try { config = JSON.parse(config); }
132+
catch { }
133+
if ( typeof config !== 'object' ) { return; }
134+
Object.assign(proxyApplyFn, config);
135+
}
136+
registerScriptlet(proxyApplyConfig , {
137+
name: 'proxy-apply-config.js',
138+
dependencies: [
139+
proxyApplyFn,
140+
],
141+
priority: 100,
142+
});

src/js/scriptlet-filtering-core.js

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ import { redirectEngine as reng } from './redirect-engine.js';
2525

2626
/******************************************************************************/
2727

28-
// For debugging convenience: all the top function calls will appear
29-
// at the bottom of a generated content script
30-
const codeSorter = (a, b) => {
31-
if ( a.startsWith('try') ) { return 1; }
32-
if ( b.startsWith('try') ) { return -1; }
33-
return 0;
34-
};
35-
3628
const normalizeRawFilter = (parser, sourceIsTrusted = false) => {
3729
const args = parser.getScriptletArgs();
3830
if ( args.length !== 0 ) {
@@ -61,7 +53,7 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => {
6153
const fname = match && match[1];
6254
const content = patchScriptlet(fname, details.js, args.slice(1));
6355
if ( fname ) {
64-
targetWorldMap.set(token, details.js);
56+
targetWorldMap.set(token, { code: details.js });
6557
}
6658
const dependencies = details.dependencies || [];
6759
while ( dependencies.length !== 0 ) {
@@ -70,17 +62,20 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => {
7062
const details = reng.contentFromName(token, 'fn/javascript') ||
7163
reng.contentFromName(token, 'text/javascript');
7264
if ( details === undefined ) { continue; }
73-
targetWorldMap.set(token, details.js);
65+
targetWorldMap.set(token, { code: details.js });
7466
if ( Array.isArray(details.dependencies) === false ) { continue; }
7567
dependencies.push(...details.dependencies);
7668
}
77-
targetWorldMap.set(rawToken, [
78-
'try {',
79-
`\t${content}`,
80-
'} catch (e) {',
81-
debug ? '\tconsole.error(e);' : '',
82-
'}',
83-
].join('\n'));
69+
targetWorldMap.set(rawToken, {
70+
code: [
71+
'try {',
72+
`\t${content}`,
73+
'} catch (e) {',
74+
debug ? '\tconsole.error(e);' : '',
75+
'}',
76+
].join('\n'),
77+
priority: details.priority ?? 0,
78+
});
8479
};
8580

8681
// Fill-in scriptlet argument placeholders.
@@ -251,17 +246,24 @@ export class ScriptletFilteringEngine {
251246
}
252247
}
253248

249+
const sortedCalls = map => Array.from(map).toSorted((a, b) => {
250+
const ap = a[1].priority;
251+
const bp = b[1].priority;
252+
if ( ap === bp ) { return a[0].localeCompare(b[0]); }
253+
if ( ap === undefined ) { return 1; }
254+
if ( bp === undefined ) { return -1; }
255+
return bp - ap;
256+
}).map(a => a[1].code);
257+
254258
const mainWorldCode = [];
255-
for ( const js of mainWorldMap.values() ) {
259+
for ( const js of sortedCalls(mainWorldMap) ) {
256260
mainWorldCode.push(js);
257261
}
258-
mainWorldCode.sort(codeSorter);
259262

260263
const isolatedWorldCode = [];
261-
for ( const js of isolatedWorldMap.values() ) {
264+
for ( const js of sortedCalls(isolatedWorldMap) ) {
262265
isolatedWorldCode.push(js);
263266
}
264-
isolatedWorldCode.sort(codeSorter);
265267

266268
const scriptletDetails = {
267269
mainWorld: mainWorldCode.join('\n\n'),

0 commit comments

Comments
 (0)