Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Pages: 1 | 2

Application Domains

As seen with security domains, class definitions can be isolated from one another when SWFs are in different security sandboxes, each having their own complete set of definitions to work with. This separation of class definitions (and similar definitions like functions, interfaces, and namespaces) is managed by a subdomain of security domains known as application domains. Every application domain is contained within one, and only one, security domain, though a single security domain can contain any number of application domains.

Application Domains in Security Domains
Application domains exist inside security domains

While security domains sandbox for security, application domains sandbox for definition separation. Specifically, they decide how definition conflicts are resolved and what SWF code have inherent access to what definitions.

Application domains work together in a linked hierarchy making them more complex than security domains which are each isolated and individualized. These hierarchies mirror the display list in Flash. Each application domain can contain any number of child application domains while each child maintains only one parent. Child domains inherit definitions in its parent as well as all of the ancestors of that parent just like position or scale is inherited by child display objects.

At the root of this hierarchy is the system domain, the application domain that contains the native definitions of the Flash Player API. These include definitions like Array, XML, and flash.display.Sprite - anything native to Flash Player. System domains have a one to one relationship with security domains; every security domain contains a single system domain that is created for it when the security domain is first created.

When a new SWF is placed into its respective security domain as it initializes an instance of Flash Player, an application domain is created to contain the ActionScript definitions that were compiled with it. This application domain is then made a child of the system domain in that security domain. As a child domain of the system domain, the native Flash Player definitions are inherently made available to the definitions of that application domain through inheritance.

Child Application Domain
New SWF domain loaded into and inherits from system domain

More on inheritance is covered in the Application Domain Inheritance section.

Application Domain Placement

The initial SWF instantiating Flash Player always has its definitions loaded into an application domain that is a direct child of the system domain. When that, or some other preexisting SWF (parent) loads another SWF, it has control over where the definitions of the new SWF is placed. There are 4 primary options on where the child SWF's definitions are to placed:

  1. A new child application domain of the parent SWF's application domain (default)
  2. Merged in with the same application domain of the parent SWF
  3. A new child application domain of the system domain in the parent SWF's security domain
  4. A new child application domain of the system domain in a different security domain

All but the very last of the variations each require that the child SWF be loaded into the same security domain as the parent. The fourth and final variation is the only option for SWFs loaded into separate security domains.

Application Domain Placement
Application domain placement options when loading child SWFs

Not mentioned is the option of loading definitions directly into, or in a new child domain of, any other application domain that was already created for a different, previously loaded SWF. Working with these domains requires more complex ApplicationDomain instance management, usually requiring you to work up an application domain hierarchy using ApplicationDomain.parentDomain - a reference which, as a helpful tip, you may find behaves differently depending on which security sandbox you're testing with (local vs. network). This use case is extremely uncommon so not covered in any more detail here.

The placement of application domains is defined by the applicationDomain property of LoaderContext. This property takes an ApplicationDomain instance which can be obtained through ApplicationDomain.currentDomain (much like security domains with SecurityDomain.currentDomain), or by creating a new instance through the use of the new keyword. When creating new instances, a parent domain is passed into the ApplicationDomain constructor to indicate where in the hierarchy the application domain is to exist. Passing no parent implies the system domain will become the parent.

// an application domain that results in definitions that are
// "Merged in with the same application domain of 
// the parent SWF"
var current:ApplicationDomain = ApplicationDomain.currentDomain;

// an application domain that results in definitions being
// added to "A new child application domain of the parent
// SWF's application domain"
var currentChild:ApplicationDomain = new ApplicationDomain(current);

// an application domain that results in definitions being
// added to "A new child application domain of the system
// domain in the parent SWF's security domain"
var systemChild:ApplicationDomain = new ApplicationDomain();

The following example defines a new application domain for a child SWF's definitions that is a child of the parent SWF's application domain. A LoaderContext instance references the ApplictionDomain instance and is passed to the Loader.load call. This is the same behavior that would occur if a LoaderContext was not used at all.

