Displaying HTML content in mobile apps

Adobe AIR 2.5 and later

The StageWebView class displays HTML content using the system browser control on mobile devices and using the standard Adobe® AIR® HTMLLoader control on desktop computers. Check the StageWebView.isSupported property to determine whether the class is supported on the current device. Support is not guaranteed for all devices in the mobile profile.

In all profiles, the StageWebView class supports only limited interaction between the HTML content and the rest of the application. You can control navigation, but no cross-scripting or direct exchange of data is allowed. You can load content from a local or remote URL or pass in a string of HTML.

Adobe recommends

AIR2.5 StageWebView demo – OAuth Support

Mark Doherty
Evangelist Mark Doherty demonstrates how to log in to an OAuth protected site like Twitter using the StageWebView class.

StageWebView objects

A StageWebView object is not a display object and cannot be added to the display list. Instead, it operates as a viewport attached directly to the stage. StageWebView content draws over the top of any display list content. There is no way to control the drawing order of multiple StageWebView objects.

To display a StageWebView object, you assign the stage on which the object is to appear to the stage property of the StageWebView. Set the size of the display using the viewPort property.

Set the x and y coordinates of the viewPort property between -8192 and 8191. The maximum value of stage width and height is 8191. If the size exceeds the maximum values, an exception is thrown.

The following example creates a StageWebView object, sets the stage and viewPort properties, and displays a string of HTML:

var webView:StageWebView = new StageWebView(); 
webView.viewPort = new Rectangle( 0, 0, this.stage.stageWidth, this .stage.stageHeight); 
webView.stage = this.stage; 
var htmlString:String = "<!DOCTYPE HTML>" + 
                        "<html><body>" + 
                        "<p>King Philip could order five good steaks.</p>" + 
                        "</body></html>"; 
webView.loadString( htmlString );

To hide a StageWebView object, set its stage property to null. To destroy the object entirely, call the dispose() method. Calling dispose() is optional, but does help the garbage collector reclaim the memory used by the object sooner.

Content

You can load content into a StageWebView object using two methods: loadURL() and loadString().

The loadURL() method loads a resource at the specified URL. You can use any URI scheme supported by the system web browser control, including: data:, file:, http:, https:, and javascript:. The app: and app-storage: schemes are not supported. AIR does not validate the URL string.

The loadString() method loads a literal string containing HTML content. The location of a page loaded with this method is expressed as:
  • On Desktop: about:blank

  • On iOS: htmlString

  • On Android: the data URI format of the encoded htmlString

The URI scheme determines the rules for loading embedded content or data.

URI scheme

Load local resource

Load remote resource

Local XMLHttpRequest

Remote XMLHttpRequest

data:

No

Yes

No

No

file:

Yes

Yes

Yes

Yes

http:, https:

No

Yes

No

Same domain

about: (loadString() method)

No

Yes

No

No

Note: If the stage’s displayState property is set to FULL_SCREEN, in Desktop, you cannot type in a text field displayed in the StageWebView. However, in iOS and Android, you can type in a text field on StageWebView even if the stage’s displayState is FULL_SCREEN.

The following example uses a StageWebView object to display Adobe’s website:

package  { 
    import flash.display.MovieClip; 
    import flash.media.StageWebView; 
    import flash.geom.Rectangle; 
     
    public class StageWebViewExample extends MovieClip{ 
 
        var webView:StageWebView = new StageWebView(); 
         
        public function StageWebViewExample() { 
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle( 0, 0, stage.stageWidth, stage.stageHeight ); 
            webView.loadURL( "http://www.adobe.com" ); 
        } 
    } 
}

On Android devices, you must specify the Android INTERNET permission in order for the app to successfully load remote resources.

In Android 3.0+, an application must enable hardware acceleration in the Android manifestAdditions element of the AIR application descriptor to display plug-in content in a StageWebView object. See Enabling Flash Player and other plug-ins in a StageWebView object.

JavaScript URI

You can use a JavaScript URI to call a function defined in the HTML page that is loaded by a StageWebView object. The function you call using the JavaScript URI runs in the context of the loaded web page. The following example uses a StageWebView object to call a JavaScript function:

package { 
    import flash.display.*; 
    import flash.geom.Rectangle; 
    import flash.media.StageWebView; 
    public class WebView extends Sprite 
    { 
        public var webView:StageWebView = new StageWebView(); 
        public function WebView() 
        {     
            var htmlString:String = "<!DOCTYPE HTML>" + 
            "<html><script type=text/javascript>" + 
            "function callURI(){" + 
            "alert(\"You clicked me!!\");"+ 
            "}</script><body>" + 
            "<p><a href=javascript:callURI()>Click Me</a></p>" + 
            "</body></html>"; 
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle( 0, 0, stage.stageWidth, stage.stageHeight ); 
            webView.loadString( htmlString ); 
        } 
    } 
}

