将群晖NoteStationWebClipper移植到Firefox上
摆弄NAS,是数码爱好者的必玩项目。如果碰巧你又是个Firefox用户,相信你会遇到Synology web clipper不支持Firefox的问题。
我尝试将chrome版的web clipper移植到Firefox上,并已经在扩展中心上架,在扩展中心中搜索synology,就会找到。
本次迁移,相当于将一个chrome插件移植到firefox上,过程中踩的坑在此分享一下,希望对你有用。
Note Station介绍
NoteStation是群晖的笔记应用,活用Note可以将日常浏览网页时看到的好文章予以保存。日常使用十分方便。属于收藏利器级的应用。
但是Synology仅提供了适用于chrome的浏览器插件,没有该插件的Firefox版本。
这个问题在5年前就有人在Synology的论坛上提出来了,但是没有什么人理会。既然没有,那就只好自力更生,将Chrome插件移植到Firefox上。
Firefox插件与Chrome的异同
chrome的插件格式是脱胎于firefox的,理论上有兼容的基础。
格式
firefox与chrome的扩展格式其实都是zip,firefox用了xpi后缀,chrome用了crx后缀。科技以换名为本。
manifest.json
两者的扩展入口都是这个manifest.json文件,你把获取到的扩展包解开就能看到了。如下图。
manifest文件是一个key-value对的配置文件,其中大部分项firefox是支持的,这一点,你可以查阅mozilla的开发站点。Mozilla的开发者网站上内容很全面,可以在上面学到很多前端开发的知识。英文版信息更全。
回到manifest的问题, 其有一个browser_specific_settings
的key中,需要配置一下gecko.id。随便起个名字就行。
异步调用机制
需要参考firefox关于与chrome不兼容的说明
注意这个描述
Asynchronous APIs:
In Firefox: Asynchronous APIs are implemented using promises. In Chrome and Edge: Asynchronous APIs are implemented using callbacks. (cf. Chrome bug 328932)
firefox使用promise机制,chrome使用callback回调机制。
As a porting aid, the Firefox implementation of WebExtensions supports chrome, using callbacks, as well as browser, using promises. This means that many Chrome extensions will just work in Firefox without any changes.
但是出于兼容性考虑,firefox也实现了chrome的机制,业界良心有木有。
实际踩坑过程
后面就不那么多废话了
原版的extjs在firefox运行的不太正常
原版插件代码是基于extjs-3.4的。
本人在前端开发上的水平有限,反复调试后发现代码总在若干个地方undefined或者type error。通过寻找ext-js-3.4.1.gpl,借助其中的debug版对照分析源法,发现问题在于Ext.ns并没有如预期地将名称空间进行提升。
即Ext.ns("Ext.util")
后,仍然无法使用Ext.util
调用namespace中的函数。
进一步发现Ext.namespace
这个函数执行的不正常,其中用到了连等赋值。但没有预期般的效果。
namespace: function () {
var W = arguments.length,
X = 0,
t, V, e, Z, Y, aa;
for (; X < W; ++X) {
e = arguments[X];
Z = arguments[X].split(".");
aa = window[Z[0]];
if (aa === undefined) {
aa = window[Z[0]] = {}
}
Y = Z.slice(1);
t = Y.length;
for (V = 0; V < t; ++V) {
aa = aa[Y[V]] = aa[Y[V]] || {}
}
}
return aa
}
例如,传入参数 "SYNO.ux.colorField"
,它将字符串按.
来split,顶层用window['SYNO']来替代,然后层叠式指向。以下是我的理解。
a = window['SYNO']; // 让a指向SYNO
a['ux'] = {}; // 为a,即SYNO创建ux key
a = a['ux']; // 让a指向SYNO.ux
a['colorField'] = {}; // 为a,即SYNO.ux创建colorField key
这种做法在firefox95上运行的不正常。
进一步排查,还发现在真实页面运行时,Ext
甚至无法加入到window元素中。SYNO
对象也有同样的问题。
以上我都不知道为什么,但是通过在创建Ext后立即强行为window赋值,
window['Ext'] = Ext;
...
window['SYNO'] = SYNO;
通过上述方式解决了。
请注意,这个问题在firefox的debug模式的inspect功能下,并不会复现。可能和真实网页的全局设置有关。
回调机制问题
当通过web clipper剪取网页时,远程服务器会返回存储的结果,像这样
执行回调的代码如下
chrome.runtime.sendMessage({
action: "ajax_request",
data: {
api: "SYNO.NoteStation.Notebook",
version: 1,
method: "list"
}
}, this.loadNotebookCallback.createDelegate(this))
回调结果处理如下,以下是被我修改过的
createNoteCallback: function (a) {
SYNO.SDS.NoteStationClipper.Clipper.closeAllOtherFrame();
Ext.fly(document.body).setOverflow(SYNO.SDS.NoteStationClipper.Clipper.ORIGINAL_OVERFLOW);
//if (!a.success) {
//如果不成功,返回commfail
//可这里总是不成功,没办法,不让它执行,总是走成功的路径
if (false) {
alert(_NSC("common", "commfail"));
return
}
/*var b = Ext.util.JSON.decode(a.response.responseText);
if (!b.success) {
SYNO.SDS.NoteStationClipper.Clipper.openStatusFrame({
status: "failure",
text: _NSC("message", "capture_fail"),
desc: "Something went wrong. Please try again. Error code :" + b.error.code
})
}*/ else {
SYNO.SDS.NoteStationClipper.Clipper.openStatusFrame({
status: "success",
text: _NSC("message", "capture_success"),
//desc: a.opt.params.title,
desc: "Pretend to be done XDDD",
//data: b.data
data: "have not solved the callback mechnisam"
})
}
}
在执行得到结果后,使用回调来解析返回结果并做出相应动作。我在这里总是只得到一个空的{}
,导致解析失败,而这失败对应的提示是一个 alert comm fail
,最终一个浏览器弹框,太丑了。其实无论成功与否,都得自己检查一下吧,所以我干脆就始终让它都按返回成功来处理了。。。
这就是firefox自己说的与chrome不太兼容的地方了。firefox使用的是promise机制,虽然它也实现了chrome的回调,但似乎多少有点问题。
打包
在完成迁移后,打包有个小坑,你不要对文件夹做zip,而是要对所有文件做zip。这会导致差一个文件夹的层级,这个差异会让firefox在线检查工具无法找到你的manifest文件。
其他
- 注册一个火狐开发者账号非常有帮助。
- 建议认真阅读mozilla的文档,“编写第一个扩展”、“编写第二个扩展”、"与chrome不兼容之处" 这三篇文章一定要好好看一下,非常有帮助。
- 打开firefox调试器在异常处断点,非常有帮助。
我的扩展
我已经将扩展发布在了Mozilla的Add-on页面,欢迎下载使用。 因为我不擅长前端开发,希望有高手能彻底修复上述问题。