var context:LoaderContext = new LoaderContext();
// load child application domain as a child
// of this application domain 
var current:ApplicationDomain = ApplicationDomain.currentDomain;
context.applicationDomain = new ApplicationDomain(current);

var loader:Loader = new Loader();
var url:String = "child.swf";
loader.load(new URLRequest(url), context);

Incidentally, ApplicationDomain instances cannot be directly compared to one another. Each instance is a unique reference that internally stores a map of its placement in the hierarchy that is not directly exposed to ActionScript.

var current1:ApplicationDomain = ApplicationDomain.currentDomain;
var current2:ApplicationDomain = ApplicationDomain.currentDomain;
trace(current1 == current2); // false

Also, you should not try get a direct reference to the system domain through the parentDomain property. If you need an ApplicationDomain instance to represent the system domain, always use new ApplicationDomain().

Application Domain Inheritance

The inheritance of definitions through application domains is not entirely unlike class inheritance. In both cases definitions in parents are made available to their children while child definitions are not directly exposed to the parents.

Application domain inheritance, however, differs in that child domains aren't given the option to override parent definitions with their own. If a child domain contains a certain definition with the same fully qualified name (name including its package path) as a definition in one of it's ancestor domains, the parent domains's definition will be referenced instead of the child's.

Child Application Domain Inheritance
Child application domains use their parent domain definitions over their own

The reasoning behind this can be summarized by the following: you cannot change a class definition out from under an existing class instance. If an instance is created from one version of a class definition and another variation of that definition is then loaded into the player, the class instance would become corrupt with a definition conflicting with that from which it was originally made. Flash player protects definitions by preventing new versions of a definition from ever replacing an existing one.

One of the implications of this behavior is that you're not ever allowed to override native definitions in the ActionScript API with your own. Since a SWF's application domain is always an ancestor of the system domain - where these definitions are defined- there's never an opportunity for a SWF to be able to provide a natively-named definition that does not yet already exist. If a SWF is compiled with such a definition, it will be ignored, overridden by the version in the system domain.

When merging definitions into an existing application domain, the same rules for inheritance apply, even though no inheritance is actually taking place. New application domain definitions are only merged into the existing application domain if they do not conflict with pre-existing definitions that already exist there.

Adding Definitions in Application Domain
Definitions added to an existing application domain will not replace existing definitions

One minor difference with the dropped definitions in this case is that, unlike with child application domains, these definitions become completely inaccessible. Child domain definitions, even if being overridden by the parent domain can still be accessed from the application domain directly through getDefinition which is covered in the Getting Definitions Dynamically section.

Definitions loaded into an application domain are persistent within that domain, existing as long as the application domain exists. New application domains that are created to hold a loaded SWFs definitions are removed from memory when that SWF is unloaded. If that SWFs definitions were added to another, preexisting domain, they would exist in memory as long as that domain exists, which would be determined by whatever SWF it was created for. If consistently loading new SWF definitions into an existing domain, such as the domain of the first SWF, it could lead to an increase of memory use as definitions continue to add up there. This would be undesirable for something like ad rotation where any number of child SWFs (ads) could be loaded in, each adding their definitions to the same application domain.

Additionally, since definitions loaded in this manner do not unload with the child SWF, reloading that a second time SWF will reuse the original definitions loaded into the application domain from the first time the SWF was loaded. Generally this would not be a problem since the definitions are the same, but it would mean that the static state of classes would not reset; static variables that were changed the first time the SWF was loaded would still retain their changed values rather than resetting to their defaults when the SWF was reloaded.

Different circumstances require different approaches.

Child Domains: Definition Versioning

Inheritance through child domains make it easy for parent SWFs to share their definitions with child SWFs. And because definitions in child domains are overridden by parent definitions of the same name, the parent application domain gets to control what versions of those definitions are used by its children.

