需要 jQuery 到 Tampermonkey 脚本和控制台中的安全变量

require jQuery to a safe variable in Tampermonkey script and console

本文关键字:安全 变量 控制台 jQuery Tampermonkey 脚本 需要      更新时间:2023-09-26

通常我使用以下代码片段,这通常有效:

(function () {
    function main() {
        //...script body...
    }
    var OGloboCSS = { // var could be named anything, appropriate to the page
        addJQuery: function (callback) {
            var script = document.createElement("script");
            script.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js");
            script.addEventListener('load', function () {
                var script = document.createElement("script");
                script.textContent = "window.jQ=jQuery.noConflict(true);(" + callback.toString() + ")();";
                document.body.appendChild(script);
            }, false);
            document.body.appendChild(script);
        }
    };
    OGloboCSS.addJQuery(main);
})();

但是偶尔我会在 https://steamcommunity.com 上收到这样的错误:

拒绝加载脚本 "https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js" 因为它违反了以下内容安全策略指令: "script-src 'unsafe-eval' 'self' 'unsafe-inline' 'unsafe-eval' https://steamcommunity-a.akamaihd.net/https://api.steampowered.com/ http://www.google-analytics.com https://ssl.google-analytics.com https://www.google.com https://www.gstatic.com https://apis.google.com"。

解决方案基本上是,你必须使用TamperMonkey的@require标头指令,如下所示:

// @require      https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
// ==/UserScript==
window.jQ = $;
(function () {
/*this used to be: function main() */
    //...same script body, without container function...
})();

这工作得很好,但它不能安全地将 jQuery 加载到 jQ 变量,它会破坏$。这真的是一个问题吗?是的,你可以在 steamcommunity 的讨论线程中.com分页中断中看到这一点。

有没有办法@require jQ=然后@grant jQ - 仍然在 Tampermonkey 脚本和控制台中给我jQ而不会破坏页面内变量

除了避免版本冲突和破坏内容页面之外,我还在包含jQuery的默认用户脚本模板中做了一些其他事情。

下面的模板努力实现以下目标,以便成为在新脚本中包含jQuery的通用模板,而不管内容页面的当前或未来状态如何,无论它是否包含或缺少任何版本的jQuery。

    避免使用无
  • @grant的安全沙箱
  • 通常对插入脚本的位置/执行时间保持不可知
  • 为我们的用户脚本包含特定版本的 jQuery
  • 保留内容页的 $ 和 jQuery 引用,无论它们是否是 jQuery。
  • 允许我们的脚本在其 jQuery 版本中使用 $ 别名

在这里,使用@grant none是关键

//@grant          none

@grant 没有一个适用于我的 99% 的脚本。它允许通过维护 UserScript(this) 的全局作用域来轻松访问内容页的变量、函数和对象,并通过在两个作用域中搜索变量、函数标识符来提供对内容页的全局作用域(窗口)的非限定访问,窗口顺序。因此,如果避免重复内容页面中存在的标识符,则可以访问内容页面变量、函数和函数返回值而不进行限定("this."或"window.")。您可以从脚本中的任何位置执行此操作。

这突出了OP的原始问题。@grant none 提供的便利也是 @require 指令在加载时覆盖窗口的 $ 和 jQuery 引用的原因。jQuery库包含noConflict()函数已经有一段时间了,以处理这个问题。

我们可以立即对上面的答案进行一些快速改进,以提高我们的安全性/兼容性/重用性。

window.jQ = $.noConflict(true);
  • 此调用未将 $ 显式限定为作用域
  • 如果内容页面还包含一些其他具有别名 $ 的库,它将无法特别好地工作。特别是如果内容页面在我们到达这里之前已经调用了jQuery noConflict本身。这是一个不必要的兔子洞。
  • 这段代码有点令人震惊地将对我们的库的引用放在窗口的范围内,内容页面以后很可能会以某种方式对其进行更改。出于明显的原因,将任何内容公开到窗口范围通常是不好的做法。

此代码改为

this.$ = window.jQuery.noConflict(true);
  • 将我们的库保留在其所属的范围内
  • 为我们提供了作用域中的 $ 别名,以继续在我们的脚本中使用
  • 将窗口的 $ 和 jQuery 别名恢复为加载 jQuery 时的状态,无论它们是否实际上是 jQuery
  • 给了我们最好的机会,使用脚本加载的jQuery对象来调用noConflict

最后一点可能有点矫枉过正,但为什么不明确并使用 jQuery 标识符进行 noConflict 调用。虽然此时如果 $ 不是 jQuery 在这个调用运行时(@require对于另一个使用 $ 的库,在此代码运行之前自己使用它等),这将是我们的错),但它免费防止了以后的调试工作。

虽然我们可以在那里完成并感到满意,但我们也可以走得更远,以确保跨最广泛的页面的兼容性,并通过最广泛的内容页面更改。

(function ($, undefined) {
  $(function () {
    //Your code here;
  });
})(window.jQuery.noConflict(true));

虽然仍然不能保证,但这种模式通过将 $ 别名保持在全局范围之外并利用 jQuery 的 DOM Ready 事件来确保页面完成,为您的函数代码提供了在最广泛的页面生命周期可能性中执行的最佳机会。除此之外,它还确保代码具有一致的未定义值。

// ==UserScript==
// @name          jQuery safe inclusion template
// @description   include jQuery and make sure window.$ is the content page's jQuery version, and this.$ is our jQuery version.
// @version       0.0.1
// @author        Sonic Beard
// @match         http://*.site.com/*
// @require       http://cdn.jsdelivr.net/jquery/2.1.3/jquery.min.js
// @grant         none
// ==/UserScript==
(function ($, undefined) {
  $(function () {
    //Your code here;
  });
})(window.jQuery.noConflict(true));

TamperMonkey 似乎没有这样的功能,但另一种方法是立即调用 $.noConflict,并将removeAll参数设置为 true 。这将导致 jQuery 重置原始$jQuery回其原始值。

示例用户脚本:

// ==UserScript==
// @name         jQuery noConflict test
// @namespace    http://example.com/
// @version      0.1
// @description  test jQuery noConflict
// @author       You
// @match        https://steamcommunity.com/
// @require      https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
// @grant        none
// ==/UserScript==
window.jQ = $.noConflict(true);

从下面的示例控制台输入中可以看到,jQ@require加载的jQuery 2.1.3,jQuery是页面加载的jQuery版本,$仍然是原始原型库对象,没有jQuery的fn属性证明了这一点。

控制台输入/输出示例:

> jQ.fn.jquery
< "2.1.3"
> jQuery.fn.jquery
< "1.11.1"
> $.fn
< undefined