handlerJavascriptCode.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { customAlphabet } from 'nanoid/non-secure';
  2. import { sendMessage } from '@/utils/message';
  3. import { automaRefDataStr } from '../utils';
  4. const nanoid = customAlphabet('1234567890abcdef', 5);
  5. function getAutomaScript(refData, everyNewTab) {
  6. const varName = `automa${nanoid()}`;
  7. let str = `
  8. const ${varName} = ${JSON.stringify(refData)};
  9. ${automaRefDataStr(varName)}
  10. function automaSetVariable(name, value) {
  11. ${varName}.variables[name] = value;
  12. }
  13. function automaNextBlock(data, insert = true) {
  14. document.body.dispatchEvent(new CustomEvent('__automa-next-block__', { detail: { data, insert, refData: ${varName} } }));
  15. }
  16. function automaResetTimeout() {
  17. document.body.dispatchEvent(new CustomEvent('__automa-reset-timeout__'));
  18. }
  19. `;
  20. if (everyNewTab) str = automaRefDataStr(varName);
  21. return str;
  22. }
  23. function javascriptCode(block) {
  24. const automaScript = block.data.everyNewTab
  25. ? ''
  26. : getAutomaScript(block.refData, block.data.everyNewTab);
  27. return new Promise((resolve, reject) => {
  28. let documentCtx = document;
  29. if (block.frameSelector) {
  30. const iframeCtx = document.querySelector(
  31. block.frameSelector
  32. )?.contentDocument;
  33. if (!iframeCtx) {
  34. reject(new Error('iframe-not-found'));
  35. return;
  36. }
  37. documentCtx = iframeCtx;
  38. }
  39. const scriptAttr = `block--${block.id}`;
  40. const isScriptExists = documentCtx.querySelector(
  41. `.automa-custom-js[${scriptAttr}]`
  42. );
  43. if (isScriptExists) {
  44. resolve('');
  45. return;
  46. }
  47. const promisePreloadScripts =
  48. block.data?.preloadScripts?.map(async (item) => {
  49. try {
  50. const { protocol } = new URL(item.src);
  51. const isValidUrl = /https?/.test(protocol);
  52. if (!isValidUrl) return null;
  53. const script = await sendMessage(
  54. 'fetch:text',
  55. item.src,
  56. 'background'
  57. );
  58. const scriptEl = documentCtx.createElement('script');
  59. scriptEl.type = 'text/javascript';
  60. scriptEl.innerHTML = script;
  61. return {
  62. ...item,
  63. script: scriptEl,
  64. };
  65. } catch (error) {
  66. return null;
  67. }
  68. }) || [];
  69. Promise.allSettled(promisePreloadScripts).then((result) => {
  70. const preloadScripts = result.reduce((acc, { status, value }) => {
  71. if (status !== 'fulfilled' || !value) return acc;
  72. acc.push(value);
  73. documentCtx.body.appendChild(value.script);
  74. return acc;
  75. }, []);
  76. const script = document.createElement('script');
  77. script.setAttribute(scriptAttr, '');
  78. script.classList.add('automa-custom-js');
  79. script.textContent = `(() => {
  80. ${automaScript}
  81. try {
  82. ${block.data.code}
  83. } catch (error) {
  84. console.error(error);
  85. automaNextBlock({ $error: true, message: error.message });
  86. }
  87. })()`;
  88. if (!block.data.everyNewTab) {
  89. let timeout;
  90. const cleanUp = (detail = {}) => {
  91. script.remove();
  92. preloadScripts.forEach((item) => {
  93. if (item.removeAfterExec) item.script.remove();
  94. });
  95. clearTimeout(timeout);
  96. resolve({
  97. columns: {
  98. data: detail?.data,
  99. insert: detail?.insert,
  100. },
  101. variables: detail?.refData?.variables,
  102. });
  103. };
  104. documentCtx.body.addEventListener(
  105. '__automa-next-block__',
  106. ({ detail }) => {
  107. cleanUp(detail || {});
  108. }
  109. );
  110. documentCtx.body.addEventListener('__automa-reset-timeout__', () => {
  111. clearTimeout(timeout);
  112. timeout = setTimeout(cleanUp, block.data.timeout);
  113. });
  114. timeout = setTimeout(cleanUp, block.data.timeout);
  115. } else {
  116. resolve();
  117. }
  118. documentCtx.documentElement.appendChild(script);
  119. });
  120. });
  121. }
  122. export default javascriptCode;