summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js')
-rw-r--r--data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js287
1 files changed, 287 insertions, 0 deletions
diff --git a/data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js b/data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js
new file mode 100644
index 0000000..9ee9859
--- /dev/null
+++ b/data/extensions/uBlock0@raymondhill.net/js/resources/set-constant.js
@@ -0,0 +1,287 @@
+/*******************************************************************************
+
+ 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 { registerScriptlet } from './base.js';
+import { runAt } from './run-at.js';
+import { safeSelf } from './safe-self.js';
+
+/******************************************************************************/
+
+export function validateConstantFn(trusted, raw, extraArgs = {}) {
+ const safe = safeSelf();
+ let value;
+ if ( raw === 'undefined' ) {
+ value = undefined;
+ } else if ( raw === 'false' ) {
+ value = false;
+ } else if ( raw === 'true' ) {
+ value = true;
+ } else if ( raw === 'null' ) {
+ value = null;
+ } else if ( raw === "''" || raw === '' ) {
+ value = '';
+ } else if ( raw === '[]' || raw === 'emptyArr' ) {
+ value = [];
+ } else if ( raw === '{}' || raw === 'emptyObj' ) {
+ value = {};
+ } else if ( raw === 'noopFunc' ) {
+ value = function(){};
+ } else if ( raw === 'trueFunc' ) {
+ value = function(){ return true; };
+ } else if ( raw === 'falseFunc' ) {
+ value = function(){ return false; };
+ } else if ( raw === 'throwFunc' ) {
+ value = function(){ throw ''; };
+ } else if ( /^-?\d+$/.test(raw) ) {
+ value = parseInt(raw);
+ if ( isNaN(raw) ) { return; }
+ if ( Math.abs(raw) > 0x7FFF ) { return; }
+ } else if ( trusted ) {
+ if ( raw.startsWith('json:') ) {
+ try { value = safe.JSON_parse(raw.slice(5)); } catch { return; }
+ } else if ( raw.startsWith('{') && raw.endsWith('}') ) {
+ try { value = safe.JSON_parse(raw).value; } catch { return; }
+ }
+ } else {
+ return;
+ }
+ if ( extraArgs.as !== undefined ) {
+ if ( extraArgs.as === 'function' ) {
+ return ( ) => value;
+ } else if ( extraArgs.as === 'callback' ) {
+ return ( ) => (( ) => value);
+ } else if ( extraArgs.as === 'resolved' ) {
+ return Promise.resolve(value);
+ } else if ( extraArgs.as === 'rejected' ) {
+ return Promise.reject(value);
+ }
+ }
+ return value;
+}
+registerScriptlet(validateConstantFn, {
+ name: 'validate-constant.fn',
+ dependencies: [
+ safeSelf,
+ ],
+});
+
+/******************************************************************************/
+
+export function setConstantFn(
+ trusted = false,
+ chain = '',
+ rawValue = ''
+) {
+ if ( chain === '' ) { return; }
+ const safe = safeSelf();
+ const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue);
+ const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
+ function setConstant(chain, rawValue) {
+ const trappedProp = (( ) => {
+ const pos = chain.lastIndexOf('.');
+ if ( pos === -1 ) { return chain; }
+ return chain.slice(pos+1);
+ })();
+ const cloakFunc = fn => {
+ safe.Object_defineProperty(fn, 'name', { value: trappedProp });
+ return new Proxy(fn, {
+ defineProperty(target, prop) {
+ if ( prop !== 'toString' ) {
+ return Reflect.defineProperty(...arguments);
+ }
+ return true;
+ },
+ deleteProperty(target, prop) {
+ if ( prop !== 'toString' ) {
+ return Reflect.deleteProperty(...arguments);
+ }
+ return true;
+ },
+ get(target, prop) {
+ if ( prop === 'toString' ) {
+ return function() {
+ return `function ${trappedProp}() { [native code] }`;
+ }.bind(null);
+ }
+ return Reflect.get(...arguments);
+ },
+ });
+ };
+ if ( trappedProp === '' ) { return; }
+ const thisScript = document.currentScript;
+ let normalValue = validateConstantFn(trusted, rawValue, extraArgs);
+ if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) {
+ normalValue = cloakFunc(normalValue);
+ }
+ let aborted = false;
+ const mustAbort = function(v) {
+ if ( trusted ) { return false; }
+ if ( aborted ) { return true; }
+ aborted =
+ (v !== undefined && v !== null) &&
+ (normalValue !== undefined && normalValue !== null) &&
+ (typeof v !== typeof normalValue);
+ if ( aborted ) {
+ safe.uboLog(logPrefix, `Aborted because value set to ${v}`);
+ }
+ return aborted;
+ };
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/156
+ // Support multiple trappers for the same property.
+ const trapProp = function(owner, prop, configurable, handler) {
+ if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; }
+ const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop);
+ let prevGetter, prevSetter;
+ if ( odesc instanceof safe.Object ) {
+ owner[prop] = normalValue;
+ if ( odesc.get instanceof Function ) {
+ prevGetter = odesc.get;
+ }
+ if ( odesc.set instanceof Function ) {
+ prevSetter = odesc.set;
+ }
+ }
+ try {
+ safe.Object_defineProperty(owner, prop, {
+ configurable,
+ get() {
+ if ( prevGetter !== undefined ) {
+ prevGetter();
+ }
+ return handler.getter();
+ },
+ set(a) {
+ if ( prevSetter !== undefined ) {
+ prevSetter(a);
+ }
+ handler.setter(a);
+ }
+ });
+ safe.uboLog(logPrefix, 'Trap installed');
+ } catch(ex) {
+ safe.uboErr(logPrefix, ex);
+ }
+ };
+ const trapChain = function(owner, chain) {
+ const pos = chain.indexOf('.');
+ if ( pos === -1 ) {
+ trapProp(owner, chain, false, {
+ v: undefined,
+ init: function(v) {
+ if ( mustAbort(v) ) { return false; }
+ this.v = v;
+ return true;
+ },
+ getter: function() {
+ if ( document.currentScript === thisScript ) {
+ return this.v;
+ }
+ safe.uboLog(logPrefix, 'Property read');
+ return normalValue;
+ },
+ setter: function(a) {
+ if ( mustAbort(a) === false ) { return; }
+ normalValue = a;
+ }
+ });
+ return;
+ }
+ const prop = chain.slice(0, pos);
+ const v = owner[prop];
+ chain = chain.slice(pos + 1);
+ if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) {
+ trapChain(v, chain);
+ return;
+ }
+ trapProp(owner, prop, true, {
+ v: undefined,
+ init: function(v) {
+ this.v = v;
+ return true;
+ },
+ getter: function() {
+ return this.v;
+ },
+ setter: function(a) {
+ this.v = a;
+ if ( a instanceof safe.Object ) {
+ trapChain(a, chain);
+ }
+ }
+ });
+ };
+ trapChain(window, chain);
+ }
+ runAt(( ) => {
+ setConstant(chain, rawValue);
+ }, extraArgs.runAt);
+}
+registerScriptlet(setConstantFn, {
+ name: 'set-constant.fn',
+ dependencies: [
+ runAt,
+ safeSelf,
+ validateConstantFn,
+ ],
+});
+
+/******************************************************************************/
+
+export function setConstant(
+ ...args
+) {
+ setConstantFn(false, ...args);
+}
+registerScriptlet(setConstant, {
+ name: 'set-constant.js',
+ aliases: [
+ 'set.js',
+ ],
+ dependencies: [
+ setConstantFn,
+ ],
+});
+
+/*******************************************************************************
+ *
+ * trusted-set-constant.js
+ *
+ * Set specified property to any value. This is essentially the same as
+ * set-constant.js, but with no restriction as to which values can be used.
+ *
+ **/
+
+export function trustedSetConstant(
+ ...args
+) {
+ setConstantFn(true, ...args);
+}
+registerScriptlet(trustedSetConstant, {
+ name: 'trusted-set-constant.js',
+ requiresTrust: true,
+ aliases: [
+ 'trusted-set.js',
+ ],
+ dependencies: [
+ setConstantFn,
+ ],
+});