Browse Source

refactor: reduce `injected` file size

* declare the super frequently used names first to ensure their minified name is 1 char
* inline the unnecessary IS_TOP, location
tophf 2 years ago
parent
commit
eb4a8d0ab6

+ 5 - 1
.eslintrc.js

@@ -96,11 +96,15 @@ module.exports = {
 function makeOverrides() {
   /* Note that `injected` uses several more `common` files indirectly, but we check just these
    * two automatically because they are trivial by design and must always pass the check */
-  const GLOBALS_SHARED = getGlobals('*');
+  const GLOBALS_SHARED = {
+    ...getGlobals('*'),
+    global: false, // defined in webpack.conf
+  };
   const GLOBALS_INJECTED = {
     ...getGlobals('injected'),
     PAGE_MODE_HANDSHAKE: false,
     VAULT_ID: false,
+    global: false, // defined in webpack.conf
   };
   function getGlobals(path) {
     const res = {};

+ 5 - 1
scripts/webpack.conf.js

@@ -92,9 +92,13 @@ const defsObj = {
   'process.env.DEV': JSON.stringify(!isProd),
   'process.env.TEST': JSON.stringify(process.env.BABEL_ENV === 'test'),
 };
+/** `window` and `document` are unforgeable so we extract them primarily to improve minification.
+ * The document's value can change only in about:blank but we don't inject there. */
+const setUnforgeablesHeader = 'const global = this, { document, window } = global;';
 // avoid running webpack bootstrap in a potentially hacked environment
 // after documentElement was replaced which triggered reinjection of content scripts
 const skipReinjectionHeader = `{
+  ${setUnforgeablesHeader}
   const INIT_FUNC_NAME = '${INIT_FUNC_NAME}';
   if (window[INIT_FUNC_NAME] !== 1)`;
 
@@ -133,7 +137,7 @@ const modify = (page, entry, init) => modifyWebpackConfig(
 module.exports = Promise.all([
   modify((config) => {
     addWrapperWithGlobals('common', config, defsObj, getGlobals => ({
-      header: () => `{ ${getGlobals()}`,
+      header: () => `{ ${setUnforgeablesHeader} ${getGlobals()}`,
       footer: '}',
       test: /^(?!injected|public).*\.js$/,
     }));

+ 0 - 7
src/common/safe-globals-shared.js

@@ -2,16 +2,9 @@
 
 /**
  * This file is used first by the entire `src` including `injected`.
- * `global` is used instead of WebPack's polyfill which we disable in webpack.conf.js.
  * Not exporting NodeJS built-in globals as this file is imported in the test scripts.
  */
 
-const global = (function _() {
-  return process.env.TEST ? globalThis : this; // eslint-disable-line no-undef
-}());
-/** These two are unforgeable so we extract them primarily to improve minification.
- * The document's value can change only in about:blank but we don't inject there. */
-const { document, window } = global;
 export const VIOLENTMONKEY = 'Violentmonkey';
 export const AUTO = 'auto';
 export const CONTENT = 'content';

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

@@ -40,14 +40,14 @@ addHandlers({
   },
 
   RegisterMenu({ id, cap }) {
-    if (IS_TOP) {
+    if (window === top) {
       ensureNestedProp(menus, id, cap, 1);
       sendSetPopup(true);
     }
   },
 
   UnregisterMenu({ id, cap }) {
-    if (IS_TOP) {
+    if (window === top) {
       delete menus[id]?.[cap];
       sendSetPopup(true);
     }

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

@@ -54,7 +54,7 @@ export function injectPageSandbox({ [kSessionId]: sessionId }) {
   } else {
     setOwnProp(global, VAULT_WRITER, tellBridgeToWriteVault, false);
   }
-  if (useOpener(opener) || useOpener(!IS_TOP && parent)) {
+  if (useOpener(opener) || useOpener(window !== top && parent)) {
     startHandshake();
   } else {
     /* Sites can do window.open(sameOriginUrl,'iframeNameOrNewWindowName').opener=null, spoof JS

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

@@ -3,8 +3,11 @@
 /**
  * `safeCall` is used by our modified babel-plugin-safe-bind.js.
  * `export` is stripped in the final output and is only used for our NodeJS test scripts.
+ * To ensure the minified name is 1 char we declare the super frequently used names first.
  */
 
+export const { apply: safeApply } = Reflect;
+export const safeCall = safeApply.call.bind(safeApply.call); // ~75 "::" calls
 export const {
   Blob: SafeBlob,
   CustomEvent: SafeCustomEvent,
@@ -21,11 +24,11 @@ export const {
   dispatchEvent: fire,
   removeEventListener: off,
 } = global;
+// eslint-disable-next-line no-restricted-syntax
+export const createNullObj = Object.create.bind(Object, null); // 25 calls
 export const SafeError = Error;
 export const ResponseProto = SafeResponse[PROTO];
-export const { apply: safeApply } = Reflect;
 export const hasOwnProperty = safeApply.call.bind(({}).hasOwnProperty);
-export const safeCall = safeApply.call.bind(safeApply.call);
 export const { forEach, includes } = []; // `push` is unsafe as it may call a setter; use safePush()
 export const { getElementsByTagName } = document;
 export const { then } = SafePromise[PROTO];
@@ -39,8 +42,6 @@ export const {
   getPrototypeOf,
   keys: objectKeys,
 } = Object;
-// eslint-disable-next-line no-restricted-syntax
-export const createNullObj = Object.create.bind(Object, null);
 export const { random: mathRandom } = Math;
 export const { toStringTag: toStringTagSym } = Symbol; // used by ProtectWebpackBootstrapPlugin
 export const { stopImmediatePropagation } = Event[PROTO];

+ 1 - 1
src/injected/index.js

@@ -5,7 +5,7 @@ import './content';
 
 // Script installation in Firefox as it does not support `onBeforeRequest` for `file:`
 // Using pathname and a case-sensitive check to match webRequest `urls` filter behavior
-if (IS_FIREFOX && IS_TOP
+if (IS_FIREFOX && window === top
 && location.protocol === 'file:'
 && location.pathname.endsWith('.user.js')
 && document.contentType === 'application/x-javascript' // FF uses this for file: scheme

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

@@ -6,9 +6,7 @@
  * WARNING! Don't use exported functions from @/common anywhere in injected!
  */
 
-export const { location } = global;
 export const PROTO = 'prototype';
-export const IS_TOP = top === window;
 export const CALLBACK_ID = '__CBID';
 export const kFileName = 'fileName';
 

+ 1 - 1
src/injected/web/gm-global-wrapper.js

@@ -146,7 +146,7 @@ function proxyDescribe(local, name, wrapper, events) {
     || name === 'window'
     || name === 'self'
     || name === 'globalThis'
-    || name === 'top' && IS_TOP // `top` is unforgeable
+    || name === 'top' && window === top // `top` is unforgeable
     || name === 'parent' && window === window::getWindowParent();
   if (isWindow) {
     desc.value = wrapper;

+ 7 - 7
src/injected/web/safe-globals.js

@@ -4,10 +4,15 @@
 /**
  * `safeCall` is used by our modified babel-plugin-safe-bind.js.
  * `export` is stripped in the final output and is only used for our NodeJS test scripts.
+ * To ensure the minified name is 1 char we declare the super frequently used names first.
  */
 
-export const cloneInto = PAGE_MODE_HANDSHAKE ? null : global.cloneInto;
 export let
+  safeCall, // ~50 "::" calls
+  createNullObj, // ~25 calls
+  forEach, // ~15 calls
+  safeApply,
+  safeBind,
   // window
   SafeCustomEvent,
   SafeDOMParser,
@@ -38,22 +43,16 @@ export let
    * its length or from an unassigned `hole`. */
   concat,
   filter,
-  forEach,
   indexOf,
   // Element.prototype
   remove,
   // String.prototype
   slice,
-  // safeCall
-  safeApply,
-  safeBind,
-  safeCall,
   // various values
   builtinGlobals,
   // various methods
   URLToString,
   arrayIsArray,
-  createNullObj,
   createObjectURL,
   formDataEntries,
   hasOwnProperty,
@@ -71,6 +70,7 @@ export let
   getDetail, // CustomEvent
   getRelatedTarget; // MouseEvent
 
+export const cloneInto = PAGE_MODE_HANDSHAKE ? null : global.cloneInto;
 /**
  * VAULT consists of the parent's safe globals to protect our communications/globals
  * from a page that creates an iframe with src = location and modifies its contents