瀏覽代碼

fix: wrong element coordinate inside an iframe

Ahmad Kholid 3 年之前
父節點
當前提交
37435745c5

+ 4 - 3
src/content/blocksHandler/handlerEventClick.js

@@ -1,4 +1,5 @@
 import { sendMessage } from '@/utils/message';
+import { getElementPosition } from '../utils';
 import handleSelector from '../handleSelector';
 
 function eventClick(block) {
@@ -6,13 +7,13 @@ function eventClick(block) {
     handleSelector(block, {
       async onSelected(element) {
         if (block.debugMode) {
-          const { width, height, x, y } = element.getBoundingClientRect();
+          const { x, y } = await getElementPosition(element);
           const payload = {
             tabId: block.activeTabId,
             method: 'Input.dispatchMouseEvent',
             params: {
-              x: x + width / 2,
-              y: y + height / 2,
+              x,
+              y,
               button: 'left',
             },
           };

+ 6 - 5
src/content/blocksHandler/handlerHoverElement.js

@@ -1,20 +1,21 @@
 import { sendMessage } from '@/utils/message';
+import { getElementPosition } from '../utils';
 import handleSelector from '../handleSelector';
 
 function eventClick(block) {
   return new Promise((resolve, reject) => {
     handleSelector(block, {
       async onSelected(element) {
-        const { width, height, x, y } = element.getBoundingClientRect();
+        const { x, y } = await getElementPosition(element);
         const payload = {
           tabId: block.activeTabId,
           method: 'Input.dispatchMouseEvent',
           params: {
-            type: 'mousePressed',
-            x: x + width / 2,
-            y: y + height / 2,
-            button: 'left',
+            x,
+            y,
             clickCount: 1,
+            button: 'left',
+            type: 'mousePressed',
           },
         };
 

+ 2 - 5
src/content/blocksHandler/handlerTriggerEvent.js

@@ -1,6 +1,7 @@
 import { sendMessage } from '@/utils/message';
 import simulateEvent from '@/utils/simulateEvent';
 import simulateMouseEvent from '@/utils/simulateEvent/mouseEvent';
+import { getElementPosition } from '../utils';
 import handleSelector from '../handleSelector';
 
 const modifiers = {
@@ -64,11 +65,7 @@ function triggerEvent({ data, id, frameSelector, debugMode, activeTabId }) {
           const eventHandler = eventHandlers[data.eventType];
 
           if (debugMode && eventHandler) {
-            const { x, y, width, height } = element.getBoundingClientRect();
-            const elCoordinate = {
-              x: x + width / 2,
-              y: y + height / 2,
-            };
+            const elCoordinate = await getElementPosition(element);
             const sendCommand = (method, params = {}) => {
               const payload = {
                 method,

+ 24 - 1
src/content/index.js

@@ -7,11 +7,34 @@ import executedBlock from './executedBlock';
 import blocksHandler from './blocksHandler';
 import handleTestCondition from './handleTestCondition';
 
+function messageListener({ data, source }) {
+  if (data !== 'automa:get-frame') return;
+
+  let frameRect = { x: 0, y: 0 };
+
+  document.querySelectorAll('iframe').forEach((iframe) => {
+    if (iframe.contentWindow !== source) return;
+
+    frameRect = iframe.getBoundingClientRect();
+  });
+
+  source.postMessage(
+    {
+      frameRect,
+      type: 'automa:the-frame-rect',
+    },
+    '*'
+  );
+}
+
 (() => {
   if (window.isAutomaInjected) return;
-
   window.isAutomaInjected = true;
 
+  if (window.self === window.top) {
+    window.addEventListener('message', messageListener);
+  }
+
   browser.runtime.onMessage.addListener((data) => {
     return new Promise((resolve, reject) => {
       if (data.isBlock) {

+ 59 - 0
src/content/utils.js

@@ -32,3 +32,62 @@ function automaRefData(keyword, path = '') {
 }
   `;
 }
+
+function messageTopFrame(windowCtx) {
+  return new Promise((resolve) => {
+    let timeout = null;
+    let isResolved = false;
+
+    const messageListener = ({ data }) => {
+      if (data.type !== 'automa:the-frame-rect' || isResolved) return;
+
+      clearTimeout(timeout);
+      isResolved = true;
+      windowCtx.removeEventListener('message', messageListener);
+      resolve(data.frameRect);
+    };
+
+    timeout = setTimeout(() => {
+      if (isResolved) return;
+
+      isResolved = true;
+      windowCtx.removeEventListener('message', messageListener);
+      resolve(null);
+    }, 5000);
+
+    windowCtx.addEventListener('message', messageListener);
+    windowCtx.top.postMessage('automa:get-frame', '*');
+  });
+}
+export async function getElementPosition(element) {
+  const elWindow = element.ownerDocument.defaultView;
+  const isInFrame = elWindow !== window.top;
+  const { width, height, x, y } = element.getBoundingClientRect();
+  const position = {
+    x: x + width / 2,
+    y: y + height / 2,
+  };
+
+  if (!isInFrame) return position;
+
+  try {
+    const frameEl = elWindow.frameElement;
+    let frameRect = null;
+
+    if (frameEl) {
+      frameRect = frameEl.getBoundingClientRect();
+    } else {
+      frameRect = await messageTopFrame(elWindow);
+
+      if (!frameRect) throw new Error('Iframe not found');
+    }
+
+    position.x += frameRect.x;
+    position.y += frameRect.y;
+
+    return position;
+  } catch (error) {
+    console.error(error);
+    return position;
+  }
+}