How to intercept XMLHttpRequests from Greasemonkey script?

I would like to capture the contents of AJAX requests using Greasemonkey.

Does anyone know how to do this?

+49
javascript ajax greasemonkey
Mar 10 '09 at 10:59
source share
7 answers

The accepted answer is almost correct, but it can improve a bit:

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

Prefer to use apply + arguments to invoke, because you do not need to explicitly know all the arguments you need to open that can change!

+58
Mar 27 '15 at 4:35
source

How do I modify the XMLHttpRequest.prototype.open modification or submit replacement methods that set their own callbacks and call the original methods? The callback can complete its task and then call the callback to the specified source code.

In other words:

 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 ; 
+6
Mar 10 '09 at 11:20
source

Tested on Chrome 55 and Firefox 50.1.0

In my case, I wanted to change the responseText, which in Firefox was a read-only property, so I had to wrap the entire XMLHttpRequest object. I did not implement the entire API (in particular, responseType), but it was good enough to use for all the libraries that I have.

Using:

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

the code:

 (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); 
+3
Jan 27 '17 at 17:04 on
source

You can replace the unsafeWindow.XMLHttpRequest object in the document with a wrapper. Small code (not tested):

 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 }; }; 

But this has one small problem: Greasemonkey scripts are executed after the page loads, so the page can use or store the original XMLHttpRequest object during its loading sequence, so requests executed before your script or with the real XMLHttpRequest object will not be tracked by your script. I can’t get around this limitation.

+1
Mar 10 '09 at 11:30
source

I wrote code to intercept ajax calls when writing a proxy. It should work on most browsers.

Here it is: https://github.com/creotiv/AJAX-calls-intercepter

+1
Jan 05 2018-12-12T00:
source

Not sure if you can do this with greasemonkey, but if you create an extension, you can use the observer and observer-client-observer services.

0
Mar 10 '09 at 11:02
source

Based on the proposed solution, I implemented the file "xhr-extensions.ts", which can be used in typewriting solutions. How to use:

  1. Add the code file to your solution

  2. Import like this

     import { XhrSubscription, subscribToXhr } from "your-path/xhr-extensions"; 
  3. Subscribe like that

     const subscription = subscribeToXhr(xhr => { if (xhr.status != 200) return; ... do something here. }); 
  4. Unsubscribe when you no longer need a subscription

     subscription.unsubscribe(); 

The contents of the file 'xhr-extensions.ts'

  export class XhrSubscription { constructor( private callback: (xhr: XMLHttpRequest) => void ) { } next(xhr: XMLHttpRequest): void { return this.callback(xhr); } unsubscribe(): void { subscriptions = subscriptions.filter(s => s != this); } } let subscriptions: XhrSubscription[] = []; export function subscribeToXhr(callback: (xhr: XMLHttpRequest) => void): XhrSubscription { const subscription = new XhrSubscription(callback); subscriptions.push(subscription); return subscription; } (function (open) { XMLHttpRequest.prototype.open = function () { this.addEventListener("readystatechange", () => { subscriptions.forEach(s => s.next(this)); }, false); return open.apply(this, arguments); }; })(XMLHttpRequest.prototype.open); 
0
Dec 12 '18 at 11:15
source



All Articles