/* eslint-disable */ // click-helper.js // This script is injected into the page to handle click operations if (window.__CLICK_HELPER_INITIALIZED__) { // Already initialized, skip } else { window.__CLICK_HELPER_INITIALIZED__ = true; /** * Click on an element matching the selector or at specific coordinates * @param {string} selector - CSS selector for the element to click * @param {boolean} waitForNavigation - Whether to wait for navigation to complete after click * @param {number} timeout - Timeout in milliseconds for waiting for the element or navigation * @param {Object} coordinates - Optional coordinates for clicking at a specific position * @param {number} coordinates.x - X coordinate relative to the viewport * @param {number} coordinates.y - Y coordinate relative to the viewport * @returns {Promise} - Result of the click operation */ async function clickElement( selector, waitForNavigation = false, timeout = 5000, coordinates = null, ) { try { let element = null; let elementInfo = null; let clickX, clickY; if (coordinates && typeof coordinates.x === 'number' && typeof coordinates.y === 'number') { clickX = coordinates.x; clickY = coordinates.y; element = document.elementFromPoint(clickX, clickY); if (element) { const rect = element.getBoundingClientRect(); elementInfo = { tagName: element.tagName, id: element.id, className: element.className, text: element.textContent?.trim().substring(0, 100) || '', href: element.href || null, type: element.type || null, isVisible: true, rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height, top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left, }, clickMethod: 'coordinates', clickPosition: { x: clickX, y: clickY }, }; } else { elementInfo = { clickMethod: 'coordinates', clickPosition: { x: clickX, y: clickY }, warning: 'No element found at the specified coordinates', }; } } else { element = document.querySelector(selector); if (!element) { return { error: `Element with selector "${selector}" not found`, }; } const rect = element.getBoundingClientRect(); elementInfo = { tagName: element.tagName, id: element.id, className: element.className, text: element.textContent?.trim().substring(0, 100) || '', href: element.href || null, type: element.type || null, isVisible: true, rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height, top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left, }, clickMethod: 'selector', }; // First sroll so that the element is in view, then check visibility. element.scrollIntoView({ behavior: 'auto', block: 'center', inline: 'center' }); await new Promise((resolve) => setTimeout(resolve, 100)); elementInfo.isVisible = isElementVisible(element); if (!elementInfo.isVisible) { return { error: `Element with selector "${selector}" is not visible`, elementInfo, }; } const updatedRect = element.getBoundingClientRect(); clickX = updatedRect.left + updatedRect.width / 2; clickY = updatedRect.top + updatedRect.height / 2; } let navigationPromise; if (waitForNavigation) { navigationPromise = new Promise((resolve) => { const beforeUnloadListener = () => { window.removeEventListener('beforeunload', beforeUnloadListener); resolve(true); }; window.addEventListener('beforeunload', beforeUnloadListener); setTimeout(() => { window.removeEventListener('beforeunload', beforeUnloadListener); resolve(false); }, timeout); }); } if (element && elementInfo.clickMethod === 'selector') { element.click(); } else { simulateClick(clickX, clickY); } // Wait for navigation if needed let navigationOccurred = false; if (waitForNavigation) { navigationOccurred = await navigationPromise; } return { success: true, message: 'Element clicked successfully', elementInfo, navigationOccurred, }; } catch (error) { return { error: `Error clicking element: ${error.message}`, }; } } /** * Simulate a mouse click at specific coordinates * @param {number} x - X coordinate relative to the viewport * @param {number} y - Y coordinate relative to the viewport */ function simulateClick(x, y) { const clickEvent = new MouseEvent('click', { view: window, bubbles: true, cancelable: true, clientX: x, clientY: y, }); const element = document.elementFromPoint(x, y); if (element) { element.dispatchEvent(clickEvent); } else { document.dispatchEvent(clickEvent); } } /** * Check if an element is visible * @param {Element} element - The element to check * @returns {boolean} - Whether the element is visible */ function isElementVisible(element) { if (!element) return false; const style = window.getComputedStyle(element); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return false; } const rect = element.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) { return false; } if ( rect.bottom < 0 || rect.top > window.innerHeight || rect.right < 0 || rect.left > window.innerWidth ) { return false; } const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const elementAtPoint = document.elementFromPoint(centerX, centerY); if (!elementAtPoint) return false; return element === elementAtPoint || element.contains(elementAtPoint); } // Listen for messages from the extension chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { if (request.action === 'clickElement') { clickElement( request.selector, request.waitForNavigation, request.timeout, request.coordinates, ) .then(sendResponse) .catch((error) => { sendResponse({ error: `Unexpected error: ${error.message}`, }); }); return true; // Indicates async response } else if (request.action === 'chrome_click_element_ping') { sendResponse({ status: 'pong' }); return false; } }); }