Quantcast
Channel: coderrr » javascript
Viewing all articles
Browse latest Browse all 3

Firefox 3 internals, blocking alerts and XMLHttpRequests

$
0
0

In my quest to find something which acts similar to an alert() box in Firefox 3 (for anti-anti-frame-busting), but without the annoying user-experience, I discovered a few things that I thought I should share. Note this is all FF3 specific.

Why do alert() and other javascript dialogs block javascript execution and in particular javascript timers?
It actually turns out that they do this implicitly, as a side effect of being modal dialogs. Modal dialogs halt the execution of the thread they have been created from until the user closes the dialog. Since all the javascript from a single page is run in a single thread the dialog effectively stops all of it.

embedding/components/windowwatcher/src/nsWindowWatcher.cpp:949

// alert() eventually calls this function
nsWindowWatcher::OpenWindowJSInternal(...)
/* ... */
    printf("About to show the dialog...\n");
    newChrome->ShowAsModal();
    printf("You won't see this until the user closes the dialog\n");

Why don’t synchronous XMLHttpRequests block javascript timers?
Synchronous XHRs do block the execution of the javascript they were called from but not the execution of other javascript timers. That’s a little unexpected given the nature of other synchronous things like the above modal dialog. You would expect some code which blocks your javascript function to be blocking the actual thread its executing on. But Firefox does something interesting while making the requesting.

content/base/src/nsXMLHTTPRequest.cpp:1986

 // If we're synchronous, spin an event loop here and wait
  if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
    nsIThread *thread = NS_GetCurrentThread();
    while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
      if (!NS_ProcessNextEvent(thread)) {
        rv = NS_ERROR_UNEXPECTED;
        break;
      }
    }
  }

Internally Firefox processes the synchronous request asynchronously, in an event loop. And one of the things that this event loop does is schedule timers for execution.

Even though this event loop is asynchronously processing the XHR request. It is still synchronous in the sense that it will block normal execution of the thread until it returns. This can produce some interesting effects. Take this example which performs two overlapping synchronous XHRs:

setInterval('document.body.innerHTML += "."', 1000)

xhr = new XMLHttpRequest()
xhr.open("GET", "http://request-which-takes-1-second.com/", false)

setTimeout(function() {
  xhr2 = new XMLHttpRequest()
  xhr2.open("GET", "http://request-which-takes-10-seconds.com/", false)

  document.body.innerHTML += 'start2'
  xhr2.send(null)
  document.body.innerHTML += 'done2'
},500)

document.body.innerHTML += 'start1'
xhr.send(null)
document.body.innerHTML += 'done1'

In this example we queue the 2nd XHR to start about half a second after the first one starts. Since the first one only takes a second we might expect to see the text done1 before we see done2. But since it was the event loop of the first XHR which actually initiated the 2nd XHR, the first XHR won’t return until the function which initiated the 2nd XHR returns. We end up getting output like this:

start1start2...............done2done1


Viewing all articles
Browse latest Browse all 3