{"version":3,"file":"browser/utils.mjs","sources":["webpack://@agent-infra/browser-use/./src/browser/utils.ts"],"sourcesContent":["import {\n  type HTTPRequest,\n  type HTTPResponse,\n} from 'puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js';\nimport { ElementHandle, Frame, Page as PuppeteerPage } from 'puppeteer-core';\nimport {\n  BrowserContextConfig,\n  DEFAULT_BROWSER_CONTEXT_CONFIG,\n  PartialWithRequired,\n} from './types';\nimport { DOMElementNode } from '../dom/views';\n\nexport async function scrollIntoViewIfNeeded(\n  element: ElementHandle,\n  timeout = 2500,\n): Promise<void> {\n  const startTime = Date.now();\n\n  // eslint-disable-next-line no-constant-condition\n  while (true) {\n    // Check if element is in viewport\n    const isVisible = await element.evaluate((el) => {\n      const rect = el.getBoundingClientRect();\n\n      // Check if element has size\n      if (rect.width === 0 || rect.height === 0) return false;\n\n      // Check if element is hidden\n      const style = window.getComputedStyle(el);\n      if (\n        style.visibility === 'hidden' ||\n        style.display === 'none' ||\n        style.opacity === '0'\n      ) {\n        return false;\n      }\n\n      // Check if element is in viewport\n      const isInViewport =\n        rect.top >= 0 &&\n        rect.left >= 0 &&\n        rect.bottom <=\n          (window.innerHeight || document.documentElement.clientHeight) &&\n        rect.right <=\n          (window.innerWidth || document.documentElement.clientWidth);\n\n      if (!isInViewport) {\n        // Scroll into view if not visible\n        el.scrollIntoView({\n          behavior: 'auto',\n          block: 'center',\n          inline: 'center',\n        });\n        return false;\n      }\n\n      return true;\n    });\n\n    if (isVisible) break;\n\n    // Check timeout\n    if (Date.now() - startTime > timeout) {\n      throw new Error('Timed out while trying to scroll element into view');\n    }\n\n    // Small delay before next check\n    await new Promise((resolve) => setTimeout(resolve, 100));\n  }\n}\n\nexport async function locateElement(\n  page: PuppeteerPage,\n  element: DOMElementNode,\n  _options?: Partial<BrowserContextConfig>,\n): Promise<ElementHandle | null> {\n  const options = {\n    ...DEFAULT_BROWSER_CONTEXT_CONFIG,\n    ..._options,\n  };\n  if (!page) {\n    // throw new Error('Puppeteer page is not connected');\n    console.warn('Puppeteer is not connected');\n    return null;\n  }\n  let currentFrame: PuppeteerPage | Frame = page;\n\n  // Start with the target element and collect all parents\n  const parents: DOMElementNode[] = [];\n  let current = element;\n  while (current.parent) {\n    parents.push(current.parent);\n    current = current.parent;\n  }\n\n  // Process all iframe parents in sequence (in reverse order - top to bottom)\n  const iframes = parents.reverse().filter((item) => item.tagName === 'iframe');\n  for (const parent of iframes) {\n    const cssSelector = parent.enhancedCssSelectorForElement(\n      options.includeDynamicAttributes,\n    );\n    const frameElement: ElementHandle | null =\n      await currentFrame.$(cssSelector);\n    if (!frameElement) {\n      // throw new Error(`Could not find iframe with selector: ${cssSelector}`);\n      console.warn(`Could not find iframe with selector: ${cssSelector}`);\n      return null;\n    }\n    const frame: Frame | null = await frameElement.contentFrame();\n    if (!frame) {\n      // throw new Error(`Could not access frame content for selector: ${cssSelector}`);\n      console.warn(\n        `Could not access frame content for selector: ${cssSelector}`,\n      );\n      return null;\n    }\n    currentFrame = frame;\n  }\n\n  const cssSelector = element.enhancedCssSelectorForElement(\n    options.includeDynamicAttributes,\n  );\n\n  try {\n    const elementHandle: ElementHandle | null =\n      await currentFrame.$(cssSelector);\n    if (elementHandle) {\n      // Scroll element into view if needed\n      await scrollIntoViewIfNeeded(elementHandle);\n      return elementHandle;\n    }\n  } catch (error) {\n    console.error('Failed to locate element:', error);\n  }\n\n  return null;\n}\n\nexport async function waitForStableNetwork(\n  page: PuppeteerPage | null,\n  _options?: Partial<BrowserContextConfig>,\n) {\n  const options = {\n    ...DEFAULT_BROWSER_CONTEXT_CONFIG,\n    ..._options,\n  };\n  if (!page) {\n    throw new Error('Puppeteer page is not connected');\n  }\n\n  const RELEVANT_RESOURCE_TYPES = new Set([\n    'document',\n    'stylesheet',\n    'image',\n    'font',\n    'script',\n    'iframe',\n  ]);\n\n  const RELEVANT_CONTENT_TYPES = new Set([\n    'text/html',\n    'text/css',\n    'application/javascript',\n    'image/',\n    'font/',\n    'application/json',\n  ]);\n\n  const IGNORED_URL_PATTERNS = new Set([\n    // Analytics and tracking\n    'analytics',\n    'tracking',\n    'telemetry',\n    'beacon',\n    'metrics',\n    // Ad-related\n    'doubleclick',\n    'adsystem',\n    'adserver',\n    'advertising',\n    // Social media widgets\n    'facebook.com/plugins',\n    'platform.twitter',\n    'linkedin.com/embed',\n    // Live chat and support\n    'livechat',\n    'zendesk',\n    'intercom',\n    'crisp.chat',\n    'hotjar',\n    // Push notifications\n    'push-notifications',\n    'onesignal',\n    'pushwoosh',\n    // Background sync/heartbeat\n    'heartbeat',\n    'ping',\n    'alive',\n    // WebRTC and streaming\n    'webrtc',\n    'rtmp://',\n    'wss://',\n    // Common CDNs\n    'cloudfront.net',\n    'fastly.net',\n  ]);\n\n  const pendingRequests = new Set();\n  let lastActivity = Date.now();\n\n  const onRequest = (request: HTTPRequest) => {\n    // Filter by resource type\n    const resourceType = request.resourceType();\n    if (!RELEVANT_RESOURCE_TYPES.has(resourceType)) {\n      return;\n    }\n\n    // Filter out streaming, websocket, and other real-time requests\n    if (\n      ['websocket', 'media', 'eventsource', 'manifest', 'other'].includes(\n        resourceType,\n      )\n    ) {\n      return;\n    }\n\n    // Filter out by URL patterns\n    const url = request.url().toLowerCase();\n    if (\n      Array.from(IGNORED_URL_PATTERNS).some((pattern) => url.includes(pattern))\n    ) {\n      return;\n    }\n\n    // Filter out data URLs and blob URLs\n    if (url.startsWith('data:') || url.startsWith('blob:')) {\n      return;\n    }\n\n    // Filter out requests with certain headers\n    const headers = request.headers();\n    if (\n      // biome-ignore lint/complexity/useLiteralKeys: <explanation>\n      headers['purpose'] === 'prefetch' ||\n      headers['sec-fetch-dest'] === 'video' ||\n      headers['sec-fetch-dest'] === 'audio'\n    ) {\n      return;\n    }\n\n    pendingRequests.add(request);\n    lastActivity = Date.now();\n  };\n\n  const onResponse = (response: HTTPResponse) => {\n    const request = response.request();\n    if (!pendingRequests.has(request)) {\n      return;\n    }\n\n    // Filter by content type\n    const contentType = response.headers()['content-type']?.toLowerCase() || '';\n\n    // Skip streaming content\n    if (\n      [\n        'streaming',\n        'video',\n        'audio',\n        'webm',\n        'mp4',\n        'event-stream',\n        'websocket',\n        'protobuf',\n      ].some((t) => contentType.includes(t))\n    ) {\n      pendingRequests.delete(request);\n      return;\n    }\n\n    // Only process relevant content types\n    if (\n      !Array.from(RELEVANT_CONTENT_TYPES).some((ct) => contentType.includes(ct))\n    ) {\n      pendingRequests.delete(request);\n      return;\n    }\n\n    // Skip large responses\n    const contentLength = response.headers()['content-length'];\n    if (contentLength && Number.parseInt(contentLength) > 5 * 1024 * 1024) {\n      // 5MB\n      pendingRequests.delete(request);\n      return;\n    }\n\n    pendingRequests.delete(request);\n    lastActivity = Date.now();\n  };\n\n  // Add event listeners\n  page.on('request', onRequest as any);\n  page.on('response', onResponse as any);\n\n  try {\n    const startTime = Date.now();\n\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n      await new Promise((resolve) => setTimeout(resolve, 100));\n\n      const now = Date.now();\n      const timeSinceLastActivity = (now - lastActivity) / 1000; // Convert to seconds\n\n      if (\n        pendingRequests.size === 0 &&\n        timeSinceLastActivity >= options.waitForNetworkIdlePageLoadTime\n      ) {\n        break;\n      }\n\n      const elapsedTime = (now - startTime) / 1000; // Convert to seconds\n      if (elapsedTime > options.maximumWaitPageLoadTime) {\n        console.debug(\n          `Network timeout after ${options.maximumWaitPageLoadTime}s with ${pendingRequests.size} pending requests:`,\n          Array.from(pendingRequests).map((r) => (r as HTTPRequest).url()),\n        );\n        break;\n      }\n    }\n  } finally {\n    // Clean up event listeners\n    page.off('request', onRequest as any);\n    page.off('response', onResponse as any);\n  }\n  console.debug(\n    `Network stabilized for ${options.waitForNetworkIdlePageLoadTime} seconds`,\n  );\n}\n\nexport async function waitForPageAndFramesLoad(\n  page: PuppeteerPage | null,\n  /** timeout in seconds */\n  timeoutOverwrite?: number,\n  _options?: Partial<BrowserContextConfig>,\n): Promise<void> {\n  const options = {\n    ...DEFAULT_BROWSER_CONTEXT_CONFIG,\n    ..._options,\n  };\n  // Start timing\n  const startTime = Date.now();\n\n  // Wait for page load\n  try {\n    await waitForStableNetwork(page, options);\n  } catch (error) {\n    console.warn('Page load failed, continuing...');\n  }\n\n  // Calculate remaining time to meet minimum wait time\n  const elapsed = (Date.now() - startTime) / 1000; // Convert to seconds\n  const minWaitTime = timeoutOverwrite || options.minimumWaitPageLoadTime;\n  const remaining = Math.max(minWaitTime - elapsed, 0);\n\n  console.debug(\n    `--Page loaded in ${elapsed.toFixed(2)} seconds, waiting for additional ${remaining.toFixed(2)} seconds`,\n  );\n\n  // Sleep remaining time if needed\n  if (remaining > 0) {\n    await new Promise((resolve) => setTimeout(resolve, remaining * 1000)); // Convert seconds to milliseconds\n  }\n}\n\nexport async function waitForPageLoadState(\n  page: PuppeteerPage,\n  timeout?: number,\n) {\n  const timeoutValue = timeout || 8000;\n  await page?.waitForNavigation({ timeout: timeoutValue });\n}\n"],"names":["scrollIntoViewIfNeeded","element","timeout","startTime","Date","isVisible","el","rect","style","window","isInViewport","document","Error","Promise","resolve","setTimeout","locateElement","page","_options","options","DEFAULT_BROWSER_CONTEXT_CONFIG","console","currentFrame","parents","current","iframes","item","parent","cssSelector","frameElement","frame","elementHandle","error","waitForStableNetwork","RELEVANT_RESOURCE_TYPES","Set","RELEVANT_CONTENT_TYPES","IGNORED_URL_PATTERNS","pendingRequests","lastActivity","onRequest","request","resourceType","url","Array","pattern","headers","onResponse","response","_response_headers_contenttype","contentType","t","ct","contentLength","Number","now","timeSinceLastActivity","elapsedTime","r","waitForPageAndFramesLoad","timeoutOverwrite","elapsed","minWaitTime","remaining","Math","waitForPageLoadState","timeoutValue"],"mappings":"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"}