Navigation events

When a user clicks a link in the HTML, the StageWebView object dispatches a locationChanging event. You can call the preventDefault() method of the event object to stop the navigation. Otherwise, the StageWebView object navigates to the new page and dispatches a locationChange event. When the page load has finished, the StageWebView dispatches a complete event.

A locationChanging event is dispatched on every HTML redirect. The locationChange and complete events are dispatched at the appropriate time.

On iOS, a locationChanging event is dispatched before a locationChange event, except for the first loadURL() or loadString() methods. A locationChange event is also dispatched for navigational changes through iFrames and Frames.

The following example illustrates how you can prevent a location change and open the new page in the system browser instead.

package  { 
    import flash.display.MovieClip; 
    import flash.media.StageWebView; 
    import flash.events.LocationChangeEvent; 
    import flash.geom.Rectangle; 
    import flash.net.navigateToURL; 
    import flash.net.URLRequest; 
     
    public class StageWebViewNavEvents extends MovieClip{ 
        var webView:StageWebView = new StageWebView(); 
         
        public function StageWebViewNavEvents() { 
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle( 0, 0, stage.stageWidth, stage.stageHeight ); 
        webView.addEventListener( LocationChangeEvent.LOCATION_CHANGING, onLocationChanging ); 
            webView.loadURL( "http://www.adobe.com" ); 
        } 
        private function onLocationChanging( event:LocationChangeEvent ):void 
        { 
            event.preventDefault(); 
            navigateToURL( new URLRequest( event.location ) ); 
        } 
    } 
}

History

As a user clicks links in the content displayed in a StageWebView object, the control saves the backwards and forwards history stacks. The following example illustrates how to navigate through the two history stacks. The example uses the Back and Search soft keys.

package  { 
    import flash.display.MovieClip; 
    import flash.media.StageWebView; 
    import flash.geom.Rectangle; 
    import flash.events.KeyboardEvent; 
    import flash.ui.Keyboard; 
     
    public class StageWebViewExample extends MovieClip{ 
 
        var webView:StageWebView = new StageWebView(); 
         
        public function StageWebViewExample() 
        { 
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle( 0, 0, stage.stageWidth, stage.stageHeight ); 
            webView.loadURL( "http://www.adobe.com" ); 
             
            stage.addEventListener( KeyboardEvent.KEY_DOWN, onKey ); 
        } 
         
        private function onKey( event:KeyboardEvent ):void 
        { 
            if( event.keyCode == Keyboard.BACK && webView.isHistoryBackEnabled ) 
            { 
                trace("back"); 
                webView.historyBack(); 
                event.preventDefault(); 
            } 
            if( event.keyCode == Keyboard.SEARCH && webView.isHistoryForwardEnabled ) 
            { 
                trace("forward"); 
                webView.historyForward(); 
            } 
        } 
    } 
}

Focus

Even though it is not a display object, the StageWebView class contains members that allow you to manage the focus transitions into and out of the control.

When the StageWebView object gains focus, it dispatches a focusIn event. You use this event to manage the focus elements in your application, if necessary.

When the StageWebView relinquishes the focus, it dispatches a focusOut event. A StageWebView instance can relinquish focus when a user “tabs” past the first or last control on the web page using a device trackball or directional arrows. The direction property of the event object lets you know whether the focus flow is rising up past the top of the page or down through the bottom of the page. Use this information to assign focus to the appropriate display object above or below the StageWebView.

On iOS, focus cannot be set programmatically. StageWebView dispatches focusIn and focusOut events with the direction property of FocusEvent set to none. If the user touches inside the StageWebView, the focusIn event is dispatched. If the user touches outside the StageWebView, the focusOut event is dispatched.

The following example illustrates how focus passes from the StageWebView object to Flash display objects:

