Today's Question:  What does your personal desk look like?        GIVE A SHOUT

IE ActiveX(”htmlfile”) Transport, Part II

  Michael Carter        2011-09-05 04:07:02       4,853        0    

In my last post I discussed using the ActiveX(”htmlfile”) technique to provide a usable streaming transport in Internet Explorer. The solution I provided will work, but since writing the last article I’ve made significant progress in understanding why IE behaves the way it does with respect to the streaming transport.

The previous solution amounted to creating an array of messages, pushing messages on that array from the htmlfile iframe, and popping messages off of the array in the parent window, and processing them. Here is the function we use to create that solution:

function connect_htmlfile(url, callback) {
    var transferDoc = new ActiveXObject("htmlfile");
    transferDoc.open();
    transferDoc.write(
        "<html><script>" +
        "document.domain='" + document.domain + "';" +
        "</script></html>");
    transferDoc.close();
    var ifrDiv = transferDoc.createElement("div");
    transferDoc.body.appendChild(ifrDiv);
    ifrDiv.innerHTML = "<iframe src='" + url + "'></iframe>";
    var messageQueue = [];
    transferDoc.message_queue = messageQueue;
    var check_for_msgs = function() {
        // psuedo code
        // while messageQueue.not_empty:
        //   msg = messageQueue.pop();
        //   callback(msg);
    }
    // Check updates ten times a second.
    setInterval(check_for_msgs, 100);
}

In the iframe:

<script>parent.messageQueue.append("arbitrary data")</script>

So this does indeed work, albeit with a performance hit for the callback, as well as an added worst-case 100 ms delay for each event. This is not ideal, but at least it works. Unfortunately, what wasn’t adequately explained in my previous article was why it works.

Well, I have the answer in this article. I was prompted to investigate when I received a message on the Orbited mailing list from “Ufo” explaining that it is unnecessary to push messages into a queue and de-queue them later. In fact, the htmlfile transport can be used almost identically to the standard iframe transport. This is the way I originally attempted to implement this transport, and I encountered the premature htmlfile closing issue, so I was a bit surprised by the news. Here is the working (I tested it) code suggested by Ufo on the mailing list:


function connect_htmlfile(url, callback) {
    var transferDoc = new ActiveXObject("htmlfile");
    transferDoc.open();
    transferDoc.write(
        "<html><script>" +
        "document.domain='" + document.domain + "';" +
        "</script></html>");
    transferDoc.close();
    var ifrDiv = transferDoc.createElement("div");
    transferDoc.body.appendChild(ifrDiv);
    ifrDiv.innerHTML = "<<frame src='" + url + "'></iframe>";
    transferDoc.callback = callback;
    setInterval( function () {}, 10000);
}

In the iframe:

<script>parent.callback("arbitrary data")</script>

The last line in connect_html creates a timer that operates every ten seconds and does absolutely nothing. With this line added, the transport suddenly works. Without it, we have the same problems as before. So my original solution worked because of the setInterval, not because of appending messages to a queue. But it seems to make no sense that a timer which does nothing will magically cause the ActiveX(”htmlfile”) connection not to close. I was perplexed by this until I started digging deeper.

Let’s start with the answer. This is a problem of garbage collection. The reason it works is because that last line creates an anonymous function that forms a closure over the scope in which transferDoc was originally defined. Without this closure, there is no reference to transferDoc so its marked by the garbage collector for deletion. The garbage collector doesn’t do its job immediately though. Rather, it operates after a specified interval of instructions on DOM objects. Actually, I believe the garbage collector for JavaScript objects is different than that for DOM objects, so you can do plenty of JavaScript-only manipulations without causing the garbage collection process to occur.

The new code looks like this:

function connect_htmlfile(url, callback) {
    // ... Same stuff as before ...
    dummy = function() {}
}

This will solve the problem without the setInterval, because we’re still creating a closure around the scope of transferDoc. But this causes a different problem, as first experienced by Andrew Betts and explained in the same mailing thread: once the user navigates away from the page, the htmlfile persists.

The cause of this requires a brief bit of background. In IE there are various methods of causing memory leaks. Normally that’s not a huge deal—the user ends up with a few hundred kilobytes of memory missing, but life goes on. Clearly it isn’t ideal, but neither is it fatal. However, when a live HTTP connection (the htmlfile’s iframe) fails to be garbage collected, we have a huge problem. Not only does it waste server resources to send events to a nonexistent page, it also wreaks havoc on any application logic trying to detect disconnects (for instance, a Comet chat application might want to send a “user has departed” message). Even worse though is that the user will quickly run into the two-connection-per-server limit to the Comet server.

The final solution is very simple. Remember Alex Russell’s original code for the htmlfile transport? We can use it almost exactly, if we remove the var from the transferDoc assignment. The code looks like this:


function connect_htmlfile(url, callback) {
    // no more 'var transferDoc...'
    transferDoc = new ActiveXObject("htmlfile");
    transferDoc.open();
    transferDoc.write(
        "<html><script>" +
        "document.domain='" + document.domain + "';" +
        "</script></html>");
    transferDoc.close();
    var ifrDiv = transferDoc.createElement("div");
    transferDoc.body.appendChild(ifrDiv);
    ifrDiv.innerHTML = "<iframe src='" + url + "'></iframe>";
    transferDoc.callback = callback;
}

And then for the iframe code:

<script>parent.callback(["arbitrary", "data", ["goes", "here"]);</script>

By leaving the page we lose all references to transferDoc and it is thus marked for garbage collection. But surprisingly, navigating away doesn’t always immediately close the connection. The issue is that the garbage collection doesn’t happen right away. This is perfectly fine for data structures that we no longer need, but it is unacceptable for a live HTTP connection to remain open for an unspecified time after the user has left.

The solution is to create an onunload function. The function does two things:

  1. Remove the reference to transferDoc
  2. Explicitly call the garbage collector

function htmlfile_close() {
    transferDoc = null;
    CollectGarbage();
}

Gotchas

Any additional references to transferDoc will need to be deleted before CollectGarbage() will actually close the connection. This isn’t as easy to avoid as you might think. In Orbited we still declare transferDoc as var transferDoc = … but then we attach it to the Orbited object: Orbited.transferDoc = transferDoc. The htmlfile_close function would set Orbited.transferDoc = null before garbage collection. But this failed to close the htmlfile connection! The reason was that we had created an additional and unrelated anonymous function defined in the same scope as transferDoc. This function kept a reference to the transferDoc, and even though the function never interacted with transferDoc at all, it was still never closed.

Strangely enough, even if you lose or even explicitly remove all references to the anonymous function that encloses the transferDoc variable’s scope, and all references to transferDoc are elsewhere removed, garbage collection still fails to close the connection.

Thanks

Special thanks to Andrew Betts and Ufo/Camka for discussing the problem in depth on the Orbited mailing list.

Source : http://cometdaily.com/2007/11/18/ie-activexhtmlfile-transport-part-ii/

IE  STREAMING  HTMLFILE  HTTP  TRANSPORT  ACT 

Share on Facebook  Share on Twitter  Share on Weibo  Share on Reddit 

  RELATED


  0 COMMENT


No comment for this article.