Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Managing ActionScript 3.0 Application Domains and Class Definitions

Tech 2

Application Domain Architecture

Within the Flash Player security model, application domains act as the partitioning layer for code definitions. While security domains enforce data permissions between sandboxes, application domains manage the hierarchical segmentation of classes, interfaces, functions, and namespaces. Every application domain resides exclusively within a single security domain, though a security domain may host multiple application domains arranged in a tree structure.

The root of this hierarchy is the system domain, which contains the native Flash Player API definitions (such as Array, XML, and flash.display.Sprite). When a SWF file initializes, it is assigned a specific application domain within its respective security sandbox. By default, this domain becomes a child of the system domain, allowing the SWF to inherit native definitions automatically.

Placement Strategies for Loaded Content

When a parent SWF loads a child SWF, the LoaderContext object determines where the child's definitions are stored. There are three primary locations for placing these definitions within the same security domain:

  • Child of the Current Domain: Creates a new branch under the parent's domain (default behavior).
  • Merged into Current Domain: Adds the child's definitions directly to the parent's existing domain.
  • Child of System Domain: Places the child in a separate branch directly under the system domain, isolating it from the parent.

The following example demonstrates loading a SWF into a new child domain, effectively isolating its definitions while maintaining access to the parent's definitions:

var loadParams:LoaderContext = new LoaderContext();

// Create a new domain that inherits from the current domain
var currentDomain:ApplicationDomain = ApplicationDomain.currentDomain;
loadParams.applicationDomain = new ApplicationDomain(currentDomain);

var swfLoader:Loader = new Loader();
var request:URLRequest = new URLRequest("module.swf");

swfLoader.load(request, loadParams);

It is important to note that ApplicationDomain instances are unique references. Comparing two references to ApplicationDomain.currentDomain will return false, even if they represent the same logical domain.

Definition Inheritance and Shadowing

The inheritance mechanism of application domains functions similarly to the display list: child domains have access to parent definitions, but the reverse is not true. However, a critical rule exists regarding conflicts: if a child domain contains a definition with the exact same fully qualified name (including package path) as its parent, the parent's definition takes precedence. The child's definition is effectively shadowed.

This behavior prevents instability by ensuring that existing object instances continue to use their original class definitions. It also implies that native Flash Player APIs cannot be overridden, as they reside in the top-level system domain. When merging definitions into an existing domain, conflicting definitions are ignored, and only unique additions are registered.

Versioning via Child Domains

Parent-domain shadowing allows developers to update logic across multiple modules without recompiling them. Consider a modular application where every sub-module uses a Widget class. If the parent application loads these modules into child domains, an updated Widget class compiled into the parent will automatically override the older versions in the children.

Original Widget (in child SWF):

package components {
    import flash.display.Sprite;

    public class Widget extends Sprite {
        public function Widget() {
            graphics.beginFill(0xCCCCCC);
            graphics.drawCircle(0, 0, 20);
        }
    }
}

Updated Widget (in parent SWF):

package components {
    import flash.display.Sprite;

    public class Widget extends Sprite {
        public function Widget() {
            graphics.beginFill(0xFF0000); // Change color to red
            graphics.drawCircle(0, 0, 20);
        }
    }
}

By ensuring the parent includes this updated definition and loading child SWFs into a child domain, all modules will render the red widget instead of the gray one, regardless of the code compiled within the child SWFs.

Isolation via Separate Domains

In scenarios where compatibility between unknown libraries is a concern, total isolation is preferable. By loading a SWF into a new domain that is a direct child of the system domain (new ApplicationDomain()), the loaded content does not inherit the parent's custom classes. This prevents definition collisions.

var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext();

// Load into a separate domain branch under the system domain
context.applicationDomain = new ApplicationDomain();

loader.load(new URLRequest("plugin.swf"), context);

While this isolates class definitions, it does not isolate changes to the global system prototypes. For instance, modifying XML.prettyIndent in the parent SWF will affect the child SWF as well, because they share the same underlying system domain definitions.

Runtime Shared Libraries (RSL)

To reduce file size and share code across multiple SWFs, developers use Runtime Shared Libraries. An RSL is a SWF loaded specifically to provide its definitions to the loading application. By loading the RSL into the ApplicationDomain.currentDomain, its classes become available to the main application and any subsequently loaded modules.

To implement an RSL, the main application is compiled against a SWC (library) containing the interfaces, but the actual implementation is loaded at runtime.

Loading the RSL:

var libLoader:Loader = new Loader();
var context:LoaderContext = new LoaderContext();

// Merge RSL definitions into the current domain
context.applicationDomain = ApplicationDomain.currentDomain;

libLoader.load(new URLRequest("coreLibrary.swf"), context);

libLoader.contentLoaderInfo.addEventListener(Event.INIT, onLibLoaded);

function onLibLoaded(e:Event):void {
    // Safe to instantiate classes from coreLibrary.swf
    var asset:SharedAsset = new SharedAsset(); 
    addChild(asset);
}

If the main application attempts to instantiate a class from the library before the RSL is loaded, a VerifyError will occur because the definition is missing during the verification phase.

Dynamic Definition Retrieval

Sometimes it is necessary to access a class definition that resides in a loaded SWF without merging it into the current domain. The ApplicationDomain.getDefinition() method facilitates this. It requires the fully qualified name of the class and returns the definition as an Object.

try {
    var loadedDomain:ApplicationDomain = loader.contentLoaderInfo.applicationDomain;
    var AssetClass:Class = loadedDomain.getDefinition("com.example.Graphic") as Class;
    
    var instance:Object = new AssetClass();
    addChild(instance as DisplayObject);
} catch (err:Error) {
    trace("Definition not found: " + err.message);
}

This technique allows for dynamic instantiation but requires the variable type to be declared as Object or a common interface, rather than the specific class type, to avoid verification errors at compile time.

Handling Definition Conflicts

If two different application domains contain a class with the same name, and an object from one domain is cast to the type of the other, a TypeError (Type Coercion failed) occurs. Although the class names may be identical, Flash Player treats them as distinct types because they originate from separate memory spaces.

// Error example:
// TypeError: Error #1034: Type Coercion failed: cannot convert 
// com.example::Graphic@4a8b10 to com.example.Graphic.

To avoid this, ensure that shared classes are loaded into a common domain or use generic Object types when passing data across domain boundaries.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.