Child Application Domain Inherits Parent Definitions
Child application domain inherits from its parent

Consider a SWF-based web site that loads separate SWFs to represent the content of different pages within the site. The main SWF simply being a shell for loading these pages. Each of the page SWFs have similar behavior based on a common code library. A shared class may be, for example, a PageTitle class which defines a display object used to display a page's title text.

Now consider a second SWF on that same domain that serves a slightly different purpose but uses the same child pages for some of its content. This particular SWF, however, requires that the titles in child pages not use selectable text (the current behavior being selectable). To do this, at least within the scope of this example, the PageTitle class would need to be updated to make its TextField have a selectable value of false. The problem is, the original SWF needs to retain the old behavior of having selectable title text.

To solve this problem, each page could be duplicated and recompiled with the changes. But this would require additional disk space for the copies, increase bandwidth requirements, and has a negative impact on site maintainability. A better solution would be to include a revised version of the PageTitle class in the contents of the second SWF. Then, as child pages are loaded into child application domains, they'll inherit and use that version of the class over their own.

Original PageTitle class used by all child pages:

package {
	import flash.display.Sprite;
	import flash.text.TextField;

	public class PageTitle extends Sprite {

		private var title:TextField;

		public function PageTitle(titleText:String){
			title = new TextField();
			title.text = titleText;
			addChild(title);
		}
	}
}

The version of the PageTitle class compiled into the second SWF shell:

package {
	import flash.display.Sprite;
	import flash.text.TextField;

	public class PageTitle extends Sprite {

		private var title:TextField;

		public function PageTitle(titleText:String){
			title = new TextField();
			title.text = titleText;
			title.selectable = false; // changed
			addChild(title);
		}
	}
}

When the new shell is compiled, it's compiled with the changed version of the class, loading all of the child pages into child application domains.

PageTitle; // include a reference of edited 
// class to have it compiled in the SWF even
// though the SWF is not using it directly

// load child pages into child application domains
// loaded SWFs will use the version of PageTitle
// in this SWF rather than their own
function addChildPage(url:String):void {
	var context:LoaderContext = new LoaderContext();
	var current:ApplicationDomain = ApplicationDomain.currentDomain;
	context.applicationDomain = new ApplicationDomain(current);
	
	var loader:Loader = new Loader();
	addChild(loader);
	loader.load(new URLRequest(url), context);
}

This approach allows class definitions to be changed in loaded content without recompiling or changing their default behavior. Changes are simply the result of a parent application domain replacing definitions in the child with its own revised versions.

Note that the above example could have also omitted the use of the LoaderContext for the same effect.

Even if child SWFs don't have to serve multiple purposes, it might come down to it just being easier to update definitions in the loader rather than all the children. In fact, child SWFs could be compiled without these classes at all, relying entirely on the versions supplied by the parent. More information on how that works is seen in the Same Domain: Runtime Shared Libraries section.

Separate Domains: Preventing Conflicts

Sometimes you'll have projects which require loading SWF content that shouldn't be affected by parent application domain inheritance. You may not want the SWF to inherit parent definitions or maybe not even know what definitions it uses at all. Whatever the case, it would be best to avoid any kind of definition sharing between the main SWF and the loaded content. This is when you load child SWF definitions into a new application domain that is a child of the system domain.

Child SWF Domain as Child of System Domain
Child application domain made a separate child of system domain

Because no inheritance exists between the parent and child SWFs, both can have definitions of the same name - which may or may not be the same definitions - that can co-exist within their own sandboxes without having to worry about creating a naming conflict.

Here's a scenario: Let's say you have training application that loads training modules through external SWFs. Hundreds if not thousands of these modules exist, having been developed by many different people throughout many years that this application has been in use. As the modules were being developed, the codebase they've been using has evolved. This creates a discrepancy between the versions of the codebase between different modules, and even the training application itself (which also uses the same codebase). Because the training application and the different modules use and rely on different versions of the same codebase, the training application will need to be sure that its version of the codebase is not used by the modules since its version could be incompatible with their design. To do this, the training application has to load modules in a new application domain that is a direct child of the system domain rather than in application domains that are children of its own.

