Entries tagged ‘flash’

Firefox 3, or, technically, Gecko 1.9, comes with support for full page zoom. It works by scaling CSS pixels when the page is rendered – from the perspective of JavaScript or CSS there is no difference between a page zoomed to 200%, 50% or 100%.

I’ve found two ways of detecting the zoom level. One way to detect zoom level changes relies on the fact that percentage values are not zoomed. A percentage value is relative to the viewport width, and thus unaffected by page zoom. If you insert two elements, one with a position in percentages, and one with the same position in pixels, they’ll move apart when the page is zoomed. Find the ratio between the positions of both elements and you’ve got the zoom level. See test case.

The problem with this solution is that it does not detect the zoom level when the page is first loaded.

Another solution I found relies on Flash. Not so much a problem for sIFR, for which I started this little investigation, but not the best solution either. Flash can be configured not to scale with its container page by setting Stage.scaleMode = "noscale";. The Stage.height value gives the height of the Flash movie as its actually rendered by the browser. In other words, this value is not affected by page zoom. The height assigned to the Flash movie <object>, as it exists in the DOM, however, is affected by the zoom level. Simply dividing Stage.height by <object>.height gives the zoom level, even on page load. See test case.

Of course, these two methods are hacks. It would be great if the browser can be queried for the zoom level.

Addendum: I just tested both approaches in Opera and IE 7. The percentages trick works in Opera, but not in IE. IE’s page zoom is quite a hack anyway, so that’s to be expected. The Flash solution works in all browsers, and is far more precise than the percentages approach. Definitely the way forward I’d say!

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.

Caught Redhanded: QuickTime Stealing Flash

posted February 8th, 2006, no comments, tagged ,

Sometimes QuickTime may take over handling Flash content. As QuickTime is only compatible with Flash 5, this can pose major problems. Here’s some code which detects if QuickTime is handling Flash:

var qtFlash = false; // Is QuickTime rendering Flash?
var flashPlugin = navigator.plugins['Shockwave Flash'];
for(var i = 0; i < navigator.mimeTypes.length; i++) {
    var mime = navigator.mimeTypes[i];
    if(mime.type == 'application/x-shockwave-flash' 
    && mime.enabledPlugin != flashPlugin) {
        qtFlash = true;
        break;
    }
}

[Update April 28, 2006] If there are multiple Flash versions installed this test won’t work. Instead try this:

var qtFlash = false; // Is QuickTime rendering Flash?
var flashVersion = 7; // Stub for real detection code
for(var i = 0; i < navigator.mimeTypes.length; i++) {
    var mime = navigator.mimeTypes[i];
    if(mime.type == 'application/x-shockwave-flash' 
    && !new RegExp('.*Shockwave\\sFlash\\s' + flashVersion
    + '.*').exec(mime.enabledPlugin.description)) {
        qtFlash = true;
        break;
    }
}

If you want to test this see these Mozilla guidelines and apply them in reverse order.

This workaround doesn’t work in IE, though, which leaves me wondering how to detect QuickTime for that browser. Here’s the Flash detection code for IE in sIFR:

if(this.ieWin) {
  try {
    this.flashVersion = parseFloat(
                new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7')
                .GetVariable('$version').match(/([\d,?]+)/)[1].replace(/,/g, '.')
            );
  } catch(e) {}
}

I haven’t been able to get IE to use QuickTime, so the question is: will IE be able to create a Shockwave Flash object if QuickTime is handling Flash? If not, problem solved. If so, any ideas on detecting QuickTime?