Developing sandboxed applications

Sandboxed applications contain sub-applications that are loaded into separate security domains. By definition, they are therefore loaded into separate application domains as well. As a result, they can be multi-versioned, but are untrusted. Because they are untrusted, their interoperability with the main applications is limited. Types of sandboxed applications include portals, mashups, and dashboards.

Sandboxed applications must be compiled with the same or older version of the compiler that the main application is compiled with.

When compiling sandboxed applications, you should include the MarshallingSupport class into the main application and sub-applications. You do this with the includes compiler argument, as the following example shows:
-includes=mx.managers.systemClasses.MarshallingSupport

If you are using any third-party applications, you should load them as sandboxed applications. In addition, if you are using multi-versioned applications that use RPC classes or DataServices-related functionality, you should also consider loading them as sandboxed applications. Otherwise, you might be required to provide additional code such as a bootstrap loader.

In a sandboxed configuration, loaded sub-applications are typically not on the same domain as the main application. This means that the applications might not necessarily trust the loaded applications by default. In addition, all sub-applications are not necessarily always visible at the same time, so the main sandboxed application must be able to load and unload sub-applications at any time.

In sandboxed applications, each sub-application is loaded into a separate application domain and a separate security domain. The interoperability across security domains is very limited. The sub-application cannot access most stage properties, methods, and events. It cannot get mouse and keyboard events from other security domains. It also cannot perform drag and drop operations to or from the main application, and pop-up controls are clipped at the boundaries of the sub-application. Data sharing between the main application and sub-application requires marshaling.

If you try to use the parent chain of an application object to access properties of the main application from a sub-application in a different security domain, you will encounter security errors at run-time. In addition, you cannot access the application through the SWFLoader.content object.

For details about the architecture of a sandboxed application, see About sandboxed applications.

Pop-up controls in sandboxed applications

Pop-up controls are parented by the application at the sandbox root of their security domain. This is because the sandbox root handles requests to display a modal window from its children.

Because pop-up controls are parented by the sandbox root, centering a popup in a sandboxed application centers it in the area of the screen occupied by the sub-application and not the entire application. It also means that pop-up controls are sometimes clipped by scroll bars and masks on the sub-application.

A sub-application in a separate security domain from the main application has the following behavior:

  • Launching a modal dialog box dims the entire application, but the pop-up can only be dragged within the boundaries of the sub-application.

  • Centering a pop-up centers it over the sub-application, not the main application.

  • Dragging pop-up controls works over the sub-application only. If you drag a pop-up outside the sub-application, it is clipped.

  • Focus shifts to the pop-up control when you first launch a pop-up.

A sandboxed application cannot display a window or dialog box outside the bounds of its application. This rule prevents an untrusted application from phishing for passwords by displaying a dialog box on top of all the applications. When displaying a popup window, the PopUpManager checks if the parent application trusts it and if it trusts the parent application before asking the parent to host the window. If the parent hosts a window it is displayed over the parent's content as well as the child's content. If no mutual trust exists between a main and sub-application, then the PopUpManager hosts the dialog box locally so that it can only be displayed over the content of the application itself. But if the parent trusts the child, the dialog box is not clipped by the boundaries of the child’s application.

When a main application does not trust a sub-application, the main application’s SWFLoader uses masking with a scrollRect and scroll bars to keep the sandboxed application’s content restricted to its own application space.

Pop-up-related controls such as ColorPicker, ComboBox, DateField, PopUpButton, PopUpMenuButton, and Menu sometimes display their contents in unexpected ways if their normal position would cause them to be clipped.

Alert controls in sandboxed applications

Alerts, like other pop-up controls in sandboxed applications, are clipped at the edge of the loaded application. When an Alert is being displayed, the main application and all sub-applications are covered with a modal dialog box to prevent interaction with their controls. The blur effect only applies to the sub-application that launched the Alert box, and its child applications. The blur effect is not applied to the parent application or sibling applications.

Styles and style modules in sandboxed applications

The StyleManager does not pass styles from a parent application to a child in a different application domain or security domain. Similarly, a main application does not inherit styles from a sub-application. Therefore, either define styles within your sub-application and do not depend on the sub-application inheriting styles from the main application, or load a style module into the main application’s application domain.

If you want a main application and a sub-application to use the same runtime style sheets, load the style module into the main application’s application domain. Sub-application’s styles are merged with the main application’s styles. Main applications do not inherit styles that are defined in style modules that are loaded into sub-applications.

A style module must be compiled with the same version of the Flex framework as the application into which it is loaded. The main application and sub-application might not be able to load the same style module, unless they are compiled with the same version of the framework.

