Greasemonkey 踩坑之路
本文最后更新于:2020年12月25日 上午
场景
最近在玩 Greasemonkey 脚本,遇到了各种奇怪的问题,便于此处统一记录一下。
window 对象不能和外部交换数据
场景
在写 Greasemonkey 脚本时遇到的一个奇怪的问题,吾辈想要把某些数据添加到 window
对象上,方便在 DevTool console 中进行测试。然而却由此印发了一个新的问题,即 window
对象不是真正的 window
对象的问题。
1 |
|
控制台正常输出了一句话。然而,当吾辈在 console 中输入 window.rxliuli
的结果却是 undefined
。
解决
吾辈估计又是 Greasemonkey 自身的问题,所以不得不去翻了 Wiki 上查找,直到看到了 Greasemonkey Manual:Environment。里面有这么一段话
Depending on the usage, the special Greasemonkey environment may seem perfectly normal, or excessively limiting.
The Greasemonkey environment is a vanilla XPCNativeWrapper of the content window, with only certain extra bits added in to emulate a normal environment, or changed. Specifically:
- window is an XPCNativeWrapper of the content window.
- document is the document object of the XPCNativeWrapper window object.
- XPathResult is added so that document.evaluate() works.
- Unless the @unwrap metadata imperative is present in the user script header, the entire script is wrapped inside an anonymous function, to guarantee the script’s identifiers do not collide with identifiers present in the Mozilla JavaScript sandbox. This function wrapper captures any function definitions and var variable declarations made (e.g. var i = 5;) into the function’s local scope. Declarations made without var will however end up on the script’s this object, which in Greasemonkey is the global object, contrary to in the normal browser object model, where the window object fills this function. In effect, after i = 5;, the values of window[‘i’] and window.i remain undefined, whereas this[‘i’] and this.i will be 5. See also: Global object
- In order to access variables on the page, use the unsafeWindow object. To use values defined in a script, simply reference them by their names.
大意是 Greasemonkey 为了安全所以 Greasemonkey 脚本是在沙箱中执行的,并且限制了一些内容。其中就包括了 window
对象并非浏览器的原生对象,而是 XPCNativeWrapper
。
所以,XPCNativeWrapper
是什么。。。?(一个 Greasemonkey 的坑太多了吧 #吐血)
吾辈找到了两篇文章
看完之后表示只知道 XPCNativeWrapper
是在扩展中用来保护不受信任的对象,并非浏览器客户端本身的 API。
好吧,说了这么多解决方案是什么呢?
答案很简单,其实使用 unsafeWindow 对象就能像使用原生的 window
对象行为一致,即便这是不推荐的方法,但有时仍然是必须的!
Greasemonkey API 显示 undefined
场景
在 Greasemonkey 手册:API 写出的 API 有很多都不能正常使用,吾辈打印下来的结果是
1 |
|
测试环境如下:
- Windows 10 Ltsc
- Chrome 71
- tampermonkey 4.7.44
解决
吾辈在翻 GitHub Issue 找到了问题所在,原因是这些 API 必须要手动获取准许才行。
即使用 // @grant GM.[Function]
来获取需要的 API,所以吾辈的脚本变成了下面这样:
1 |
|
问题解决了,现在,所有的 API 都有值了。
内存爆炸
场景
使用了 GM.setValue()/GM.getValue()
两个 API,结果内存分分钟爆炸。吾辈安装 Chrome 以来第一次碰到加载网页能把内存耗尽的情况,果然 GM 的限制不是没有道理的呢
1 |
|
解决
Debug 之后发现是调用 GM.setValue()
没有使用 await
造成的异步请求数量不断积累最终导致网页崩溃。果然 Promise 什么的还是要小心一点好呀
当然,不信的话你也可以新建一个 Greasemonkey 脚本尝试一下内存爆炸的感觉咯
递归不是主要问题,吾辈 PC 上的 Chrome 最多到 1.4w+ 次递归就会抛出异常(网页没有崩溃),还没到 1.4w+ 次,所以说递归不是主要问题呀
Greasemonkey 加载时机太晚
场景
Greasemonkey 的加载是在页面加载完毕时,类似于 window.onload
,所以造成了一个问题:如果想要在网站的 JavaScript 代码中与 Greasemonkey 脚本交互,那么必须要等到 Greasemonkey 加载完成,而加载完成的时机是不确定的。
吾辈目前想要的解决方案有三种
等待一段时间再调用,例如等个几秒 Greasemonkey 脚本可能就加载了
思路
现在没有人,我等会再来问一次!
实现
1 |
|
使用
1 |
|
延迟到 Greasemonkey 脚本加载完成再与之交互
思路
有人吗? 没有的话我等会再来问!
实现
1 |
|
使用
1 |
|
暴露出需要交互的函数等到 Greasemonkey 加载完成后进行回调
思路
现在没有人,有人的时候再叫我!
实现
1 |
|
使用
1 |
|