Puppeteer 扩展隔离世界实操:用 page.extensionRealms() 驱动 Chrome 扩展内容脚本

发布时间:2026/9/8 23:33:35

Puppeteer 扩展隔离世界实操:用 page.extensionRealms() 驱动 Chrome 扩展内容脚本
Puppeteer 扩展隔离世界实操用 page.extensionRealms() 驱动 Chrome 扩展内容脚本【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer导读本文聚焦 PuppeteerJavaScript API for Chrome and Firefox中用于与Chrome 扩展内容脚本content scripts执行环境交互的Page.extensionRealms()方法它返回页面主 frame 上由扩展注入内容脚本所创建的扩展执行世界extension execution realm列表配合Realm上的evaluate()等方法可以让自动化测试代码真正运行在内容脚本的上下文中。读完本文你将掌握扩展隔离世界的底层模型、如何按扩展 ID 定位目标 realm以及如何在自己的 E2E 测试中注入并断言内容脚本的执行结果。什么是扩展执行世界Extension Execution RealmChrome 扩展的内容脚本默认运行在隔离世界isolated world中它可以访问 DOM但与页面主世界main world的 JavaScript 全局对象相互隔离二者不能直接共享变量。在 Puppeteer 的 Realm 抽象下这种由扩展内容脚本在页面 frame 中创建的隔离世界就被称为extension execution realm / extension realm。Page.extensionRealms() 的 API 文档这样描述其语义Retrieves the list of extension execution realms in the main frame of the page. These realms correspond to extension content scripts running on the page.也就是说该方法返回页面主 frame上所有正在运行的扩展内容脚本所对应的执行世界。注意两个关键限定返回的 realm 来自page.mainFrame()主 frame并不包含 iframe 中的内容脚本世界只要某扩展的内容脚本注入了该页面它的隔离世界就会出现在列表中无论脚本此刻是否正在执行任务。从方法签名看该方法在Page上被声明为抽象方法abstract语义上等价于对主 frame 调用extensionRealms()class Page { abstract extensionRealms(): Realm[]; }从 Page 到 FrameAPI 继承关系Page.extensionRealms()实际上是 Frame.extensionRealms() 的快捷方式shortcut。API 文档原文明确写到Shortcut formainFrame().extensionRealms().二者在 Puppeteer 公共 API 层均被抽象声明分别在 api/Page.ts 与 api/Frame.ts 中可以找到对应的public抽象方法// api/Frame.ts /** * Retrieves the list of extension execution realms associated with this frame. * Extension execution realms are created by extension content scripts injected * into the frame. */ abstract extensionRealms(): Realm[]; // api/Page.ts —— 快捷方式注释与抽象声明 /** * Retrieves the list of extension execution realms in the main frame of the page. * Shortcut for Frame.extensionRealms | mainFrame().extensionRealms(). */ abstract extensionRealms(): Realm[];而具体协议实现则由 CDP 与 WebDriver BiDi 两个后端的页面/frame 子类各自完成。例如 CDP 后端在 cdp/Frame.ts 中的实现非常直观——直接返回该 frame 内部维护的全部扩展世界对象override extensionRealms(): Realm[] { return Object.values(this.extensionWorlds); }从这段实现可以推断CDP 后端会为每个 frame 维护一张「扩展世界注册表」凡是进入该页面的扩展内容脚本世界都会登记其中extensionRealms()只是把它暴露出来。BiDi 后端同样在 bidi/Frame.ts 中提供了对应的重写实现BiDi 的 page 类路径见 bidi/Page.ts。返回值 Realm内容脚本世界的操作入口返回的每个元素都是 Realm 类的实例。它是对一个 JavaScript 执行环境world/context的抽象在扩展场景下通常对应扩展内容脚本所在的隔离世界。Realm 类提供以下与扩展调试相关的成员详见其 API 文档与 api/Realm.ts 源码成员类型/签名说明originstring \| undefined只读Experimental创建该 Realm 的来源。若是扩展内容脚本创建的世界通常返回形如chrome-extension://extension-id的扩展 originextension()PromiseExtension \| nullExperimental返回创建该 Realm 的 Extension 对象若 realm 由扩展注入产生通常会被填充evaluate(pageFunction, args)Promise...在 realm 的上下文中求值函数并返回结果支持 Promise 结果与 JSHandle 参数evaluateHandle(pageFunction, args)PromiseJSHandle同上但返回结果的 JSHandle 引用waitForFunction(pageFunction, options, args)PromiseJSHandle在 realm 上下文中轮询等待某函数返回真值[disposeSymbol]()—释放该 realm由于Page类本身也继承自Realm代表主世界执行环境Realm实际上是 Puppeteer 中Page、Frame与各类 worker 求值能力的公共基类。前置条件让扩展先运行起来extensionRealms()依赖扩展真正被加载并注入页面因此通常需要配合以下任一方式先启动扩展详见 Chrome Extensions 指南方式一启动时通过 launch 选项加载import puppeteer from puppeteer; import path from path; const pathToExtension path.join(process.cwd(), my-extension); const browser await puppeteer.launch({ enableExtensions: [pathToExtension], });方式二运行时动态安装const browser await puppeteer.launch({enableExtensions: true}); const extensionId await browser.installExtension(pathToExtension);无论哪种方式之后用browser.newPage()创建页面并page.goto()导航到会注入内容脚本的网址扩展内容脚本的隔离世界就会随之出现在page.extensionRealms()的结果中。实战定位扩展世界并在内容脚本上下文中求值为什么需要在内容脚本的上下文里求值因为页面主世界的window与扩展隔离世界并不共享变量——内容脚本写入window的全局数据、它修改的 DOM 属性乃至注入的元素都需要在那个隔离世界内才能真正读取到。page.extensionRealms()正是为这类断言而生的入口。Chrome Extensions 指南 给出了完整的推荐流程先用browser.installExtension()拿到扩展 ID再遍历page.extensionRealms()借助realm.extension()返回的 Extension 对象按id精确匹配目标扩展最后在选中的 realm 上调用evaluate()import puppeteer from puppeteer; import path from path; const pathToExtension path.join(process.cwd(), my-extension); const browser await puppeteer.launch({enableExtensions: [pathToExtension]}); // 1. 打开一个会触发内容脚本注入的页面 const page await browser.newPage(); await page.goto(https://example.com); // 2. 安装扩展并获取其 ID const extensionId await browser.installExtension(pathToExtension); // 3. 在主 frame 的扩展世界中找到属于该扩展的那一个 const realms page.extensionRealms(); let extensionRealm; for (const realm of realms) { const extension await realm.extension(); if (extension?.id extensionId) { extensionRealm realm; break; } } if (!extensionRealm) { throw new Error(Extension realm not found); } // 4. 在内容脚本上下文内执行代码 const result await extensionRealm.evaluate(() { return document.title; }); console.log(result); await browser.close();也可以利用Realm.origin进行更轻量的筛选——扩展世界通常带有形如chrome-extension://id的 originExperimentalconst target page .extensionRealms() .find(realm realm.origin?.includes(extensionId));为什么需要按 ID 匹配而不是直接取第一个同一页面可能同时注入多个扩展的内容脚本extensionRealms()返回的数组顺序并不代表任何优先级。若直接把数组第一项当作目标扩展的世界可能匹配到错误扩展甚至非扩展来源的世界。因此在选取 realm 前应先判断extension()返回对象或origin是否与目标扩展 ID 相符正如上文realm.extension()返回的 Extension 带有id、name、version、enabled、path等可核对属性。边界情况与常见报错找不到目标 realm抛Extension realm not found通常是内容脚本未匹配当前 URL、扩展未启用或注入时机早于installExtension()/导航完成。可先确认扩展的 manifest 中content_scripts.matches覆盖了所访问页面。主 frame 局限page.extensionRealms()只覆盖主 frame。若内容脚本运行在 iframe 中需改用frame.extensionRealms()配合page.frames()遍历子 frame 找到目标 frame来完成。注入时序extensionRealms()是同步快照式查询若内容脚本尚未注入完成列表可能为空必要时结合waitForFunction或重试逻辑等待。深度理解这套 API 在自动化中的定位page.extensionRealms()是 Puppeteer Chrome 扩展 E2E 测试体系中的一环与其配套的扩展能力还包括后台上下文访问通过browser.waitForTarget()等待service_workerMV3或background_pageMV2类型的 target再经target.worker()/target.page()操作扩展后台弹出页测试extension.triggerAction(page)或page.triggerExtensionAction(extension)模拟用户点击工具栏图标后等待 popup target扩展实例管理Extension 类下的pages()、workers()方法以及browser.extensions()/browser.uninstallExtension()。从源码结构看整套扩展能力均建立在统一抽象的Extension、Realm与 Page/Frame 之上并同时落地于 CDP 与 BiDi 两条协议链路这意味着无论在传统 CDP 还是 WebDriver BiDi 模式下编写扩展测试extensionRealms()的调用方式都保持一致。仓库中 test/src/cdp/extensions.test.ts 与 test/src/cdp/realms.test.ts 即为覆盖扩展加载、realm 枚举与求值链路的测试用例可作为编写自己测试的参考蓝本。小结page.extensionRealms()返回主 frame上扩展内容脚本的隔离世界列表是mainFrame().extensionRealms()的快捷方式二者在 api/Page.ts 与 api/Frame.ts 中均有抽象声明。返回的Realm提供evaluate()、evaluateHandle()、waitForFunction()、origin与extension()等成员是「在扩展上下文中执行断言」的关键入口。CDP 后端通过 frame 内部维护的extensionWorlds注册表返回全部扩展世界见 cdp/Frame.tsBiDi 后端亦提供对应实现。实战时结合browser.launch({enableExtensions})/browser.installExtension()与按扩展 ID 的 realm 匹配即可在内容脚本上下文内可靠地求值并断言结果。【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