trainingapplication.swf:

var moduleLoader:Loader = new Loader();
addChild(moduleLoader);

// load modules into application domains that
// are direct children of the system domain
// so that this application domain does not
// share definitions with them
function loadModule(url:String):void {
	var context:LoaderContext = new LoaderContext();
	context.applicationDomain = new ApplicationDomain();
	
	moduleLoader.load(new URLRequest(url), context);
}

Unfortunately, definition separation in this case is not absolute. All content within the same security domain shares the same system application domain. Any changes made to definitions there will be reflected in all other application domains in that security domain. So even though a child SWF is loaded into a separated application domain that is a direct child of the system domain, changes to that system domain from the parent SWF would still be reflected in the child.

This is evident with the XML.prettyIndent example. One SWF, no matter where it is in the application domain hierarchy, if in the same security domain, will affect all other SWFs with changes made to definitions in the shared system domain.

parent.swf:

trace(XML.prettyIndent); // 2
XML.prettyIndent = 5;
trace(XML.prettyIndent); // 5

var loader:Loader = new Loader();

var context:LoaderContext = new LoaderContext();
// separate application domain from this one
context.applicationDomain = new ApplicationDomain();

var url:String = "child.swf";
loader.load(new URLRequest(url), context); 

child.swf:

trace(XML.prettyIndent); // 5

As a best practice, changes to definitions like this should be done on a temporary basis, returning values to their defaults after use.

var originalPrettyIndent:int = XML.prettyIndent;
XML.prettyIndent = 5;
trace(myXML.toXMLString());
XML.prettyIndent = originalPrettyIndent;

Also, as a precaution, you may want to make sure such values are what you expect them to be in case someone else changed them out from under you.

Same Domain: Runtime Shared Libraries

Probably the most powerful usage of application domains involves adding new definitions into a pre-existing application domain. Whereas child domains only allows definitions to be shared from a parent to a child, merging definitions in to the same application domain provides a single domain from which all definitions can be shared by every SWF using it, both parent and child.

Parent Domain Adds Child Definitions
Parent application domain includes child definitions

Runtime shared libraries (RSLs) use application domains in this manner. RSLs are code libraries that exist as separate SWFs that can be loaded in at runtime. They allow one or more SWFs share a single codebase without each SWF having to embed that same code into their SWF file, thus reducing redundancy, file size, and making code more maintainable. RSL definitions are loaded into the application domain of the loader, thereby allowing that application to access the new definitions.

A couple prerequisites need to be met for RSLs to function properly. First, the ActionScript compiler needs to know that certain definitions are going to be used by a SWF but should not be compiled in with the SWF when published.

The compiler does this now with native Flash Player definitions. Though a SWF will use native player definitions in its code (Array, XML, Sprite, etc.), those definitions only exist within the Flash Player executable itself and should not, nor cannot be compiled into the SWF when its published. The compiler uses a special SWC (a pre-compiled SWF code library) called playerglobal.swc to recognize these native definitions. It contains the interface for these definitions - their names and type information - allowing the compiler to compile code using them, but at the same time the compiler is smart enough to know that they are not included with the final SWF.

The compiler can reference other SWC libraries that can behave like playerglobal.swc. Definitions in these libraries can be referenced by code in your SWF, but not compiled into the SWF when published. These libraries are linked as "External" libraries - external in that they are externally loaded rather than be internal to the SWF.

I will not cover how exactly this is setup as it changes depending on your authoring tool. You should be able to reference your tool's documentation to learn how to set this up for your particular environment.

