Flash in Safari 3 Bug: Back/Forward Kills Flash Loader
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
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
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;
}
}
}
}
September 29th, 2008 at 4:25 pm
[...] Link to the original site [...]
January 9th, 2010 at 12:32 am
This issue was killing my web services. Thanks for the tip. The retry on delay seems to be the way to go.