[experimental] Add `json-stringify` scriptlet

Will be evaluated this current dev cycle and as a result
might be removed before next stable.

Other changes to increase code consistency.
This commit is contained in:
Raymond Hill 2023-10-17 17:33:49 -04:00
parent c0ea5ebab5
commit 51359c6585
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 68 additions and 40 deletions

View File

@ -60,8 +60,8 @@ function safeSelf() {
'addEventListener': self.EventTarget.prototype.addEventListener, 'addEventListener': self.EventTarget.prototype.addEventListener,
'removeEventListener': self.EventTarget.prototype.removeEventListener, 'removeEventListener': self.EventTarget.prototype.removeEventListener,
'fetch': self.fetch, 'fetch': self.fetch,
'jsonParse': self.JSON.parse.bind(self.JSON), 'JSON_parse': self.JSON.parse.bind(self.JSON),
'jsonStringify': self.JSON.stringify.bind(self.JSON), 'JSON_stringify': self.JSON.stringify.bind(self.JSON),
'log': console.log.bind(console), 'log': console.log.bind(console),
uboLog(...args) { uboLog(...args) {
if ( scriptletGlobals.has('canDebug') === false ) { return; } if ( scriptletGlobals.has('canDebug') === false ) { return; }
@ -428,7 +428,7 @@ function setConstantCore(
if ( Math.abs(cValue) > 0x7FFF ) { return; } if ( Math.abs(cValue) > 0x7FFF ) { return; }
} else if ( trusted ) { } else if ( trusted ) {
if ( cValue.startsWith('{') && cValue.endsWith('}') ) { if ( cValue.startsWith('{') && cValue.endsWith('}') ) {
try { cValue = safe.jsonParse(cValue).value; } catch(ex) { return; } try { cValue = safe.JSON_parse(cValue).value; } catch(ex) { return; }
} }
} else { } else {
return; return;
@ -544,14 +544,14 @@ function setConstantCore(
/******************************************************************************/ /******************************************************************************/
builtinScriptlets.push({ builtinScriptlets.push({
name: 'replace-node-text-core.fn', name: 'replace-node-text.fn',
fn: replaceNodeTextCore, fn: replaceNodeTextFn,
dependencies: [ dependencies: [
'run-at.fn', 'run-at.fn',
'safe-self.fn', 'safe-self.fn',
], ],
}); });
function replaceNodeTextCore( function replaceNodeTextFn(
nodeName = '', nodeName = '',
pattern = '', pattern = '',
replacement = '' replacement = ''
@ -581,8 +581,8 @@ function replaceNodeTextCore(
: replacement; : replacement;
node.textContent = after; node.textContent = after;
if ( shouldLog !== 0 ) { if ( shouldLog !== 0 ) {
safe.uboLog('replace-node-text-core.fn before:\n', before); safe.uboLog('replace-node-text.fn before:\n', before);
safe.uboLog('replace-node-text-core.fn after:\n', after); safe.uboLog('replace-node-text.fn after:\n', after);
} }
return sedCount === 0 || (sedCount -= 1) !== 0; return sedCount === 0 || (sedCount -= 1) !== 0;
}; };
@ -630,10 +630,11 @@ function replaceNodeTextCore(
builtinScriptlets.push({ builtinScriptlets.push({
name: 'object-prune.fn', name: 'object-prune.fn',
fn: objectPrune, fn: objectPruneFn,
dependencies: [ dependencies: [
'matches-stack-trace.fn', 'matches-stack-trace.fn',
'safe-self.fn', 'safe-self.fn',
'should-log.fn',
], ],
}); });
// When no "prune paths" argument is provided, the scriptlet is // When no "prune paths" argument is provided, the scriptlet is
@ -642,7 +643,7 @@ builtinScriptlets.push({
// //
// https://github.com/uBlockOrigin/uBlock-issues/issues/1545 // https://github.com/uBlockOrigin/uBlock-issues/issues/1545
// - Add support for "remove everything if needle matches" case // - Add support for "remove everything if needle matches" case
function objectPrune( function objectPruneFn(
obj, obj,
rawPrunePaths, rawPrunePaths,
rawNeedlePaths, rawNeedlePaths,
@ -664,8 +665,8 @@ function objectPrune(
return; return;
} }
} }
if ( objectPrune.findOwner === undefined ) { if ( objectPruneFn.findOwner === undefined ) {
objectPrune.findOwner = (root, path, prune = false) => { objectPruneFn.findOwner = (root, path, prune = false) => {
let owner = root; let owner = root;
let chain = path; let chain = path;
for (;;) { for (;;) {
@ -696,7 +697,7 @@ function objectPrune(
const next = chain.slice(pos + 1); const next = chain.slice(pos + 1);
let found = false; let found = false;
for ( const key of Object.keys(owner) ) { for ( const key of Object.keys(owner) ) {
found = objectPrune.findOwner(owner[key], next, prune) || found; found = objectPruneFn.findOwner(owner[key], next, prune) || found;
} }
return found; return found;
} }
@ -705,34 +706,34 @@ function objectPrune(
chain = chain.slice(pos + 1); chain = chain.slice(pos + 1);
} }
}; };
objectPrune.mustProcess = (root, needlePaths) => { objectPruneFn.mustProcess = (root, needlePaths) => {
for ( const needlePath of needlePaths ) { for ( const needlePath of needlePaths ) {
if ( objectPrune.findOwner(root, needlePath) === false ) { if ( objectPruneFn.findOwner(root, needlePath) === false ) {
return false; return false;
} }
} }
return true; return true;
}; };
objectPrune.logJson = (json, msg, reNeedle) => { objectPruneFn.logJson = (json, msg, reNeedle) => {
if ( reNeedle.test(json) === false ) { return; } if ( reNeedle.test(json) === false ) { return; }
safeSelf().uboLog(`objectPrune()`, msg, location.hostname, json); safeSelf().uboLog(`objectPrune()`, msg, location.hostname, json);
}; };
} }
const jsonBefore = logLevel ? JSON.stringify(obj, null, 2) : ''; const jsonBefore = logLevel ? safe.JSON_stringify(obj, null, 2) : '';
if ( logLevel === true || logLevel === 'all' ) { if ( logLevel === true || logLevel === 'all' ) {
objectPrune.logJson(jsonBefore, `prune:"${rawPrunePaths}" log:"${logLevel}"`, reLogNeedle); objectPruneFn.logJson(jsonBefore, `prune:"${rawPrunePaths}" log:"${logLevel}"`, reLogNeedle);
} }
if ( prunePaths.length === 0 ) { return; } if ( prunePaths.length === 0 ) { return; }
let outcome = 'nomatch'; let outcome = 'nomatch';
if ( objectPrune.mustProcess(obj, needlePaths) ) { if ( objectPruneFn.mustProcess(obj, needlePaths) ) {
for ( const path of prunePaths ) { for ( const path of prunePaths ) {
if ( objectPrune.findOwner(obj, path, true) ) { if ( objectPruneFn.findOwner(obj, path, true) ) {
outcome = 'match'; outcome = 'match';
} }
} }
} }
if ( logLevel === outcome ) { if ( logLevel === outcome ) {
objectPrune.logJson(jsonBefore, `prune:"${rawPrunePaths}" log:"${logLevel}"`, reLogNeedle); objectPruneFn.logJson(jsonBefore, `prune:"${rawPrunePaths}" log:"${logLevel}"`, reLogNeedle);
} }
if ( outcome === 'match' ) { return obj; } if ( outcome === 'match' ) { return obj; }
} }
@ -1005,7 +1006,7 @@ function jsonPruneFetchResponseFn(
const response = responseBefore.clone(); const response = responseBefore.clone();
return response.json().then(objBefore => { return response.json().then(objBefore => {
if ( typeof objBefore !== 'object' ) { return responseBefore; } if ( typeof objBefore !== 'object' ) { return responseBefore; }
const objAfter = objectPrune( const objAfter = objectPruneFn(
objBefore, objBefore,
rawPrunePaths, rawPrunePaths,
rawNeedlePaths, rawNeedlePaths,
@ -1390,19 +1391,10 @@ builtinScriptlets.push({
name: 'json-prune.js', name: 'json-prune.js',
fn: jsonPrune, fn: jsonPrune,
dependencies: [ dependencies: [
'match-object-properties.fn',
'object-prune.fn', 'object-prune.fn',
'parse-properties-to-match.fn',
'safe-self.fn', 'safe-self.fn',
'should-log.fn',
], ],
}); });
// When no "prune paths" argument is provided, the scriptlet is
// used for logging purpose and the "needle paths" argument is
// used to filter logging output.
//
// https://github.com/uBlockOrigin/uBlock-issues/issues/1545
// - Add support for "remove everything if needle matches" case
function jsonPrune( function jsonPrune(
rawPrunePaths = '', rawPrunePaths = '',
rawNeedlePaths = '', rawNeedlePaths = '',
@ -1414,7 +1406,7 @@ function jsonPrune(
JSON.parse = new Proxy(JSON.parse, { JSON.parse = new Proxy(JSON.parse, {
apply: function(target, thisArg, args) { apply: function(target, thisArg, args) {
const objBefore = Reflect.apply(target, thisArg, args); const objBefore = Reflect.apply(target, thisArg, args);
const objAfter = objectPrune( const objAfter = objectPruneFn(
objBefore, objBefore,
rawPrunePaths, rawPrunePaths,
rawNeedlePaths, rawNeedlePaths,
@ -1426,6 +1418,42 @@ function jsonPrune(
}); });
} }
/******************************************************************************/
builtinScriptlets.push({
name: 'json-prune-stringify.js',
fn: jsonPruneStringify,
dependencies: [
'object-prune.fn',
'safe-self.fn',
],
});
function jsonPruneStringify(
rawPrunePaths = '',
rawNeedlePaths = '',
stackNeedle = ''
) {
const safe = safeSelf();
const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true });
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
JSON.stringify = new Proxy(JSON.stringify, {
apply: function(target, thisArg, args) {
const objBefore = args[0];
if ( objBefore instanceof Object ) {
const objAfter = objectPruneFn(
objBefore,
rawPrunePaths,
rawNeedlePaths,
stackNeedleDetails,
extraArgs
);
args[0] = objAfter || objBefore;
}
return Reflect.apply(target, thisArg, args);
},
});
}
/******************************************************************************* /*******************************************************************************
* *
* json-prune-fetch-response.js * json-prune-fetch-response.js
@ -1506,13 +1534,13 @@ function jsonPruneXhrResponse(
if ( typeof innerResponse === 'object' ) { if ( typeof innerResponse === 'object' ) {
objBefore = innerResponse; objBefore = innerResponse;
} else if ( typeof innerResponse === 'string' ) { } else if ( typeof innerResponse === 'string' ) {
try { objBefore = safe.jsonParse(innerResponse); } try { objBefore = safe.JSON_parse(innerResponse); }
catch(ex) { } catch(ex) { }
} }
if ( typeof objBefore !== 'object' ) { if ( typeof objBefore !== 'object' ) {
return (xhrDetails.response = innerResponse); return (xhrDetails.response = innerResponse);
} }
const objAfter = objectPrune( const objAfter = objectPruneFn(
objBefore, objBefore,
rawPrunePaths, rawPrunePaths,
rawNeedlePaths, rawNeedlePaths,
@ -1522,7 +1550,7 @@ function jsonPruneXhrResponse(
let outerResponse; let outerResponse;
if ( typeof objAfter === 'object' ) { if ( typeof objAfter === 'object' ) {
outerResponse = typeof innerResponse === 'string' outerResponse = typeof innerResponse === 'string'
? safe.jsonStringify(objAfter) ? safe.JSON_stringify(objAfter)
: objAfter; : objAfter;
} else { } else {
outerResponse = innerResponse; outerResponse = innerResponse;
@ -1557,7 +1585,7 @@ function evaldataPrune(
apply(target, thisArg, args) { apply(target, thisArg, args) {
const before = Reflect.apply(target, thisArg, args); const before = Reflect.apply(target, thisArg, args);
if ( typeof before === 'object' ) { if ( typeof before === 'object' ) {
const after = objectPrune(before, rawPrunePaths, rawNeedlePaths); const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths);
return after || before; return after || before;
} }
return before; return before;
@ -3305,7 +3333,7 @@ builtinScriptlets.push({
fn: removeNodeText, fn: removeNodeText,
world: 'ISOLATED', world: 'ISOLATED',
dependencies: [ dependencies: [
'replace-node-text-core.fn', 'replace-node-text.fn',
], ],
}); });
function removeNodeText( function removeNodeText(
@ -3313,7 +3341,7 @@ function removeNodeText(
condition, condition,
...extraArgs ...extraArgs
) { ) {
replaceNodeTextCore(nodeName, '', '', 'condition', condition || '', ...extraArgs); replaceNodeTextFn(nodeName, '', '', 'condition', condition || '', ...extraArgs);
} }
/******************************************************************************* /*******************************************************************************
@ -3648,7 +3676,7 @@ builtinScriptlets.push({
fn: replaceNodeText, fn: replaceNodeText,
world: 'ISOLATED', world: 'ISOLATED',
dependencies: [ dependencies: [
'replace-node-text-core.fn', 'replace-node-text.fn',
], ],
}); });
function replaceNodeText( function replaceNodeText(
@ -3657,7 +3685,7 @@ function replaceNodeText(
replacement, replacement,
...extraArgs ...extraArgs
) { ) {
replaceNodeTextCore(nodeName, pattern, replacement, ...extraArgs); replaceNodeTextFn(nodeName, pattern, replacement, ...extraArgs);
} }
/******************************************************************************* /*******************************************************************************