1

GM_xmlhttpRequest我需要调用一个由网页中的事件触发的跨域。

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

4

1 回答 1

1

To allow pages to use GM_xmlhttpRequest, you have to:

  1. Overwrite unsafeWindow.XMLHttpRequest with a custom method.
  2. Circumvent the security by detecting any desired requests via a poller, e.g. setInterval.

I have previously written such an User script for personal use, at the local protocol. Do not abuse it, and create very strict matching patterns to prevent exposing cross-site XHR to arbitrary websites.

The code does the following:

  • Move the original XMLHttpRequest object to unsafeWindow.GM_XHR.original.
  • To restore the original object, invoke GM_XHR.restore().
  • The XMLHttpRequest object is overwritten by a custom method, which adds any requests to an array queue.
  • A poller periodically checks the content of the queue, and performs all requests.

Cross-site 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);
};
于 2012-03-23T20:33:12.200 回答