飞控入门学习路径:从姿态解算到自定义模式实战

飞控入门学习路径:从姿态解算到自定义模式实战

2026/9/8 23:33:35

简介:面向飞控初学者的系统化学习资料包,围绕飞行控制系统的核心环节展开,涵盖单片机基础、GPS定位原理、传感器数据处理与飞控算法入门,并配套模块资料和视频讲解,帮助读者从硬件搭建到代码调试逐步建立完整知识框架。…

get-shit-done 模型解析单一事实源改造:Model Catalog 模块如何终结 resolve-model 的 SDK/CLI 模型漂移(3229)

get-shit-done 模型解析单一事实源改造:Model Catalog 模块如何终结 resolve-model 的 SDK/CLI 模型漂移(3229)

2026/9/8 23:23:34

get-shit-done 模型解析单一事实源改造:Model Catalog 模块如何终结 resolve-model 的 SDK/CLI 模型漂移(#3229) 【免费下载链接】get-shit-done A light-weight and powerful meta-prompting, context engineering and spec-driven developm…

FastAPI 高级指南:在 OpenAPI 中声明附加响应(Additional Responses)的完整实战解析

FastAPI 高级指南:在 OpenAPI 中声明附加响应(Additional Responses)的完整实战解析

2026/9/8 23:23:34

FastAPI 高级指南:在 OpenAPI 中声明附加响应(Additional Responses)的完整实战解析 【免费下载链接】fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production 项目地址: https://gitcode.com/G…

沙迪克操作面板详解:从按键布局到坐标设定与菜单逻辑

沙迪克操作面板详解:从按键布局到坐标设定与菜单逻辑

2026/9/9 0:23:37

简介:这份资源是SODICK(沙迪克)数控电火花机床操作面板模拟软件的RAR压缩包,面向模具制造及精密加工领域的学习者、培训学员和编程人员,用于在普通PC上体验与实体机床一致的控制界面与操作流程。压缩包共2000个文件&am…

网页MCP崛起:从本地stdio到远程URL的协议演进与实战

网页MCP崛起:从本地stdio到远程URL的协议演进与实战

2026/9/9 0:23:37

“我在 Agent 里让它把蓝湖这个页面的标注信息拉出来,结果它真的直接通过一个网页地址就读取到了,全程没让我装任何本地服务。” 这是一位读者上周给我的留言。他说的“网页地址”,就是 MCP endpoint。而那句“现在网页都能提供 MCP 了&…

AI Agent开发必懂:Harness与Runtime的区别与协作

AI Agent开发必懂:Harness与Runtime的区别与协作

2026/9/9 0:23:37

做 AI Agent 开发的人,大概率都见过这两张脸:Agent Harness 和 Agent Runtime。猛一看像是同一个东西的两种叫法,再看一眼文档,又发现两者经常在同一段话里交替出现。我在 GitHub issue 和社区讨论里已经见过不少人把这两个词混着…

OpenMAIC多智能体交互课堂:可视化协作原理与部署实践

OpenMAIC多智能体交互课堂:可视化协作原理与部署实践

2026/9/9 0:23:37

最近在折腾多智能体应用的时候,挖到了一个很有意思的开源项目——OpenMAIC,全称可以理解为Open Multi-Agent Interactive Classroom,多智能体交互课堂。这名字听起来像教学工具,实际上它是一个把多个大模型智能体组织起来&#xf…

应用全局状态管理:AppStorage 与 LocalStorage 实战与避坑

应用全局状态管理:AppStorage 与 LocalStorage 实战与避坑

2026/9/9 0:23:37

应用全局状态管理:AppStorage 与 LocalStorage 实战与避坑 前言 在很多教程里,State、Prop、Link、Provide/Consume 被反复讲解,但“跨页面、跨组件树、甚至跨 Ability”的全局状态该怎么存,却常被一笔带过。鸿蒙提供了两套全局单…

从Arduino到ESP-IDF:ESP32产品级开发实战与避坑指南

从Arduino到ESP-IDF:ESP32产品级开发实战与避坑指南

2026/9/9 0:13:37

简介:面向嵌入式与物联网开发者的ESP32 IDF课程资源包,专注C语言下的ESP-IDF框架开发。课程内容从ESP-IDF环境安装、工程结构开始,逐步覆盖GPIO、I2C、SPI、UART等硬件接口操作,以及Wi-Fi客户端/服务器、TCP/IP/UDP和BLE通信配置。…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/7 20:21:46

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/8 4:55:53

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/8 22:37:26

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

扩散模型图像恢复实战:从DDPM原理到PyQt5可视化系统

扩散模型图像恢复实战:从DDPM原理到PyQt5可视化系统

2026/9/9 0:03:36

简介:面向毕业设计场景的PyQt5扩散模型图像恢复项目,提供完整Python源码与项目说明,适合图像处理、深度学习方向的高年级本科生与研究生参考。项目在模块设计上覆盖图像处理、扩散模型、参数配置、用户界面与结果评估五部分,具体涉…

开关电源环路裕量测试实战:相位裕量与增益裕量详解

开关电源环路裕量测试实战:相位裕量与增益裕量详解

2026/9/9 0:03:36

1. 项目概述:为什么环路裕量测试是电子工程师绕不开的“体检项目”“从零开始的电子工程师生活(6)——环路裕量测试”,这个标题一出来,老电源工程师可能已经下意识摸了摸示波器探头,新同事则大概率在想&…

定时插座芯片怎么选?专用定时IC与单片机MCU选型对比

定时插座芯片怎么选?专用定时IC与单片机MCU选型对比

2026/9/9 0:03:36

拆开市面上不同价位的定时插座,你会发现一个有意思的现象:有的里面躺着一颗黑色的软封装芯片,丝印都看不清;有的则是一块小小的蓝色或绿色PCB,上面赫然印着STM8或者STC的字样。同样叫"定时插座",…

远程协作的工作台整理

远程协作的工作台整理

2026/9/8 4:23:39

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/8 3:19:39

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/8 4:00:23

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…