Puppeteer 的 Browser.extensions():获取浏览器已安装扩展清单的实现原理与实战用法
Puppeteer 的 Browser.extensions()获取浏览器已安装扩展清单的实现原理与实战用法【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer本文以 Puppeteer API 文档中的Browser.extensions()方法为核心讲解它的签名、返回值Mapstring, Extension的结构以及背后的Extension实例能提供哪些能力扩展 ID、版本、名称、路径、启停状态以及 service worker、扩展页面和动作触发。结合仓库中的 CDP 协议实现、BiDi 实现限制与测试用例读完后你不仅知道如何列出浏览器中的扩展还能理解该方法的底层调用链、缓存机制及其适用前提。方法签名与返回值API 文档 对该方法的定义非常简洁它用于获取浏览器中所有已安装扩展的映射表其中键是扩展 ID值是对应的 Extension 实例。class Browser { abstract extensions(): PromiseMapstring, Extension; }返回值PromiseMapstring, Extension从签名可以看出三个要点异步方法返回 Promise必须await后才能拿到 Map键值结构以扩展 IDChrome 中形如 32 位随机字母串的extensionId为键天然支持extensions.get(extensionId)直接按键取用抽象方法Browser是抽象类该方法声明为abstract具体行为由各协议CDP / BiDi的子类实现这也是不同协议间行为差异的根源见后文 BiDi 限制一节。在 抽象接口源码 中可以看到完整定义/** * Retrieves a map of all extensions installed in the browser, where the keys * are extension IDs and the values are the corresponding {link Extension} instances. * * public */ abstract extensions(): PromiseMapstring, Extension;返回值中的 Extension 实例extensions()返回的每个值都是 Extension 抽象类的实例。该类的构造器标记为内部internal第三方代码不应直接 new 或继承它实例完全由 Puppeteer 内部根据协议响应创建。Extension 提供 5 个只读属性和 3 个实例方法见 Extension 文档成员类型说明idstring扩展的唯一标识符versionstring扩展 manifest 中声明的版本号namestring扩展 manifest 中声明的名称pathstring扩展在文件系统中的路径enabledboolean扩展是否处于启用状态workers()PromiseWebWorker[]当前活跃的扩展 service worker 列表pages()PromisePage[]当前活跃且可见的扩展页面列表triggerAction(page)Promisevoid在指定页面上触发扩展默认动作模拟点击工具栏动作图标在 Extension 源码 中构造器对id和version做了非空校验缺失任一项会直接抛出Extension ID and version are required错误说明这两个字段是扩展身份的最小必需集constructor( id: string, version: string, name: string, path: string, enabled: boolean, ) { if (!id || !version) { throw new Error(Extension ID and version are required); } // ... }CDP 实现Extensions.getExtensions 与实例缓存extensions()的 CDP 协议实现位于 CdpBrowseroverride async extensions(): PromiseMapstring, Extension { const response await this.#connection.send(Extensions.getExtensions); const extensionsMap new Mapstring, Extension(); for (const currExtension of response.extensions) { if (this.#extensions.has(currExtension.id)) { extensionsMap.set( currExtension.id, this.#extensions.get(currExtension.id)!, ); } else { const newExtension new CdpExtension( currExtension.id, currExtension.version, currExtension.name, currExtension.path, currExtension.enabled, this, this.logger, ); extensionsMap.set(currExtension.id, newExtension); } } this.#extensions extensionsMap; return this.#extensions; }从源码结构看该实现包含两层逻辑协议调用底层是 CDP 的Extensions.getExtensions命令浏览器端一次性返回id / version / name / path / enabled五个字段正好对应 Extension 的五个属性实例缓存CdpBrowser内部维护#extensions缓存 Map。重复调用extensions()时已见过的扩展 ID 会复用同一个CdpExtension对象只有新安装的扩展才会新建实例。这意味着在同一浏览器会话中不同时刻拿到的同一个扩展的Extension实例是稳定的相等可以放心持有引用。每个新建的实例是 CdpExtension它在抽象类之外补充了具体行为。以workers()为例它通过过滤browser.targets()中类型为service_worker且 URL 以chrome-extension://id开头的 target 来定位扩展的后端 worker源码 L36-L64pages()则过滤page/background_page类型且同前缀 URL 的 targetL66-L94。值得注意的是这些方法对已关闭的 target 做了容错——若 worker 或 page 在取值过程中关闭会忽略target closed类错误并继续返回其余结果而不是整体抛错。triggerAction(page)则直接下发 CDP 命令Extensions.triggerAction携带扩展 ID 与目标页签 IDL96-L101效果等同于用户在工具栏点击扩展动作图标。适用前提与 BiDi 限制该方法的可用性取决于底层协议。在 BiDi 协议的浏览器实现中BidiBrowser.extensions() 直接抛出不支持错误override extensions(): PromiseMapstring, Extension { throw new UnsupportedOperation(); }也就是说Chrome/Chromium 走 CDP 协议Puppeteer 的默认协议时extensions()可正常使用Firefox 或启用 BiDi 协议的场景下该方法不可用会抛出UnsupportedOperation错误使用扩展功能还需要先通过LaunchOptions.enableExtensions允许扩展加载或运行时调用browser.installExtension()安装具体操作参考官方指南 Chrome Extensions。实战列出扩展并读取其属性官方指南 chrome-extensions.md 给出了与本文方法配套的标准用法。先安装扩展再用extensions()按 ID 取出实例并读取属性import puppeteer from puppeteer; import path from path; const pathToExtension path.join(process.cwd(), my-extension); const browser await puppeteer.launch({ enableExtensions: true, }); // 运行时安装扩展返回扩展 ID const extensionId await browser.installExtension(pathToExtension); // 列出所有已安装扩展按键取用 const extensions await browser.extensions(); const extension extensions.get(extensionId); console.log(extension?.name); // manifest 中的名称 console.log(extension?.version); // manifest 中的版本 console.log(extension?.path); // 扩展在文件系统中的路径 console.log(extension?.enabled); // 是否启用 // 卸载 await browser.uninstallExtension(extensionId);也可以在启动时直接声明加载的扩展路径const browser await puppeteer.launch({ enableExtensions: [pathToExtension], });拿到Extension实例后还可以进一步与页面交互await extension.triggerAction(page)触发扩展动作await extension.workers()获取 service worker 以便在其中执行evaluateawait extension.pages()获取扩展页面。测试用例中的行为印证仓库测试 test/src/cdp/extensions.test.ts 中的should list extensions and their properties用例验证了该方法的核心契约const extensionId await browser.installExtension(extensionPath); const target await browser.waitForTarget(target { return ( target.url().includes(extensionId) target.type() service_worker ); }); const extensions await browser.extensions(); const extension extensions.get(extensionId); expect(extension).toBeDefined(); expect(extension?.name).toBe(Simple extension); expect(extension?.version).toBe(0.1); expect(extension?.path).toBe(extensionPath); expect(extension?.enabled).toBe(true); expect(extension?.id).toBe(extensionId);该测试确认了三点Map 以installExtension返回的 ID 为键可命中name、version取自扩展的 manifestpath、enabled字段分别对应安装路径与启用状态。同文件中的should list extension workers用例L96-L116则验证了extensions().get(id)返回的实例可直接调用triggerAction(page)与workers()形成“列出扩展 → 触发动作 → 获取 worker”的完整闭环。小结Browser.extensions()是 Puppeteer 扩展测试体系的查询入口一次Extensions.getExtensions协议调用即可拿到全部扩展及其元数据内部缓存保证实例稳定。使用时需记住两个前提——走 CDP 协议、先允许扩展加载enableExtensions或installExtension而在 BiDi 协议下该方法会抛出UnsupportedOperation此时应回退到 CDP 或使用browser.waitForTarget()等 target 级 API 定位扩展的 service worker 与页面。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →