Browse Source

fix: multiple content scripts is injected

Ahmad Kholid 3 years ago
parent
commit
3f575461a4

+ 9 - 45
src/background/workflow-engine/blocks-handler.js

@@ -2,10 +2,11 @@
 import browser from 'webextension-polyfill';
 import { objectHasKey, fileSaver, isObject } from '@/utils/helper';
 import { tasks } from '@/utils/shared';
+import { executeWebhook } from '@/utils/webhookUtil';
+import executeContentScript from '@/utils/execute-content-script';
 import dataExporter, { generateJSON } from '@/utils/data-exporter';
 import compareBlockValue from '@/utils/compare-block-value';
 import errorMessage from './error-message';
-import { executeWebhook } from '@/utils/webhookUtil';
 
 function getBlockConnection(block, index = 1) {
   const blockId = block.outputs[`output_${index}`]?.connections[0]?.node;
@@ -34,45 +35,6 @@ function generateBlockError(block, code) {
 
   return error;
 }
-function executeContentScript(tabId) {
-  return new Promise((resolve, reject) => {
-    let frameTimeout;
-    let timeout;
-    const frames = {};
-
-    const onMessageListener = (_, sender) => {
-      if (sender.frameId !== 0) frames[sender.url] = sender.frameId;
-
-      clearTimeout(frameTimeout);
-      frameTimeout = setTimeout(() => {
-        clearTimeout(timeout);
-        browser.runtime.onMessage.removeListener(onMessageListener);
-        resolve(frames);
-      }, 250);
-    };
-
-    browser.tabs
-      .executeScript(tabId, {
-        file: './contentScript.bundle.js',
-        allFrames: true,
-      })
-      .then(() => {
-        browser.tabs.sendMessage(tabId, {
-          type: 'give-me-the-frame-id',
-        });
-        browser.runtime.onMessage.addListener(onMessageListener);
-
-        timeout = setTimeout(() => {
-          clearTimeout(frameTimeout);
-          resolve(frames);
-        }, 5000);
-      })
-      .catch((error) => {
-        console.error(error);
-        reject(error);
-      });
-  });
-}
 
 export async function closeTab(block) {
   const nextBlockId = getBlockConnection(block);
@@ -106,7 +68,7 @@ export async function trigger(block) {
   const nextBlockId = getBlockConnection(block);
   try {
     if (block.data.type === 'visit-web' && this.tabId) {
-      this.frames = executeContentScript(this.tabId);
+      this.frames = executeContentScript(this.tabId, 'trigger');
     }
 
     return { nextBlockId, data: '' };
@@ -246,16 +208,18 @@ export async function newWindow(block) {
 }
 
 function tabUpdatedListener(tab) {
-  return new Promise((resolve, reject) => {
+  return new Promise((resolve) => {
     this._listener({
       name: 'tab-updated',
       id: tab.id,
-      callback: (tabId, changeInfo, deleteListener) => {
+      callback: async (tabId, changeInfo, deleteListener) => {
         if (changeInfo.status !== 'complete') return;
 
+        const frames = await executeContentScript(tabId, 'newtab');
+
         deleteListener();
 
-        executeContentScript(tabId).then(resolve, reject);
+        resolve(frames);
       },
     });
   });
@@ -335,7 +299,7 @@ export async function activeTab(block) {
       currentWindow: true,
     });
 
-    this.frames = await executeContentScript(tab.id);
+    this.frames = await executeContentScript(tab.id, 'activetab');
 
     this.frameId = 0;
     this.tabId = tab.id;

+ 4 - 6
src/background/workflow-engine/index.js

@@ -7,6 +7,7 @@ import errorMessage from './error-message';
 import referenceData from '@/utils/reference-data';
 import workflowState from '../workflow-state';
 import * as blocksHandler from './blocks-handler';
+import executeContentScript from '@/utils/execute-content-script';
 
 let reloadTimeout;
 
@@ -41,13 +42,10 @@ function tabUpdatedHandler(tabId, changeInfo) {
       clearTimeout(reloadTimeout);
       reloadTimeout = null;
 
-      browser.tabs
-        .executeScript(tabId, {
-          file: './contentScript.bundle.js',
-        })
-        .then(() => {
+      executeContentScript(tabId, 'update tab')
+        .then((frames) => {
           this.tabId = tabId;
-
+          this.frames = frames;
           this.isPaused = false;
         })
         .catch((error) => {

+ 2 - 0
src/content/blocks-handler.js

@@ -233,6 +233,8 @@ export function link(block) {
       return;
     }
 
+    markElement(element, block);
+
     const url = element.href;
 
     if (url) window.location.href = url;

+ 23 - 21
src/content/index.js

@@ -3,28 +3,30 @@ import { toCamelCase } from '@/utils/helper';
 import elementSelector from './element-selector';
 import * as blocksHandler from './blocks-handler';
 
-browser.runtime.onMessage.addListener((data) => {
-  if (data.isBlock) {
-    const handler = blocksHandler[toCamelCase(data.name)];
+(() => {
+  browser.runtime.onMessage.addListener((data) => {
+    if (data.isBlock) {
+      const handler = blocksHandler[toCamelCase(data.name)];
 
-    if (handler) {
-      return handler(data);
-    }
-    console.error(`"${data.name}" doesn't have a handler`);
-
-    return Promise.resolve('');
-  }
+      if (handler) {
+        return handler(data);
+      }
+      console.error(`"${data.name}" doesn't have a handler`);
 
-  return new Promise((resolve) => {
-    if (data.type === 'content-script-exists') {
-      resolve(true);
-    } else if (data.type === 'select-element') {
-      elementSelector();
-      resolve(true);
-    } else if (data.type === 'give-me-the-frame-id') {
-      browser.runtime.sendMessage({
-        type: 'this-is-the-frame-id',
-      });
+      return Promise.resolve('');
     }
+
+    return new Promise((resolve) => {
+      if (data.type === 'content-script-exists') {
+        resolve(true);
+      } else if (data.type === 'select-element') {
+        elementSelector();
+        resolve(true);
+      } else if (data.type === 'give-me-the-frame-id') {
+        browser.runtime.sendMessage({
+          type: 'this-is-the-frame-id',
+        });
+      }
+    });
   });
-});
+})();

+ 0 - 2
src/content/shortcut.js

@@ -17,8 +17,6 @@ function getTriggerBlock(workflow) {
   return trigger;
 }
 
-window.__halo = 'halo';
-
 (async () => {
   try {
     const { shortcuts, workflows } = await browser.storage.local.get([

+ 62 - 0
src/utils/execute-content-script.js

@@ -0,0 +1,62 @@
+import browser from 'webextension-polyfill';
+
+function getFrames(tabId) {
+  return new Promise((resolve) => {
+    const frames = {};
+    let frameTimeout;
+    let timeout;
+
+    const onMessageListener = (_, sender) => {
+      if (sender.frameId !== 0) frames[sender.url] = sender.frameId;
+
+      clearTimeout(frameTimeout);
+      frameTimeout = setTimeout(() => {
+        clearTimeout(timeout);
+        browser.runtime.onMessage.removeListener(onMessageListener);
+        resolve(frames);
+      }, 250);
+    };
+
+    browser.tabs.sendMessage(tabId, {
+      type: 'give-me-the-frame-id',
+    });
+    browser.runtime.onMessage.addListener(onMessageListener);
+
+    timeout = setTimeout(() => {
+      clearTimeout(frameTimeout);
+      resolve(frames);
+    }, 5000);
+  });
+}
+
+async function contentScriptExist(tabId) {
+  try {
+    await browser.tabs.sendMessage(tabId, { type: 'content-script-exists' });
+
+    return true;
+  } catch (error) {
+    return false;
+  }
+}
+
+export default async function (tabId) {
+  try {
+    const isScriptExists = await contentScriptExist(tabId);
+
+    if (!isScriptExists) {
+      await browser.tabs.executeScript(tabId, {
+        file: './contentScript.bundle.js',
+        allFrames: true,
+      });
+    }
+
+    await new Promise((resolve) => setTimeout(resolve, 1000));
+
+    const frames = await getFrames(tabId);
+
+    return frames;
+  } catch (error) {
+    console.error(error);
+    return {};
+  }
+}

+ 3 - 0
src/utils/reference-data.js

@@ -41,6 +41,9 @@ export default function (block, data) {
       replacedBlock.data[blockDataKey],
       (match) => {
         const key = match.slice(2, -2).replace(/\s/g, '');
+
+        if (!key) return '';
+
         const { dataKey, path } = parseKey(key);
 
         if (