Tuesday, July 11, 2006

ExternalInterface Out Of Memory Error

With the release of Flash Player 9, one of the web sites for which I am responsible began throwing "out of memory" errors in Internet Explorer. This happened when a user would navigate away from the page. Stepping into the debugger revealed the following problematic code:


function __flash_unloadHandler() {
mySwf.style.display = 'none';
for (var prop in mySwf) {
if (typeof(mySwf[prop]) == "function") {
mySwf[prop]=null;
}
}
if (__flash_savedUnloadHandler != null) {
__flash_savedUnloadHandler();
}
}


function __flash_setupUnloadHandler() {
if (window.onunload != __flash_unloadHandler) {
__flash_savedUnloadHandler = window.onunload;
window.onunload = __flash_unloadHandler;
}
}


__flash_setupUnloadHandler();


My first reaction was "Whaa? I didn't write any of that code", but then I remembered this neat little article by Brad Neuberg showing how the Flash Player uses JavaScript methods for serializing incoming ExternalInterface calls. So, my problem was related to ExternalInterface.

A quick search for "ExternalInterface out of memory error" yielded the following Flashcoders discussion: http://chattyfig.figleaf.com/pipermail/flashcoders/2006-July/168998.html. So I was not alone, and it seems the issue is related to having multiple swfs using ExternalInterface on one page.

(The same search also turned up this page, where Geoff Stearns talks about fixing problems with audio streams and SWFObject. The code which fixes Geoff's issue looks mighty familiar when I look at my debugger.)

The part where IE gets stuck is in executing __flash_savedUnloadHandler(); At first glance it looks to me like __flash_savedUnloadHandler() would be executed for every swf you have embedded in your page. In the Flashcoders thread I referred to earlier, Alexis Glass suggests the problem may be related to the fact that the unload handler is saved in a global variable:
...it saves the previous unload handler in a global variable so that if
ExternalInterface is initialized twice on one page it recursively calls the
__flash_savedUnloadHandler until IE complains about no memory.

It may be possible to override the faulty JavaScript, but I don't want to go down that road for fear it will break with a future release. So, I will have to revert back to good ol' FSCommand and getURL for the time being. Yuk. Anybody got something better for me?

As an aside, in my research I also came across John Dowdell's response to Brad Neuberg's original article. I know it's a bit dated now, but the ensuing comments are interesting, because John seems to be focused on the context of Brad's usage of ExternalInterface, whereas Brad seems to be offering the exact points of failure. It seems that John is saying "You're out of scope, the point is moot" while Brad is saying "My personal scope of use is not important, here are in-scope examples of how ExternalInterface is broken". I encountered the same "perspective gap" when dealing with Macromedia's customer support folks a few years ago. They seem to be trained to focus on the context in which you are using the functionality, so they can offer alternate means to your particular end. But that can be frustrating when you are trying to present a unit test which is failing for no particular reason, and they are preoccupied with the question of why you want to do it in the first place.

Was this post helpful to you? If so, please consider making a small donation to keep this blog going.

10 Comments:

Anonymous Anonymous said...

Sorry that point wasn't clear... from what I could read of the Mozilla docs, that API did not specify what implementations "should" do when transferring large amounts of data, or doing fast sequential messaging, or (in this case) many clients simultaneously communicating within a single HTML page. Changing just the Player wouldn't necessarily change all the browsers which implement that API.

(It was the question of "how big can messages get?" which was out-of-scope of the original API... there was no ad-hominem "you are out of scope".)

11:49 AM  
Blogger tom said...

I see... If I understand what you're getting at, it's that the Flash Player's ExternalInterface implementation follows browser manufacturer specifications with respect to the NPRuntime API and ActiveX to the extent that they are defined.

However, (correct me if I'm wrong here) the ActiveX and NPRuntime APIs do not provide guidance on serialization techniques for data you might wish to pass into a plugin. I'm under the impression that the Flash Player implements its own serialization routines, given the '__flash__' prefix on the JavaScript function names. As such, it's a Flash Player issue when said routines perform poorly, or cause "Out of Memory" errors in a browser. Agreed?

12:52 PM  
Anonymous Anonymous said...

"However, (correct me if I'm wrong here) the ActiveX and NPRuntime APIs do not provide guidance on serialization techniques for data you might wish to pass into a plugin."

Yes, I think so... the specification followed by browsers and plugins is a messaging specification, not a data-transfer specification, and doesn't even discuss the size or latency of messaging, much less any data structures within that messaging:
http://www.mozilla.org/projects/plugins/npruntime.html

I haven't even seen any clear guidance on which browsers implement that messaging API, and how they differ in either mainline cases or edge cases.

2:16 PM  
Anonymous Anonymous said...

Looking at what that code is actually doing, yes, I think that any developer with a quarter of a brain would realise that exposing 2 functions, both of which are called '__flash_unloadHandler' is rather daft.


For you people stuck with this problem, I would suggest the following solution:
Add a window.onbeforeunload handler and stomp all over the window.onunload handler added by ExternalInterface:

function onUnloaded() {
window.onunload = null;
}

<body onbeforeunload="onUnloaded()" >


The above code is nothing but a work-around.

If you were keen and wanted to do what the original crappy code was doing then you could use the following code:

function unloadFlash(obj) {
for (var prop in obj) {
if (typeof(obj[prop]) == "function") {
obj[prop]=null;
}
}
}

function onUnloaded() {
window.onunload = null; // stomp over flash's unloader
unloadFlash(document.getElementById("aflax_obj_0"));
unloadFlash(document.getElementById("aflax_obj_1"));
}

Hope this all helps.

12:55 AM  
Anonymous Anonymous said...

I am curious - what degubber did you use to catch this code?

3:54 PM  
Blogger tom said...

Microsoft Script Editor.

7:43 AM  
Anonymous Anonymous said...

Thanks for the info on this problem. I ran into it on a page I'm building as well... Just to let you know that the latest version of the Flash player (9.0.28.0) has a fix for the problem.

4:44 AM  
Anonymous Anonymous said...

ralph lauren poloburberry polo shirtthe north face jacketcolumbia jacketspyder jacketed hardy clothing ed hardy clothesed hardy shirts ed hardy t-shirts ed hardy sunglasses ed hardy mensed hardy womens Wholesale HandbagsCheap HandbagsWomens HandbagsCheap PursesDesigner HandbagsTennis RacquetTennis Racket
cheap tennis racquet
tennis racquet discount
cheap tennis racket
discount Tennis Rackethead junior tennis racketwilson tennis racquet
wilson tennis racket
head tennis racketbabolat tennis racket

1:56 AM  
Anonymous Anonymous said...

God bless you!I really agree with your opinions.Also,there are some new fashion things here,gillette razor blades.gillette mach3 razor bladesfor men.As for ladies,gillette venus razor blades must the best gift for you in summer,gillette fusion blades are all the best choice for you.

2:59 AM  
Anonymous Anonymous said...

fantastic!God bless you!Meanwhile,you can visit my China Wholesale,we have the highest quality but the lowest price fashion products wholesale from China.Here are the most popular China Wholesale products for all of you.Also the polo clothing is a great choice for you.

3:01 AM  

Post a Comment

<< Home