When loading a style module into a sub-application, if you don’t specify an application domain, the module is loaded into a sibling application domain of the sub-application. This can result in an error when the sub-application tries to use classes that are defined in the style module.

To load a style module into a sub-application, load the style module into a child application domain of the sub-application. The loadStyleDeclarations() method has two optional parameters, applicationDomain and securityDomain. You use these properties to control the application domain and the security domain into which style modules get loaded.

The following example loads a style module into a child application domain of the sub-application:

private function loadStyle():void {  
    /* Load style module into a child ApplicationDomain by specifying 
       ApplicationDomain.currentDomain. */ 
    var eventDispatcher:IEventDispatcher = styleManager.loadStyleDeclarations( 
        currentTheme + ".swf", true, false, ApplicationDomain.currentDomain);  
    eventDispatcher.addEventListener(StyleEvent.COMPLETE, completeHandler);  
} 

For more information on using style modules, see Loading style sheets at run time.

Fonts in sandboxed applications

Applications that are in the same application domain are able to use the same embedded fonts by specifying the font name. However, if the sub-application is loaded into a different application domain (as is the case with sandboxed applications), then the sub-application must embed the font to use it.

Focus in sandboxed applications

The FocusManager class in the main application and sub-application integrate to create a seamless focus scheme. Users can “tab through” the sub-application, regardless of whether the application is in the same or a different security domain as the main application. Shift tabbing also works. The FocusManager class is one of the few manager classes that supports interoperability even across security domains.

When an application is loaded, the main application keeps track of that SWF file in a list of focus candidates. When the user moves focus into the sub-application, the sub-application’s FocusManager takes over focus duties until the user moves focus outside the sub-application. At that time, the main application’s FocusManager resumes control.

When a pop-up is dismissed, the focus is moved to the last place that had focus. This behavior can be another pop-up or it can be in the main application.

When focus is on a control that is in a different security sandbox, calls to the getFocus() method on that application’s FocusManager return null. Calls to the UIComponent.getFocus() method also return null.

The FocusManager’s moveFocus() method lets you programmatically transfer focus to a control under the jurisdiction of another FocusManager. It also lets you transfer the control of focus to another FocusManager.

Focus management across application domains works even with modal dialog boxes. When a different top-level window is activated, the SystemManager deactivates the FocusManager in the formerly active top-level window. The SystemManager also activates the FocusManager in the other window and changes the depth level (z-order) of the windows.

Cursors in sandboxed applications

If the applications are in different security domains, then a custom cursor in the sub-application only appears over the area of the screen that is allocated to that sub-application. Moving the mouse outside the bounds of the sub-application passes control of the cursor to the main application’s CursorManager.

These rules apply to the busy cursor as well. If a busy cursor is visible and you move the cursor to the main application that is in a different security domain, the cursor changes back to the last used cursor in the main application.

Localizing sandboxed applications

As with style modules, the main application cannot access resource modules used in a sub-application, and vice versa. Each sub-application must load its own resource modules.

Each multi-versioned application has its own ResourceManager instance. As a result, each sub-application has its own localeChain.

If more than one sub-application has resource bundles for the same locale with the same name, then the first one in wins. The contents of all resource bundles of that name in other sub-applications are ignored. Those sub-applications use the one that was defined first.

Like style modules, load resource modules into the child application domain of a sub-application. You control the application domain and the security domain into which resource bundles are loaded. The loadResourceModule() method of IResourceManager has two optional parameters, applicationDomain and securityDomain.

Also like style modules, all resource modules in an application must be compiled with the same version of the Flex framework. Do this whether that application is a main application or a sub-application. You cannot use multiple resource modules that were compiled with two different versions of the framework in the same main application or sub-application.

The following example loads a resource module into a child application domain of the sub-application:

private function loadBundle():void {  
    /* Load resource module into a child ApplicationDomain by specifying 
       ApplicationDomain.currentDomain. */ 
    var eventDispatcher:IEventDispatcher = ResourceManager.loadResourceModule( 
        "MyBundle.swf", true, false, ApplicationDomain.currentDomain);  
    eventDispatcher.addEventListener(StyleEvent.COMPLETE, completeHandler);  
} 

ToolTip objects in sandboxed applications

When in a different security domain, ToolTip objects are parented by the sub-application's SystemManager and are therefore clipped and masked by the main application. The ToolTipManager styles and positions the tip in a sub-application so that it fits within the sub-application’s area of the screen only. If the ToolTip object is larger than the area of the sub-application, the ToolTip object is clipped.

