Archive for April, 2006

Pre-fetching Flash

posted April 29th, 2006, 10 comments, tagged ,

One of the advantages of sIFR is that the Flash files are cached on the client, saving bandwith. Except, of course, when they aren’t cached, as was pointed out on the sIFR forum. Luckily a solution was hinted at by Marc van den Dobbelsteen from Webbforce. Requesting the Flash file using a document.write() call fixes the problem. Thus, I set out on a journey through seemingly endless browser reloads and cache emptying…

Diagnosing the problem

When sIFR starts replacing elements it adds Flash movies to the document in a very short time. The browser needs to request these movies, which often have the same source. Testcases show that both Internet Explorer and Safari will stupidly request the same source for each movie, until the first request comes back. Upon reload (when the source is in the cache) no further requests are made.

Here are some log files:

# IE
“GET flash-caching/no-caching.html HTTP/1.1″ 200 511
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

What’s interesting here is the 206 response. It seems IE isn’t happy with requesting the file and asks for it again, but this time it wants a partial file. For the five equal Flash elements on the page, seven requests are made.

# Safari 417.9.2
“GET flash-caching/no-caching.html HTTP/1.1″ 200 511
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

Safari is a bit smarter, but still requests the same file five times.

Fixing the problem

Loading the Flash file once, before other requests are made, stops the user agent from requesting it too many times. This gives us a clue for fixing the problem. In Safari we request it through a script in the head:

new Image().src = 'test.swf';

But alas, this doesn’t work in IE. Here’s the IE code:

var head = document.getElementsByTagName('head')[0];
var node = document.createElement('embed');
head.appendChild(node);
node.setAttribute('src', 'test.swf');

You might want to remove the embed element later on, just to keep the DOM tidy. Also note that to be certain the file has been loaded, you need to load the other Flash movies after onload.

Now we have the following in our logs:

# Safari 417.9.2 Prefetching
“GET flash-caching/caching-safari.html HTTP/1.1″ 200 1014
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

# IE Prefetching
“GET flash-caching/caching-iexplore.html HTTP/1.1″ 200 1057
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249

That’s quite an improvement! Safari requests the file exactly once, while IE is stubborn and requests it twice.

Implementing the fix in sIFR 3

A slightly more specific version of the code above is used in sIFR 3. You can pre-fetch files using sIFR.prefetch('one.swf', 'two.swf', 'etc.swf'). This works pretty well, except that in Internet Explorer sIFR is allowed to kick in before the document has fully loaded.

A trade-off has to be made. Let’s say that the first time the website is loaded, sIFR is not allowed to kick in before the document is fully loaded. The next time it is, since the files no longer have to be pre-fetched. Determining whether the site is loaded for the first time is done through a session cookie. This brings us to the following situations:

  • The user visits the site for the first time. The Flash movies are pre-fetched and sIFR will only kick in on page load.
  • The user navigates to another page on the site. No pre-fetching takes place, the Flash movies are loaded from cache and sIFR kicks in right away.
  • The user has cookies disabled. The Flash movies are prefetched for every page load, but will come from the cache after the first load. Depending on the load time for the page there are extraneous requests for the first load. sIFR does kick in right away.

Of course the cookie path and the use of the cookie itself can be configured.

This fix makes sIFR respond faster and saves bandwidth. And of course, Internet Explorer and Safari can be are a pain to work with.

This problem is now known as bug 8659 in the OpenDarwin bug tracker.

Yesterday I was hunting down an Internet Explorer memory leak at the office. I started with disabling all events which might leak, and then began to check them one by one. With a stroke of luck the first event I checked caused the biggest leak, which was several megabytes on each page load. The event wasn’t to blame, though.

In fact the real bug was something much harder to grasp than the closure-induced leak. And, since I couldn’t find it described elsewhere, I’ve decided to coin it the Externally Declared Global Variable Leak. It has been confirmed in IE6 and IE7.

Here’s the problem in a nutshell:

  • When you create circular references between window objects
  • And declare global variables (someWindow.globalVariable) on window object A, from window object B (or vice versa)
  • These variables will leak
  • Even if they are not referenced

There are a number of possible solutions:

  • Prevent or clear the circular references
  • Declare the global variables inside the window object
  • Use eval() to declare the variables from another window
  • Remove the variables

I’ve created three testcases, one leaking, another one leaking, and a non leaking one. For fun I’ve added a ~ 20 MB object to the tests, so the leaking will be rather obvious.

As you can see in the tests the leak occurs if the global variable is declared in the main window. Declaring the circular reference variable – which is global as well – in the iframe window has no effect. This leads me to believe the JScript engine thinks the leaking variables are still referenced, because they were declared by another window object.

So, yet another thing to look out for when developing for IE. Happy leak hunting!

Anne, Marten and I are going to be at Reboot 8.0 this year. See you there!

In related news, BarCamp Amsterdam II has been announced. See you there as well!