/* eslint-disable */ // enhanced-search-helper.js // Enhanced search automation with multiple submission methods if (window.__ENHANCED_SEARCH_HELPER_INITIALIZED__) { // Already initialized, skip } else { window.__ENHANCED_SEARCH_HELPER_INITIALIZED__ = true; /** * Perform Google search with enhanced reliability * @param {string} selector - CSS selector for the search box * @param {string} query - Search query * @returns {Promise} - Result of the search operation */ async function performGoogleSearch(selector, query) { try { console.log(`🔍 Attempting Google search with selector: ${selector}, query: ${query}`); // Find the search element const searchElement = document.querySelector(selector); if (!searchElement) { return { success: false, error: `Search element with selector "${selector}" not found`, }; } // Focus and clear the search box searchElement.focus(); await sleep(200); // Clear existing content searchElement.select(); await sleep(100); // Fill the search box searchElement.value = query; // Trigger input events to ensure the page recognizes the input searchElement.dispatchEvent(new Event('input', { bubbles: true })); searchElement.dispatchEvent(new Event('change', { bubbles: true })); await sleep(500); // Try multiple submission methods const submissionSuccess = await submitGoogleSearch(searchElement, query); if (submissionSuccess) { console.log(`✅ Google search submitted successfully using selector: ${selector}`); return { success: true, selector, query, method: submissionSuccess.method, }; } else { return { success: false, error: 'All submission methods failed', }; } } catch (error) { console.error('Error in performGoogleSearch:', error); return { success: false, error: `Unexpected error: ${error.message}`, }; } } /** * Try multiple methods to submit Google search * @param {Element} searchElement - The search input element * @param {string} query - Search query * @returns {Promise} - Success result or null */ async function submitGoogleSearch(searchElement, query) { const methods = [ { name: 'enter_key', action: async () => { console.log('🔄 Method 1: Trying Enter key'); searchElement.focus(); await sleep(200); const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true, }); searchElement.dispatchEvent(enterEvent); await sleep(1000); // Check if search was successful if (await checkSearchResultsLoaded()) { return { method: 'enter_key' }; } return null; }, }, { name: 'search_button', action: async () => { console.log('🔄 Method 2: Trying search button'); const buttonSelectors = [ 'input[value*="Google Search"]', 'button[aria-label*="Google Search"]', 'input[type="submit"][value*="Google Search"]', '.gNO89b', // Google Search button class 'center input[type="submit"]:first-of-type', 'button[type="submit"]', '[role="button"][aria-label*="search"]', '.Tg7LZd', ]; for (const buttonSelector of buttonSelectors) { try { const button = document.querySelector(buttonSelector); if (button) { button.click(); await sleep(1000); if (await checkSearchResultsLoaded()) { return { method: 'search_button', selector: buttonSelector }; } } } catch (e) { continue; } } return null; }, }, { name: 'form_submit', action: async () => { console.log('🔄 Method 3: Trying form submission'); const form = searchElement.closest('form'); if (form) { form.submit(); await sleep(1000); if (await checkSearchResultsLoaded()) { return { method: 'form_submit' }; } } return null; }, }, { name: 'double_enter', action: async () => { console.log('🔄 Method 4: Trying double Enter'); searchElement.focus(); await sleep(200); // First Enter const enterEvent1 = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true, }); searchElement.dispatchEvent(enterEvent1); await sleep(300); // Second Enter const enterEvent2 = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true, }); searchElement.dispatchEvent(enterEvent2); await sleep(1000); if (await checkSearchResultsLoaded()) { return { method: 'double_enter' }; } return null; }, }, ]; for (const method of methods) { try { const result = await method.action(); if (result) { console.log(`✅ Submission method "${method.name}" successful`); return result; } } catch (error) { console.debug(`Submission method "${method.name}" failed:`, error); continue; } } console.warn('❌ All submission methods failed'); return null; } /** * Check if Google search results have loaded * @returns {Promise} */ async function checkSearchResultsLoaded() { const resultIndicators = [ '#search', // Main search results container '#rso', // Results container '.g', // Individual result '.tF2Cxc', // Modern Google result container '#result-stats', // Search statistics '.yuRUbf', // Result link container ]; for (const indicator of resultIndicators) { const element = document.querySelector(indicator); if (element && element.children.length > 0) { return true; } } return false; } /** * Extract search results from the current page with intelligent selector discovery * @param {number} maxResults - Maximum number of results to extract * @returns {Promise} - Extracted results */ async function extractSearchResults(maxResults = 10) { try { console.log('🔍 Starting intelligent search result extraction...'); const results = []; // Try multiple selectors for Google search results const resultSelectors = [ '.tF2Cxc', // Current Google search result container '.g', // Traditional Google search result '#rso .g', // Results container with .g class '.yuRUbf', // Google result link container '.rc', // Another Google result class ]; let resultElements = []; let successfulSelector = null; // First try standard selectors for (const selector of resultSelectors) { resultElements = document.querySelectorAll(selector); if (resultElements.length > 0) { successfulSelector = selector; console.log(`✅ Found results with standard selector: ${selector}`); break; } } // If standard selectors fail, try intelligent discovery if (resultElements.length === 0) { console.log('🧠 Standard selectors failed, trying intelligent discovery...'); const discoveryResult = await discoverSearchResultElements(); resultElements = discoveryResult.elements; successfulSelector = discoveryResult.selector; } // Extract results from found elements for (let i = 0; i < Math.min(resultElements.length, maxResults); i++) { const element = resultElements[i]; try { const extractedResult = extractResultFromElement(element, i + 1); if (extractedResult) { results.push(extractedResult); } } catch (e) { console.debug(`Error extracting result ${i}:`, e); continue; } } return { success: true, results, totalFound: results.length, selectorUsed: successfulSelector, method: resultElements.length > 0 ? 'extraction' : 'none', }; } catch (error) { console.error('Error extracting search results:', error); return { success: false, error: error.message, results: [], }; } } /** * Intelligent discovery of search result elements * @returns {Object} - Object with elements array and successful selector */ async function discoverSearchResultElements() { console.log('🔬 Starting intelligent element discovery...'); // Intelligent selectors based on common patterns const intelligentSelectors = [ // Modern Google patterns (2024+) '[data-ved] h3', '[data-ved]:has(h3)', '[data-ved]:has(a[href*="http"])', '[jscontroller]:has(h3)', '[jscontroller]:has(a[href*="http"])', // Generic search result patterns 'div[class*="result"]:has(h3)', 'div[class*="search"]:has(h3)', 'article:has(h3)', 'li[class*="result"]:has(h3)', '[role="main"] div:has(h3)', // Link-based patterns 'a[href*="http"]:has(h3)', 'div:has(h3):has(a[href*="http"])', // Container patterns 'div[class*="container"] > div:has(h3)', 'div[id*="result"]:has(h3)', 'div[id*="search"]:has(h3)', // Semantic patterns '[role="article"]:has(h3)', '[role="listitem"]:has(h3)', 'div[aria-label*="result"]:has(h3)', // Fallback broad patterns 'main div:has(h3)', '#main div:has(h3)', '.main div:has(h3)', 'h3:has(+ div)', 'div:has(h3)', ]; for (const selector of intelligentSelectors) { try { const elements = document.querySelectorAll(selector); if (elements.length > 0) { // Validate that these look like search results const validElements = Array.from(elements).filter((el) => validateSearchResultElement(el), ); if (validElements.length > 0) { console.log( `✅ Found ${validElements.length} results with intelligent selector: ${selector}`, ); return { elements: validElements, selector: `intelligent-${selector}`, }; } } } catch (e) { console.debug(`Intelligent selector failed: ${selector}`, e); continue; } } // Final fallback - DOM structure analysis console.log('🔬 Trying DOM structure analysis...'); return analyzeDOMForSearchResults(); } /** * Validate that an element looks like a search result * @param {Element} element - Element to validate * @returns {boolean} - True if element looks like a search result */ function validateSearchResultElement(element) { try { // Check for common search result indicators const hasHeading = element.querySelector('h1, h2, h3, h4, h5, h6'); const hasLink = element.querySelector('a[href*="http"]'); const hasText = element.textContent && element.textContent.trim().length > 50; // Must have at least heading and link, or substantial text return (hasHeading && hasLink) || hasText; } catch (e) { return false; } } /** * Analyze DOM structure to find search results using heuristics * @returns {Object} - Object with elements array and successful selector */ function analyzeDOMForSearchResults() { console.log('🔬 Analyzing DOM structure for search results...'); try { // Look for containers with multiple links (likely search results) const heuristicSelectors = [ 'div:has(a[href*="http"]):has(h3)', 'li:has(a[href*="http"]):has(h3)', 'article:has(a[href*="http"])', 'main > div:has(h3)', '#main > div:has(h3)', '[role="main"] > div:has(h3)', 'div:has(h3):has(a[href*="http"])', 'section:has(h3):has(a[href*="http"])', ]; for (const selector of heuristicSelectors) { try { const elements = document.querySelectorAll(selector); if (elements.length > 0) { const validElements = Array.from(elements).filter((el) => validateSearchResultElement(el), ); if (validElements.length > 0) { console.log( `✅ Found ${validElements.length} results with DOM analysis: ${selector}`, ); return { elements: validElements, selector: `dom-analysis-${selector}`, }; } } } catch (e) { console.debug(`DOM analysis selector failed: ${selector}`, e); continue; } } // Ultimate fallback - any elements with links const fallbackElements = document.querySelectorAll('a[href*="http"]'); if (fallbackElements.length > 0) { console.log(`⚠️ Using fallback: found ${fallbackElements.length} link elements`); return { elements: Array.from(fallbackElements).slice(0, 10), // Limit to 10 selector: 'fallback-links', }; } console.warn('❌ DOM analysis failed to find any search results'); return { elements: [], selector: null, }; } catch (e) { console.error('Error in DOM analysis:', e); return { elements: [], selector: null, }; } } /** * Extract result data from a single element * @param {Element} element - Element to extract from * @param {number} index - Result index * @returns {Object|null} - Extracted result or null */ function extractResultFromElement(element, index) { try { // Try multiple patterns for title extraction const titleSelectors = ['h3', 'h2', 'h1', '.LC20lb', '.DKV0Md', 'a[href*="http"]']; let titleElement = null; for (const selector of titleSelectors) { titleElement = element.querySelector(selector); if (titleElement) break; } // Try multiple patterns for link extraction const linkElement = element.querySelector('a[href*="http"]') || (element.tagName === 'A' ? element : null); // Try multiple patterns for snippet extraction const snippetSelectors = ['.VwiC3b', '.s', '.st', 'p', 'div:not(:has(h1,h2,h3,h4,h5,h6))']; let snippetElement = null; for (const selector of snippetSelectors) { snippetElement = element.querySelector(selector); if (snippetElement && snippetElement.textContent.trim().length > 20) break; } // Extract data const title = titleElement?.textContent?.trim() || 'No title found'; const url = linkElement?.href || ''; const snippet = snippetElement?.textContent?.trim() || ''; // Validate we have meaningful data if (title && title !== 'No title found' && url) { return { title, url, snippet, index, }; } return null; } catch (e) { console.debug(`Error extracting from element:`, e); return null; } } /** * Sleep utility function * @param {number} ms - Milliseconds to sleep * @returns {Promise} */ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } // Listen for messages from the extension chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { if (request.action === 'performGoogleSearch') { performGoogleSearch(request.selector, request.query) .then(sendResponse) .catch((error) => { sendResponse({ success: false, error: `Unexpected error: ${error.message}`, }); }); return true; // Indicates async response } else if (request.action === 'extractSearchResults') { extractSearchResults(request.maxResults) .then(sendResponse) .catch((error) => { sendResponse({ success: false, error: `Unexpected error: ${error.message}`, results: [], }); }); return true; // Indicates async response } else if (request.action === 'enhanced_search_ping') { sendResponse({ status: 'pong' }); return false; } }); }