基于 WebRTC-Experiment 的 Firefox 屏幕捕获扩展:从自定义域名到 AMO 发布全指南
【免费下载链接】WebRTC-Experiment
WebRTC, WebRTC and WebRTC. Everything here is all about WebRTC!!
本文以 Firefox-Extensions/README.md 为主体,结合仓库内
enable-screen-capturing等目录的源码,讲解如何通过 Firefox 扩展为 WebRTC 页面启用屏幕捕获能力:包括扩展的工作原理(preference 白名单机制)、页面与扩展的通信协议、如何把arrayOfMyOwnDomains替换为自有域名、如何用 jpm 构建 XPI 并提交到 Firefox Add-ons(AMO)商店,以及 Firefox 52 之后该方案的历史定位。
在 WebRTC 发展的早期,Firefox 的 getUserMedia 屏幕捕获 API 仅对少数被硬编码信任的域名开放,普通站点无法直接调用。本仓库的 Firefox-Extensions 子项目正是为解决这一问题而生:通过一个扩展把自定义域名写入 Firefox 的屏幕共享白名单,从而让 localhost、127.0.0.1 以及你自己的 HTTPS 域名都能使用屏幕共享。读完本文,你将掌握该扩展的完整部署链路——从修改域名白名单、本地打包测试,到提交 AMO 审核发布,并理解其底层依赖的 media.getusermedia.screensharing.allowed_domains 机制。
历史背景:为什么需要这个扩展,又为何在 Firefox 52 后停更
仓库 Firefox-Extensions/README.md 开头就给出两个关键事实:
- Firefox 52 起已原生支持屏幕共享,不再需要任何附加组件(add-on);
- 本仓库自 2017 年 9 月 1 日起停止维护(discontinued)。
因此,本文讲解的扩展属于"WebRTC 屏幕捕获历史解决方案":它服务于 Firefox 52 之前的浏览器版本(扩展 package.json 中声明 "firefox": ">=38.0a1",即至少需要 Firefox 38),以及 Firefox 47/48 前后那些仍受白名单限制的过渡版本。如果你在维护老版本 Firefox 环境、或需要复刻这段历史实现用于研究,本仓库依然是最完整的参考实现。
该扩展的目标非常明确,见 Firefox-Extensions/README.md:
Enable screen capturing in Firefox for both localhost/127.0.0.1 and
https://www.webrtc-experiment.compages.
即:为本地回环地址(localhost、127.0.0.1)与 HTTPS 页面同时开启屏幕捕获能力。
仓库结构:一套问题,三版实现
Firefox-Extensions 目录下存在三个演进版本:
| 目录 | 技术形态 | 说明 |
|---|---|---|
| enable-screen-capturing/ | Add-on SDK(jpm) | 主推版本,README 中的部署步骤均针对它 |
| enable-screen-capturing-old/ | 旧式 bootstrap/XUL 扩展 | 最早期实现,含 install.rdf 与预打包好的 enable-screen-capturing.xpi |
| enable-screen-capturing-old2/ | Add-on SDK(jpm) | 中间版本,支持网页动态申请域名授权 |
主版本的核心文件为 index.js、content-script.js 与 package.json,下文的分析均以它们为准。
工作原理:prefService 与屏幕共享白名单
Firefox 通过两组 preference 控制屏幕捕获权限,见 enable-screen-capturing/index.js:
var prefService = require('sdk/preferences/service');
var configToReferListOfAllowedDomains = 'media.getusermedia.screensharing.allowed_domains';
var configToEnableScreenCapturing = 'media.getusermedia.screensharing.enabled';
media.getusermedia.screensharing.allowed_domains:允许进行屏幕共享的域名列表(逗号分隔的字符串),只有出现在这个列表里的域名才能通过getUserMedia请求屏幕/窗口媒体;media.getusermedia.screensharing.enabled:屏幕共享功能的总开关,源码注释说明该标志自 Firefox 37/38 起默认开启,扩展启动时显式把它置为true:
prefService.set(configToEnableScreenCapturing, true);
扩展的核心逻辑 addMyOwnDomains() 做的事情是:读取白名单 → 逐个检查自己的域名是否已存在 → 不存在则追加 → 写回 preference:
function addMyOwnDomains() {
var existingDomains = prefService.get(configToReferListOfAllowedDomains).split(',');
arrayOfMyOwnDomains.forEach(function(domain) {
if (existingDomains.indexOf(domain) === -1) {
existingDomains.push(domain);
}
// else { }
else if (existingDomains.indexOf(domain) !== -1) {
// Seems domain is already in the list.
// Keep it when this addon is uninstalled.
listOfSimilarAlreadyAllowedDomains.push(domain);
}
});
prefService.set(configToReferListOfAllowedDomains, existingDomains.join(','));
}
addMyOwnDomains();
一个值得注意的细节:如果 localhost、127.0.0.1 已被其他扩展加入白名单,扩展会把它记入 listOfSimilarAlreadyAllowedDomains,在卸载时不误删他人加入的域名——这正是 index.js 中 removeMyDomainOnUnInstall() 的职责:
function removeMyDomainOnUnInstall() {
var externalDomains = [];
prefService.get(configToReferListOfAllowedDomains).split(',').forEach(function(domain) {
// Skip Others Domains
if (arrayOfMyOwnDomains.indexOf(domain) === -1) {
// if its NOT mine, keep it.
externalDomains.push(domain);
} else if (listOfSimilarAlreadyAllowedDomains.indexOf(domain) !== -1) {
// seems that localhost/127.0.0.1 are already added by external users
externalDomains.push(domain);
}
});
prefService.set(configToReferListOfAllowedDomains, externalDomains.join(','));
}
卸载钩子通过 sdk/system/unload 注册,且注释明确提到"By AMO policy global preferences must be changed back to their original value"(按 AMO 政策,全局偏好必须在扩展卸载时恢复原值),这保证了扩展不会在 Firefox 中留下永久性的全局配置污染。
除了写白名单,扩展还通过 sdk/page-mod 的 PageMod 仅在匹配自己域名模式的页面注入内容脚本(contentScriptWhen: "start"),patternsOfMyDomains 同时支持通配符与正则表达式:
var patternsOfMyDomains = ['*.webrtc-experiment.com', '*.rtcmulticonnection.herokuapp.com', /https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/.*/];
页面与扩展的通信协议
检测协议:checkIfScreenCapturingEnabled
主版本扩展支持页面主动查询"我的域名是否已启用屏幕捕获"。页面侧发起请求(见 enable-screen-capturing/README.md):
// ask addon to check if screen capturing enabled for specific domains
window.postMessage({
checkIfScreenCapturingEnabled: true
}, "*");
// watch addon's response
// addon will return "isScreenCapturingEnabled=true|false"
window.addEventListener("message", function(event) {
if (event.source !== window) return;
var addonMessage = event.data;
if(!addonMessage || typeof addonMessage.isScreenCapturingEnabled === 'undefined') return;
if(addonMessage.isScreenCapturingEnabled === true) {
alert(JSON.stringify(addonMessage.domains) + '\n are enabled for screen capturing.');
}
else {
alert(JSON.stringify(addonMessage.domains) + '\n are NOT enabled for screen capturing.');
}
}, false);
请求协议(Request):
| 字段 | 值 | 含义 |
|---|---|---|
checkIfScreenCapturingEnabled | true | 请求扩展检查屏幕捕获是否已对相关域名启用 |
响应协议(Response):
| 字段 | 含义 |
|---|---|
isScreenCapturingEnabled | true 表示域名已启用屏幕捕获 |
domains | 已启用屏幕捕获的域名列表 |
在内容脚本侧(content-script.js),消息被桥接到扩展主进程:内容脚本先校验 event.source === document.defaultView(防止外部域名伪造消息,见源码中引用的 issue 11),然后通过 self.port.emit('is-screen-capturing-enabled') 把请求转给主进程的 PageMod.onAttach 处理器;主进程读取 preference、筛选出属于自己的已启用域名后,经 worker.port.emit('is-screen-capturing-enabled-response', ...) 返回,内容脚本再以 window.postMessage(response, '*') 交还给页面。
动态授权协议:enableScreenCapturing(旧版扩展)
中间版本 enable-screen-capturing-old2 支持更激进的用法:网页可以直接请求"永久"启用指定域名,但需要用户通过 window.confirm 确认。页面侧代码(见 enable-screen-capturing-old2/README.md):
// request addon to enable screen capturing for your domains
window.postMessage({
enableScreenCapturing: true,
domains: ["www.yourdomain.com", "yourdomain.com"]
}, "*");
// watch addon's response
// addon will return "enabledScreenCapturing=true" for success
// else "enabledScreenCapturing=false" for failure (i.e. user rejection)
window.addEventListener("message", function(event) {
var addonMessage = event.data;
if(!addonMessage || typeof addonMessage.enabledScreenCapturing === 'undefined') return;
if(addonMessage.enabledScreenCapturing === true) {
// addonMessage.domains === [array-of-your-domains]
alert(JSON.stringify(addonMessage.domains) + ' are enabled for screen capturing.');
}
else {
// reason === 'user-rejected'
alert(addonMessage.reason);
}
}, false);
该版本的内容脚本会把请求的域名以 JSON 形式拼进确认弹窗,用户确认后通过 self.port.emit('installation-confirmed', addonMessage.domains) 通知主进程(见 content-script.js),主进程随后把这些域名追加进 arrayOfMyOwnDomains 并重新调用 addMyOwnDomains();若用户拒绝,则返回 enabledScreenCapturing: false 与 reason: 'user-rejected'。
客户端配合:getScreenId.js 中的 Firefox 分支
仓库的 getScreenId.js/getScreenId.js 展示了页面侧如何与"已启用白名单"的 Firefox 协同:当检测到 navigator.mozGetUserMedia 时,回调直接返回 sourceId === 'firefox' 和对应的屏幕约束:
// for Firefox:
// sourceId == 'firefox'
// screen_constraints = {...}
if (!!navigator.mozGetUserMedia) {
callback(null, 'firefox', {
video: {
mozMediaSource: 'window',
mediaSource: 'window'
}
});
return;
}
也就是说,一旦域名被扩展写入白名单,Firefox 便会放行以 mozMediaSource: 'window'(或 'screen')为约束的 getUserMedia 请求,页面拿到 MediaStream 后即可用于 RTCPeerConnection 推流。这与本扩展的用途形成完整闭环:扩展负责开白名单,页面负责发起捕获请求。
自定义你自己的域名:修改 arrayOfMyOwnDomains
主版本 README 给出的部署第一步是修改域名白名单:
- 打开 enable-screen-capturing/index.js;
- 定位到第 7 行附近的
arrayOfMyOwnDomains数组(在旧版old2中位于第 11 行,见 enable-screen-capturing-old2/index.js); - 把数组替换为你自己的域名列表。
// replace your own domains with below array
var arrayOfMyOwnDomains = ['webrtc-experiment.com', 'www.webrtc-experiment.com', 'localhost', '127.0.0.1'];
实践建议:
- 必须同时列出裸域名与
www子域(如webrtc-experiment.com与www.webrtc-experiment.com),因为白名单按精确域名匹配; localhost与127.0.0.1通常应保留,便于本地联调;- 若你的站点包含多个子域,请逐一加入,或参考
patternsOfMyDomains中的*.webrtc-experiment.com通配写法设计匹配模式; - 修改后记得同步检查 package.json 中的
title、description等元数据,它们会展示在 AMO 商店页面上。
本地构建与打包:jpm 工作流
该扩展基于 Mozilla Add-on SDK,打包工具是 jpm。README 中给出的完整构建命令如下:
[sudo] npm install jpm --global
jpm run -b nightly # test in Firefox Nightly without making the XPI
jpm xpi # it will create xpi file
逐步说明:
npm install jpm --global:全局安装jpm(部分系统需要sudo);jpm run -b nightly:在 Firefox Nightly 中直接运行扩展做冒烟测试,无需先生成 XPI 文件,适合迭代调试;jpm xpi:在 enable-screen-capturing/ 目录内执行,生成可提交的 XPI 安装包。
package.json 是 jpm 的清单文件,其中 "main": "index.js" 声明扩展入口,"engines": { "firefox": ">=38.0a1" } 声明兼容的 Firefox 版本范围,"permissions": { "unsafe-content-script": true } 允许内容脚本访问页面作用域(便于调用 window.postMessage/window.confirm 等页面 API)。
提交到 AMO:发布你的扩展
按照 README 的部署清单,发布到 Firefox Add-ons 商店需要五步:
- 注册开发者账号:在 addons.mozilla.org 注册(
users/register页面); - 使用唯一的扩展名:修改 package.json 中的
name/title字段,避免与商店已有扩展冲突; - 加入自己的域名:修改 index.js 的
arrayOfMyOwnDomains; - 生成 XPI:执行上述
jpm xpi命令打包; - 提交 XPI 审核:在 AMO 开发者后台的提交页面按流程上传,选择合法的浏览器版本(README 建议选择 Firefox 38 到 Firefox 45 区间),然后提交给 Mozilla 人工审核(review)。
README 特别提醒:提交审核是"hard/tough step"(最困难的一步),需要仔细阅读并遵循 AMO 的每一步指引;审核周期通常为 2~3 小时,通过后扩展才会对公众可见。提交前请务必确认扩展遵守 AMO 政策——尤其是前文提到的"卸载时必须把全局 preference 恢复原值"这一条,仓库源码已经内置了相应实现(sdk/system/unload 钩子)。
版本限制与适用前提
由于仓库已于 2017 年停更,且 Firefox 52 起原生支持屏幕共享,请务必明确本方案的历史适用性:
- 扩展声明支持 Firefox >= 38.0a1;
- README 源码注释推测
media.getusermedia.screensharing.enabled在 47~48 版本附近可能被移除,但这一推断属作者当时的前瞻性判断,不代表已发生的版本事实; - 当前主流 Firefox 请直接使用
navigator.mediaDevices.getDisplayMedia()进行屏幕共享,无需任何扩展。
配套测试
主版本在 enable-screen-capturing/test/test-index.js 提供了基于 SDK sdk/test 的最小单元测试骨架,包含同步与异步两种用例模式,可作为自测扩展入口是否正常加载的参考。
License
本子项目(Firefox-Extensions)以 MIT 许可证 发布,版权归 Muaz Khan 所有。仓库根目录同样采用 MIT 许可(见 LICENSE),你在其基础上二次开发、重新打包发布自己的域名白名单版本是允许的,但请在衍生作品中保留原作者版权声明。
【免费下载链接】WebRTC-Experiment
WebRTC, WebRTC and WebRTC. Everything here is all about WebRTC!!
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)