如何在网页中调用GM_xmlhttpRequest?

时间:2012-03-23 20:21:59

标签: javascript xmlhttprequest cross-domain greasemonkey gm-xmlhttprequest

我需要调用跨域GM_xmlhttpRequest,该域名将由网页中的事件触发。

如何从网页访问greasemonkey沙箱中的功能?

1 个答案:

答案 0 :(得分:2)

要允许网页使用GM_xmlhttpRequest,您必须:

  1. 使用自定义方法覆盖unsafeWindow.XMLHttpRequest
  2. 通过轮询器检测任何所需的请求来绕过安全性,例如: setInterval
  3. 我以前在本地协议中编写了这样一个供个人使用的用户脚本。 不要滥用,并创建非常严格的匹配模式,以防止跨网站XHR暴露于任意网站。

    代码执行以下操作:

    • 将原始XMLHttpRequest对象移至unsafeWindow.GM_XHR.original
    • 要恢复原始对象,请调用GM_XHR.restore()
    • XMLHttpRequest对象被自定义方法覆盖,该方法将任何请求添加到数组队列。
    • 轮询器定期检查队列的内容,并执行所有请求。

    跨站点XMLHttpRequest,greasemonkey userscript

    // ==UserScript==
    // @name           Cross-site XMLHttpRequest
    // @namespace      Rob W
    // @description    Enables cross-site XHR
    // @include        file:///home/*
    // ==/UserScript==
    
    /*
     * http://www.w3.org/TR/XMLHttpRequest
     * http://wiki.greasespot.net/GM_xmlhttpRequest
     * https://developer.mozilla.org/en/XMLHttpRequest
     * 
     * https://developer.mozilla.org/En/nsIDOMProgressEvent
     * https://developer.mozilla.org/en/nsIChannel
     */
    
    // Configuration. Allow paths via a RegExp:
    var enabled = [
        "^file:///home/rob/Documenten/etc/",
        "^file:///home/rob/Documenten/test\.html$"
    ];
    
    var XHR = {
        allowed: new RegExp(enabled.join("|")),
        allowed_atm: function(url){
            return XHR.allowed.test(url || location.protocol + "//" + location.pathname)
        },
        original: unsafeWindow.XMLHttpRequest,
        restore: function(){
            unsafeWindow.XMLHttpRequest = XHR.original;
        }
    };
    if (XHR.allowed_atm()) {
        // Request queue
        var reqs = [];
    
        // Inititate the actual gm_xmlhttpRequest.
        // Also define the `.abort()` method
        var do_xmlHttpRequest = function(details) {
            details.abort = GM_xmlhttpRequest(details).abort;
        }
    
        // Set poller, used to circumvent the security policy
        window.setInterval(function(){
            while(reqs.length) {
                do_xmlHttpRequest(reqs.shift());
            }
        }, 50);
    
        // unsafeWindow.XMLHttpRequest will be overwritten by:
        var xmlhttprequest = function() {
            var o = {headers: {}},
                t = this,
                sent = false,
                currentHeaders = "";
    
            t.channel = {
                name: ""
            };
    
            o.onprogress = function(r){
                if(r.lengthComputable) {
                    t.channel.contentLength = r.total;
                    currentHeaders = "Content-Length: " + r.total;
                }
                t.status = r.status;
                t.statusText = r.statusText;
                t.channel.name = r.finalUrl;
            };
    
            t.abort = function() {
                if(typeof o.abort == "function") o.abort();
                else t.onreadystatechange = null;
                sent = false;
            };
            t.getAllResponseHeaders = function() {
                return t.responseHeaders ? t.responseHeaders + (/(^|\n)Content-Length:\s?\d/i.test(t.responseHeaders)?"":"\n" + currentHeaders) : currentHeaders;
            };
            t.getResponseHeader = function(header) {
                console_log("Method not supported. getResponseHeader: " + header);
                return "";
            };
            t.open = function(method, url, async, user, password) {
                o.method = method;
                o.url = url;
                t.channel.name = url; //finalUrl?
                //o.synchronous = !async; //Not implemented for safety reasons
                if (typeof user != "undefined") o.user = user;
                if (typeof password != "undefined") o.password = password;
            };
            t.overrideMimeType = function(mimetype) {
                o.overrideMimeType = mimetype;
            };
            t.send = function(data){
                var readyState4reached = false;
                if (typeof t.onabort == "function") r.onabort = t.onabort;
                if (typeof t.onerror == "function") r.onerror = t.onerror;
                o.onload = function(r){
                    o.onreadystatechange(r);
                    if (typeof t.onload == "function") t.onload();
                };
                o.data = data;
                o.onreadystatechange = function(r) {
                    t.channel.name = r.finalUrl;
                    if (t.responseHeaders = r.responseHeaders) {
                        var tmp;
                        if(tmp = t.responseHeaders.match(/Content-Length:\s?(\d+)/i)) {
                            t.channel.contentLength = tmp[1];
                        }
                    }
                    t.readyState = r.readyState;
                    t.responseText = r.responseText;
                    t.status = r.status;
                    t.statusText = r.statusText;
                    if(readyState4reached) return;
                    if(!readyState4reached && t.readyState == 4) readyState4reached = true;
                    typeof t.onreadystatechange == "function" && t.onreadystatechange();
                }
                if(!sent) reqs.push(o);
                sent = true;
            };
            t.setRequestHeader = function(name, value) {
                o.headers[name] = value;
            };
        }
        /* Event binding */
        unsafeWindow.XMLHttpRequest = xmlhttprequest;
        unsafeWindow.GM_XHR = {original: XHR.original, restore: XHR.restore};
    
        // Log the exposed method in the console, to not forget about it:
        console.log("GM cross-site XHR activated at: " + location.href);
    };