123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- import { customAlphabet } from 'nanoid/non-secure';
- import browser from 'webextension-polyfill';
- import cloneDeep from 'lodash.clonedeep';
- import { parseJSON, isObject } from '@/utils/helper';
- import {
- jsContentHandler,
- automaFetchClient,
- jsContentHandlerEval,
- } from '../utils/javascriptBlockUtil';
- import {
- waitTabLoaded,
- messageSandbox,
- automaRefDataStr,
- checkCSPAndInject,
- } from '../helper';
- const nanoid = customAlphabet('1234567890abcdef', 5);
- function getAutomaScript({ varName, refData, everyNewTab, isEval = false }) {
- let str = `
- const ${varName} = ${JSON.stringify(refData)};
- ${automaRefDataStr(varName)}
- function automaSetVariable(name, value) {
- const variables = ${varName}.variables;
- if (!variables) ${varName}.variables = {}
- ${varName}.variables[name] = value;
- }
- function automaNextBlock(data, insert = true) {
- 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() {
- 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 });
- }
- `;
- if (everyNewTab) str = automaRefDataStr(varName);
- return str;
- }
- async function executeInWebpage(args, target, worker) {
- if (!target.tabId) {
- throw new Error('no-tab');
- }
- if (worker.engine.isMV2) {
- args[0] = cloneDeep(args[0]);
- const result = await worker._sendMessageToTab({
- label: 'javascript-code',
- data: args,
- });
- return result;
- }
- const { debugMode } = worker.engine.workflow.settings;
- const cspResult = await checkCSPAndInject({ target, debugMode }, () => {
- const { 0: blockData, 1: preloadScripts, 3: varName } = args;
- const automaScript = getAutomaScript({
- varName,
- isEval: true,
- refData: blockData.refData,
- everyNewTab: blockData.data.everyNewTab,
- });
- const jsCode = jsContentHandlerEval({
- blockData,
- automaScript,
- preloadScripts,
- });
- return jsCode;
- });
- if (cspResult.isBlocked) return cspResult.value;
- const [{ result }] = await browser.scripting.executeScript({
- args,
- target,
- world: 'MAIN',
- func: jsContentHandler,
- });
- if (typeof result?.columns?.data === 'string') {
- result.columns.data = parseJSON(result.columns.data, {});
- }
- return result;
- }
- export async function javascriptCode({ outputs, data, ...block }, { refData }) {
- let nextBlockId = this.getBlockConnections(block.id);
- if (data.everyNewTab) {
- const isScriptExist = this.preloadScripts.some(({ id }) => id === block.id);
- if (!isScriptExist)
- this.preloadScripts.push({ id: block.id, data: cloneDeep(data) });
- if (!this.activeTab.id) return { data: '', nextBlockId };
- } else if (!this.activeTab.id && data.context !== 'background') {
- throw new Error('no-tab');
- }
- const payload = {
- ...block,
- data,
- refData: { variables: {} },
- frameSelector: this.frameSelector,
- };
- if (data.code.includes('automaRefData')) {
- const newRefData = {};
- Object.keys(refData).forEach((keyword) => {
- if (!data.code.includes(keyword)) return;
- newRefData[keyword] = refData[keyword];
- });
- payload.refData = { ...newRefData, secrets: {} };
- }
- const preloadScriptsPromise = await Promise.allSettled(
- data.preloadScripts.map(async (script) => {
- const { protocol } = new URL(script.src);
- const isValidUrl = /https?/.test(protocol);
- if (!isValidUrl) return null;
- const response = await fetch(script.src);
- if (!response.ok) throw new Error(response.statusText);
- const result = await response.text();
- return {
- script: result,
- id: `automa-script-${nanoid()}`,
- removeAfterExec: script.removeAfterExec,
- };
- })
- );
- const preloadScripts = preloadScriptsPromise.reduce((acc, item) => {
- if (item.status === 'fulfilled') acc.push(item.value);
- return acc;
- }, []);
- const instanceId = `automa${nanoid()}`;
- const automaScript =
- data.everyNewTab && (!data.context || data.context !== 'background')
- ? ''
- : getAutomaScript({
- varName: instanceId,
- refData: payload.refData,
- everyNewTab: data.everyNewTab,
- });
- if (data.context !== 'background') {
- await waitTabLoaded({
- tabId: this.activeTab.id,
- ms: this.settings?.tabLoadTimeout ?? 30000,
- });
- }
- const inSandbox =
- (this.engine.isMV2 || this.engine.isPopup) &&
- BROWSER_TYPE !== 'firefox' &&
- data.context === 'background';
- const result = await (inSandbox
- ? messageSandbox('javascriptBlock', {
- instanceId,
- preloadScripts,
- refData: payload.refData,
- blockData: cloneDeep(payload.data),
- })
- : executeInWebpage(
- [payload, preloadScripts, automaScript, instanceId],
- {
- tabId: this.activeTab.id,
- frameIds: [this.activeTab.frameId || 0],
- },
- this
- ));
- if (result) {
- if (result.columns.data?.$error) {
- throw new Error(result.columns.data.message);
- }
- if (result.variables) {
- await Promise.allSettled(
- Object.keys(result.variables).map(async (varName) => {
- await this.setVariable(varName, result.variables[varName]);
- })
- );
- }
- let insert = true;
- let replaceTable = false;
- if (isObject(result.columns.insert)) {
- const {
- insert: insertData,
- nextBlockId: inputNextBlockId,
- replaceTable: replaceTableParam,
- } = result.columns.insert;
- replaceTable = Boolean(replaceTableParam);
- insert = typeof insertData === 'boolean' ? insertData : true;
- if (inputNextBlockId) {
- let customNextBlockId = this.getBlockConnections(inputNextBlockId);
- const nextBlock = this.engine.blocks[inputNextBlockId];
- if (!customNextBlockId && nextBlock) {
- customNextBlockId = [
- {
- id: inputNextBlockId,
- blockId: inputNextBlockId,
- connections: new Map([]),
- },
- ];
- }
- if (!customNextBlockId)
- throw new Error(`Can't find block with "${inputNextBlockId}" id`);
- nextBlockId = customNextBlockId;
- }
- } else {
- insert = result.columns.insert;
- }
- const columnData = result.columns.data;
- if (insert && columnData) {
- const columnDataObj =
- typeof columnData === 'string'
- ? parseJSON(columnData, null)
- : columnData;
- if (columnDataObj) {
- const params = Array.isArray(columnDataObj)
- ? columnDataObj
- : [columnDataObj];
- if (replaceTable) {
- this.engine.referenceData.table = [];
- Object.keys(this.engine.columns).forEach((key) => {
- this.engine.columns[key].index = 0;
- });
- }
- this.addDataToColumn(params);
- }
- }
- }
- return {
- nextBlockId,
- data: result?.columns.data || {},
- };
- }
- export default javascriptCode;
|