【问题标题】:How to make Tampermonkey userscript execute before other scripts of page?如何让 Tampermonkey 用户脚本在页面的其他脚本之前执行?
【发布时间】:2022-10-07 00:49:08
【问题描述】:

我需要登录一个强制使用 IE 的网页。

该页面类似于以下代码。

<Script Language=\"javascript\">
alert(\"Please use IE to login!\");
window.opener = null;
window.close();
</Script>

<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">

<html xmlns=\"http://www.w3.org/1999/xhtml\">
<body>
...
</body>
</html>

我想使用 Chrome 登录,所以我必须阻止这个脚本关闭窗口。 我曾尝试使用 Tampermonkey 脚本,但我的脚本总是比页面脚本执行得晚。

元数据// @run-at document-start 似乎不起作用。

下面是我尝试过的代码。

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://localhost:8080/666/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=undefined.localhost
// @grant        none
// @run-at       document-start
// ==/UserScript==

// reference: https://github.com/jspenguin2017/Snippets/blob/master/onbeforescriptexecute.html
(function() {
    \'use strict\';
    
    const Event = class {
        constructor(script, target) {
            this.script = script;
            this.target = target;

            this._cancel = false;
            this._replace = null;
            this._stop = false;
        }

        preventDefault() {
            this._cancel = true;
        }
        stopPropagation() {
            this._stop = true;
        }
        replacePayload(payload) {
            this._replace = payload;
        }
    };

    let callbacks = [];
    window.addBeforeScriptExecuteListener = (f) => {
        if (typeof f !== \"function\") {
            throw new Error(\"Event handler must be a function.\");
        }
        callbacks.push(f);
    };
    window.removeBeforeScriptExecuteListener = (f) => {
        let i = callbacks.length;
        while (i--) {
            if (callbacks[i] === f) {
                callbacks.splice(i, 1);
            }
        }
    };

    const dispatch = (script, target) => {
        if (script.tagName !== \"SCRIPT\") {
            return;
        }

        const e = new Event(script, target);

        if (typeof window.onbeforescriptexecute === \"function\") {
            try {
                window.onbeforescriptexecute(e);
            } catch (err) {
                console.error(err);
            }
        }else{
            console.log(\"window.onbeforescriptexecute no defined\");
        }

        for (const func of callbacks) {
            if (e._stop) {
                break;
            }
            try {
                func(e);
            } catch (err) {
                console.error(err);
            }
        }

        if (e._cancel) {
            script.textContent = \"\";
            script.remove();
        } else if (typeof e._replace === \"string\") {
            script.textContent = e._replace;
        }
    };
    const observer = new MutationObserver((mutations) => {
        window.close = ()=>{return;};
        for (const m of mutations) {
            for (const n of m.addedNodes) {
                dispatch(n, m.target);
            }
        }
    });
    observer.observe(document, {
        childList: true,
        subtree: true,
    });
    
    //example
    (() => {
            \"use strict\";
            window.onbeforescriptexecute = (e) => {
                // You should check if textContent exists as this property is
                // buggy sometimes
                if (!e.script.textContent) {
                    return;
                }

                // Prevent execution of a script
                if (e.script.textContent.includes(\"window.close()\")) {
                    e.preventDefault();
                    //e.stopPropagation();
                }

                // Change the code that runs
                if (e.script.textContent.includes(\"console.log\")) {
                    // Original payload is e.script.textContent, you can
                    // manipulate it however you want, just pass the final
                    // payload to e.replacePayload when you are done
                    e.replacePayload(\"console.log(2);\");
                    // Later event handlers can override your payload, you
                    // can call e.stopPropagation to make sure the current
                    // payload is applied
                }
            };
    })();
})();

    标签: javascript google-chrome tampermonkey


    【解决方案1】:

    问题在于,通过监听任何事件,您的回调将在文档解析后执行。这是因为事件是异步调用的,文档解析以及任何&lt;script&gt; 都是一次性执行的——就好像它是一个连续的 JavaScript 函数一样。即使触发了任何事件,afaik 浏览器也只会在完成解析后才能访问它们。

    此外,Chrome 甚至不支持您正在收听的事件,而不是官方支持。

    当试图处理你遇到的讨厌的东西时,你需要覆盖讨厌的代码使用的任何函数并更改它们以破坏它。我将从包装window.close 开始:

    window.close = function() { console.log("Nice try, but no"); }
    

    另外,考虑一下你是否能弄清楚它是如何检测 IE 和假 IE 的。例如,如果它使用navigator.userAgent,您需要更改它。请注意,某些属性是不可分配的,因为它们是 getter/setter。如果你只是分配navigator.userAgent = "totally IE",它不会改变。但是您可以更改设置器:

    Object.defineProperty(navigator, "userAgent", {get:()=>"I am IE! For real!", set:()=>{}})
    

    你去了,完全是IE。如果它只使用 userAgent,您还可以使用一些专门为伪造用户代理设计的扩展。

    【讨论】:

      猜你喜欢
      • 2017-01-13
      • 1970-01-01
      • 2019-11-05
      • 1970-01-01
      • 1970-01-01
      • 2015-09-30
      • 2023-01-12
      • 2021-04-03
      相关资源
      最近更新 更多