Explorar el Código

refactor: extract ensureNestedProp + cosmetics

tophf hace 4 años
padre
commit
3b1394248e

+ 5 - 3
scripts/webpack.conf.js

@@ -81,8 +81,6 @@ const defsObj = {
   'process.env.HANDSHAKE_ID': HANDSHAKE_ID,
   'process.env.HANDSHAKE_ACK': '1',
 };
-const definitions = new webpack.DefinePlugin(defsObj);
-
 // avoid running webpack bootstrap in a potentially hacked environment
 // after documentElement was replaced which triggered reinjection of content scripts
 const skipReinjectionHeader = `if (window['${INIT_FUNC_NAME}'] !== 1)`;
@@ -90,7 +88,11 @@ const skipReinjectionHeader = `if (window['${INIT_FUNC_NAME}'] !== 1)`;
 const modify = (page, entry, init) => modifyWebpackConfig(
   (config) => {
     Object.assign(config, WEBPACK_OPTS);
-    config.plugins.push(definitions);
+    config.plugins.push(new webpack.DefinePlugin({
+      ...defsObj,
+      // Conditional compilation to remove unsafe and unused stuff from `injected`
+      'process.env.IS_INJECTED': JSON.stringify(/injected/.test(page) && page),
+    }));
     config.optimization.minimizer.find((m, i, arr) => (
       m.constructor.name === 'TerserPlugin' && arr.splice(i, 1)
     ));

+ 3 - 5
src/common/index.js

@@ -58,8 +58,6 @@ const COMMANDS_WITH_SRC = [
   'SetPopup',
 */
 ];
-// Used in safe context
-// eslint-disable-next-line no-restricted-syntax
 const getBgPage = () => browser.extension.getBackgroundPage?.();
 
 /**
@@ -85,11 +83,11 @@ export function sendTabCmd(tabId, cmd, data, options) {
 }
 
 // Used by `injected`
-// ignoreError is always `true` when sending from the background script because it's a broadcast
-export function sendMessage(payload, { retry, ignoreError } = {}) {
+export function sendMessage(payload, { retry } = {}) {
   if (retry) return sendMessageRetry(payload);
   let promise = browser.runtime.sendMessage(payload);
-  if (ignoreError || window === getBgPage()) {
+  // Ignoring errors when sending from the background script because it's a broadcast
+  if (!process.env.IS_INJECTED && window === getBgPage()) {
     promise = promise.catch(noop);
   }
   return promise;

+ 1 - 1
src/injected/content/bridge.js

@@ -13,7 +13,7 @@ const assignHandlers = (dest, src, force) => {
   }
 };
 const allowCmd = (cmd, dataKey) => {
-  (allowed[cmd] || (allowed[cmd] = createNullObj()))[dataKey] = true;
+  ensureNestedProp(allowed, cmd, dataKey, true);
 };
 const XHR = ['HttpRequest', 'AbortRequest'];
 const ADD_ELEMENT = ['AddElement'];

+ 1 - 2
src/injected/content/gm-api-content.js

@@ -45,8 +45,7 @@ bridge.addHandlers({
 
   RegisterMenu({ id, cap }) {
     if (IS_TOP) {
-      const commandMap = menus[id] || (menus[id] = createNullObj());
-      commandMap[cap] = 1;
+      ensureNestedProp(menus, id, cap, 1);
       sendSetPopup(true);
     }
   },

+ 4 - 3
src/injected/content/safe-globals-content.js

@@ -8,7 +8,7 @@
 export const {
   Blob: BlobSafe,
   CustomEvent: CustomEventSafe,
-  Error: ErrorSafe,
+  Error, // for #/common e.g. in sendMessage
   MouseEvent: MouseEventSafe,
   Object, // for minification and guarding webpack Object(import) calls
   Promise: PromiseSafe,
@@ -19,13 +19,14 @@ export const {
   dispatchEvent: fire,
   removeEventListener: off,
 } = global;
+export const ErrorSafe = Error;
 export const ResponseProto = Response[PROTO];
 export const { hasOwnProperty, toString: objectToString } = {};
 export const { apply, call } = hasOwnProperty;
 export const safeCall = call.bind(call);
 export const { forEach, includes, push } = [];
 export const { createElementNS, getElementsByTagName } = document;
-export const { then } = Promise[PROTO];
+export const { then } = PromiseSafe[PROTO];
 export const { charCodeAt, indexOf: stringIndexOf, slice } = '';
 export const { append, appendChild, attachShadow, remove, setAttribute } = Element[PROTO];
 export const {
@@ -41,5 +42,5 @@ export const { decode: tdDecode } = TextDecoderSafe[PROTO];
 export const { get: getHref } = describeProperty(HTMLAnchorElement[PROTO], 'href');
 export const getDetail = describeProperty(CustomEventSafe[PROTO], 'detail').get;
 export const getRelatedTarget = describeProperty(MouseEventSafe[PROTO], 'relatedTarget').get;
-export const logging = assign({ __proto__: null }, console);
+export const logging = assign(createNullObj(), console);
 export const IS_FIREFOX = !global.chrome.app;

+ 11 - 0
src/injected/safe-globals-injected.js

@@ -49,6 +49,16 @@ export const vmOwnFuncToString = () => '[Violentmonkey property]';
 /** Using __proto__ because Object.create(null) may be spoofed */
 export const createNullObj = () => ({ __proto__: null });
 
+export const ensureNestedProp = (obj, bucketId, key, defaultValue) => {
+  const bucket = obj[bucketId] || (
+    obj[bucketId] = createNullObj()
+  );
+  const val = bucket[key] ?? (
+    bucket[key] = (defaultValue ?? createNullObj())
+  );
+  return val;
+};
+
 export const promiseResolve = () => (async () => {})();
 
 export const vmOwnFunc = (func, toString) => (
@@ -76,6 +86,7 @@ export const log = (level, ...args) => {
 
 /**
  * Picks into `this`
+ * WARNING! `this` must use __proto__:null or already have own properties on the picked keys.
  * @param {Object} obj
  * @param {string[]} keys
  * @returns {Object} same object as `this`

+ 1 - 2
src/injected/web/gm-api.js

@@ -48,8 +48,7 @@ export function makeGmApi() {
     GM_addValueChangeListener(key, fn) {
       if (!isString(key)) key = `${key}`;
       if (!isFunction(fn)) return;
-      const keyHooks = changeHooks[this.id] || (changeHooks[this.id] = createNullObj());
-      const hooks = keyHooks[key] || (keyHooks[key] = createNullObj());
+      const hooks = ensureNestedProp(changeHooks, this.id, key);
       const i = objectValues(hooks)::indexOf(fn);
       let listenerId = i >= 0 && objectKeys(hooks)[i];
       if (!listenerId) {