ToolTip styles in the main application are not inherited by ToolTip objects in the sub-application.

This applies to error tips and data tips on List objects in sandboxed sub-applications as well.

Layouts in sandboxed applications

For controls that have pop-up or drop-down menus, when they are in a separate security domain, they are initially displayed unclipped. These controls are restricted to the sub-application’s space, so if they try to go outside that bounding area, they are clipped.

Deep linking in sandboxed applications

The BrowserManager controls deep linking support in applications built with Flex. It is a singleton within its security domain. Sub-applications in sibling application domains cannot access the main application’s BrowserManager, regardless of whether a sub-application is trusted or untrusted. As a result, a sub-application in a separate security domain or application domain cannot modify the URL nor can it access the URL.

If the sub-application is untrusted, do not give it access to the URL. If the sub-application is trusted (such as with a multi-versioned application that is not sandboxed), you can write custom code that handles the interaction between the sub-application and the main application’s BrowserManager. Typically, you call this method in the main application that accesses the URL, or create an interface that acts as a gatekeeper for this interaction.

If you try to get an instance of the BrowserManager from within a sub-application, Flash Player throws an error.

Dragging and dropping in sandboxed applications

When the sub-application is in a different security domain from the main application, the user cannot drag data between the applications. The DragProxy lets the user drag an item to the edge of the sub-application's area of the screen. At that point, the mouse cursor changes back to whatever mouse cursor is correct for the new security domain.

The proxy for the item stays at the boundary of the sub-application, and might be clipped.

Listening for mouse events with sandboxed applications

Mouse interaction between sub-applications and main applications can be confusing, especially when those events occur in different application domains. This interaction is further muddied when the applications are in different security domains. To listen for mouse events outside the security domain, you use the SandboxMouseEvent object. You can listen for this event in a main application for a mouse event that is triggered from the sub-application, and vice versa.

The following application is like the application in the topic, Listening for mouse events in multi-versioned applications, with some exceptions. For example, while the MouseEvent.MOUSE_MOVE and MouseEvent.MOUSE_UP events are registered, the SandboxMouseEvent.MOUSE_UP_SOMEWHERE event is also registered. This event can be registered by any SystemManager within the security domain. All applications can then receive notification if this event is triggered. To get the mouse position, this application uses the globalToLocal() method. You can see how to determine the absolute position of an object in a sub-application when you don’t have access to the stage.

