Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets · gorhill/uBlock@95a3be9 · GitHub | Latest TMZ Celebrity News & Gossip | Watch TMZ Live
Skip to content

Commit 95a3be9

Browse files
committed
Add jsonl-prune-xhr-response/jsonl-prune-fetch-response scriptlets
As discussed internally with filter list volunteers.
1 parent 27e2d6a commit 95a3be9

File tree

9 files changed

+1134
-738
lines changed

9 files changed

+1134
-738
lines changed

platform/chromium/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
},
9090
"incognito": "split",
9191
"manifest_version": 2,
92-
"minimum_chrome_version": "85.0",
92+
"minimum_chrome_version": "93.0",
9393
"name": "uBlock Origin",
9494
"options_ui": {
9595
"page": "dashboard.html",

platform/firefox/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
"browser_specific_settings": {
1818
"gecko": {
1919
"id": "uBlock0@raymondhill.net",
20-
"strict_min_version": "79.0"
20+
"strict_min_version": "92.0"
2121
},
2222
"gecko_android": {
23-
"strict_min_version": "79.0"
23+
"strict_min_version": "92.0"
2424
}
2525
},
2626
"commands": {

src/js/resources/json-prune.js

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
/*******************************************************************************
2+
3+
uBlock Origin - a comprehensive, efficient content blocker
4+
Copyright (C) 2019-present Raymond Hill
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see {http://www.gnu.org/licenses/}.
18+
19+
Home: https://github.com/gorhill/uBlock
20+
21+
*/
22+
23+
import {
24+
matchObjectPropertiesFn,
25+
parsePropertiesToMatchFn,
26+
} from './utils.js';
27+
28+
import { objectPruneFn } from './object-prune.js';
29+
import { proxyApplyFn } from './proxy-apply.js';
30+
import { registerScriptlet } from './base.js';
31+
import { safeSelf } from './safe-self.js';
32+
33+
/******************************************************************************/
34+
35+
function jsonPrune(
36+
rawPrunePaths = '',
37+
rawNeedlePaths = '',
38+
stackNeedle = ''
39+
) {
40+
const safe = safeSelf();
41+
const logPrefix = safe.makeLogPrefix('json-prune', rawPrunePaths, rawNeedlePaths, stackNeedle);
42+
const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true });
43+
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
44+
JSON.parse = new Proxy(JSON.parse, {
45+
apply: function(target, thisArg, args) {
46+
const objBefore = Reflect.apply(target, thisArg, args);
47+
if ( rawPrunePaths === '' ) {
48+
safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2));
49+
}
50+
const objAfter = objectPruneFn(
51+
objBefore,
52+
rawPrunePaths,
53+
rawNeedlePaths,
54+
stackNeedleDetails,
55+
extraArgs
56+
);
57+
if ( objAfter === undefined ) { return objBefore; }
58+
safe.uboLog(logPrefix, 'Pruned');
59+
if ( safe.logLevel > 1 ) {
60+
safe.uboLog(logPrefix, `After pruning:\n${safe.JSON_stringify(objAfter, null, 2)}`);
61+
}
62+
return objAfter;
63+
},
64+
});
65+
}
66+
registerScriptlet(jsonPrune, {
67+
name: 'json-prune.js',
68+
dependencies: [
69+
objectPruneFn,
70+
safeSelf,
71+
],
72+
});
73+
74+
/******************************************************************************/
75+
76+
function jsonPruneFetchResponseFn(
77+
rawPrunePaths = '',
78+
rawNeedlePaths = ''
79+
) {
80+
const safe = safeSelf();
81+
const logPrefix = safe.makeLogPrefix('json-prune-fetch-response', rawPrunePaths, rawNeedlePaths);
82+
const extraArgs = safe.getExtraArgs(Array.from(arguments), 2);
83+
const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url');
84+
const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true });
85+
const logall = rawPrunePaths === '';
86+
const applyHandler = function(target, thisArg, args) {
87+
const fetchPromise = Reflect.apply(target, thisArg, args);
88+
if ( propNeedles.size !== 0 ) {
89+
const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ];
90+
if ( objs[0] instanceof Request ) {
91+
try {
92+
objs[0] = safe.Request_clone.call(objs[0]);
93+
} catch(ex) {
94+
safe.uboErr(logPrefix, 'Error:', ex);
95+
}
96+
}
97+
if ( args[1] instanceof Object ) {
98+
objs.push(args[1]);
99+
}
100+
const matched = matchObjectPropertiesFn(propNeedles, ...objs);
101+
if ( matched === undefined ) { return fetchPromise; }
102+
if ( safe.logLevel > 1 ) {
103+
safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`);
104+
}
105+
}
106+
return fetchPromise.then(responseBefore => {
107+
const response = responseBefore.clone();
108+
return response.json().then(objBefore => {
109+
if ( typeof objBefore !== 'object' ) { return responseBefore; }
110+
if ( logall ) {
111+
safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2));
112+
return responseBefore;
113+
}
114+
const objAfter = objectPruneFn(
115+
objBefore,
116+
rawPrunePaths,
117+
rawNeedlePaths,
118+
stackNeedle,
119+
extraArgs
120+
);
121+
if ( typeof objAfter !== 'object' ) { return responseBefore; }
122+
safe.uboLog(logPrefix, 'Pruned');
123+
const responseAfter = Response.json(objAfter, {
124+
status: responseBefore.status,
125+
statusText: responseBefore.statusText,
126+
headers: responseBefore.headers,
127+
});
128+
Object.defineProperties(responseAfter, {
129+
ok: { value: responseBefore.ok },
130+
redirected: { value: responseBefore.redirected },
131+
type: { value: responseBefore.type },
132+
url: { value: responseBefore.url },
133+
});
134+
return responseAfter;
135+
}).catch(reason => {
136+
safe.uboErr(logPrefix, 'Error:', reason);
137+
return responseBefore;
138+
});
139+
}).catch(reason => {
140+
safe.uboErr(logPrefix, 'Error:', reason);
141+
return fetchPromise;
142+
});
143+
};
144+
self.fetch = new Proxy(self.fetch, {
145+
apply: applyHandler
146+
});
147+
}
148+
registerScriptlet(jsonPruneFetchResponseFn, {
149+
name: 'json-prune-fetch-response.fn',
150+
dependencies: [
151+
matchObjectPropertiesFn,
152+
objectPruneFn,
153+
parsePropertiesToMatchFn,
154+
safeSelf,
155+
],
156+
});
157+
158+
/******************************************************************************/
159+
160+
function jsonPruneFetchResponse(...args) {
161+
jsonPruneFetchResponseFn(...args);
162+
}
163+
registerScriptlet(jsonPruneFetchResponse, {
164+
name: 'json-prune-fetch-response.js',
165+
dependencies: [
166+
jsonPruneFetchResponseFn,
167+
],
168+
});
169+
170+
/******************************************************************************/
171+
172+
function jsonPruneXhrResponseFn(
173+
rawPrunePaths = '',
174+
rawNeedlePaths = ''
175+
) {
176+
const safe = safeSelf();
177+
const logPrefix = safe.makeLogPrefix('json-prune-xhr-response', rawPrunePaths, rawNeedlePaths);
178+
const xhrInstances = new WeakMap();
179+
const extraArgs = safe.getExtraArgs(Array.from(arguments), 2);
180+
const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url');
181+
const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true });
182+
self.XMLHttpRequest = class extends self.XMLHttpRequest {
183+
open(method, url, ...args) {
184+
const xhrDetails = { method, url };
185+
let outcome = 'match';
186+
if ( propNeedles.size !== 0 ) {
187+
if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) {
188+
outcome = 'nomatch';
189+
}
190+
}
191+
if ( outcome === 'match' ) {
192+
if ( safe.logLevel > 1 ) {
193+
safe.uboLog(logPrefix, `Matched optional "propsToMatch", "${extraArgs.propsToMatch}"`);
194+
}
195+
xhrInstances.set(this, xhrDetails);
196+
}
197+
return super.open(method, url, ...args);
198+
}
199+
get response() {
200+
const innerResponse = super.response;
201+
const xhrDetails = xhrInstances.get(this);
202+
if ( xhrDetails === undefined ) {
203+
return innerResponse;
204+
}
205+
const responseLength = typeof innerResponse === 'string'
206+
? innerResponse.length
207+
: undefined;
208+
if ( xhrDetails.lastResponseLength !== responseLength ) {
209+
xhrDetails.response = undefined;
210+
xhrDetails.lastResponseLength = responseLength;
211+
}
212+
if ( xhrDetails.response !== undefined ) {
213+
return xhrDetails.response;
214+
}
215+
let objBefore;
216+
if ( typeof innerResponse === 'object' ) {
217+
objBefore = innerResponse;
218+
} else if ( typeof innerResponse === 'string' ) {
219+
try {
220+
objBefore = safe.JSON_parse(innerResponse);
221+
} catch {
222+
}
223+
}
224+
if ( typeof objBefore !== 'object' ) {
225+
return (xhrDetails.response = innerResponse);
226+
}
227+
const objAfter = objectPruneFn(
228+
objBefore,
229+
rawPrunePaths,
230+
rawNeedlePaths,
231+
stackNeedle,
232+
extraArgs
233+
);
234+
let outerResponse;
235+
if ( typeof objAfter === 'object' ) {
236+
outerResponse = typeof innerResponse === 'string'
237+
? safe.JSON_stringify(objAfter)
238+
: objAfter;
239+
safe.uboLog(logPrefix, 'Pruned');
240+
} else {
241+
outerResponse = innerResponse;
242+
}
243+
return (xhrDetails.response = outerResponse);
244+
}
245+
get responseText() {
246+
const response = this.response;
247+
return typeof response !== 'string'
248+
? super.responseText
249+
: response;
250+
}
251+
};
252+
}
253+
registerScriptlet(jsonPruneXhrResponseFn, {
254+
name: 'json-prune-xhr-response.fn',
255+
dependencies: [
256+
matchObjectPropertiesFn,
257+
objectPruneFn,
258+
parsePropertiesToMatchFn,
259+
safeSelf,
260+
],
261+
});
262+
263+
/******************************************************************************/
264+
265+
function jsonPruneXhrResponse(...args) {
266+
jsonPruneXhrResponseFn(...args);
267+
}
268+
registerScriptlet(jsonPruneXhrResponse, {
269+
name: 'json-prune-xhr-response.js',
270+
dependencies: [
271+
jsonPruneXhrResponseFn,
272+
],
273+
});
274+
275+
/******************************************************************************/
276+
277+
// There is still code out there which uses `eval` in lieu of `JSON.parse`.
278+
279+
function evaldataPrune(
280+
rawPrunePaths = '',
281+
rawNeedlePaths = ''
282+
) {
283+
proxyApplyFn('eval', function(context) {
284+
const before = context.reflect();
285+
if ( typeof before !== 'object' ) { return before; }
286+
if ( before === null ) { return null; }
287+
const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths);
288+
return after || before;
289+
});
290+
}
291+
registerScriptlet(evaldataPrune, {
292+
name: 'evaldata-prune.js',
293+
dependencies: [
294+
objectPruneFn,
295+
proxyApplyFn,
296+
],
297+
});
298+
299+
/******************************************************************************/

0 commit comments

Comments
 (0)

TMZ Celebrity News – Breaking Stories, Videos & Gossip

Looking for the latest TMZ celebrity news? You've come to the right place. From shocking Hollywood scandals to exclusive videos, TMZ delivers it all in real time.

Whether it’s a red carpet slip-up, a viral paparazzi moment, or a legal drama involving your favorite stars, TMZ news is always first to break the story. Stay in the loop with daily updates, insider tips, and jaw-dropping photos.

🎥 Watch TMZ Live

TMZ Live brings you daily celebrity news and interviews straight from the TMZ newsroom. Don’t miss a beat—watch now and see what’s trending in Hollywood.