Bläddra i källkod

refactor: switch frame handler

Ahmad Kholid 3 år sedan
förälder
incheckning
2fe2dd6916

+ 1 - 1
src/background/workflow-engine/blocks-handler/handler-active-tab.js

@@ -22,7 +22,7 @@ async function activeTab(block) {
       currentWindow: true,
     });
 
-    this.frames = await executeContentScript(tab.id, 'activetab');
+    this.frames = await executeContentScript(tab.id);
 
     this.frameId = 0;
     this.tabId = tab.id;

+ 5 - 1
src/background/workflow-engine/blocks-handler/handler-interaction-block.js

@@ -3,7 +3,11 @@ import { getBlockConnection } from '../helper';
 
 async function interactionHandler(block, { refData }) {
   const nextBlockId = getBlockConnection(block);
-  const messagePayload = { ...block, refData };
+  const messagePayload = {
+    ...block,
+    refData,
+    frameSelector: this.frameSelector,
+  };
 
   try {
     const data = await this._sendMessageToTab(messagePayload, {

+ 1 - 1
src/background/workflow-engine/blocks-handler/handler-new-tab.js

@@ -10,7 +10,7 @@ function tabUpdatedListener(tab) {
       callback: async (tabId, changeInfo, deleteListener) => {
         if (changeInfo.status !== 'complete') return;
 
-        const frames = await executeContentScript(tabId, 'newtab');
+        const frames = await executeContentScript(tabId);
 
         deleteListener();
 

+ 22 - 6
src/background/workflow-engine/blocks-handler/handler-switch-to.js

@@ -1,5 +1,6 @@
 import { objectHasKey } from '@/utils/helper';
 import { getBlockConnection } from '../helper';
+import executeContentScript, { getFrames } from '../execute-content-script';
 
 async function switchTo(block) {
   const nextBlockId = getBlockConnection(block);
@@ -8,28 +9,43 @@ async function switchTo(block) {
     if (block.data.windowType === 'main-window') {
       this.frameId = 0;
 
+      delete this.frameSelector;
+
       return {
         data: '',
         nextBlockId,
       };
     }
 
-    const { url } = await this._sendMessageToTab(block, { frameId: 0 });
+    const frames = await getFrames(this.tabId);
+    const { url, isSameOrigin } = await this._sendMessageToTab(block, {
+      frameId: 0,
+    });
+
+    if (isSameOrigin) {
+      this.frameSelector = block.data.selector;
+
+      return {
+        data: block.data.selector,
+        nextBlockId,
+      };
+    }
 
-    if (objectHasKey(this.frames, url)) {
+    if (objectHasKey(frames, url)) {
       this.frameId = this.frames[url];
 
+      await executeContentScript(this.tabId, this.frameId);
+      await new Promise((resolve) => setTimeout(resolve, 1000));
+
       return {
         data: this.frameId,
         nextBlockId,
       };
     }
 
-    const error = new Error('no-iframe-id');
-    error.data = { selector: block.selector };
-
-    throw error;
+    throw new Error('no-iframe-id');
   } catch (error) {
+    error.data = { selector: block.data.selector };
     error.nextBlockId = nextBlockId;
 
     throw error;

+ 1 - 1
src/background/workflow-engine/blocks-handler/handler-trigger.js

@@ -6,7 +6,7 @@ async function trigger(block) {
 
   try {
     if (block.data.type === 'visit-web' && this.tabId) {
-      this.frames = await executeContentScript(this.tabId, 'trigger');
+      this.frames = await executeContentScript(this.tabId);
     }
 
     return { nextBlockId, data: '' };

+ 25 - 34
src/background/workflow-engine/execute-content-script.js

@@ -1,37 +1,30 @@
 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);
-  });
+export async function getFrames(tabId) {
+  try {
+    const frames = await browser.webNavigation.getAllFrames({ tabId });
+    const framesObj = frames.reduce((acc, { frameId, url }) => {
+      const key = url === 'about:blank' ? '' : url;
+
+      acc[key] = frameId;
+
+      return acc;
+    }, {});
+
+    return framesObj;
+  } catch (error) {
+    console.error(error);
+    return {};
+  }
 }
 
-async function contentScriptExist(tabId) {
+async function contentScriptExist(tabId, frameId = 0) {
   try {
-    await browser.tabs.sendMessage(tabId, { type: 'content-script-exists' });
+    await browser.tabs.sendMessage(
+      tabId,
+      { type: 'content-script-exists' },
+      { frameId }
+    );
 
     return true;
   } catch (error) {
@@ -39,19 +32,17 @@ async function contentScriptExist(tabId) {
   }
 }
 
-export default async function (tabId) {
+export default async function (tabId, frameId = 0) {
   try {
-    const isScriptExists = await contentScriptExist(tabId);
+    const isScriptExists = await contentScriptExist(tabId, frameId);
 
     if (!isScriptExists) {
       await browser.tabs.executeScript(tabId, {
+        frameId,
         file: './contentScript.bundle.js',
-        allFrames: true,
       });
     }
 
-    await new Promise((resolve) => setTimeout(resolve, 1000));
-
     const frames = await getFrames(tabId);
 
     return frames;

+ 5 - 5
src/content/blocks-handler/handler-switch-to.js

@@ -4,15 +4,15 @@ function switchTo(block) {
   return new Promise((resolve, reject) => {
     handleElement(block, {
       onSelected(element) {
+        console.log(element, element.tagName);
         if (element.tagName !== 'IFRAME') {
-          resolve('');
+          reject(new Error('not-iframe'));
           return;
         }
 
-        resolve({ url: element.src });
-      },
-      onSuccess() {
-        resolve('');
+        const isSameOrigin = element.contentDocument !== null;
+
+        resolve({ url: element.src, isSameOrigin });
       },
       onError(error) {
         reject(error);

+ 16 - 2
src/content/helper.js

@@ -9,7 +9,7 @@ export function markElement(el, { id, data }) {
 }
 
 export function handleElement(
-  { data, id },
+  { data, id, frameSelector },
   { onSelected, onError, onSuccess, returnElement }
 ) {
   if (!data || !data.selector) {
@@ -17,11 +17,25 @@ export function handleElement(
     return null;
   }
 
+  let documentCtx = document;
+
+  if (frameSelector) {
+    const iframeCtx = document.querySelector(frameSelector)?.contentDocument;
+
+    if (!iframeCtx && returnElement) return null;
+    if (!iframeCtx && onError) {
+      onError(new Error('iframe-not-found'));
+      return;
+    }
+
+    documentCtx = iframeCtx;
+  }
+
   try {
     data.blockIdAttr = `block--${id}`;
 
     const selectorType = data.findBy || 'cssSelector';
-    const element = FindElement[selectorType](data);
+    const element = FindElement[selectorType](data, documentCtx);
 
     if (returnElement) return element;
 

+ 6 - 0
src/content/index.js

@@ -4,6 +4,10 @@ import elementSelector from './element-selector';
 import blocksHandler from './blocks-handler';
 
 (() => {
+  if (window.isAutomaInjected) return;
+
+  window.isAutomaInjected = true;
+
   browser.runtime.onMessage.addListener((data) => {
     if (data.isBlock) {
       const handler = blocksHandler[toCamelCase(data.name)];
@@ -26,6 +30,8 @@ import blocksHandler from './blocks-handler';
         browser.runtime.sendMessage({
           type: 'this-is-the-frame-id',
         });
+
+        resolve();
       }
     });
   });

+ 2 - 0
src/locales/en/newtab.json

@@ -93,6 +93,8 @@
       "stop-timeout": "Workflow is stopped because of timeout",
       "no-workflow": "Can't find workflow with \"{workflowId}\" ID",
       "element-not-found": "Can't find an element with \"{selector}\" selector.",
+      "not-iframe": "Element with \"{selector}\" selector is not an Iframe element",
+      "iframe-not-found": "Can't find an Iframe element with \"{selector}\" selector.",
       "workflow-infinite-loop": "Can't execute the workflow to prevent an infinite loop",
       "no-iframe-id": "Can't find Frame ID for the iframe element with \"{selector}\" selector",
       "no-tab": "Can't connect to a tab, use \"New tab\" or \"Active tab\" block before using the \"{name}\" block."

+ 1 - 0
src/manifest.json

@@ -31,6 +31,7 @@
     "proxy",
     "alarms",
     "storage",
+    "webNavigation",
     "unlimitedStorage",
     "<all_urls>"
   ],

+ 6 - 6
src/utils/find-element.js

@@ -1,24 +1,24 @@
 class FindElement {
-  static cssSelector(data) {
+  static cssSelector(data, documentCtx) {
     const selector = data.markEl
       ? `${data.selector.trim()}:not([${data.blockIdAttr}])`
       : data.selector;
 
     if (data.multiple) {
-      const elements = document.querySelectorAll(selector);
+      const elements = documentCtx.querySelectorAll(selector);
 
       if (elements.length === 0) return null;
 
       return elements;
     }
 
-    return document.querySelector(selector);
+    return documentCtx.querySelector(selector);
   }
 
-  static xpath(data) {
-    return document.evaluate(
+  static xpath(data, documentCtx) {
+    return documentCtx.evaluate(
       data.selector,
-      document,
+      documentCtx,
       null,
       XPathResult.FIRST_ORDERED_NODE_TYPE,
       null