Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to keep track of number of outstanding AJAX requests in firefox

I am using Selenium to test a web application and am not allowed to modify the application's javascript code. I am trying to track the number of outstanding AJAX requests by using GreaseMonkey to override XMLHttpRequest.send. The new send() will basically wrap what was set as the onreadystatechange callback, check the readyState, incrementing or decrementing the counter as appropriate, and calling the original callback function.

The problem that I'm having appears to be a privilege issue because if I just browse to a page in a normal firefox browser, open firebug and paste in the following code, it seems to work fine:

document.ajax_outstanding = 0;
if (typeof XMLHttpRequest.prototype.oldsend != 'function') {
    XMLHttpRequest.prototype.oldsend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function() {
        console.log('in new send');
        console.log('this.onreadystatechange = ' + this.onreadystatechange);
        this.oldonreadystatechange = this.onreadystatechange;
        this.onreadystatechange = function() {
            if (this.readyState == 2) {
                /* LOADED */
                document.ajax_outstanding++;
                console.log('set ajax_outstanding to ' + document.ajax_outstanding);
            }
            this.oldonreadystatechange.handleEvent.apply(this, arguments);
            if (this.readyState == 4) {
                /* COMPLETED */
                document.ajax_outstanding--;
                console.log('set ajax_outstanding to ' + document.ajax_outstanding);
            }
        };
        this.oldsend.apply(this, arguments);
    };
}

Now if I use a slightly modified version of that snippet from within a GreaseMonkey user script like so:

unsafeWindow.document.ajax_outstanding = 0;
if (typeof unsafeWindow.XMLHttpRequest.prototype.oldsend != 'function') {
    unsafeWindow.XMLHttpRequest.prototype.oldsend = unsafeWindow.XMLHttpRequest.prototype.send;
    unsafeWindow.XMLHttpRequest.prototype.send = function() {
        GM_log('in new send');
        GM_log('this.onreadystatechange = ' + this.onreadystatechange);
        this.oldonreadystatechange = this.onreadystatechange;
        this.onreadystatechange = function() {
            if (this.readyState == 2) {
                /* LOADED */
                unsafeWindow.document.ajax_outstanding++;
                GM_log('set ajax_outstanding to ' + unsafeWindow.document.ajax_outstanding);
            }
            this.oldonreadystatechange.handleEvent.apply(this, arguments);
            if (this.readyState == 4) {
                /* COMPLETED */
                unsafeWindow.document.ajax_outstanding--;
                GM_log('set ajax_outstanding to ' + unsafeWindow.document.ajax_outstanding);
            }
        };
        this.oldsend.apply(this, arguments);
    };
}

and I go to a page, do something that causes an AJAX request, I get the following message in the javascript error console:

http://www.blah.com/gmscripts/overrides: in new send
uncaught exception: [Exception... "Illegal value" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: file:///tmp/customProfileDir41e7266f56734c97a2ca02b1f7f528e1/extensions/%7Be4a8a97b-f2ed-450b-b12d-ee082ba24781%7D/components/greasemonkey.js :: anonymous :: line 372" data: no]

So it appears to be throwing the exception when trying to access this.onreadystatechange

Presumably, this is due to the sandboxed environment. Any help would be greatly appreciated. I am not tied to this solution, so any other suggestions for doing what I need are welcome. It's just that I've tried several others and this seems to be the most promising. The requirement is that I need to make sure that the counter gets to 0 after the readyState goes to 4 and the onreadystatechange callback has finished execution.

like image 521
Tommy Avatar asked Dec 10 '10 15:12

Tommy


People also ask

How many AJAX requests are there?

It is worth noting that browsers can generally only handle 6 ajax requests at a time, this may catch you out.

How do I know if AJAX request is running?

You could use ajaxStart and ajaxStop to keep track of when requests are active. Show activity on this post. Show activity on this post. Checking for null to determine if the request object exists is important, but if it is not null you should really check request.

How do I know AJAX call is complete?

The ajaxStop() method specifies a function to run when ALL AJAX requests have completed. When an AJAX request completes, jQuery checks if there are any more AJAX requests. The function specified with the ajaxStop() method will run if no other requests are pending.

How many XHR requests are allowed?

How many concurrent AJAX (XmlHttpRequest) requests are allowed in popular browsers? Bookmark this question. Show activity on this post. In Firefox 3, the answer is 6 per domain: as soon as a 7th XmlHttpRequest (on any tab) to the same domain is fired, it is queued until one of the other 6 finish.


1 Answers

I've made something myself: http://jsfiddle.net/rudiedirkx/skp28agx/ (updated 22 Jan 2015)

The script (should run before anything else):

(function(xhr) {
    xhr.active = 0;
    var pt = xhr.prototype;
    var _send = pt.send;
    pt.send = function() {
        xhr.active++;
        this.addEventListener('readystatechange', function(e) {
            if ( this.readyState == 4 ) {
                setTimeout(function() {
                    xhr.active--;
                }, 1);
            }
        });
        _send.apply(this, arguments);
    }
})(XMLHttpRequest);

And the jsFiddle's test script:

window.onload = function() {
    var btn = document.querySelector('button'),
        active = btn.querySelector('span');

    btn.onclick = function() {
        // jQuery for easy ajax. `delay` is a jsFiddle argument
        // to keep some requests active longer.
        jQuery.post('/echo/json/', {
            delay: Math.random() * 3,
        });
    };

    updateActive();

    function updateActive() {
        active.textContent = XMLHttpRequest.active;
        requestAnimationFrame(updateActive);
    }
};

It updates the counter in the button every animation frame (~ 60 times per second), separate from the AJAX requests. Whatever you do, however fast and much you click it, the counter should always end up at 0 after a few seconds.

like image 63
Rudie Avatar answered Sep 21 '22 14:09

Rudie