Search code examples
javascriptajaxxmlhttprequestgreasemonkey

Intercepting XMLHttpRequest in Greasemonkey while using @grant the right way


So my goal was to hijack all XMLHttpRequest responses and do something with them in my script's scope. But since I @grant something I had to use unsafeWindow couldn't do it easy way. (I don't know why, but I couldn't change unsafeWindow.XMLHttpRequest.prototype.open. There was no error, but XMLHttpRequest didn't work at all.) So I ended up with this piece of code:

// ==UserScript==
// @name        a
// @include     *
// @version     1
// @grant       GM_xmlhttpRequest
// ==/UserScript==

realProcess = function(xhr) {
    // do something
}

function hijackAjax(process) {
    if(typeof process != "function") {
        process = function(e){ console.log(e); };
    }
    window.addEventListener("hijack_ajax", function(event) {
        process(event.detail);
    }, false);
    function injection() {
        var open = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function() {
            this.addEventListener("load", function() {
                window.dispatchEvent(new CustomEvent("hijack_ajax", {detail: this}));
            }, false);
            open.apply(this, arguments);
        };
    }
    window.setTimeout("(" + injection.toString() + ")()", 0);
}
hijackAjax(realProcess);

My question is: can it be done better/faster/more elegant?

EDIT: Updated code according to hints.


Solution

  • The solution depends on how you want to do interact. You could inject all the logic into the site's scope, however, there is a barrier between userscript's scope and site's scope if you want to interact. It is not trivial to exchange data between both scopes. unsafeWindow is evil, avoid usage. Inject the logic as string to be evaluatet inside a setTimeout(String), so that it is executed in site's scope. If you need to interact with the userscript in privileged scope, setup a messaging system with window.dispatchEvent(new CustomEvent('eventname', {'detail': scalarData}));. You won't be able to transfer complex datatype declared in a scope with other privileges.