Browse Source

feat: faster wrapper prop resolution (#1532)

tophf 3 years ago
parent
commit
deb78e001e
3 changed files with 27 additions and 8 deletions
  1. 13 2
      src/background/utils/preinject.js
  2. 12 3
      src/injected/web/gm-api-wrapper.js
  3. 2 3
      src/injected/web/index.js

+ 13 - 2
src/background/utils/preinject.js

@@ -38,6 +38,9 @@ const KEY_XHR_INJECT = 'xhrInject';
 const BAD_URL_CHAR = IS_FIREFOX
   ? /[#&',/:;?=+]/g // FF shows `@` fine as ASCII but mangles it as full-width
   : /[#&',/:;?=+@]/g;
+const GRANT_NONE_VARS = `{GM,GM_info,unsafeWindow${
+  IS_FIREFOX ? '' : ',cloneInto,createObjectIn,exportFunction'
+}}`;
 const expose = {};
 let isApplied;
 let injectInto;
@@ -282,9 +285,17 @@ function prepareScript(script) {
   const reqsSlices = reqs ? [].concat(...reqs.map(req => [req, '\n;'])) : [];
   const hasReqs = reqsSlices.length;
   const wrap = !meta.unwrap;
+  const { grant } = meta;
+  const numGrants = grant.length;
+  const grantNone = !numGrants || numGrants === 1 && grant[0] === 'none';
   const injectedCode = [
+    wrap && `window.${dataKey}=function(${
+      // using a shadowed name to avoid scope pollution
+      grantNone ? GRANT_NONE_VARS : 'GM'}${
+      IS_FIREFOX ? `,${dataKey}){try{` : '){'}${
+      grantNone ? '' : 'with(this)with(c)delete c,'
     // hiding module interface from @require'd scripts so they don't mistakenly use it
-    wrap && `window.${dataKey}=function(module,${dataKey}){try{with(module)((define,module,exports)=>{`,
+    }((${grantNone ? 'define,module,exports' : ''})=>{`,
     ...reqsSlices,
     // adding a nested IIFE to support 'use strict' in the code when there are @requires
     hasReqs && wrap && '(()=>{',
@@ -292,7 +303,7 @@ function prepareScript(script) {
     // adding a new line in case the code ends with a line comment
     !code.endsWith('\n') && '\n',
     hasReqs && wrap && '})()',
-    wrap && `})()}catch(e){${dataKey}(e)}}`,
+    wrap && `})()${IS_FIREFOX ? `}catch(e){${dataKey}(e)}` : ''}}`,
     // 0 at the end to suppress errors about non-cloneable result of executeScript in FF
     IS_FIREFOX && ';0',
     // Firefox lists .user.js among our own content scripts so a space at start will group them

+ 12 - 3
src/injected/web/gm-api-wrapper.js

@@ -29,7 +29,10 @@ export function makeGmApiWrapper(script) {
   // Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
   const { meta } = script;
   const grant = meta.grant;
-  if (grant.length === 1 && grant[0] === 'none') {
+  let wrapper;
+  let numGrants = grant.length;
+  if (numGrants === 1 && grant[0] === 'none') {
+    numGrants = 0;
     grant.length = 0;
   }
   const { id } = script.props;
@@ -62,7 +65,7 @@ export function makeGmApiWrapper(script) {
   if (grant::indexOf(WINDOW_FOCUS) >= 0) {
     gm.focus = vmOwnFunc(() => bridge.post('TabFocus', 0, context));
   }
-  if (!gmApi && grant.length) gmApi = makeGmApi();
+  if (!gmApi && numGrants) gmApi = makeGmApi();
   grant::forEach((name) => {
     const gm4name = name::slice(0, 3) === 'GM.' && name::slice(3);
     const fn = gmApi[gm4name ? `GM_${GM4_ALIAS[gm4name] || gm4name}` : name];
@@ -74,7 +77,13 @@ export function makeGmApiWrapper(script) {
       }
     }
   });
-  return grant.length ? makeGlobalWrapper(gm) : gm;
+  if (numGrants) {
+    wrapper = makeGlobalWrapper(gm);
+    /* Exposing the fast cache of resolved properties,
+     * using a name that'll never be added to the web platform */
+    gm.c = gm;
+  }
+  return { gm, wrapper };
 }
 
 function makeGmInfo(script, resources) {

+ 2 - 3
src/injected/web/index.js

@@ -117,9 +117,8 @@ async function onCodeSet(item, fn) {
   }
   const run = () => {
     bridge.post('Run', item.props.id, item);
-    const wrapper = makeGmApiWrapper(item);
-    const thisArg = item.meta.grant.length ? wrapper : global;
-    thisArg::fn(wrapper, logging.error);
+    const { gm, wrapper } = makeGmApiWrapper(item);
+    (wrapper || global)::fn(gm, logging.error);
   };
   const el = document::getCurrentScript();
   const wait = waiters?.[stage];