Though SWCs are used for compilation, RSLs are, themselves, SWFs, just like any other loaded Flash content. When compiling library code, both a SWF and a SWC should be made - the SWF for loading at runtime and the SWC to reference in your external library path.

Compiling a Shared Library
Shared Library SWCs are used by the compiler; Shared library SWFs are loaded at runtime

The other prerequisite deals with code execution. Using external libraries, a SWF will be published without definitions that it depends on to function. Should Flash Player attempt to run code that doesn't contain an expected definition, a verify error can occur, (for the most part) breaking your SWF completely.

Flash Player verifies classes when they're first used in code. If certain definitions are not present in the application domain when this verification process occurs, an error is thrown.

There are actually two different kinds of errors that can occur as a result of missing definitions. Verify errors are the worst of the two, representing a catastrophic failure in a class's ability to function. These are a result of a specific type being used and not being available. However missing definition references can also create a failure that results in reference errors. These errors only interrupt the normal flow of code execution within code executing within an already verified class.

var instance:DoesNotExist;
// VerifyError: Error #1014: Class DoesNotExist could not be found.
// Occurs as the class containing this definition is verified
var instance:Object = new DoesNotExist();
// ReferenceError: Error #1065: Variable DoesNotExist is not defined.
// Occurs when this line of code executes

The main difference is that verify errors relate to a class's definition while reference errors are specific to code execution. For any code within a class to execute, verification would have needed to pass. Since the reference error example above uses a type of Object for the instance variable, verification was able to succeed and code was able to execute (only to then fail with a reference error).

Note: Strict Mode

External libraries are one way you can compile SWFs that use definitions that aren't compiled into the SWF. Another approach is to turn strict mode off. This greatly reduces the compiler's strictness with how variable values are used. For class usage, you can reference classes that do not exist without invoking a compiler error. You can't use them as types - something which would cause a verify error at runtime - but you can reference classes by name as seen with the reference error example above. This approach is not recommended because of possible errors that might slip through undetected.

For SWFs loading RSLs, this means classes that use or reference definitions in an RSL should be avoided until the dependent RSLs load. A preloader at the start of an application can facilitate this, loading RSLs before main application code - which uses RSL code - runs.

The example below includes a SWF that loads in a RSL containing a Doughnut class. Though this class is used directly in the SWF's own, internal code, it was not compiled into the SWF because it was referenced through a SWC defined as an external library. The RSL SWF is loaded before the Doughnut class is used so verification does not fail.

Doughnut.as (compiled into doughnutLibrary.swc and doughnutLibrary.swf):

package {
	import flash.display.Sprite;
	
	public class Doughnut extends Sprite {
		public function Doughnut(){

			// draw a doughnut shape
			graphics.beginFill(0xFF99AA);
			graphics.drawCircle(0, 0, 50);
			graphics.drawCircle(0, 0, 25);
		}
	}
}

ShapesMain.as (where primary Shapes.swf content is defined):

package {
	import flash.display.Sprite;
	
	public class ShapesMain extends Sprite {
		public function ShapesMain(){

			// The Doughnut class is accessible
			// through the linked doughnutLibrary.swc
			// though not compiled into Shapes.swf
			// because its linked as an external library
			var donut:Doughnut = new Doughnut();
			donut.x = 100;
			donut.y = 100;
			addChild(donut);
		}
	}
}

Shapes.swf (RSL loader):

var rslLoader:Loader = new Loader();
rslLoader.contentLoaderInfo.addEventListener(Event.INIT, rslInit);

// load definitions in the RSL SWF into the
// current application domain
var context:LoaderContext = new LoaderContext();
context.applicationDomain = ApplicationDomain.currentDomain;

var url:String = "doughnutLibrary.swf";
rslLoader.load(new URLRequest(url), context);

function rslInit(event:Event):void {

	// only when the RSL definitions have been imported
	// into the application domain do we allow use of and
	// thereby verification of the ShapesMain class which
	// makes use of the Doughnut definition in the RSL
	addChild(new ShapesMain());
}

