Flash in Safari 3 Bug: Back/Forward Kills Flash Loader

September 29th, 2008 by Steven Sacks

I have discovered a pretty nasty bug in Safari 3 that kills Loader.

If you are loading something with Loader (an image or swf) and you press the forward or back buttons on the browser to go between anchor tags, the Loader immediately stops. This means the loader never fires its onComplete nor does the image or swf it was loading show up. It just dies and you have to call Loader.load() to get it to start again.

All you have to do to see this happen is type in a few irrelevant anchor tags in the URL and then press forward or back while Flash is loading a series of images. You will see it all of a sudden stop.

This does not happen in Firefox, Internet Explorer or Google Chrome, only Safari 3. On OSX, it just stops with no warning or error. On Windows, it not only stops, it throws one of two errors. Either "URL Not Found" (more common) or "Load Not Completed" (less common).

Here is some sample code that trivially shows this happen. This code loads in some XML that has a bunch of image path nodes, creates a Sprite with a background square to put a loader inside of, positions them into 15 columns, and as each one loads, the alpha of the Sprite is set to the bytesLoaded / bytesTotal. When you press forward/back the alpha gets stuck wherever it is.

I don't know if this is a known issue or not, but it is easily reproducible, as evidenced by this single Document class simply loading a series of images one after the other.

package
{
        import flash.display.*;
        import flash.events.*;
        import flash.net.*;

        public class Main extends MovieClip
        {
                private var container:Sprite = new Sprite();
                private var loader:URLLoader = new URLLoader();
                private var images:XMLList;
                private var index:int;
                private var row:int;

                public function Main()
                {
                        super();
                        addChild(container);
                        loader.addEventListener(Event.COMPLETE, onXMLLoaded);
                        loader.load(new URLRequest("images.xml"));
                }
                private function onXMLLoaded(event:Event):void
                {
                        images = XML(event.target.data)..image;
                        loadNext();
                }
                private function loadNext(event:Event = null):void
                {
                        if (index < images.length())
                        {
                                var spr:Sprite = new Sprite();
                                spr.graphics.lineStyle(0, 0x000000);
                                spr.graphics.beginFill(0x00FFFF);
                                spr.graphics.drawRect(0, 0, 100, 100);
                                spr.graphics.endFill();
                                spr.alpha = 0;
                                //
                                var loader:Loader = new Loader();
                                loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadNext);
                                loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
                                spr.addChild(loader);
                                //
                                var col:int = index % 15;
                                spr.scaleX = spr.scaleY = 0.4;
                                spr.x = col * 70;
                                spr.y = row * 100;
                                if (col == 14) row++;
                                //
                                container.addChild(spr);
                                //
                                var path:String = "images/" + String(images[index++]);
                                loader.load(new URLRequest(path));
                        }
                }
                private function onProgress(event :P rogressEvent):void
                {
                        container.getChildAt(container.numChildren - 1).alpha = event.bytesLoaded / event.bytesTotal;
                }
        }
}


Update: I have a solution and here it is:

It's untested code taken from working code, but you'll get the idea.

package
{
        import flash.external.ExternalInterface;
        import flash.display.Loader;
        import flash.net.URLRequest;
        import flash.utils.Timer;
        import flash.events.*;

        public class SafariLoader extends EventDispatcher
        {
                private var safariTimer:Timer;
                private var isSafari:Boolean;

                private var loader:Loader;
                private var req:URLRequest;

                public function SafariLoader()
                {
                        super();
                        checkSafari();
                }
                public function load(url:String = null):void
                {
                        if (url) req = new URLRequest(url);
                        loader = new Loader();
                        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
                        if (isSafari)
                        {
                                loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
                                onProgress();
                        }
                        loader.load(req);
                }
                private function checkSafari():void
                {
                        isSafari = Boolean(ExternalInterface.call(asual.util.Browser.isSafari()));
                        if (isSafari)
                        {
                                safariTimer = new Timer(1000, 1);
                                safariTimer.addEventListener(TimerEvent.TIMER, onSafariStop);
                        }
                }
                private function onSafariStop(event:TimerEvent):void
                {
                        loader.load();
                }
                private function onProgress(event :P rogressEvent = null):void
                {
                        safariTimer.reset();
                        safariTimer.start();
                }
                private function onComplete(event:Event):void
                {
                        loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
                        loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
                        dispatchEvent(new Event(Event.COMPLETE));
                        if (isSafari)
                        {
                                safariTimer.reset();
                                safariTimer.removeEventListener(TimerEvent.TIMER, onSafariStop);
                                safariTimer = null;
                        }
                }
        }
}

Posted in Bugs, Flash

2 Responses

  1. Fredericton Designers » Blog Archive » Flash in Safari 3 Bug: Back/Forward Kills Flash Loader

    [...] Link to the original site [...]

  2. jfroom

    This issue was killing my web services. Thanks for the tip. The retry on delay seems to be the way to go.

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

About Steven Sacks

I am a professional Flash developer with over 13 years of programming experience. I have consulted for high-profile agencies and companies in San Francisco, Los Angeles, Atlanta and New York, and developed numerous award-winning websites and rich internet applications for clients including Adobe, Fox Sports, FX Networks, Anheuser-Busch, GE, DirecTV, ESPN, The Weather Channel, Home Depot, and Coca-Cola.

I am the author of the open-source Gaia Framework for Adobe Flash, which dramatically reduces development time and makes developing Flash sites much easier.