1
0

handlerCreateElement.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { customAlphabet } from 'nanoid/non-secure';
  2. import browser from 'webextension-polyfill';
  3. import { automaRefDataStr, checkCSPAndInject } from '../helper';
  4. const nanoid = customAlphabet('1234567890abcdef', 5);
  5. function getAutomaScript(refData) {
  6. const varName = `automa${nanoid()}`;
  7. const str = `
  8. const ${varName} = ${JSON.stringify(refData)};
  9. ${automaRefDataStr(varName)}
  10. function automaSetVariable(name, value) {
  11. ${varName}.variables[name] = value;
  12. }
  13. function automaExecWorkflow(options = {}) {
  14. window.dispatchEvent(new CustomEvent('automa:execute-workflow', { detail: options }));
  15. }
  16. `;
  17. return str;
  18. }
  19. async function handleCreateElement(block, { refData }) {
  20. if (!this.activeTab.id) throw new Error('no-tab');
  21. const { data } = block;
  22. const preloadScriptsPromise = await Promise.allSettled(
  23. data.preloadScripts.map((item) => {
  24. if (!item.src.startsWith('http'))
  25. return Promise.reject(new Error('Invalid URL'));
  26. return fetch(item.src)
  27. .then((response) => response.text())
  28. .then((result) => ({ type: item.type, script: result }));
  29. })
  30. );
  31. const preloadScripts = preloadScriptsPromise.reduce((acc, item) => {
  32. if (item.status === 'rejected') return acc;
  33. acc.push(item.value);
  34. return acc;
  35. }, []);
  36. data.preloadScripts = preloadScripts;
  37. const payload = {
  38. ...block,
  39. data,
  40. preloadCSS: data.preloadScripts.filter((item) => item.type === 'style'),
  41. };
  42. if (data.javascript && !this.engine.isMV2) {
  43. payload.data.dontInjectJS = true;
  44. payload.data.automaScript = getAutomaScript({ ...refData, secrets: {} });
  45. }
  46. await this._sendMessageToTab(payload, {}, data.runBeforeLoad ?? false);
  47. if (data.javascript && !this.engine.isMV2) {
  48. const target = {
  49. tabId: this.activeTab.id,
  50. frameIds: [this.activeTab.frameId || 0],
  51. };
  52. const { debugMode } = this.engine.workflow?.settings || {};
  53. const result = await checkCSPAndInject(
  54. {
  55. target,
  56. debugMode,
  57. options: {
  58. awaitPromise: false,
  59. returnByValue: false,
  60. },
  61. },
  62. () => {
  63. let jsPreload = '';
  64. preloadScripts.forEach((item) => {
  65. if (item.type === 'style') return;
  66. jsPreload += `${item.script}\n`;
  67. });
  68. const automaScript = payload.data?.automaScript || '';
  69. return `(() => { ${jsPreload} \n ${automaScript}\n${data.javascript} })()`;
  70. }
  71. );
  72. if (!result.isBlocked) {
  73. await browser.scripting.executeScript({
  74. world: 'MAIN',
  75. target,
  76. args: [
  77. data.javascript,
  78. block.id,
  79. payload.data?.automaScript || '',
  80. preloadScripts,
  81. ],
  82. func: (code, blockId, $automaScript, $preloadScripts) => {
  83. const baseId = `automa-${blockId}`;
  84. $preloadScripts.forEach((item) => {
  85. if (item.type === 'style') return;
  86. const script = document.createElement(item.type);
  87. script.id = `${baseId}-script`;
  88. script.textContent = item.script;
  89. document.body.appendChild(script);
  90. });
  91. const script = document.createElement('script');
  92. script.id = `${baseId}-javascript`;
  93. script.textContent = `(() => { ${$automaScript}\n${code} })()`;
  94. document.body.appendChild(script);
  95. },
  96. });
  97. }
  98. }
  99. return {
  100. data: '',
  101. nextBlockId: this.getBlockConnections(block.id),
  102. };
  103. }
  104. export default handleCreateElement;