package  { 
    import flash.display.MovieClip; 
    import flash.media.StageWebView; 
    import flash.geom.Rectangle; 
    import flash.events.KeyboardEvent; 
    import flash.ui.Keyboard; 
    import flash.text.TextField; 
    import flash.text.TextFieldType; 
    import flash.events.FocusEvent; 
    import flash.display.FocusDirection; 
    import flash.events.LocationChangeEvent; 
     
    public class StageWebViewFocusEvents extends MovieClip{ 
        var webView:StageWebView = new StageWebView(); 
        var topControl:TextField = new TextField(); 
        var bottomControl:TextField = new TextField(); 
         
        public function StageWebViewFocusEvents() 
        { 
            trace("Starting"); 
            topControl.type = TextFieldType.INPUT; 
            addChild( topControl ); 
            topControl.height = 60; 
            topControl.width = stage.stageWidth; 
            topControl.background = true; 
            topControl.text = "One control on top."; 
            topControl.addEventListener( FocusEvent.FOCUS_IN, flashFocusIn ); 
            topControl.addEventListener( FocusEvent.FOCUS_OUT, flashFocusOut ); 
             
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle( 0, 60, stage.stageWidth, stage.stageHeight 
- 120 ); 
            webView.addEventListener( FocusEvent.FOCUS_IN, webFocusIn ); 
            webView.addEventListener(FocusEvent.FOCUS_OUT, webFocusOut ); 
            webView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, 
                                     function( event:LocationChangeEvent ):void 
                                     { 
                                         event.preventDefault(); 
                                     } ); 
            webView.loadString("<form action='#'><input/><input/><input/></form>"); 
            webView.assignFocus(); 
             
            bottomControl.type = TextFieldType.INPUT; 
            addChild( bottomControl ); 
            bottomControl.y = stage.stageHeight - 60; 
            bottomControl.height = 60; 
            bottomControl.width = stage.stageWidth; 
            bottomControl.background = true; 
            bottomControl.text = "One control on the bottom.";             
            bottomControl.addEventListener( FocusEvent.FOCUS_IN, flashFocusIn ); 
            bottomControl.addEventListener( FocusEvent.FOCUS_OUT, flashFocusOut );        } 
         
        private function webFocusIn( event:FocusEvent ):void 
        { 
            trace("Web focus in"); 
        } 
         
        private function webFocusOut( event:FocusEvent ):void 
        { 
            trace("Web focus out: " + event.direction); 
            if( event.direction == FocusDirection.TOP ) 
            { 
                stage.focus = topControl; 
            } 
            else 
            { 
                stage.focus = bottomControl; 
            } 
        } 
 
        private function flashFocusIn( event:FocusEvent ):void 
        { 
            trace("Flash focus in"); 
            var textfield:TextField = event.target as TextField; 
            textfield.backgroundColor = 0xff5566; 
        } 
         
        private function flashFocusOut( event:FocusEvent ):void 
        { 
            trace("Flash focus out"); 
            var textfield:TextField = event.target as TextField; 
            textfield.backgroundColor = 0xffffff; 
        } 
         
    } 
}

Bitmap capture

A StageWebView object is rendered above all display list content. You cannot add a content above a StageWebView object. For example, you cannot expand a drop-down over the StageWebView content. To solve this issue, capture a snapshot of the StageWebView. Then, hide the StageWebView and add the bitmap snapshot instead.

The following example illustrates how to capture the snapshot of a StageWebView object using the drawViewPortToBitmapData method. It hides the StageWebView object by setting the stage to null. After the web page is fully loaded, it calls a function that captures the bitmap and displays it. When you run, the code displays two labels, Google and Facebook. Clicking the label captures the corresponding web page and displays it as a snapshot on the stage.

package 
{ 
    import flash.display.Bitmap; 
    import flash.display.BitmapData; 
    import flash.display.Sprite; 
    import flash.events.*; 
    import flash.geom.Rectangle; 
    import flash.media.StageWebView; 
    import flash.net.*; 
    import flash.text.TextField; 
    public class stagewebview extends Sprite 
    { 
        public var webView:StageWebView=new StageWebView(); 
        public var textGoogle:TextField=new TextField(); 
        public var textFacebook:TextField=new TextField(); 
        public function stagewebview() 
        { 
            textGoogle.htmlText="<b>Google</b>"; 
            textGoogle.x=300; 
            textGoogle.y=-80;         
            addChild(textGoogle); 
            textFacebook.htmlText="<b>Facebook</b>"; 
            textFacebook.x=0; 
            textFacebook.y=-80; 
            addChild(textFacebook); 
            textGoogle.addEventListener(MouseEvent.CLICK,goGoogle); 
            textFacebook.addEventListener(MouseEvent.CLICK,goFaceBook); 
            webView.stage = this.stage; 
            webView.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); 
        } 
    public function goGoogle(e:Event):void 
        { 
            webView.loadURL("http://www.google.com"); 
            webView.stage = null; 
            webView.addEventListener(Event.COMPLETE,handleLoad); 
        } 
     
    public function goFaceBook(e:Event):void 
        { 
            webView.loadURL("http://www.facebook.com"); 
            webView.stage = null; 
            webView.addEventListener(Event.COMPLETE,handleLoad); 
        } 
    public function handleLoad(e:Event):void 
        { 
            var bitmapData:BitmapData = new BitmapData(webView.viewPort.width, webView.viewPort.height); 
            webView.drawViewPortToBitmapData(bitmapData); 
            var webViewBitmap:Bitmap=new Bitmap(bitmapData); 
            addChild(webViewBitmap); 
        } 
    } 
}