The Shapes SWF here represents the main SWF application. It's functional content is defined in a separate class called ShapesMain. This class isn't instantiated until the RSL loader has been able to import the RSL definitions (Doughnut) into the application domain. Without the RSL definitions, a verify error would have occurred as soon as the ShapesMain class was verified and the Doughnut class was found missing from the application domain.

Note: RSLs with Flex

The Flex framework has its own implementation of handling RSLs. The approaches explained here are very low level and generally should not be used when developing a Flex application. For more information around RSL usage in Flex, see Flex Runtime Shared Libraries (Flex 4).

Getting Definitions Dynamically

Definitions not included in a application domain, or inherited by a parent domain, can be obtained from other domains dynamically through ApplicationDomain.getDefinition. This method returns a reference to a definition that exists in an application domain or any of its parents. Calling getDefinition with the current application domain mirrors the global function getDefinitionByName.

Outside of ApplicationDomain.currentDomain, you can also get a reference to an ApplicationDomain instance - one specific to a SWF - using LoaderInfo.applicationDomain. This is what would be used to find ApplicationDomain references for other SWFs or application domains as the following example demonstrates with a com.example.Box class that was compiled in the SWF loaded into the loader Loader instance.

try {
	var domain:ApplicationDomain = loader.contentLoaderInfo.applicationDomain;
	var boxClass:Class = domain.getDefinition("com.example.Box") as Class;
	var boxInstance:Object = new boxClass();
}catch(err:Error){
	trace(err.message);
}

There are a couple of points of interest in this code snippet. First, notice the the return value of getDefinition was cast to a the type Class with as Class. This is needed because getDefinition returns a value of the type Object since it's capable of returning any kind of definition (functions, namespaces, interfaces) rather than just classes. Also, the whole operation is wrapped in a try-catch. This is needed because failure to find a definition through getDefinition throws an error. Alternatively, you could also check this using ApplicationDomain.hasDefinition which would let you know if getDefinition would be able to successfully find the desired definition or not.

Definitions acquired dynamically that are not a part of (or inherited by) the current application domain cannot be used as variable types. As seen with RSLs, if a type is used for a class but not contained within the application domain when the class is verified, an error will be thrown. In the above example you can see the boxInstance variable is typed as Object instead of Box because the Box class is not a known definition in the application domain.

Same-definition Collisions

Sometimes, definitions may get crossed and you may have a definition in one application domain that matches a definition in another, unrelated application domain and an assignment is made across the two types. When this happens, you'll get an error that resembles the following:

TypeError: Error #1034: Type Coercion failed: cannot convert 
	com.example::MyClass@51e1101 to com.example.MyClass.

The separation of the definitions can be seen with the identifier following the @ symbol in the class name indicating that the definition exists in a different memory space. Even though the definitions may (or may not) be exactly the same in code, they have been defined in Flash Player by two separated application (or security) domains meaning two instances of the class exist.

Only native Flash Player definitions can bridge this gap allowing a type like Object to work for both cases, even going across security domains. In fact, you'll probably use Object to type instances whose type is not a part of the current application domain most of the time.

Though using a more generic type like Object will help solve collision errors, a different use of application domain placement would actually allow the definitions to match.

Conclusion

This tutorial has covered a lot of information. The first half dove deep into security domains and how they affect content on different source domains. Security domain sandboxing by Flash Player protects the user and their data, and it's important that developers of Flash content both respect and understand the restrictions imposed by Flash Player in that space.

The second half covered application domains - a different kind of sandbox that lives within security sandboxes and separates ActionScript definitions. These domains work together in a hierarchy that helps delegate the sharing and reuse of definitions across multiple SWFs.

There are a number of pitfalls between both security domains and application domains. Hopefully what has been covered here has you prepared for them. Not only should you now know what to expect, but also what needs to be done to make your content work the way you want it to.

Pages: 1 | 2