<?xml version="1.0" encoding="utf-8"?>
<!-- apploading/ZoomerPattern4.mxml -->
<s:Application 
    creationComplete="setup()" 
    height="250"
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

     <fx:Script>
     <![CDATA[
          import mx.core.UIComponent;
          import mx.events.SandboxMouseEvent;
          import mx.managers.PopUpManager;

          [Bindable]
          public var data1:Array = ["Ice Cream", "Fudge", "Whipped Cream", "Nuts"];

          public var zoomTool:UIComponent;

          public function setup():void {
               // Draw the zoom rectangle.
               zoomWidget.graphics.lineStyle(1);
               zoomWidget.graphics.beginFill(0, 0);
               zoomWidget.graphics.drawRect(0, 0, 17, 17);
               zoomWidget.graphics.endFill();
               
               // Listen for mouse down events.
               zoomWidget.addEventListener(MouseEvent.MOUSE_DOWN, zoom_mouseDownHandler);
          }

          private var lastX:int;
          private var lastY:int;

          private function zoom_mouseDownHandler(event:MouseEvent):void {
               // When the mouse is down, listen for the move and up events.              
               // The getSandboxRoot() method lets you listen to all mouse activity in your
               // SecurityDomain.
               
               systemManager.getSandboxRoot().addEventListener(
                    MouseEvent.MOUSE_MOVE, zoom_mouseMoveHandler, true);
               systemManager.getSandboxRoot().addEventListener(
                    MouseEvent.MOUSE_UP, zoom_mouseUpHandler, true);

               // The SandboxMouseEvents provide you with some mouse information, 
               // but not its position
               systemManager.getSandboxRoot().addEventListener(
                    SandboxMouseEvent.MOUSE_UP_SOMEWHERE, zoom_mouseUpHandler);

               
               // Update last position of the mouse.
               lastX = event.stageX;
               lastY = event.stageY;

               // Create and pop up the zoomTool. This is the rectangle that is dragged around.
               // It must be a popup so that it can float over other content.
               zoomTool = new UIComponent();
               PopUpManager.addPopUp(zoomTool, this);
               
               var pt:Point = new Point(zoomWidget.transform.pixelBounds.x, 
                    zoomWidget.transform.pixelBounds.y);
               pt = zoomTool.parent.globalToLocal(pt);
               zoomTool.x = pt.x;
               zoomTool.y = pt.y;
               zoomTool.graphics.lineStyle(1);
               zoomTool.graphics.beginFill(0, 0);
               zoomTool.graphics.drawRect(0, 0, 17, 17);
               zoomTool.graphics.endFill();

               // Hide the rectangle that was the target.
               zoomWidget.visible = false;
          }

          private function zoom_mouseMoveHandler(event:MouseEvent):void {
               // Update the position of the dragged rectangle.
               zoomTool.x += event.stageX - lastX;
               zoomTool.y += event.stageY - lastY;
               lastX = event.stageX;
               lastY = event.stageY;

               var bm:BitmapData = new BitmapData(16, 16);
               
               // Capture the bits on the screen.  
               // Use the globalToLocal() method to get the coordinates of the rectangle.
               // Untrusted sub-applications do not have access to the stage so you have
               // to call the globalToLocal() method on a point.
               var pt:Point = new Point(zoomTool.transform.pixelBounds.x + 2, 
                    zoomTool.transform.pixelBounds.y + 2);
               pt = DisplayObject(systemManager.getSandboxRoot()).globalToLocal(pt);
               bm.draw(DisplayObject(systemManager.getSandboxRoot()), 
                    new Matrix(1, 0, 0, 1, -pt.x, -pt.y));

               // Create a Bitmap to hold the bits.
               if (zoomed.numChildren == 0) {
                    var bmp:Bitmap = new Bitmap();
                    zoomed.addChild(bmp);
               } else
                    bmp = zoomed.getChildAt(0) as Bitmap;

               // Set the bits.
               bmp.bitmapData = bm;

               // Zoom in on the bits.
               bmp.scaleX = bmp.scaleY = 8;
          }

          private function zoom_mouseUpHandler(event:Event):void {
               // Remove the listeners.
               systemManager.getSandboxRoot().removeEventListener(
                    MouseEvent.MOUSE_MOVE, zoom_mouseMoveHandler, true);
               systemManager.getSandboxRoot().removeEventListener(
                    MouseEvent.MOUSE_UP, zoom_mouseUpHandler, true);            
               systemManager.getSandboxRoot().removeEventListener(
                    SandboxMouseEvent.MOUSE_UP_SOMEWHERE, zoom_mouseUpHandler, true);

               // Replace the target rectangle.             
               zoomWidget.visible = true;
               
               // Remove the dragged rectangle.
               PopUpManager.removePopUp(zoomTool);
          }

     ]]>
     </fx:Script>
  
    <mx:HBox>
          <mx:HBox backgroundColor="0x00eeee" height="140" paddingTop="4" paddingRight="4">
               <mx:Label text="Drag Rectangle"/>
               <mx:UIComponent id="zoomWidget" width="17" height="17"/>
               <mx:Canvas id="zoom" 
                    borderStyle="solid" 
                    width="132" 
                    height="132"
               >
                    <mx:UIComponent id="zoomed" width="128" height="128"/>
               </mx:Canvas>
          </mx:HBox>
          <mx:List dataProvider="{data1}"/>
     </mx:HBox>
</s:Application>
The following example main application loads the previous example application. It sets the value of the trustContent property to false to mimic the behavior of a remotely loaded untrusted sub-application. It also links the PopUpManager because the sub-application uses it.
<?xml version="1.0" encoding="utf-8"?>
<!-- apploading/MainZoomerPattern4.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

     <fx:Script>
          <![CDATA[
               /* The PopUpManager must be linked in to all applications that are 
                  at the same security sandbox root. Because the sub-application
                  in this example uses the PopUpManager, the main application must also
                  link it it. */
               import mx.managers.PopUpManager; PopUpManager;
          ]]>
     </fx:Script>

     <mx:Text text="Portal (untrusted versioning application):"/>
     
     <mx:SWFLoader id="swf1" 
          loadForCompatibility="true" 
          trustContent="false" 
          source="ZoomerPattern4.swf"/>
          
</s:Application>

Accessing flashVars variables in sandboxed applications

You can access application parameters that were passed into the sub-application as flashVars variables. To do this, you access the Array of parameters on the application object by using the getSandboxRoot() method.

The following example gets a reference to the root application and accesses the parameters:

var app:DisplayObject = DisplayObject( 
    SystemManager.getSandboxRoot()["application"]); 
var parameters:Object = app["parameters"];