Browse Source

feat: add "block execution timeout" in block settings

Ahmad Kholid 2 years ago
parent
commit
c37e107c89

+ 26 - 3
src/components/newtab/workflow/edit/BlockSetting/BlockSettingGeneral.vue

@@ -1,13 +1,28 @@
 <template>
   <div class="block-setting-general">
     <ui-list>
-      <ui-list-item v-if="browserType !== 'firefox'" small>
+      <div v-if="props.data.id !== 'delay'" class="flex items-center">
+        <div class="flex-1 overflow-hidden">
+          <p class="text-overflow">
+            {{ t('workflow.blocks.base.settings.blockTimeout.title') }}
+          </p>
+          <p class="line-clamp leading-tight text-gray-600">
+            {{ t('workflow.blocks.base.settings.blockTimeout.description') }}
+          </p>
+        </div>
+        <ui-input
+          v-model.number="state.blockTimeout"
+          placeholder="0"
+          class="w-24"
+        />
+      </div>
+      <ui-list-item v-if="isDebugSupported" small>
         <ui-switch v-model="state.debugMode" class="mr-4" />
         <div class="flex-1 overflow-hidden">
           <p class="text-overflow">
             {{ t('workflow.settings.debugMode.title') }}
           </p>
-          <p class="text-overflow">
+          <p class="text-overflow leading-tight text-gray-600">
             Execute block using the Chrome DevTools Protocol
           </p>
         </div>
@@ -27,10 +42,18 @@ const props = defineProps({
 });
 const emit = defineEmits(['change']);
 
+const supportedDebugBlocks = [
+  'forms',
+  'event-click',
+  'trigger-event',
+  'press-key',
+];
 const browserType = BROWSER_TYPE;
+const isDebugSupported =
+  browserType !== 'firefox' && supportedDebugBlocks.includes(props.data.id);
 
 const { t } = useI18n();
-const state = reactive({});
+const state = reactive({ blockTimeout: 0 });
 
 watch(
   () => state,

+ 5 - 12
src/components/newtab/workflow/edit/EditBlockSettings.vue

@@ -48,30 +48,23 @@ const emit = defineEmits(['change', 'close']);
 
 const { t } = useI18n();
 
-let currActiveTab = 'on-error';
-const browserType = BROWSER_TYPE;
+const currActiveTab = 'general';
 const isOnErrorSupported = !excludeOnError.includes(props.data.id);
-const supportedBlocks = ['forms', 'event-click', 'trigger-event', 'press-key'];
 const tabs = [
+  { id: 'general', name: t('settings.menu.general') },
   {
     id: 'on-error',
     name: props.onErrorLabel || t('workflow.blocks.base.onError.button'),
   },
   { id: 'lines', name: t('workflow.blocks.base.settings.line.title') },
 ];
-const isDebugSupported =
-  browserType !== 'firefox' && supportedBlocks.includes(props.data.id);
 
 if (props.data?.itemId) {
   tabs.pop();
 }
-
-if (isDebugSupported) {
-  currActiveTab = 'general';
-  tabs.unshift({ id: 'general', name: t('settings.menu.general') });
-} else if (!isOnErrorSupported) {
-  if (!props.data?.itemId) currActiveTab = 'lines';
-  tabs.shift();
+if (!isOnErrorSupported) {
+  const onErrorTabIndex = tabs.findIndex((tab) => tab.id === 'on-error');
+  tabs.splice(onErrorTabIndex, 1);
 }
 
 const defaultSettings = {

+ 4 - 0
src/locales/en/blocks.json

@@ -24,6 +24,10 @@
         },
         "settings": {
           "title": "Block settings",
+          "blockTimeout": {
+            "title": "Block execution timeout (millisecond)",
+            "description": "The maximum time the block is executing (0 to disable)"
+          },
           "line": {
             "title": "Lines",
             "label": "Label",

+ 30 - 1
src/workflowEngine/WorkflowWorker.js

@@ -12,6 +12,29 @@ import renderString from './templating/renderString';
 import { convertData, waitTabLoaded } from './helper';
 import injectContentScript from './injectContentScript';
 
+function blockExecutionWrapper(blockHandler, blockData) {
+  return new Promise((resolve, reject) => {
+    let timeout = null;
+    const timeoutMs = blockData?.settings?.blockTimeout;
+    if (timeoutMs && timeoutMs > 0) {
+      timeout = setTimeout(() => {
+        reject(new Error('Timeout'));
+      }, timeoutMs);
+    }
+
+    blockHandler()
+      .then((result) => {
+        resolve(result);
+      })
+      .catch((error) => {
+        reject(error);
+      })
+      .finally(() => {
+        clearTimeout(timeout);
+      });
+  });
+}
+
 class WorkflowWorker {
   constructor(id, engine, options = {}) {
     this.id = id;
@@ -213,11 +236,17 @@ class WorkflowWorker {
           nextBlockId: this.getBlockConnections(block.id),
         };
       } else {
-        result = await handler.call(this, replacedBlock, {
+        const bindedHandler = handler.bind(this, replacedBlock, {
           refData,
           prevBlock,
           ...(execParam || {}),
         });
+        result = await blockExecutionWrapper(bindedHandler, block.data);
+        // result = await handler.call(this, replacedBlock, {
+        //   refData,
+        //   prevBlock,
+        //   ...(execParam || {}),
+        // });
 
         if (this.engine.isDestroyed) return;