在阅读了上个月的“让我们编写一个 Web 扩展”之后,我受到启发尝试将一个真实的附加组件移植到 WebExtension。具体来说,我尝试将流行的开源“Reddit Enhancement Suite”(RES)的 Chrome 版本移植到 Firefox。以下是我学到的东西,以及您今天可以做些什么来为自己的附加组件做好过渡准备。
注意:RES 的作者对 WebExtensions 感到兴奋,并计划正式移植他们的附加组件,但这不是。如果您想使用 RES,则应从 AMO安装受支持的版本。
首先,我想强调的是 WebExtensions 是一个长期的、多年的项目。我们的首批版本将专注于构建一个基础的、得到良好支持的、跨浏览器的 API。这意味着可能需要一段时间才能准备好支持依赖于独特浏览器功能的复杂附加组件,但我们最终会实现这一点。
由于这里的所有内容都还处于非常早期和实验阶段,因此如果您想继续操作,则需要使用 Firefox 的 Nightly 版本。这只是一个抢先体验,不应将其作为部署计划。
也就是说,如果您有 Chrome 扩展或跨浏览器附加组件,现在是尝试使用 WebExtensions 并提供反馈的绝佳时机。您的输入对于帮助 Mozilla 确定要优先考虑和最初支持哪些 API 至关重要。
准备移植
- 下载并安装 Firefox 的 Nightly 版本。
- 创建一个 新的配置文件 用于测试和开发。
- 访问
about:config
并将xpinstall.signatures.required
设置为false
。
声明 Firefox 兼容性
您必须通过向您的 manifest.json 添加 applications
密钥来明确标记您的附加组件与 Firefox 兼容。它看起来像这样
"applications": {
"gecko": {
"id": "YOUR_ADDON_ID"
}
},
将 "YOUR_ADDON_ID"
设置为类似于 "ext@example.org"
的虚构字符串。如果您计划将用户从现有的 Firefox 附加组件直接升级到同一附加组件的 WebExtension 版本,则应重新使用在 package.json 的 "id"
字段中找到的值。
检查清单支持
下一步是将 manifest.json 中的密钥与 Firefox 支持的密钥 进行比较。不支持的密钥将被忽略,因此您可以将它们保留在清单中,直到我们开始实现它们,届时它们应该可以正常工作。
查看 Reddit Enhancement Suite 的清单,我们处于非常好的状态。元数据属性都已实现,并且对 background
、content_scripts
和 web_accessible_resources
的支持足以与 RES 一起使用。
让我们看看缺少什么以及有什么影响
options_page
:我们没有这个也没关系,因为 RES 还会通过content_scripts
插入到其设置的链接,而不是仅仅依赖于options_page
属性。page_action
:我们在这里也没问题。RES 仅将页面操作用作切换通过content_scripts
插入到页面中的复选框的快捷方式。permissions
:RES 请求的所有权限都受支持,除了尚未实现的history
。RES 仅 使用历史记录 API 将链接标记为已访问 在从“展开”按钮内联预览图像时。缺少此功能意味着功能略有下降,但没有什么灾难性的。optional_permissions
:我们尚不支持可选权限,对于 RES 而言,这意味着我们将不支持通过展开按钮从 Twitter 或 OneDrive 嵌入内联预览。不幸的是,但这并不是一个障碍。
此时,我对我们的前景感到非常乐观。我们需要的多数 API 都已支持,并且我们应该能够提供 RES 的大部分功能,尽管有少数 API 缺失。
到 Bugzilla!
由于我们已经确定了 Firefox 的 API 覆盖范围相对于我们的需求存在一些差距,因此是时候前往 Bugzilla 了。提交和投票错误是您作为附加组件开发人员可以做出的两个最重要的贡献。除了让您了解进度之外,它还有助于我们判断哪些 API 最重要。
注意:Bugzilla 具有 有点深奥 的搜索语法。要查找所有提到
history
API 的已打开和已关闭的 WebExtension 错误,尝试搜索ALL Component:WebExtensions #history
,这应该会显示 错误 1208334:“为开放的扩展 API 实现历史记录 API”。
由于我正在撰写本文,因此我已经确保为上述 API 提交了错误。如果您想收到其进度的通知,可以随意在这些错误上抄送自己,或者如果错误对您特别重要,则可以单击“重要性”字段旁边的“投票”链接。
- 错误 1212684:为开放的扩展 API 实现 options_page 清单属性
- 错误 1197422:为开放的扩展 API 实现 pageAction API
- 错误 1208334:为开放的扩展 API 实现历史记录 API
- 错误 1197420:为开放的扩展 API 实现权限 API 和 optional_permissions 清单属性
如果您需要提交 WebExtension 错误,请针对“工具包”产品中的“WebExtensions”组件提交,并使用“dev-doc-needed”关键字进行标记。此链接应预先填充所有正确的字段:提交 WebExtension 错误。
使用 grep 搜索代码
除了清单属性外,我们还需要确保 Firefox 确实支持我们需要的 API。我们在 AreWeWebExtensionsYet.com 上设置了一个 API 进度可视化仪表板,但对于具体内容,您必须 转到 MDN。由于 Chrome 的扩展 API 在全局 chrome
对象上作为属性公开,因此我们可以运行 grep 来找出我们使用了什么
$ grep -r 'chrome\.' ./Chrome ./lib
./Chrome/background.js: chrome.tabs.sendMessage(event.id, { requestType: 'subredditStyle', action: 'toggle' }, function(response) {
./Chrome/background.js:chrome.pageAction.onClicked.addListener(handlePageActionClick);
./Chrome/background.js:chrome.runtime.onMessage.addListener(
# and so on...
在 RES 依赖的 API 中,只有少数几个未实现
- history.addUrl
- pageAction.hide、onClicked、setIcon 和 show
- permissions.remove、request
- tabs.getCurrent
在深入研究代码之前,让我们回到 Bugzilla 并确保已为这些内容提交了错误。上面提到的错误涵盖了历史记录、页面操作和权限,但它们没有涵盖 tabs.getCurrent
。我已经为此提交了 错误 1212890。
技巧和解决方法
现在我们已经确定了我们的限制,我们需要找到解决方法。在短期内,我们只需插入一些防护措施,在调用 API 上的方法之前检查其是否存在。例如,让我们看看 history.addUrl
如何在 background.js 中使用
case 'addURLToHistory':
chrome.history.addUrl({url: request.url});
break;
只要 chrome.history.addUrl
未定义,这就会抛出错误。相反,让我们在使用它之前检查其是否存在
case 'addURLToHistory':
if (chrome.history && chrome.history.addUrl) {
chrome.history.addUrl({url: request.url});
}
break;
这可以防止脚本崩溃,但这意味着 addURLToHistory
会在 错误 1208334 得到解决之前静默失败。在某些情况下,例如在 RES 中,这可能是可以接受的。如果不是,您需要找到一种创造性的解决方法或等待相关错误得到解决。请记住:提交并投票错误!这是我们了解需要处理哪些内容的方式。
页面操作是另一个很好的例子:虽然在浏览器 UI 中有一个按钮很方便,但在 错误 1197422 被修复之前,您也可以通过使用内容脚本将自定义 UI 插入目标页面来提供相同的功能。
最后,我们可以通过将所有 optional_permissions
从 manifest.json 移动到正常的 permissions
块中来解决 permissions.request()
的缺乏。这确实有效,但最好不要请求超过您需要的权限,并且更改 permissions
部分通常会导致您的用户被提示重新授权您的附加组件。如果可能,只需等待 错误 1197420。
打包您的 WebExtension
我们正在 错误 1185460 中改进工作流程,但目前
- 将您的文件压缩,以便 manifest.json 位于 zip 文件的根目录。
- 将其从
.zip
重命名为.xpi
。 - 导航到
about:addons
。 - 将您的 XPI 拖放到页面上。
- 在提示中单击“安装”。
如果出现任何错误,请查看 MDN 上的 打包和安装文档 以获取故障排除技巧。
测试它
尽管 WebExtensions 是 Mozilla 的一项全新计划,但我们已经实现了支持 Reddit Enhancement Suite 所需的大多数构建块。只要我们已正确绕过不受支持的 API 调用,事情应该可以正常工作。
让我们加载它并看看现实是否符合我们的预期……
嘿!看起来不错!也许它正在工作?让我们尝试在滚动到页面底部时加载更多内容的功能……
……不行。:( 那么,出了什么问题?
调试
要找出失败的原因,我们需要打开浏览器控制台。它是浏览器中发生的所有事件的全局日志,并且是 WebExtensions 中未捕获的异常出现的地方。它位于开发者菜单中。
注意:尽管它们相关,但浏览器控制台与该菜单中的Web 控制台不同。
查看浏览器控制台,出现了一个未捕获的异常:“TypeError: window.Favico 不是构造函数”。
当 orangered.js 调用
favicon = new window.Favico();
错误的根本原因是 Favico 库在 其内容脚本中 将自身导出为 this.Favico
,而 RES 假设它随后将在其他脚本中作为 window.Favico
提供。事实证明,Firefox 的工作方式不同。前往 Bugzilla 提交 错误 1208775!
幸运的是,有一个简单的解决方法:只需省略 window.
部分。
favicon = new Favico();
这使我们克服了该错误,并实现了工作无限滚动。万岁!此外,感谢 RES 在拉取请求 #2465 中修复了此问题!
当然,我们还没有完成。还有许多其他令人着迷和滑稽的错误需要发现,例如 错误 1208874,它阻止 RES 保存任何设置,因为每次浏览器重新启动时 WebExtension localStorage 都会被清除。糟糕!
请记住:保持浏览器控制台打开并 提交错误!
总结
正如我在文章开头提到的,WebExtensions 仍处于开发的早期阶段,并且情况正在迅速变化。例如,PageAction 支持应该 很快就会上线。也就是说,WebExtensions 已经具有惊人的功能。对于像 RES 这样隔离和最小化特定于浏览器的代码的附加组件,移植到 WebExtensions 在 Firefox 的 Nightly 版本上出奇地接近可行。
我们距离将所有这些内容引入主线 Firefox 还有几个月的时间,但看到快速进步令人鼓舞。我们每天都更接近一个未来,在这个未来中,单个附加组件代码库可以在许多浏览器中完全重复使用,并且附加组件使用与 Web 本身相同的技术编写。
如果您想关注阻止将 RES 移植到 WebExtensions 的错误,请在 错误 1208765 上抄送 RES 元错误,并查看我在 GitHub 上移植 RES 的尝试 on GitHub。
最后,考虑为 Firefox 做贡献!我们所做的一切都是开源的,大多数 WebExtension API 都是用 JavaScript 实现的。如果你能编写 JS 代码,你就能有所作为。查看 开放的 WebExtension 错误,并在 irc.mozilla.org 上的 #webextensions 频道加入讨论,开始你的贡献之旅。
最后,要感谢 Steve Sobel,Reddit Enhancement Suite 的创建者,他希望我提醒大家,任何将 RES 移植到 WebExtensions 的版本都未完成、非官方且不受支持,除非他本人亲自告诉你。不要因为我们的错误去打扰他。;-)
关于 Dan Callahan
Mozilla 开发者关系工程师,前 Mozilla Persona 开发者。
5 条评论