Browse Source

fix: JS block not working on some website

Ahmad Kholid 2 years ago
parent
commit
dded253edd

+ 1 - 1
src/content/blocksHandler/handlerJavascriptCode.js

@@ -1,4 +1,4 @@
-import { jsContentHandler } from '@/newtab/utils/javascriptBlockUtil';
+import { jsContentHandler } from '@/workflowEngine/utils/javascriptBlockUtil';
 
 function javascriptCode({ data, isPreloadScripts, frameSelector }) {
   if (!isPreloadScripts) return jsContentHandler(...data);

+ 1 - 1
src/content/index.js

@@ -283,7 +283,7 @@ window.addEventListener('__automa-fetch__', (event) => {
   const { id, resource, type } = event.detail;
   const sendResponse = (payload) => {
     window.dispatchEvent(
-      new CustomEvent(`__autom-fetch-response-${id}__`, {
+      new CustomEvent(`__automa-fetch-response-${id}__`, {
         detail: { id, ...payload },
       })
     );

+ 3 - 1
src/popup/pages/Home.vue

@@ -217,8 +217,10 @@ async function executeWorkflow(workflow) {
         },
       });
     } else {
-      sendMessage('workflow:execute', workflow, 'background');
+      await sendMessage('workflow:execute', workflow, 'background');
     }
+
+    window.close();
   } catch (error) {
     console.error(error);
   }

+ 30 - 5
src/workflowEngine/blocksHandler/handlerActiveTab.js

@@ -15,9 +15,34 @@ async function activeTab(block) {
       return data;
     }
 
-    const currentWindow = await browser.windows.getCurrent();
-    if (currentWindow.focused)
-      await browser.windows.update(currentWindow.id, { focused: false });
+    const minimizeDashboard = async (currentWindow) => {
+      if (currentWindow.type !== 'popup') return;
+
+      const [tab] = currentWindow.tabs;
+      const isDashboard = tab && tab.url.includes(browser.runtime.getURL(''));
+      const isWindowFocus =
+        currentWindow.focused || currentWindow.state === 'maximized';
+
+      if (isWindowFocus && isDashboard) {
+        const windowOptions = { focused: false };
+        if (currentWindow.state === 'maximized')
+          windowOptions.state = 'minimized';
+
+        await browser.windows.update(currentWindow.id, windowOptions);
+      }
+    };
+
+    if (this.engine.isPopup) {
+      const currentWindow = await browser.windows.getCurrent({
+        populate: true,
+      });
+      await minimizeDashboard(currentWindow);
+    } else {
+      const allWindows = await browser.windows.getAll({ populate: true });
+      for (const currWindow of allWindows) {
+        await minimizeDashboard(currWindow);
+      }
+    }
 
     await sleep(500);
 
@@ -25,9 +50,9 @@ async function activeTab(block) {
       active: true,
       lastFocusedWindow: true,
     });
-    if (!tab?.url.startsWith('http')) {
+    if (!tab || !tab?.url.startsWith('http')) {
       const error = new Error('invalid-active-tab');
-      error.data = { url: tab.url };
+      error.data = { url: tab?.url };
 
       throw error;
     }

+ 105 - 7
src/workflowEngine/blocksHandler/handlerJavascriptCode.js

@@ -4,12 +4,18 @@ import cloneDeep from 'lodash.clonedeep';
 import {
   jsContentHandler,
   automaFetchClient,
-} from '@/newtab/utils/javascriptBlockUtil';
-import { messageSandbox, automaRefDataStr, waitTabLoaded } from '../helper';
+  jsContentHandlerEval,
+} from '../utils/javascriptBlockUtil';
+import {
+  messageSandbox,
+  automaRefDataStr,
+  waitTabLoaded,
+  sendDebugCommand,
+} from '../helper';
 
 const nanoid = customAlphabet('1234567890abcdef', 5);
 
-function getAutomaScript(varName, refData, everyNewTab) {
+function getAutomaScript(varName, refData, everyNewTab, isEval = false) {
   let str = `
 const ${varName} = ${JSON.stringify(refData)};
 ${automaRefDataStr(varName)}
@@ -17,10 +23,27 @@ function automaSetVariable(name, value) {
   ${varName}.variables[name] = value;
 }
 function automaNextBlock(data, insert = true) {
-  document.body.dispatchEvent(new CustomEvent('__automa-next-block__', { detail: { data, insert, refData: ${varName} } }));
+  if (${isEval}) {
+    $automaResolve({
+      columns: { 
+        data, 
+        insert,
+      }, 
+      variables: ${varName}.variables, 
+    });
+  } else{
+    document.body.dispatchEvent(new CustomEvent('__automa-next-block__', { detail: { data, insert, refData: ${varName} } }));
+  }
 }
 function automaResetTimeout() {
- document.body.dispatchEvent(new CustomEvent('__automa-reset-timeout__'));
+  if (${isEval}) {
+    clearTimeout($automaTimeout);
+    $automaTimeout = setTimeout(() => {
+      resolve();
+    }, $automaTimeoutMs);
+  } else {
+    document.body.dispatchEvent(new CustomEvent('__automa-reset-timeout__'));
+  }
 }
 function automaFetch(type, resource) {
   return (${automaFetchClient.toString()})('${varName}', { type, resource });
@@ -32,7 +55,11 @@ function automaFetch(type, resource) {
   return str;
 }
 async function executeInWebpage(args, target, worker) {
-  if (worker.engine.isMV2 || BROWSER_TYPE === 'firefox') {
+  if (!target.tabId) {
+    throw new Error('no-tab');
+  }
+
+  if (worker.engine.isMV2) {
     args[0] = cloneDeep(args[0]);
 
     const result = await worker._sendMessageToTab({
@@ -43,6 +70,77 @@ async function executeInWebpage(args, target, worker) {
     return result;
   }
 
+  const [isBlockedByCSP] = await browser.scripting.executeScript({
+    target,
+    func: () => {
+      return new Promise((resolve) => {
+        const eventListener = ({ srcElement }) => {
+          if (!srcElement || srcElement.id !== 'automa-csp') return;
+          srcElement.remove();
+          resolve(true);
+        };
+        document.addEventListener('securitypolicyviolation', eventListener);
+        const script = document.createElement('script');
+        script.id = 'automa-csp';
+        script.innerText = 'console.log("...")';
+
+        setTimeout(() => {
+          document.removeEventListener(
+            'securitypolicyviolation',
+            eventListener
+          );
+          script.remove();
+          resolve(false);
+        }, 500);
+
+        document.body.appendChild(script);
+      });
+    },
+    world: 'MAIN',
+  });
+
+  if (isBlockedByCSP.result) {
+    await new Promise((resolve) => {
+      chrome.debugger.attach({ tabId: target.tabId }, '1.3', resolve);
+    });
+    const { 0: blockData, 1: preloadScripts, 3: varName } = args;
+    const automaScript = getAutomaScript(
+      varName,
+      blockData.refData,
+      blockData.data.everyNewTab,
+      true
+    );
+    const jsCode = jsContentHandlerEval({
+      blockData,
+      automaScript,
+      preloadScripts,
+    });
+
+    const execResult = await sendDebugCommand(
+      target.tabId,
+      'Runtime.evaluate',
+      {
+        expression: jsCode,
+        userGesture: true,
+        awaitPromise: true,
+        returnByValue: true,
+      }
+    );
+
+    const { debugMode } = worker.engine.workflow.settings;
+    if (!debugMode) await chrome.debugger.detach({ tabId: target.tabId });
+
+    if (!execResult || !execResult.result) {
+      throw new Error('Unable execute code');
+    }
+
+    if (execResult.result.subtype === 'error') {
+      throw new Error(execResult.description);
+    }
+
+    return execResult.result.value || null;
+  }
+
   const [{ result }] = await browser.scripting.executeScript({
     args,
     target,
@@ -102,7 +200,7 @@ export async function javascriptCode({ outputs, data, ...block }, { refData }) {
 
   const instanceId = `automa${nanoid()}`;
   const automaScript =
-    data.everyNewTab || data.context === 'background'
+    data.everyNewTab && (!data.context || data.context !== 'background')
       ? ''
       : getAutomaScript(instanceId, payload.refData, data.everyNewTab);
 

+ 34 - 1
src/newtab/utils/javascriptBlockUtil.js → src/workflowEngine/utils/javascriptBlockUtil.js

@@ -6,7 +6,7 @@ export function automaFetchClient(id, { type, resource }) {
       return;
     }
 
-    const eventName = `__autom-fetch-response-${id}__`;
+    const eventName = `__automa-fetch-response-${id}__`;
     const eventListener = ({ detail }) => {
       if (detail.id !== id) return;
 
@@ -32,6 +32,39 @@ export function automaFetchClient(id, { type, resource }) {
   });
 }
 
+export function jsContentHandlerEval({
+  blockData,
+  automaScript,
+  preloadScripts,
+}) {
+  const preloadScriptsStr = preloadScripts
+    .map(({ script }) => script)
+    .join('\n');
+
+  return `(() => {
+    ${preloadScriptsStr}
+
+    return new Promise(($automaResolve) => {
+      const $automaTimeoutMs = ${blockData.data.timeout};
+      let $automaTimeout = setTimeout(() => {
+        $automaResolve();
+      }, $automaTimeoutMs);
+
+      ${automaScript}
+
+      ${blockData.data.code}
+
+      ${
+        blockData.data.code.includes('automaNextBlock')
+          ? ''
+          : 'automaNextBlock()'
+      }
+    }).catch((error) => {
+      return { columns: { data: { $error: true, message: error.message } } }
+    });
+  })();`;
+}
+
 export function jsContentHandler($blockData, $preloadScripts, $automaScript) {
   return new Promise((resolve, reject) => {
     try {