Chrome 兼容性问题
WebExtension API 旨在提供所有主流浏览器的兼容性,因此扩展程序应该在任何浏览器上运行,且无需进行大量修改。
但是,Chrome(和基于 Chromium 的浏览器)、Firefox 和 Safari 之间存在重大差异。特别是
- 不同浏览器对 WebExtension API 的支持有所不同。有关详细信息,请参阅JavaScript API 的浏览器支持。
- 不同浏览器对
manifest.json
密钥的支持有所不同。有关详细信息,请参阅manifest.json
页面上的“浏览器兼容性”部分。 - 扩展 API 命名空间
- 在 Firefox 和 Safari 中:扩展 API 通过
browser
命名空间访问。出于与 Chrome 的兼容性考虑,也支持chrome
命名空间。 - 在 Chrome 中:扩展 API 通过
chrome
命名空间访问。(参见Chrome 错误 798169)
- 在 Firefox 和 Safari 中:扩展 API 通过
- 异步 API
- 在 Firefox 和 Safari 中:异步 API 使用 Promise 实现。
- 在 Chrome 中:在清单文件 V2 中,异步 API 使用回调实现。在清单文件 V3 中,大多数合适的方法都提供了对Promise的支持。(参见Chrome 错误 328932)在清单文件 V3 中,为了向后兼容,也支持回调。
本页的其余部分详细介绍了这些和其他不兼容性。
JavaScript API
chrome.* 和 browser.* 命名空间
- 在 Firefox 和 Safari 中:API 使用
browser
命名空间访问。jsbrowser.browserAction.setIcon({ path: "path/to/icon.png" });
- 在 Chrome 中:API 使用
chrome
命名空间访问。jschrome.browserAction.setIcon({ path: "path/to/icon.png" });
回调和 Promise
- 在 Firefox 和 Safari(所有版本)以及 Chrome(从清单文件版本 3 开始):异步 API 使用Promise返回值。js
function logCookie(c) { console.log(c); } function logError(e) { console.error(e); } let setCookie = browser.cookies.set({ url: "https://mdn.org.cn/", }); setCookie.then(logCookie, logError);
- 在 Chrome 中:在清单文件 V2 中,异步 API 使用回调返回值,并使用
runtime.lastError
传达错误。在清单文件 V3 中,为了向后兼容,支持回调,同时大多数合适的方法也支持Promise。jsfunction logCookie(c) { if (chrome.runtime.lastError) { console.error(chrome.runtime.lastError); } else { console.log(c); } } chrome.cookies.set({ url: "https://mdn.org.cn/" }, logCookie);
Firefox 支持 chrome 和 browser 命名空间
作为移植辅助工具,Firefox 的 WebExtensions 实现支持使用回调的chrome
和使用 Promise 的browser
。这意味着许多 Chrome 扩展程序无需更改即可在 Firefox 中运行。
注意:Firefox 和 Safari 支持browser
命名空间。Chrome 不提供browser
命名空间,直到Chrome 错误 798169得到解决。
如果您选择编写使用browser
和 Promise 的扩展程序,Firefox 提供了一个 polyfill,它应该能够使其在 Chrome 中运行:https://github.com/mozilla/webextension-polyfill。
部分支持的 API
JavaScript API 的浏览器支持页面包含 Firefox 支持的所有 API 的兼容性表格。如果 API 方法、属性、类型或事件的支持存在注意事项,则这些表格中会用星号“*”表示。选择星号会展开表格以显示说明注意事项的说明。
这些表格是从存储为GitHub 中的 JSON 文件的兼容性数据生成的。
本节的其余部分描述了构建跨浏览器扩展时可能需要考虑的主要兼容性问题。此外,请务必检查浏览器兼容性表格,因为它们可能包含其他兼容性信息。
通知 API
对于notifications.create()
,使用type "basic"
- 在 Firefox 中:
iconUrl
是可选的。 - 在 Chrome 中:
iconUrl
是必需的。
当用户点击通知时
- 在 Firefox 中:通知会立即清除。
- 在 Chrome 中:情况并非如此。
如果您连续多次快速调用notifications.create()
- 在 Firefox 中:通知可能不会显示。在
notifications.create()
回调函数中等待进行后续调用不足以防止这种情况发生。
代理 API
Firefox 和 Chrome 包含代理 API。但是,这两个 API 的设计不兼容。
- 在 Firefox 中:可以使用proxy.settings属性或proxy.onRequest设置代理,以动态提供ProxyInfo。有关 API 的更多信息,请参阅proxy。
-
在 Chrome 中:代理设置在
proxy.ProxyConfig
对象中定义。根据 Chrome 的代理设置,设置可能包含proxy.ProxyRules
或proxy.PacScript
。使用proxy.settings属性设置代理。有关 API 的更多信息,请参阅chrome.proxy。
选项卡 API
使用tabs.executeScript()
或tabs.insertCSS()
时
- 在 Firefox 中:传递的相对 URL 相对于当前页面 URL 解析。
- 在 Chrome 中:相对 URL 相对于扩展程序的基本 URL 解析。
为了跨浏览器工作,您可以将路径指定为绝对 URL,从扩展程序的根目录开始,如下所示
/path/to/script.js
调用tabs.remove()
时
- 在 Firefox 中:
tabs.remove()
Promise 在beforeunload
事件之后完成。 - 在 Chrome 中:回调不会等待
beforeunload
。
WebRequest API
- 在 Firefox 中
- 只有当它们的原始 URL 使用
http:
或https:
方案时,才能重定向请求。 activeTab
权限不允许拦截当前选项卡中的网络请求。(参见错误 1617479)- 不会为系统请求(例如,扩展程序升级或搜索栏建议)触发事件。
- 从 Firefox 57 开始:Firefox 对需要拦截
webRequest.onAuthRequired
用于代理授权的扩展程序做出了例外。请参阅webRequest.onAuthRequired
的文档。
- 从 Firefox 57 开始:Firefox 对需要拦截
- 如果扩展程序想要将公共(例如,HTTPS)URL 重定向到 扩展程序页面,则扩展程序的
manifest.json
文件必须包含一个web_accessible_resources
键,其中包含扩展程序页面的 URL。注意:任何网站都可能链接或重定向到该 URL,并且扩展程序应将任何输入(例如 POST 数据)视为来自不受信任的来源,就像普通网页一样。
- 一些
browser.webRequest.*
API 允许返回异步解析webRequest.BlockingResponse
的 Promise。
- 只有当它们的原始 URL 使用
- 在 Chrome 中:仅
webRequest.onAuthRequired
通过回调而不是 Promise 提供'asyncBlocking'
支持异步webRequest.BlockingResponse
。
Windows API
- 在 Firefox 中:
windows
API 的onFocusChanged
事件在焦点发生变化时会触发多次。
不受支持的 API
DeclarativeContent API
- 在 Firefox 中:Chrome 的 declarativeContent API 未实现。此外,Firefox 将不支持
declarativeContent.RequestContentScript
API(该 API 很少使用,并且在 Chrome 的稳定版本中不可用)。
其他不兼容性
CSS 中的 URL
- 在 Firefox 中:注入的 CSS 文件中的 URL 相对于CSS 文件本身解析。
- 在 Chrome 中:注入的 CSS 文件中的 URL 相对于注入它们的页面解析。
对后台页面中对话框的支持
web_accessible_resources
- 在 Firefox 中:资源被分配一个随机的 UUID,该 UUID 对于每个 Firefox 实例都会发生变化:
moz-extension://«random-UUID»/«path»
。这种随机性可能会阻止你执行某些操作,例如将扩展程序的 URL 添加到另一个域的 CSP 策略中。 - 在 Chrome 中:当资源列在
web_accessible_resources
中时,它可以通过chrome-extension://«your-extension-id»/«path»
访问。扩展程序 ID 对于一个扩展程序是固定的。
清单 "key" 属性
- 在 Firefox 中:由于 Firefox 对
web_accessible_resources
使用随机 UUID,因此此属性不受支持。Firefox 扩展程序可以通过browser_specific_settings.gecko.id
清单键修复其扩展程序 ID(请参阅 browser_specific_settings.gecko)。 - 在 Chrome 中:在使用解包的扩展程序时,清单可能包含一个
"key"
属性 以在不同的机器上固定扩展程序 ID。这在使用web_accessible_resources
时非常有用。
内容脚本 HTTP(S) 请求
- 在 Firefox 中:当内容脚本发出 HTTP(S) 请求时,必须提供绝对 URL。
- 在 Chrome 中:当内容脚本使用相对 URL(如
/api
)发出请求(例如,使用fetch()
)时,它会被发送到https://example.com/api
。
内容脚本环境
- 在 Firefox 中:内容脚本环境 的全局作用域不完全等于
window
(Firefox 错误 1208775)。更具体地说,全局作用域 (globalThis
) 由标准的 JavaScript 特性(照常)以及作为全局作用域原型对象的window
组成。大多数 DOM API 通过window
从页面继承,通过 X 光视觉 来保护内容脚本免受网页的修改。内容脚本可能会遇到其全局作用域中的 JavaScript 对象或来自网页的 X 光包装版本。 - 在 Chrome 中:全局作用域是
window
,并且可用的 DOM API 通常独立于网页(除了共享底层 DOM)。内容脚本无法直接访问网页中的 JavaScript 对象。
从内容脚本执行网页中的代码
- 在 Firefox 中:
eval
在内容脚本的上下文中运行代码,而window.eval
在页面的上下文中运行代码。请参阅 在内容脚本中使用eval
。 - 在 Chrome 中:
eval
和window.eval
始终在内容脚本的上下文中运行代码,而不是在页面的上下文中运行代码。
在内容脚本之间共享变量
- 在 Firefox 中:你无法通过在一个脚本中将变量分配给
this.{variableName}
,然后在另一个脚本中尝试使用window.{variableName}
来访问它们来在内容脚本之间共享变量。这是 Firefox 中沙箱环境创建的限制。此限制可能会被移除;请参阅 Firefox 错误 1208775。
导航期间的内容脚本生命周期
-
在 Firefox 中:在用户导航离开后,内容脚本仍会注入网页。但是,窗口对象的属性会被销毁。例如,如果内容脚本设置了
window.prop1 = "prop"
,然后用户导航离开并返回到页面,则window.prop1
将未定义。此问题在 Firefox 错误 1525400 中进行了跟踪。要模拟 Chrome 的行为,请侦听 pageshow 和 pagehide 事件。然后模拟内容脚本的注入或销毁。 - 在 Chrome 中:当用户从网页导航离开时,内容脚本会被销毁。如果用户点击后退按钮通过历史记录返回页面,则内容脚本会被注入网页。
"每个标签页" 缩放行为
- 在 Firefox 中:缩放级别在页面加载和标签页内的导航中保持不变。
- 在 Chrome 中:缩放更改会在导航时重置;导航标签页始终使用其每个来源的缩放因子加载页面。
manifest.json 密钥
主要的 manifest.json
页面包含一个表格,描述了浏览器对 manifest.json
键的支持情况。如果对给定键的支持存在注意事项,则表格中会用星号“*”表示。选择星号会展开表格,显示解释注意事项的说明。
这些表格是从存储为GitHub 中的 JSON 文件的兼容性数据生成的。
原生消息传递
基于连接的消息传递参数
在 Linux 和 Mac 上:Chrome 将一个参数传递给原生应用程序,该参数是启动它的扩展程序的来源,格式为 chrome-extension://«extensionID/»
(需要尾部斜杠)。这使应用程序能够识别扩展程序。
在 Windows 上:Chrome 传递两个参数
- 扩展程序的来源
- 启动应用程序的 Chrome 原生窗口的句柄
allowed_extensions
- 在 Firefox 中:清单键称为
allowed_extensions
。 - 在 Chrome 中:清单键称为
allowed_origins
。
应用程序清单位置
- 在 Chrome 中:应用程序清单的预期位置不同。请参阅 Chrome 文档中的 原生消息传递主机位置。
应用程序持久性
- 在 Firefox 中:当原生消息传递连接关闭时,如果子进程不分离,Firefox 会终止它们。在 Windows 上,浏览器将原生应用程序的进程放入 作业对象 中并终止该作业。假设原生应用程序启动了其他进程,并希望在原生应用程序被终止后这些进程保持打开状态。在这种情况下,原生应用程序必须使用
CreateProcess
而不是ShellExecute
来启动附加进程,并使用CREATE_BREAKAWAY_FROM_JOB
标志。
数据克隆算法
一些扩展程序 API 允许扩展程序将数据从扩展程序的一个部分发送到另一个部分,例如 runtime.sendMessage()
、tabs.sendMessage()
、runtime.onMessage
、runtime.port
的 postMessage()
方法以及 tabs.executeScript()
。
- 在 Firefox 中:使用 结构化克隆算法。
- 在 Chrome 中:使用 JSON 序列化算法。将来可能会切换到结构化克隆 (问题 248548)。
结构化克隆算法支持比 JSON 序列化算法更多的类型。一个值得注意的例外是具有 toJSON
方法的(DOM)对象。DOM 对象默认情况下不可克隆也不可 JSON 序列化,但是使用 toJSON()
方法,这些对象可以被 JSON 序列化(但仍然无法使用结构化克隆算法克隆)。JSON 可序列化但不可结构化克隆的对象示例包括 URL
和 PerformanceEntry
的实例。
依赖于 JSON 序列化算法的 toJSON()
方法的扩展程序可以使用 JSON.stringify()
后跟 JSON.parse()
来确保可以交换消息,因为解析后的 JSON 值始终是可结构化克隆的。