summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js')
-rw-r--r--data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js272
1 files changed, 272 insertions, 0 deletions
diff --git a/data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js b/data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js
new file mode 100644
index 0000000..50256fe
--- /dev/null
+++ b/data/extensions/uBlock0@raymondhill.net/js/resources/object-prune.js
@@ -0,0 +1,272 @@
+/*******************************************************************************
+
+ uBlock Origin - a comprehensive, efficient content blocker
+ Copyright (C) 2019-present Raymond Hill
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://github.com/gorhill/uBlock
+
+*/
+
+import { matchesStackTraceFn } from './stack-trace.js';
+import { proxyApplyFn } from './proxy-apply.js';
+import { registerScriptlet } from './base.js';
+import { safeSelf } from './safe-self.js';
+
+/******************************************************************************/
+
+function objectFindOwnerFn(
+ root,
+ path,
+ prune = false
+) {
+ const safe = safeSelf();
+ let owner = root;
+ let chain = path;
+ for (;;) {
+ if ( typeof owner !== 'object' || owner === null ) { return false; }
+ const pos = chain.indexOf('.');
+ if ( pos === -1 ) {
+ if ( prune === false ) {
+ return safe.Object_hasOwn(owner, chain);
+ }
+ let modified = false;
+ if ( chain === '*' ) {
+ for ( const key in owner ) {
+ if ( safe.Object_hasOwn(owner, key) === false ) { continue; }
+ delete owner[key];
+ modified = true;
+ }
+ } else if ( safe.Object_hasOwn(owner, chain) ) {
+ delete owner[chain];
+ modified = true;
+ }
+ return modified;
+ }
+ const prop = chain.slice(0, pos);
+ const next = chain.slice(pos + 1);
+ let found = false;
+ if ( prop === '[-]' && Array.isArray(owner) ) {
+ let i = owner.length;
+ while ( i-- ) {
+ if ( objectFindOwnerFn(owner[i], next) === false ) { continue; }
+ owner.splice(i, 1);
+ found = true;
+ }
+ return found;
+ }
+ if ( prop === '{-}' && owner instanceof Object ) {
+ for ( const key of Object.keys(owner) ) {
+ if ( objectFindOwnerFn(owner[key], next) === false ) { continue; }
+ delete owner[key];
+ found = true;
+ }
+ return found;
+ }
+ if (
+ prop === '[]' && Array.isArray(owner) ||
+ prop === '{}' && owner instanceof Object ||
+ prop === '*' && owner instanceof Object
+ ) {
+ for ( const key of Object.keys(owner) ) {
+ if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; }
+ found = true;
+ }
+ return found;
+ }
+ if ( safe.Object_hasOwn(owner, prop) === false ) { return false; }
+ owner = owner[prop];
+ chain = chain.slice(pos + 1);
+ }
+}
+registerScriptlet(objectFindOwnerFn, {
+ name: 'object-find-owner.fn',
+ dependencies: [
+ safeSelf,
+ ],
+});
+
+/******************************************************************************/
+
+// 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
+
+export function objectPruneFn(
+ obj,
+ rawPrunePaths,
+ rawNeedlePaths,
+ stackNeedleDetails = { matchAll: true },
+ extraArgs = {}
+) {
+ if ( typeof rawPrunePaths !== 'string' ) { return; }
+ const safe = safeSelf();
+ const prunePaths = rawPrunePaths !== ''
+ ? safe.String_split.call(rawPrunePaths, / +/)
+ : [];
+ const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== ''
+ ? safe.String_split.call(rawNeedlePaths, / +/)
+ : [];
+ if ( stackNeedleDetails.matchAll !== true ) {
+ if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) {
+ return;
+ }
+ }
+ if ( objectPruneFn.mustProcess === undefined ) {
+ objectPruneFn.mustProcess = (root, needlePaths) => {
+ for ( const needlePath of needlePaths ) {
+ if ( objectFindOwnerFn(root, needlePath) === false ) {
+ return false;
+ }
+ }
+ return true;
+ };
+ }
+ if ( prunePaths.length === 0 ) { return; }
+ let outcome = 'nomatch';
+ if ( objectPruneFn.mustProcess(obj, needlePaths) ) {
+ for ( const path of prunePaths ) {
+ if ( objectFindOwnerFn(obj, path, true) ) {
+ outcome = 'match';
+ }
+ }
+ }
+ if ( outcome === 'match' ) { return obj; }
+}
+registerScriptlet(objectPruneFn, {
+ name: 'object-prune.fn',
+ dependencies: [
+ matchesStackTraceFn,
+ objectFindOwnerFn,
+ safeSelf,
+ ],
+});
+
+/******************************************************************************/
+
+function trustedPruneInboundObject(
+ entryPoint = '',
+ argPos = '',
+ rawPrunePaths = '',
+ rawNeedlePaths = ''
+) {
+ if ( entryPoint === '' ) { return; }
+ let context = globalThis;
+ let prop = entryPoint;
+ for (;;) {
+ const pos = prop.indexOf('.');
+ if ( pos === -1 ) { break; }
+ context = context[prop.slice(0, pos)];
+ if ( context instanceof Object === false ) { return; }
+ prop = prop.slice(pos+1);
+ }
+ if ( typeof context[prop] !== 'function' ) { return; }
+ const argIndex = parseInt(argPos);
+ if ( isNaN(argIndex) ) { return; }
+ if ( argIndex < 1 ) { return; }
+ const safe = safeSelf();
+ const extraArgs = safe.getExtraArgs(Array.from(arguments), 4);
+ const needlePaths = [];
+ if ( rawPrunePaths !== '' ) {
+ needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/));
+ }
+ if ( rawNeedlePaths !== '' ) {
+ needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/));
+ }
+ const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true });
+ const mustProcess = root => {
+ for ( const needlePath of needlePaths ) {
+ if ( objectFindOwnerFn(root, needlePath) === false ) {
+ return false;
+ }
+ }
+ return true;
+ };
+ context[prop] = new Proxy(context[prop], {
+ apply: function(target, thisArg, args) {
+ const targetArg = argIndex <= args.length
+ ? args[argIndex-1]
+ : undefined;
+ if ( targetArg instanceof Object && mustProcess(targetArg) ) {
+ let objBefore = targetArg;
+ if ( extraArgs.dontOverwrite ) {
+ try {
+ objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg));
+ } catch {
+ objBefore = undefined;
+ }
+ }
+ if ( objBefore !== undefined ) {
+ const objAfter = objectPruneFn(
+ objBefore,
+ rawPrunePaths,
+ rawNeedlePaths,
+ stackNeedle,
+ extraArgs
+ );
+ args[argIndex-1] = objAfter || objBefore;
+ }
+ }
+ return Reflect.apply(target, thisArg, args);
+ },
+ });
+}
+registerScriptlet(trustedPruneInboundObject, {
+ name: 'trusted-prune-inbound-object.js',
+ requiresTrust: true,
+ dependencies: [
+ objectFindOwnerFn,
+ objectPruneFn,
+ safeSelf,
+ ],
+});
+
+/******************************************************************************/
+
+function trustedPruneOutboundObject(
+ propChain = '',
+ rawPrunePaths = '',
+ rawNeedlePaths = ''
+) {
+ if ( propChain === '' ) { return; }
+ const safe = safeSelf();
+ const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
+ proxyApplyFn(propChain, function(context) {
+ const objBefore = context.reflect();
+ if ( objBefore instanceof Object === false ) { return objBefore; }
+ const objAfter = objectPruneFn(
+ objBefore,
+ rawPrunePaths,
+ rawNeedlePaths,
+ { matchAll: true },
+ extraArgs
+ );
+ return objAfter || objBefore;
+ });
+}
+registerScriptlet(trustedPruneOutboundObject, {
+ name: 'trusted-prune-outbound-object.js',
+ requiresTrust: true,
+ dependencies: [
+ objectPruneFn,
+ proxyApplyFn,
+ safeSelf,
+ ],
+});
+
+/******************************************************************************/