Browse Source

fix: safe-guard internal events

+ send the feedback earlier and save getReadyState immediately when running before documentElement appears
tophf 4 years ago
parent
commit
7bc3193837
2 changed files with 19 additions and 17 deletions
  1. 17 16
      src/injected/content/inject.js
  2. 2 1
      src/injected/utils/index.js

+ 17 - 16
src/injected/content/inject.js

@@ -77,8 +77,6 @@ export function injectPageSandbox() {
  * @param {boolean} isXml
  */
 export async function injectScripts(data, isXml) {
-  // eslint-disable-next-line prefer-rest-params
-  if (!elemByTag('*')) return onElement('*', injectScripts, ...arguments);
   const { hasMore, info } = data;
   pageInjectable = isXml ? false : null;
   realms = {
@@ -120,20 +118,23 @@ export async function injectScripts(data, isXml) {
   if (hasInvoker) {
     setupContentInvoker();
   }
-  injectAll('start');
-  const onBody = (pgLists.body[0] || contLists.body[0])
-    && onElement('body', injectAll, 'body');
-  // document-end, -idle
-  if (hasMore) {
-    data = await moreData;
-    if (data) await injectDelayedScripts(data, getReadyState, hasInvoker);
-  }
-  if (onBody) {
-    await onBody;
-  }
-  realms = null;
-  pgLists = null;
-  contLists = null;
+  // Using a callback to avoid a microtask tick when the root element exists or appears.
+  onElement('*', async () => {
+    injectAll('start');
+    const onBody = (pgLists.body[0] || contLists.body[0])
+      && onElement('body', injectAll, 'body');
+    // document-end, -idle
+    if (hasMore) {
+      data = await moreData;
+      if (data) await injectDelayedScripts(data, getReadyState, hasInvoker);
+    }
+    if (onBody) {
+      await onBody;
+    }
+    realms = null;
+    pgLists = null;
+    contLists = null;
+  });
 }
 
 async function injectDelayedScripts({ info, scripts }, getReadyState, hasInvoker) {

+ 2 - 1
src/injected/utils/index.js

@@ -1,6 +1,7 @@
 export function bindEvents(srcId, destId, handle, cloneInto) {
-  document::addEventListener(srcId, e => handle(e.detail));
+  const getDetail = describeProperty(CustomEvent.prototype, 'detail').get;
   const pageContext = cloneInto && document.defaultView;
+  document::addEventListener(srcId, e => handle(e::getDetail()));
   return (cmd, params) => {
     const data = { cmd, data: params };
     const detail = cloneInto ? cloneInto(data, pageContext) : data;