Come posso intercettare XMLHttpRequests da uno script Greasemonkey?

Vorrei catturare il contenuto delle richieste AJAX usando Greasemonkey.

Qualcuno sa come si fa questo?

La risposta accettata è quasi corretta, ma potrebbe essere leggermente migliorata:

(function(open) { XMLHttpRequest.prototype.open = function() { this.addEventListener("readystatechange", function() { console.log(this.readyState); }, false); open.apply(this, arguments); }; })(XMLHttpRequest.prototype.open); 

Preferisci usare apply + arguments over call perché in questo caso non devi conoscere esplicitamente tutti gli argomenti che vengono dati in apertura e che potrebbero cambiare!

Che ne dici di modificare XMLHttpRequest.prototype.open o inviare metodi con sostituzioni che impostano i propri callback e chiamano i metodi originali? Il callback può fare la sua cosa e quindi chiamare il callback il codice originale specificato.

In altre parole:

 XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open; var myOpen = function(method, url, async, user, password) { //do whatever mucking around you want here, eg //changing the onload callback to your own version //call original this.realOpen (method, url, async, user, password); } //ensure all XMLHttpRequests use our custom open method XMLHttpRequest.prototype.open = myOpen ; 

Testato su Chrome 55 e Firefox 50.1.0

Nel mio caso volevo modificare il responseText, che in Firefox era una proprietà di sola lettura, quindi ho dovuto racchiudere l’intero object XMLHttpRequest. Non ho implementato l’intera API (in particolare il responseType), ma è stato abbastanza buono da usare per tutte le librerie che ho.

Uso:

  XHRProxy.addInterceptor(function(method, url, responseText, status) { if (url.endsWith('.html') || url.endsWith('.htm')) { return "" + responseText; } }); 

Codice:

 (function(window) { var OriginalXHR = XMLHttpRequest; var XHRProxy = function() { this.xhr = new OriginalXHR(); function delegate(prop) { Object.defineProperty(this, prop, { get: function() { return this.xhr[prop]; }, set: function(value) { this.xhr.timeout = value; } }); } delegate.call(this, 'timeout'); delegate.call(this, 'responseType'); delegate.call(this, 'withCredentials'); delegate.call(this, 'onerror'); delegate.call(this, 'onabort'); delegate.call(this, 'onloadstart'); delegate.call(this, 'onloadend'); delegate.call(this, 'onprogress'); }; XHRProxy.prototype.open = function(method, url, async, username, password) { var ctx = this; function applyInterceptors(src) { ctx.responseText = ctx.xhr.responseText; for (var i=0; i < XHRProxy.interceptors.length; i++) { var applied = XHRProxy.interceptors[i](method, url, ctx.responseText, ctx.xhr.status); if (applied !== undefined) { ctx.responseText = applied; } } } function setProps() { ctx.readyState = ctx.xhr.readyState; ctx.responseText = ctx.xhr.responseText; ctx.responseURL = ctx.xhr.responseURL; ctx.responseXML = ctx.xhr.responseXML; ctx.status = ctx.xhr.status; ctx.statusText = ctx.xhr.statusText; } this.xhr.open(method, url, async, username, password); this.xhr.onload = function(evt) { if (ctx.onload) { setProps(); if (ctx.xhr.readyState === 4) { applyInterceptors(); } return ctx.onload(evt); } }; this.xhr.onreadystatechange = function (evt) { if (ctx.onreadystatechange) { setProps(); if (ctx.xhr.readyState === 4) { applyInterceptors(); } return ctx.onreadystatechange(evt); } }; }; XHRProxy.prototype.addEventListener = function(event, fn) { return this.xhr.addEventListener(event, fn); }; XHRProxy.prototype.send = function(data) { return this.xhr.send(data); }; XHRProxy.prototype.abort = function() { return this.xhr.abort(); }; XHRProxy.prototype.getAllResponseHeaders = function() { return this.xhr.getAllResponseHeaders(); }; XHRProxy.prototype.getResponseHeader = function(header) { return this.xhr.getResponseHeader(header); }; XHRProxy.prototype.setRequestHeader = function(header, value) { return this.xhr.setRequestHeader(header, value); }; XHRProxy.prototype.overrideMimeType = function(mimetype) { return this.xhr.overrideMimeType(mimetype); }; XHRProxy.interceptors = []; XHRProxy.addInterceptor = function(fn) { this.interceptors.push(fn); }; window.XMLHttpRequest = XHRProxy; })(window); 

È ansible sostituire l’object unsafeWindow.XMLHttpRequest nel documento con un wrapper. Un piccolo codice (non testato):

 var oldFunction = unsafeWindow.XMLHttpRequest; unsafeWindow.XMLHttpRequest = function() { alert("Hijacked! XHR was constructed."); var xhr = oldFunction(); return { open: function(method, url, async, user, password) { alert("Hijacked! xhr.open()."); return xhr.open(method, url, async, user, password); } // TODO: include other xhr methods and properties }; }; 

Ma questo ha un piccolo problema: gli script Greasemonkey vengono eseguiti dopo il caricamento di una pagina, quindi la pagina può utilizzare o archiviare l’object XMLHttpRequest originale durante la sua sequenza di caricamento, quindi le richieste eseguite prima dell’esecuzione dello script o con il vero object XMLHttpRequest non verranno tracciate dalla tua sceneggiatura. In nessun modo posso vedere per aggirare questa limitazione.

Ho scritto un codice per intercettare le chiamate ajax, durante la scrittura del server proxy. Dovrebbe funzionare sulla maggior parte dei browser.

Eccolo: https://github.com/creotiv/AJAX-calls-intercepter

Non sei sicuro di poterlo fare con greasemonkey, ma se crei un’estensione puoi utilizzare il servizio observer e l’osservatore http-on-examination-response.