Переглянути джерело

Merge branch 'dev' of https://github.com/automaapp/automa into dev

Ahmad Kholid 1 рік тому
батько
коміт
3d0181e8a1

+ 15 - 0
src/content/blocksHandler/handlerForms.js

@@ -26,6 +26,21 @@ async function forms(block) {
     if (block.debugMode && data.type === 'text-field') {
       element.focus?.();
 
+      if (data.clearValue) {
+        const backspaceCommands = new Array(element.value?.length ?? 0).fill({
+          type: 'rawKeyDown',
+          unmodifiedText: 'Delete',
+          text: 'Delete',
+          windowsVirtualKeyCode: 46,
+        });
+
+        await sendMessage(
+          'debugger:type',
+          { commands: backspaceCommands, tabId: block.activeTabId, delay: 0 },
+          'background'
+        );
+      }
+
       const commands = data.value.split('').map((char) => ({
         type: 'keyDown',
         text: char === '\n' ? '\r' : char,

+ 87 - 78
src/content/index.js

@@ -240,86 +240,95 @@ async function messageListener({ data, source }) {
 
   automa('content');
 
-  browser.runtime.onMessage.addListener((data) => {
-    return new Promise((resolve, reject) => {
-      if (data.isBlock) {
-        executeBlock(data)
-          .then(resolve)
-          .catch((error) => {
-            console.error(error);
-            const elNotFound = error.message === 'element-not-found';
-            const isLoopItem = data.data?.selector?.includes('automa-loop');
-            if (elNotFound && isLoopItem) {
-              const findLoopEl = data.loopEls.find(({ url }) =>
-                window.location.href.includes(url)
-              );
-
-              const blockData = { ...data.data, ...findLoopEl, multiple: true };
-              const loopBlock = {
-                ...data,
-                onlyGenerate: true,
-                data: blockData,
-              };
-
-              blocksHandler()
-                .loopData(loopBlock)
-                .then(() => {
-                  executeBlock(data).then(resolve).catch(reject);
-                })
-                .catch((blockError) => {
-                  reject(blockError);
-                });
-              return;
-            }
-
-            reject(error);
-          });
-      } else {
-        switch (data.type) {
-          case 'input-workflow-params':
-            window.initPaletteParams?.(data.data);
-            resolve(Boolean(window.initPaletteParams));
-            break;
-          case 'content-script-exists':
-            resolve(true);
-            break;
-          case 'automa-element-selector': {
-            const selectorInstance = elementSelectorInstance();
-
-            resolve(selectorInstance);
-            break;
-          }
-          case 'context-element': {
-            let $ctxElSelector = '';
-
-            if (contextElement) {
-              $ctxElSelector = findSelector(contextElement);
-              contextElement = null;
-            }
-            if (!$ctxTextSelection) {
-              $ctxTextSelection = window.getSelection().toString();
-            }
-
-            const cloneContextData = cloneDeep({
-              $ctxLink,
-              $ctxMediaUrl,
-              $ctxElSelector,
-              $ctxTextSelection,
-            });
-
-            $ctxLink = '';
-            $ctxMediaUrl = '';
-            $ctxElSelector = '';
-            $ctxTextSelection = '';
-
-            resolve(cloneContextData);
-            break;
-          }
-          default:
-            resolve(null);
+  let locked = false;
+  const queue = [];
+
+  browser.runtime.onMessage.addListener(async (data) => {
+    const asyncExecuteBlock = async (block) => {
+      if (locked) {
+        return new Promise((resolve, reject) => {
+          queue.push([block, resolve, reject]);
+        });
+      }
+
+      try {
+        locked = true;
+        const res = await executeBlock(block);
+        return res;
+      } catch (error) {
+        console.error(error);
+        const elNotFound = error.message === 'element-not-found';
+        const isLoopItem = data.data?.selector?.includes('automa-loop');
+
+        if (!elNotFound || !isLoopItem) return Promise.reject(error);
+
+        const findLoopEl = data.loopEls.find(({ url }) =>
+          window.location.href.includes(url)
+        );
+
+        const blockData = { ...data.data, ...findLoopEl, multiple: true };
+        const loopBlock = {
+          ...data,
+          onlyGenerate: true,
+          data: blockData,
+        };
+
+        await blocksHandler().loopData(loopBlock);
+        return executeBlock(block);
+      } finally {
+        locked = false;
+      }
+    };
+
+    if (data.isBlock) {
+      const res = await asyncExecuteBlock(data);
+      while (queue.length) {
+        const [block, resolve, reject] = queue.shift();
+        requestAnimationFrame(() => {
+          asyncExecuteBlock(block).then(resolve).catch(reject);
+        });
+      }
+
+      return res;
+    }
+
+    switch (data.type) {
+      case 'input-workflow-params':
+        window.initPaletteParams?.(data.data);
+        return Boolean(window.initPaletteParams);
+      case 'content-script-exists':
+        return true;
+      case 'automa-element-selector': {
+        return elementSelectorInstance();
+      }
+      case 'context-element': {
+        let $ctxElSelector = '';
+
+        if (contextElement) {
+          $ctxElSelector = findSelector(contextElement);
+          contextElement = null;
+        }
+        if (!$ctxTextSelection) {
+          $ctxTextSelection = window.getSelection().toString();
         }
+
+        const cloneContextData = cloneDeep({
+          $ctxLink,
+          $ctxMediaUrl,
+          $ctxElSelector,
+          $ctxTextSelection,
+        });
+
+        $ctxLink = '';
+        $ctxMediaUrl = '';
+        $ctxElSelector = '';
+        $ctxTextSelection = '';
+
+        return cloneContextData;
       }
-    });
+      default:
+        return null;
+    }
   });
 })();
 

+ 6 - 5
src/workflowEngine/WorkflowEngine.js

@@ -392,17 +392,18 @@ class WorkflowEngine {
     await browser.storage.local.set({ workflowQueue });
   }
 
-  destroyWorker(workerId) {
-    this.workers.delete(workerId);
-
-    if (this.workers.size === 0) {
+  async destroyWorker(workerId) {
+    // is last worker
+    if (this.workers.size === 1 && this.workers.has(workerId)) {
       this.addLogHistory({
         type: 'finish',
         name: 'finish',
       });
       this.dispatchEvent('finish');
-      this.destroy('success');
+      await this.destroy('success');
     }
+    // wait detach debugger
+    this.workers.delete(workerId);
   }
 
   async destroy(status, message, blockDetail) {

+ 24 - 0
src/workflowEngine/WorkflowWorker.js

@@ -141,6 +141,29 @@ class WorkflowWorker {
     prevBlockData,
     nextBlockBreakpointCount = null
   ) {
+    // pre check
+    for (const connection of connections) {
+      const id = typeof connection === 'string' ? connection : connection.id;
+
+      const block = this.engine.blocks[id];
+
+      if (!block) {
+        console.error(`Block ${id} doesn't exist`);
+        this.engine.destroy('stopped');
+        return;
+      }
+
+      // pass disabled block
+      // eslint-disable-next-line no-continue
+      if (block.data.disableBlock) continue;
+
+      // check if the next block is breakpoint
+      if (block.data?.$breakpoint) {
+        // set breakpoint state
+        nextBlockBreakpointCount = 0;
+      }
+    }
+
     connections.forEach((connection, index) => {
       const { id, targetHandle, sourceHandle } =
         typeof connection === 'string'
@@ -166,6 +189,7 @@ class WorkflowWorker {
           currentBlock: this.currentBlock,
           repeatedTasks: this.repeatedTasks,
           preloadScripts: this.preloadScripts,
+          debugAttached: this.debugAttached,
         });
 
         this.engine.addWorker({