There is a problem in the heart of the Flash 9 player. It eats unsuspecting FSCommands and getURL calls. This isn't a problem with any single call, this is a potential problem with every FSCommand or getURL call you make and if you don't know what to look for it can strike at any time. The problem exists in the Flash 9 standalone player, the Flash 9 ActiveX player (for IE) and the Flash 9 plugin (for Firefox).

You may be wondering why you haven't heard about this if it's such a big problem. Well, you probably have. You may have even run into it yourself, come up with a workaround and chalked it up to random weirdness in your project, like I almost did. Symptoms of this bug have been reported in all the major Flash/Flex discussion lists and forums. This bug is subtle, insidious, not simple to explain, but very easy to reproduce.

The Basic Problem

If you make two or more getURL (AS2) or navigateToURL (AS3) calls in the same frame, only one of the getURL calls will work, the others seem to be swallowed up by the Flash player, never to be seen again. Don't believe it? Try this simple example. In previous versions of the Flash player this would have opened two browser windows. In Flash 9, only one browser window appears and it's the one for the last getURL call to display northcode.com.

getURL("http://www.adobe.com", "_blank");
getURL("http://www.northcode.com", "_blank");


If you're targeting AS3 then you would use navigateToURL instead of getURL but the results are exactly the same.

navigateToURL(new URLRequest("http://www.adobe.com"), "_blank");
navigateToURL(new URLRequest("http://www.northcode.com"), "_blank");


How often have you added something like the following to the beginning of a CD project? If you create a projector in Flash CS3 using just the code below you might be surprised at the results. The movie will scale with the window as you resize it, but it will not be in fullscreen mode.

fscommand("fullscreen", "true");
fscommand("allowscale", "true");


The fullscreen command appears to have failed, but in reality the Flash player did not even execute the fullscreen command! That's a serious departure from how previous versions of the Flash player worked.

Although it's not easy to explain (yet) those are all symptoms of the exact same problem in the Flash 9 player. It looks like we've stumbled across some strange black hole that eats FSCommand and getURL calls!

A Black Hole?

Suppose you want to call some JavaScript functions from your SWF and you want to call them asynchronously. According to the documentation for FSCommand you can just do it like this.

fscommand("this", "");
fscommand("then", "");
fscommand("that", "");


Now if we add the code below to the web page that contains the SWF file, when an FSCommand call is triggered in Flash the command and parameter strings are passed to the myDocument_DoFSCommand. In this example the function just displays the command in a JavaScript alert box.

function myDocument_DoFSCommand(command, args)
{
alert(command);
}


If you put this example together and try it, the only alert box you will see will display that. Now you've probably had an a-ha! moment and figured out that only the last FSCommand in a frame is getting called. Life is never that simple.

Some Calls Escape!

Here's a final example to confound and amaze you. Let's make a slight modification to the previous example and add a parameter to one of the FSCommand calls and see what happens.

fscommand("this", "");
fscommand("then", "hello");
fscommand("that", "");


This example will produce two alert boxes, the first one will display that and the second one will display then! That's backward from the order they were called and the alert box for "this" is still missing!

The exact same behavior can be seen with getURL and navigateToURL. It doesn't matter what URL you use in your call, it's the other arguments that determine what happens. In the example below two browser windows will be opened "flashkit.com" and "northcode.com".

getURL("http://www.adobe.com", "_blank");
getURL("http://www.flashkit.com", "myWindow");
getURL("http://www.northcode.com", "_blank");


When I first saw this I thought the good folks that wrote the Flash player had completely lost their minds, but this was actually the key to understanding everything. I haven't seen the source for the Flash player so the best I can do is use the results of these tests to provide an educated guess that explains all of the symptoms we've seen.

It's Not a Black Hole... it's a Map!?

To kick-start the explanation you should first understand why this problem seems to affect FSCommand and getURL/navigateToURL in exactly the same way. The secret is that FSCommand is actually implemented (at the bytcode level) as a call to getURL. In AS3 the navigateToURL will also ultimately generate the same bytecode and thus suffer from the same problem. These calls all do exactly the same thing.

FSCommand("fullscreen", "true");
getURL("FSCommand:fullscreen", "true"); // as2
navigateToURL(new URLRequest("fscommand:fullscreen"), "true"); // as3


It doesn't matter whether you target AS2 or AS3, the black hole will exist if you target the Flash 9 player when creating your SWF from Flash CS3 or Flex. If you choose one of the older Flash player versions then your FSCommand and getURL calls will work just like they did before.

All the FSCommand and getURL/navigateToURL calls made during a given frame are passed to the Flash player for execution. At the end of the frame, after all other ActionScript on that frame has run, the Flash player processes the FSCommand and getURL calls it encountered. So far this is exactly the same as previous Flash player versions. However, in Flash 9 some of the calls are being excluded. This is how it appears to be happening.

Each call is added to an associative array (also called a map) based on the value of its argument (not the command name or the URL). If the argument does't exist in the map it's added to the end, which preserves the order of execution. So far no problem, until we encounter two commands with the exact same argument string. In this case, the call already in the map is replaced by the new one being added instead of just being added to the end of the map. The order of execution has now been changed and one calls has been blown away. The more calls you make, the more confusing the results will seem.

Workarounds

When crafting your own FSCommands you can get around this problem by making sure that the argument strings to your commands are always different. A very easy way to do this is to append some kind of unique id to each argument string.

If you're calling built-in commands (exec, fullscreen, ALLOWSCALE, etc.) the only thing you can do unless Adobe fixes this problem is make sure you never call two FSCommands with the same argument in the same frame. That's an ugly fix for something that used to work just fine.

With getURL and navigateToURL the problem is a bit more difficult because the only parameter you can change that will make a difference is the target window, which you normally want set to "_blank". If you use different values for the target window, all of your getURL/navigateToURL calls will execute but they may not behave exactly as you intended.

Ironically, using FSCommand may provide the best workaround for this problem if you're working in a browser (this won't work in a standalone payer). Instead of calling getURL/navigateToURL directly you would call a JavsScript FSCommand handler to open the new windows for you. As long as the argument to every FSCommand call is different, the black hole won't swallow any of your calls.

FSCommand("openURL", "http:/www.northcode.com");

function myDocument_DoFSCommand(command, args)
{
if (command == "openURL")
{
window.open(args);
}
}