index.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import browser from 'webextension-polyfill';
  2. import { toCamelCase } from '@/utils/helper';
  3. import blocksHandler from './blocksHandler';
  4. import showExecutedBlock from './showExecutedBlock';
  5. import handleTestCondition from './handleTestCondition';
  6. import shortcutListener from './services/shortcutListener';
  7. const isMainFrame = window.self === window.top;
  8. function messageToFrame(frameElement, blockData) {
  9. return new Promise((resolve, reject) => {
  10. function onMessage({ data }) {
  11. if (data.type !== 'automa:block-execute-result') return;
  12. if (data.result?.$isError) {
  13. const error = new Error(data.result.message);
  14. error.data = data.result.data;
  15. reject(error);
  16. } else {
  17. resolve(data.result);
  18. }
  19. window.removeEventListener('message', onMessage);
  20. }
  21. window.addEventListener('message', onMessage);
  22. frameElement.contentWindow.postMessage(
  23. {
  24. type: 'automa:execute-block',
  25. blockData: { ...blockData, frameSelector: '' },
  26. },
  27. '*'
  28. );
  29. });
  30. }
  31. async function executeBlock(data) {
  32. const removeExecutedBlock = showExecutedBlock(data, data.executedBlockOnWeb);
  33. if (data.data?.selector?.includes('|>') && isMainFrame) {
  34. const [frameSelector, selector] = data.data.selector.split(/\|>(.+)/);
  35. const frameElement = document.querySelector(frameSelector);
  36. const frameError = (message) => {
  37. const error = new Error(message);
  38. error.data = { selector: frameSelector };
  39. return error;
  40. };
  41. if (!frameElement) throw frameError('iframe-not-found');
  42. const isFrameEelement = ['IFRAME', 'FRAME'].includes(frameElement.tagName);
  43. if (!isFrameEelement) throw frameError('not-iframe');
  44. data.data.selector = selector;
  45. data.data.$frameSelector = frameSelector;
  46. if (frameElement.contentDocument) {
  47. data.frameSelector = frameSelector;
  48. } else {
  49. const result = await messageToFrame(frameElement, data);
  50. return result;
  51. }
  52. }
  53. const handler = blocksHandler[toCamelCase(data.name)];
  54. if (handler) {
  55. const result = await handler(data);
  56. removeExecutedBlock();
  57. return result;
  58. }
  59. const error = new Error(`"${data.name}" doesn't have a handler`);
  60. console.error(error);
  61. throw error;
  62. }
  63. function messageListener({ data, source }) {
  64. if (data.type === 'automa:get-frame' && isMainFrame) {
  65. let frameRect = { x: 0, y: 0 };
  66. document.querySelectorAll('iframe').forEach((iframe) => {
  67. if (iframe.contentWindow !== source) return;
  68. frameRect = iframe.getBoundingClientRect();
  69. });
  70. source.postMessage(
  71. {
  72. frameRect,
  73. type: 'automa:the-frame-rect',
  74. },
  75. '*'
  76. );
  77. return;
  78. }
  79. if (data.type === 'automa:execute-block') {
  80. executeBlock(data.blockData)
  81. .then((result) => {
  82. window.top.postMessage(
  83. {
  84. result,
  85. type: 'automa:block-execute-result',
  86. },
  87. '*'
  88. );
  89. })
  90. .catch((error) => {
  91. console.error(error);
  92. window.top.postMessage(
  93. {
  94. result: {
  95. $isError: true,
  96. message: error.message,
  97. data: error.data || {},
  98. },
  99. type: 'automa:block-execute-result',
  100. },
  101. '*'
  102. );
  103. });
  104. }
  105. }
  106. (() => {
  107. if (window.isAutomaInjected) return;
  108. window.isAutomaInjected = true;
  109. window.addEventListener('message', messageListener);
  110. if (isMainFrame) shortcutListener();
  111. browser.runtime.onMessage.addListener((data) => {
  112. return new Promise((resolve, reject) => {
  113. if (data.isBlock) {
  114. executeBlock(data).then(resolve).catch(reject);
  115. return;
  116. }
  117. switch (data.type) {
  118. case 'condition-builder':
  119. handleTestCondition(data.data)
  120. .then((result) => resolve(result))
  121. .catch((error) => reject(error));
  122. break;
  123. case 'content-script-exists':
  124. resolve(true);
  125. break;
  126. default:
  127. }
  128. });
  129. });
  130. })();