浏览器脚本开发的难与易

这两天,写了一个简单的基于有道在线翻译的GreaseMonkey屏幕取词脚本。

点我查看GreaseFork

youdaodict-1.gif

我想做这件事很久了,从我还不是一个前端开发者的时候,就一直想做这么一个轻量的浏览器脚本,方便自己查看英文的文档和文章。没想到想了这么久,真正没做多久。

作为一个Ubuntu Linux用户,浏览器取词我有几个选择:

  1. 尝试安装有道词典Linux版本、openyoudao或者其他stardict或者goldendict这种本地词典。但我并不觉得我需要桌面软件。

  2. 有人做了个Google translate tooltip的GreaseMonkey脚本实现这个,非常棒。但谷歌的服务在国内的服务非常不稳定,取词功能经常不能正常使用。

  3. 有道提供了网页翻译2.0,通过书签执行一段代码把取词功能注入当前页面。然而,首先随着浏览器安全特性的加强,该书签不能正常使用,其次每次都要先点书签才能取词(也许是快捷键)。

选择是难

很多网站,包括cnblog发现都提供了取词版本。我面临的选择是:

  1. 在这些已有的浏览器取词脚本基础上学习修改。
  2. 凭借着自己的感觉从新设计

选择上花了很多时间。

方案一的优点有:

  1. 成熟美观。
  2. 能学习到很多东西

方案一问题在于:

  1. 源码难理解。代码量较大,都是压缩甚至混淆变量过的。
  2. 有些和当前页面的样式或者脚本搅和在一起。不易分离
  3. 被浏览器或网站安全设置废掉,未必能使用

终于,由于我的智商被有道在线翻译那个脚本所碾压,我想还是看看功能自己设计下,做个简单版本。

想的很简单

设计是易

想法很简单。

  1. 鼠标选词
  2. 向第三方发起请求,比如bing的翻译或者有道的
  3. 读取返回,弹出tooltip,格式化数据
  4. 其他辅助功能比如发音、单词本等等

设计是最简单的一环,后面你会看到时间都花到哪里了。

知易行难

通过谷歌,很容易完成第一步,在脚本中得到选中的文字。

第二步就开始面临问题。作为前信息安全专业从业者,很清楚ajax这种东西跨域是受限制的。稍微翻阅scriptish文档发现GM_xmlhttpRequest可以满足我的需求。

除却和 XMLHttpRequest这种东西并不太一样的api造成的各种细节错误,之后碰到的问题是我整个开发过程最棘手、花费时间最长的问题。

无论onload、onerror还是onreadystate的回调中,GM_log都没有打印出任何信息。

firebug和火狐内置调试器也没有显示任何通信。这和我在网络上看到的GreaseMonkey相关信息并不太相符。

经检查脚本元数据@grant,觉得已经授权这个跨域函数也没什么问题。

折腾一阵,确认API调用和细节都无法确认问题后,采取曲线调试方案。

更改请求地址到本地,确认请求确实发出了。那么,它有返回吗?

在本地用netcat模拟返回数据,仍然没有打印任何信息。我开始怀疑难道GM_xmlhttpRequest是会对返回结果做验证?必须报头正确?

第一天就这么过去了。

第二天我决定尝试代理来看来往的通信是否正常。

方便起见,先用nc充当了下代理,检查了下相互通信,未见有什么不对的。

为严谨起见,用burpsuite来设置一个透明本地代理,让浏览器指向那个代理。经过检验,完全没看出通信有什么问题。但onload和其他回调也不会被触发。

谷歌搜索得到一些stackoverflow、github issue和greasewiki上的信息,但问题仍不能确认和解决。

只是昨天晚上baidu时心心念念,发现firefox贴吧里有人吐槽scriptish不稳定的一些地方,今天又看到一些讨论,决定换回GreaseMonkey试试,事实证明这是明智的。

然而,一换发现什么都打印不出来了。后来反复尝试,发现GM_log不能用,我简直震惊了,wiki上写着玩的么,还是有什么变化。反正我发现console.log可以使用,那就继续开发下去了。

最难的部分就这么糊里糊涂过去了。

数据请求顺风顺水

一旦请求完成,解析json数据,按需展示就是水到渠成的事情。

然而,并不是那么简单。

JS异步与回调之难

JS的异步特性带来了这些不符合人类直观思维方式的流程控制风格。

按理说我应该很习惯javascript的异步操作流程控制的种种问题,但还是踩了次坑。

弹出和渲染tooltip的函数没有读到返回数据!

好在对javascript程序员debug这种问题比之前的问题简单太多。一看想起来GM_xmlhttpRequest是异步过程,而不是同步,我这里却要待异步过程返回结果再执行下一个函数。

想想promise应该不用,虽然firefox41肯定原生支持ES6 promise了。但,就这点函数干脆。。。还是回调“地狱”吧。

JS难中有易

说到ES6,ES6提供了很多方便javascript编程的好东西,通过let=>实现更好的this和作用域一致,通过Template方便字符串操作等等。

很庆幸,GreaseMonkey的话我只考虑firefox用户,反正好早的时候这些ES6特性浏览器都支持了。

JS易中又难

JS让人非常难过的一个地方,是DOM操作和各种webAPI。只能说丧心病狂。你记得清楚如何获得viewport区域大小么?知道如何获得鼠标相对viewport位置么?知道为啥获取区域高度或宽度并没有获得么?看到clientWidth、offsetWidth、availWidth...有没有想砍人?

为了让脚本能正确在屏幕边缘让tooltip出现在viewport内,在各种边界条件数学计算题这里又纠结了好久。

GreaseMonkey相比Scriptish少了一个比较方便的特性: @css。虽然可以在head标签中通过GM_addStyle()来注入样式,我总觉得会不合时宜的覆盖不该覆盖的东西,我对Google Translate Tooltip在阮一峰大大的网站上奇葩的样式表现印象深刻。所以,还是选择在DOM中注入的样式。

这是体力活,你说体力活难不难呢?

最难的部分

安全是最难以面对的一个问题。之所以,很多标签、脚本在页面上失效,都是由于近年来浏览器越来越严格的安全策略。我在开发这个脚本时碰到了两点:

  1. 在https网站页面中无法加载http的资源。在调试工具中可以看到mixed content的字样。
  2. 如果网站报头中有CSP限制。调试工具中也能看到提示。

问题一,可以通过GM_xmlhttpRequest方法实现混合协议内容,如果外部资源也支持https请求也行。当我开发发音功能时就发现有道的语音api可以用https访问。

问题二,只能通过各种CORS技术实现(参见附录)。我还没开始做。但看到Stackoverflow上有个示例

你确定要通过打开about:config禁用firefox对CSP的支持吗?

不!!

通过GM_xmlhttpRequest完成异步请求,将数据用浏览器播放出来实现跨域资源引用。这样,在一定程度上并不降低浏览器安全性,却能够实现需求,完成功能。

Cheers!

附录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容