ExternalInterface bug can mangle data from JavaScript

While developing the YUI Charts, I discovered a nasty bug with ExternalInterface that affects many versions of Flash Player 9. Basically, it involves overwriting parts of the data passed from JavaScript somewhere in the encoding or decoding process. The best way to describe this error is to explain with an example.

Consider the following Flex application. Once it is initialized, the application calls a function named flashReady() in the hosting page’s JavaScript. The function returns a complex object to display in the Flex app’s Tree control.

Screenshot of working Flex application. Click image to run the application.

Source code for the Flex app:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" applicationComplete="getDataFromJavaScript()">
	<mx:HBox>
		<mx:Tree id="tree" width="250" height="300"/>
		<mx:TextArea id="parsedData" fontFamily="Courier New" fontSize="12" width="250" height="300"/>
	</mx:HBox>
	<mx:Label id="status" text="Loading Data..."/>
	<mx:Script>
		<![CDATA[
			import com.adobe.serialization.json.JSON;

			private function getDataFromJavaScript():void
			{
				if(ExternalInterface.available)
				{
					var result:Object = ExternalInterface.call("flashReady", []);
					this.tree.dataProvider = result;

					//we're only encoding to JSON to view the result in the TextArea
					this.parsedData.text = JSON.encode(result);

					//version displayed for easy identification
					this.status.text = "Flash Player Version: " + Capabilities.version;
				}
				else
				{
					this.status.text = "ExternalInterface is not available!";
				}
			}

		]]>
	</mx:Script>
</mx:Application>

The following structure is passed from JavaScript to ActionScript.

var dataToPass =
{
	label: "Level 1-A",
	children:
	[
		{ label: "Level 2-A" },
		{
			label: "Level 2-B",
			children:
			[
				{ label: "Level 3-A" },
				{ label: "Level 3-B" }
			]
		},
		{
			label: "Level 2-C",
			children:
			[
				{
					label: "Level 3-C",
					children:
					[
						{ label: "Level 4-A" }
					]
				},
				{ label: "Level 3-D" }
			]
		}
	]
};

As you can see, the data passed through ExternalInterface has a complex hierarchy. Many of the objects have a label variable and many have children to specify branches in the Tree.

With Flash Player 9.0.28 (it affects other builds, but that one is commonly installed), the parsed data displayed in the TextArea control looks like the following code block. I’ve manually formatted it for readability.

{
	"0" : { "label" : "Level 4-A" },
	"children" :
	[
		{ "label" : "Level 4-A" }
	],
	"1" : {"label" : "Level 3-D"},
	"2" :
	{
		"0": { "label" : "Level 4-A" },
		"children" :
		[
			{ "label" : "Level 4-A" }
		],
		"1" : { "label" : "Level 3-D" },
		"label" : "Level 3-D"
	},
	"label" : "Level 3-D"
}

You’ll notice that the label property is overwritten on many of the more shallow levels. The strange numeric variables come from the indexes of the various children Arrays. Clearly, much of the data gets completely lost. You can see the very nearly empty Tree control in the following screenshot.

Screenshot of the Flex application when in a buggy Flash Player.

Thankfully, Flash Player 9 Update 3 (build 9.0.115) properly handles this type of ExternalInterface data. However, one can hardly expect everyone to install the very newest version that isn’t even a month old yet.

For the YUI Charts, I needed to find a way to get around this problem in older versions of Flash Player 9. Using the YUI JSON Utility, I encoded the data provider and various other complex properties to simple strings before passing them through ExternalInterface from JavaScript to ActionScript. On the ActionScript side, the JSON data can be decoded with AS3 corelib to turn it back into native objects.

For public releases of Flash Player (official releases and those appearing on Adobe Labs), this bug seems to affect builds below 9.0.60 (the beta version of Update 3, mentioned above). It shouldn’t affect too many applications out there as ExternalInterface isn’t widely used, in my experience. For those out there that do make heavy use of this API, it’s definitely a concern, and you will either need to resort to encoding the data somehow or requiring FP 9.0.115 as the minimum version if you discover that you’re affected.

By the way, if you ever need to test older versions of Flash Player, Adobe provides nice zipped downloads of each major version (with multiple builds of each) back to Flash Player 2.

About Josh Tynjala

Josh Tynjala is a frontend developer, open source contributor, bowler hat enthusiast, and karaoke addict. You might be familiar with his project, Feathers UI, an open source user interface library for Starling Framework that is included in the Adobe Gaming SDK.

Discussion

  1. Marlin

    I realize this is more than two years old, but this blog is also indexed by Google and no doubt comes up from time to time.

    The bug you have noted where <![CDATA[ is not handled correctly has been described by Brad Newberg here:

    http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html

    Here is Brad’s explanation:
    I found all of the JavaScript serialization methods in the Flash player (and they are in JavaScript) by using the Microsoft Debugger (they all live on window.parent; you’ll see them appear a few seconds after the Flash player loads). These methods handle all of the serialization and deserialization and all begin with the name __flash__. It turns out that they ARE using XML internally, and did not implement a real XML parser on the C++/Flash side which can correctly handle CDATA sections. They are also doing evals(), which is one of the reasons it is slow.