javascript 如何在注入代码中使用 GM_xmlhttpRequest?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11377191/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 12:58:10  来源:igfitidea点击:

How to use GM_xmlhttpRequest in Injected Code?

javascriptgoogle-chrome-extensiongreasemonkeyuserscripts

提问by akagixxer

I am writing a userscript that is injected into a webpage. The script reads some data from a web-server and I want to send messages to a listening application to react to the data.

我正在编写一个注入网页的用户脚本。该脚本从网络服务器读取一些数据,我想将消息发送到侦听应用程序以对数据做出反应。

For now, all I'm doing is trying to send a string command to my listening application and see if I can read it. My code worked before it was injected, but afterwards I get an "undefined reference error".

现在,我所做的只是尝试向我的监听应用程序发送一个字符串命令,看看我是否可以读取它。我的代码在注入之前工作,但之后我收到“未定义的引用错误”。

I suspect that this has something to do with this "Greasemonkey access violation". However, I have been unable to find a solution that works. I'm developing in Chrome.

我怀疑这与“Greasemonkey 访问冲突”有关。但是,我一直无法找到有效的解决方案。我正在 Chrome 中开发。

Here is the section of code I can't get to work.

这是我无法开始工作的代码部分。

GM_xmlhttpRequest({
   method: "POST", 
   url: "http://localhost:7777", 
   data: "testing123",
   headers:  {
         "Content-Type": "application/x-www-form-urlencoded"
             },
   onload: function(response) 
   {
      if (response.responseText.indexOf("TEST") > -1) 
      {
         console.log("Response confirmed..."); 
      }
   }
}); 

I'm pretty new to scripting so maybe I'm missing something obvious. How do I get this to work in injected code?

我对脚本很陌生,所以也许我错过了一些明显的东西。我如何让它在注入的代码中工作?

回答by Brock Adams

GM_functions will not work in injected code because injected code runs in the target page's scope. If they didwork there, then unscrupulous web-sites could also use the GM_functions -- to do unspeakable evil.

GM_函数在注入的代码中不起作用,因为注入的代码在目标页面的范围内运行。如果他们确实在那里工作,那么不道德的网站也可以使用这些GM_功能——做无法形容的邪恶。

The solutions, most preferable first:

解决方案,最可取的是:

  1. Don't inject code. Much of the time, it really isn't necessary, and it always complicates things. Only inject code if you absolutely, positively need to use some of the javascript loaded by the target page.

    For libraries like jQuery, you will get better performance using the @requiredirective (Firefox), or pasting-in the library code or using a custom manifest.jsonfileto include it (Chrome).

    By not injecting code, you:

    1. Keep the ability to easily use GM_functions
    2. Avoid or reduce the dependency on outside servers to deliver libraries.
    3. Avoid potential side effects and dependencies with/on the page's JS. (You could even use something like NoScriptto completely disable the page's JS, while your script still runs.)
    4. Prevent malicious web sites from exploiting your script to gain access to the GM_functions.

  2. Use the Tampermonkey extension(Chrome). This allows you to avoid script injection by providing better Greasemonkey emulation. You can use the @requiredirective and a more powerful/risky version of unsafeWindowthan Chrome natively provides.

  3. Split your userscript code into injected parts -- which cannot use GM_functions -- and non-injected parts. Use messaging, polling, and/or a specific DOM node to communicate between the scopes.

  1. 不要注入代码。很多时候,它真的没有必要,而且它总是使事情复杂化。仅当您绝对需要使用目标页面加载的某些 javascript 时才注入代码。

    对于像 jQuery 这样的库,使用@require指令 (Firefox) 或粘贴库代码或使用自定义manifest.json文件来包含它 (Chrome) ,您将获得更好的性能。

    通过不注入代码,您:

    1. 保持轻松使用GM_功能的能力
    2. 避免或减少对外部服务器的依赖来交付库。
    3. 避免潜在的副作用和对页面 JS 的依赖。(您甚至可以使用NoScript 之类的东西来完全禁用页面的 JS,而您的脚本仍在运行。)
    4. 防止恶意网站利用您的脚本来访问这些GM_功能。

  2. 使用Tampermonkey 扩展程序(Chrome)。这允许您通过提供更好的 Greasemonkey 模拟来避免脚本注入。您可以使用@require指令和unsafeWindow比 Chrome 本身提供的更强大/风险更高的版本。

  3. 将您的用户脚本代码拆分为注入部分——不能使用GM_函数——和非注入部分。使用消息传递、轮询和/或特定 DOM 节点在范围之间进行通信。





If you really must use injected code, here's a sample script that shows how to do it:

如果您真的必须使用注入的代码,这里有一个示例脚本,展示了如何做到这一点:

// ==UserScript==
// @name        _Fire GM_ function from injected code
// @include     https://stackoverflow.com/*
// @grant       GM_xmlhttpRequest
// ==/UserScript==
/* Warning:  Using @match versus @include can kill the Cross-domain ability of
    GM_xmlhttpRequest!  Bug?
*/

function InjectDemoCode ($) {
    $("body").prepend ('<button id="gmCommDemo">Open the console and then click me.</button>');

    $("#gmCommDemo").click ( function () {
        //--- This next value could be from the page's or the injected-code's JS.
        var fetchURL    = "http://www.google.com/";

        //--- Tag the message, in case there's more than one type flying about...
        var messageTxt  = JSON.stringify (["fetchURL", fetchURL])

        window.postMessage (messageTxt, "*");
        console.log ("Posting message");
    } );
}

withPages_jQuery (InjectDemoCode);

//--- This code listens for the right kind of message and calls GM_xmlhttpRequest.
window.addEventListener ("message", receiveMessage, false);

function receiveMessage (event) {
    var messageJSON;
    try {
        messageJSON     = JSON.parse (event.data);
    }
    catch (zError) {
        // Do nothing
    }
    console.log ("messageJSON:", messageJSON);

    if ( ! messageJSON) return; //-- Message is not for us.

    if (messageJSON[0] == "fetchURL") {
        var fetchURL    = messageJSON[1];

        GM_xmlhttpRequest ( {
            method:     'GET',
            url:        fetchURL,
            onload:     function (responseDetails) {
                            // DO ALL RESPONSE PROCESSING HERE...
                            console.log (
                                "GM_xmlhttpRequest() response is:\n",
                                responseDetails.responseText.substring (0, 80) + '...'
                            );
                        }
        } );
    }
}

function withPages_jQuery (NAMED_FunctionToRun) {
    //--- Use named functions for clarity and debugging...
    var funcText        = NAMED_FunctionToRun.toString ();
    var funcName        = funcText.replace (/^function\s+(\w+)\s*\((.|\n|\r)+$/, "");
    var script          = document.createElement ("script");
    script.textContent  = funcText + "\n\n";
    script.textContent += 'jQuery(document).ready( function () {' + funcName + '(jQuery);} );';
    document.body.appendChild (script);
};

回答by Ryan The Leach

You are posting out to localhost. If you are willing to, (Not everyone will be) setup CORS headers to allow the tampered site access on your localhost server, or all sites if it's something not expected to be secure / ran by others / published.

您正在发布到本地主机。如果您愿意,(不是每个人都会)设置 CORS 标头以允许在您的本地主机服务器或所有站点上访问被篡改的站点,如果它不安全/由其他人运行/发布。

Then you don't needto use GM_XHR at all, as long as the XHR is properly configured to be cross-domain which you can do by wrapping it in an injected script.

那么你根本不需要使用 GM_XHR,只要 XHR 被正确配置为跨域,你可以通过将它包装在注入的脚本中来实现。