随着 Greasemonkey 4 的更新,许多使用 jQuery 的用户脚本似乎都被破坏了。 在以前的版本中,以下代码可以在<script>
标签中使用 jQuery 的页面上运行:
// ==UserScript==
// <strong i="7">@name</strong> Variable access test
// <strong i="8">@namespace</strong> example.com
// ==/UserScript==
$.ready(document, function() {
console.log("Accessed jQuery successfully.")
});
但是,这不再起作用 - 相反,您只会得到一个经典的$ is not defined
。 这告诉我 Greasemonkey 可能在<script>
标签被加载之前运行用户脚本。 然而,这很奇怪,因为当document.readyState
为interactive
时脚本仍在运行!
不过,可以肯定的是,我尝试在window.addEventListener('load', ...)
之后访问 jQuery。 瞧,以下表现出不同的错误:
// ==UserScript==
// <strong i="19">@name</strong> Variable access test
// <strong i="20">@namespace</strong> example.com
// ==/UserScript==
window.addEventListener('load', function() {
console.log("Before accessing jQuery")
$;
console.log("Accessed jQuery successfully!")
});
它既不运行也不抛出错误 - 相反,它在尝试访问$
时冻结。 该脚本只是运行到该行,没有进一步。
在任何使用 jQuery 的页面上尝试它,你应该得到相同的结果。 使用@require
不能解决这个问题 - 对于某些应用程序,您需要访问与页面相同的 jQuery 实例。 这很可能也会影响 jQuery 以外的脚本。
不是真正的 Greasemonkey 问题,更多的是 Firefox 内容脚本问题。
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Accessing_page_script_objects_from_content_scripts
分配您自己的window.wrappedJSObject
或使用unsafeWindow
,其中 Greasemonkey 已经分配了包装的对象。
@Sxderp我不知道“不是真正的 Greasemonkey 问题”-Tampermonkey 和 Violentmonkey 都可以毫无问题地处理这种情况。 不仅如此,这是任何博客文章中都没有提到的重大更改。
为了按预期使用现有用户脚本并与其他流行的用户脚本管理器交叉兼容,这不应该修复吗?
还有……这真的能说明问题吗? 为什么脚本会冻结而不仅仅是查找失败?
我不确定冷冻。 复制了你的代码,它对我来说没有冻结。
编辑:抱歉,误解了“冻结”评论。 我以为你的意思是 Greasemonkey 不再起作用了。 它在该行“冻结”(停止执行),因为 Javascript 在声明错误之前命中了使用。 很确定这被认为是“致命的”。 如在,不继续执行代码。
快速查看 Violentmonkey 代码,看起来它们通过创建<script>
元素来注入脚本。 而不是使用tabs.executeScripts()
API 方法。
当然,使用它也有其自身的缺点。
所以...当 Firefox 遇到该行时,它只是默默地停止执行? 没有任何错误信息? 这是一件多么奇怪的事情。
无论如何,保持与现有用户脚本的兼容性不是目标吗? 毕竟,这不是一个小众功能——与页面自己的 JavaScript 交互是一件很常见的事情。
无论如何,保持与现有用户脚本的兼容性不是目标吗?
当然是这样。 我们并不完美,这是一个由志愿者创建的免费开源项目,只有这么多空闲时间。
在这一点上,我已经制定了如何改进这一点的计划,但它正在等待 Mozilla 实施一项功能,使其能够安全地进行。 第2549章
@arantius我想我可能有点对抗性了......抱歉。
我猜这是一个深思熟虑的决定,在这里优先考虑安全性而不是可用性。 在情况得到解决之前,我们可以做些什么作为脚本作者方面的解决方法? 在我的具体情况下,我在脚本的顶部尝试了var $ = window.wrappedJSObject.$;
和var $= unsafeWindow.$;
,但是在尝试使用$(document).ready(...)
时又给了我另一个错误: Error: Permission denied to access property Symbol.toStringTag
.
错误:权限被拒绝访问属性 Symbol.toStringTag
我不认为这是window.wrappedJSObject.$
的问题,而是您在用户脚本中创建的其他一些对象,无论是作为对象文字还是使用new
构造函数,都没有正确导入您尝试访问它的范围(可能是页面的窗口范围)。
上面的链接提供了您需要的cloneInto
详细信息。
不,我很确定问题出在window.wrappedJSObject.$
。 以下失败并出现同样的错误:
// ==UserScript==
// <strong i="7">@name</strong> Variable access test
// <strong i="8">@namespace</strong> example.com
// ==/UserScript==
var $ = window.wrappedJSObject.$;
$(document).ready(function() {});
然而,这无声地失败了:
// ==UserScript==
// <strong i="12">@name</strong> Variable access test
// <strong i="13">@namespace</strong> example.com
// ==/UserScript==
var $ = window.wrappedJSObject.$;
$.ready(function() {});
我试过使用cloneInto
,但它没有帮助。 您认为您可以提供一个简短的示例用户脚本,它使用$
的页面实例吗?
啊,在那种情况下,您没有访问该功能的权限。 然后您需要使用exportFunction
。
// ==UserScript==
// <strong i="7">@name</strong> ExampleJQuery
// <strong i="8">@version</strong> 1
// <strong i="9">@include</strong> *
// <strong i="10">@grant</strong> none
// ==/UserScript==
var unsafeWindow = window.wrappedJSObject;
var $;
// For sanity just return if we don't have the object
if (typeof unsafeWindow.$ === 'undefined') {
console.log('No jQuery object, returning');
return;
} else {
$ = unsafeWindow.$;
}
// Create the function we want to export
function onReady() {
console.log("I'm ready!");
}
// Export it. Some details on this.
// Argument 1. The function to export
// Argument 2. The scope to export it to. In general this will be window,
// or some object in the scope. While it is valid to use
// "window", I prefer to use "unsafeWindow" if I'm exporting
// into that scope. I find it less confusing.
// Return This is a reference to exported function. In general you'll
// assign this to some property of the scope you're exporting
// into. However, it's not always neccessary. For example, if
// you're going to use it as a callback (like for .ready())
// then you don't need to assign it into the exported scope.
// But if you want it globally (or scopally) accessable then
// you need to assign it.
let exported_onReady = exportFunction(onReady, unsafeWindow);
// OR
// unsafeWindow.onReady = exportFunction(onReady, unsafeWindow);
$(document).ready(exported_onReady);
// OR
// $(document).ready(unsafeWindow.onReady);
我现在已经尝试过这种方法(在不同的配置中),无论我做什么,我都会收到权限错误。 在这一点上真的不值得麻烦。
这听起来很刺耳,但我想我只需要推荐人们使用 Tampermonkey,直到这不碍事。 内容脚本,在它们当前的化身中,真的根本不适合用户脚本用例。
同意@obskyr ,只是推荐我的人使用 tampermonkey
+1,移至 Tampermonkey (https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/),似乎我所有的脚本都在那里工作。
+1,移至 Tampermonkey (https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/),似乎我所有的脚本都在那里工作。
这可能在短期内有效,并且可以很好地保持脚本运行,但从长远来看,依赖完全替代可能是错误的决定。 短期的成功可能是短视的... ;-)
我用TM作为替代品,但它有一些问题,我不能否认。 此外,FF Quantum 可能存在一些安全问题,因此它可以工作,但不应仅依赖单一解决方案......
好吧,无论如何我都需要在我的脚本中支持 TM 因为不是每个人都使用 Firefox。 所以之前我支持GM和TM,现在只是TM。 目前没发现有什么问题。
最有用的评论
我现在已经尝试过这种方法(在不同的配置中),无论我做什么,我都会收到权限错误。 在这一点上真的不值得麻烦。
这听起来很刺耳,但我想我只需要推荐人们使用 Tampermonkey,直到这不碍事。 内容脚本,在它们当前的化身中,真的根本不适合用户脚本用例。