{"version":3,"file":"browser/context.mjs","sources":["webpack://@agent-infra/browser-use/./src/browser/context.ts"],"sourcesContent":["/**\n * The following code is modified based on\n * https://github.com/nanobrowser/nanobrowser/blob/master/chrome-extension/src/background/browser/context.ts\n *\n * Apache-2.0 License\n * Copyright (c) 2024 alexchenzl\n * https://github.com/nanobrowser/nanobrowser/blob/master/LICENSE\n */\nimport {\n  type BrowserContextConfig,\n  type BrowserState,\n  DEFAULT_BROWSER_CONTEXT_CONFIG,\n} from './types';\nimport Page, { build_initial_state } from './page';\nimport { createLogger } from '../utils';\n\nconst logger = createLogger('BrowserContext');\nexport default class BrowserContext {\n  private readonly _config: BrowserContextConfig;\n  private _currentTabId: string | null = null;\n  private _attachedPages: Map<string, Page> = new Map();\n\n  constructor(config: Partial<BrowserContextConfig>) {\n    this._config = { ...DEFAULT_BROWSER_CONTEXT_CONFIG, ...config };\n  }\n\n  public getConfig(): BrowserContextConfig {\n    return this._config;\n  }\n\n  public updateCurrentTabId(url: string): void {\n    // only update tab id, but don't attach it.\n    this._currentTabId = url;\n  }\n\n  private async _getOrCreatePage(\n    url: string,\n    forceUpdate = false,\n  ): Promise<Page> {\n    const existingPage = this._attachedPages.get(url);\n    if (existingPage) {\n      logger.info('getOrCreatePage', url, 'already attached');\n      if (!forceUpdate) {\n        return existingPage;\n      }\n      // detach the page and remove it from the attached pages if forceUpdate is true\n      await existingPage.detachPuppeteer();\n      this._attachedPages.delete(url);\n    }\n    logger.info('getOrCreatePage', url, 'creating new page');\n    return new Page(url || '', '', this._config);\n  }\n\n  public async cleanup(): Promise<void> {\n    const currentPage = await this.getCurrentPage();\n    currentPage?.removeHighlight();\n    // detach all pages\n    for (const page of this._attachedPages.values()) {\n      await page.detachPuppeteer();\n    }\n    this._attachedPages.clear();\n    this._currentTabId = null;\n  }\n\n  public async attachPage(page: Page): Promise<boolean> {\n    // check if page is already attached\n    if (this._attachedPages.has(page.url())) {\n      logger.info('attachPage_has', page.url(), 'already attached');\n      return true;\n    }\n\n    if (await page.attachPuppeteer()) {\n      logger.info('attachPage', page.url(), 'attached');\n      // add page to managed pages\n      this._attachedPages.set(page.url(), page);\n      return true;\n    }\n    return false;\n  }\n\n  public async detachPage(url: string): Promise<void> {\n    // detach page\n    const page = this._attachedPages.get(url);\n    if (page) {\n      await page.detachPuppeteer();\n      // remove page from managed pages\n      this._attachedPages.delete(url);\n    }\n  }\n\n  public async getCurrentPage(): Promise<Page> {\n    // 1. If _currentTabId not set, query the active tab and attach it\n    if (!this._currentTabId) {\n      const page = await this._getOrCreatePage('');\n      await this.attachPage(page);\n      this._currentTabId = page.url();\n      return page;\n    }\n\n    // 2. If _currentTabId is set but not in attachedPages, attach the tab\n    const existingPage = this._attachedPages.get(this._currentTabId);\n    if (!existingPage) {\n      const page = await this._getOrCreatePage(this._currentTabId);\n      // set current tab id to null if the page is not attached successfully\n      await this.attachPage(page);\n      return page;\n    }\n\n    // 3. Return existing page from attachedPages\n    return existingPage;\n  }\n\n  /**\n   * Get all tab IDs from the browser and the current window.\n   * @returns A set of tab IDs.\n   */\n  public async getAllTabIds(): Promise<Set<string>> {\n    const currentPage = await this.getCurrentPage();\n\n    if (!currentPage || !currentPage.attached) {\n      logger.warning('No attached page found');\n      return new Set<string>();\n    }\n\n    const pages = await currentPage.browser?.pages();\n    const tabs =\n      pages?.map((page) => ({\n        url: page.url(),\n      })) || [];\n    return new Set(\n      tabs.map((tab) => tab.url).filter((url) => url !== undefined),\n    );\n  }\n\n  private async waitForPageEvents(\n    url: string,\n    options: {\n      waitForNavigation?: boolean;\n      waitForLoad?: boolean;\n      timeoutMs?: number;\n    } = {},\n  ): Promise<void> {\n    const {\n      waitForNavigation = true,\n      waitForLoad = true,\n      timeoutMs = 5000,\n    } = options;\n\n    const page = this._attachedPages.get(url);\n    if (!page || !page.attached || !page.puppeteerPage) {\n      logger.warning(`waitForPageEvents failed, page ${url} is not attached`);\n      return;\n    }\n\n    const puppeteerPage = page.puppeteerPage;\n    const promises: Promise<unknown>[] = [];\n\n    if (waitForNavigation) {\n      // wait for navigation to complete\n      const navPromise = puppeteerPage\n        .waitForNavigation({\n          timeout: timeoutMs,\n          waitUntil: 'domcontentloaded',\n        })\n        .catch((err) => {\n          logger.debug(\n            `waitForPageEvents failed, navigation error: ${err.message}`,\n          );\n        });\n      promises.push(navPromise);\n    }\n\n    if (waitForLoad) {\n      // wait for page to load\n      const loadPromise = puppeteerPage\n        .waitForNavigation({\n          timeout: timeoutMs,\n          waitUntil: 'load',\n        })\n        .catch((err) => {\n          logger.debug(`waitForPageEvents failed, load error: ${err.message}`);\n        });\n      promises.push(loadPromise);\n\n      const networkPromise = puppeteerPage\n        .waitForNavigation({\n          timeout: timeoutMs,\n          waitUntil: 'networkidle0',\n        })\n        .catch((err) => {\n          logger.debug(\n            `waitForPageEvents failed, network idle error: ${err.message}`,\n          );\n        });\n      promises.push(networkPromise);\n    }\n\n    // set timeout\n    const timeoutPromise = new Promise<never>((_, reject) =>\n      setTimeout(\n        () =>\n          reject(new Error(`page operation timeout, waited ${timeoutMs} ms`)),\n        timeoutMs,\n      ),\n    );\n\n    // wait for any navigation to complete or timeout\n    await Promise.race([Promise.all(promises), timeoutPromise]).catch(\n      (error) => {\n        // if timeout error, throw\n        if (error.message.includes('page operation timeout')) {\n          throw error;\n        }\n        // other errors may be because navigation is complete, we can continue\n        logger.debug(`navigation error: ${error.message}`);\n      },\n    );\n\n    // 确认页面内容已加载\n    try {\n      // 等待页面中的 body 元素出现，确保 DOM 已加载\n      await puppeteerPage.waitForSelector('body', { timeout: timeoutMs / 2 });\n    } catch (error) {\n      logger.warning(`waitForPageEvents failed, body not found: ${error}`);\n    }\n  }\n\n  public async switchTab(url: string): Promise<Page> {\n    logger.info('switchTab', url);\n\n    const page = await this._getOrCreatePage(url);\n    await this.attachPage(page);\n    this._currentTabId = url;\n    return page;\n  }\n\n  public async navigateTo(url: string): Promise<void> {\n    const page = await this.getCurrentPage();\n    if (!page) {\n      await this.openTab(url);\n      return;\n    }\n    // if page is attached, use puppeteer to navigate to the url\n    if (page.attached) {\n      await page.navigateTo(url);\n      return;\n    }\n\n    // Reattach the page after navigation completes\n    const updatedPage = await this._getOrCreatePage(url, true);\n    await this.attachPage(updatedPage);\n    this._currentTabId = url;\n  }\n\n  public async openTab(url: string): Promise<Page> {\n    // Create and attach the page after tab is fully loaded and activated\n    const page = await this._getOrCreatePage(url);\n    await this.attachPage(page);\n    this._currentTabId = url;\n\n    return page;\n  }\n\n  public async closeTab(url: string): Promise<void> {\n    await this.detachPage(url);\n    // update current tab id if needed\n    if (this._currentTabId === url) {\n      this._currentTabId = null;\n    }\n  }\n\n  /**\n   * Remove a tab from the attached pages map. This will not run detachPuppeteer.\n   * @param tabId - The ID of the tab to remove.\n   */\n  public removeAttachedPage(url: string): void {\n    this._attachedPages.delete(url);\n    // update current tab id if needed\n    if (this._currentTabId === url) {\n      this._currentTabId = null;\n    }\n  }\n\n  public async getState(): Promise<BrowserState> {\n    const currentPage = await this.getCurrentPage();\n\n    const pageState = !currentPage\n      ? build_initial_state()\n      : await currentPage.getState();\n\n    const pages = await currentPage.browser?.pages();\n    const pageInfos = await Promise.all(\n      pages?.map(async (page) => ({\n        id: page.url(),\n        url: page.url(),\n        title: await page.title(),\n      })) || [],\n    );\n\n    const browserState: BrowserState = {\n      ...pageState,\n      pages: pageInfos,\n    };\n    return browserState;\n  }\n\n  public async removeHighlight(): Promise<void> {\n    const page = await this.getCurrentPage();\n    if (page) {\n      await page.removeHighlight();\n    }\n  }\n}\n"],"names":["logger","createLogger","BrowserContext","url","forceUpdate","existingPage","Page","currentPage","page","_currentPage_browser","Set","pages","tabs","tab","undefined","options","waitForNavigation","waitForLoad","timeoutMs","puppeteerPage","promises","navPromise","err","loadPromise","networkPromise","timeoutPromise","Promise","_","reject","setTimeout","Error","error","updatedPage","pageState","build_initial_state","pageInfos","browserState","config","Map","DEFAULT_BROWSER_CONTEXT_CONFIG"],"mappings":"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"}