2012-09-02

測試 AIR 的 LoaderInfo.parentSandboxBridge 用法

最近遇到一個狀況, AIR App 要載入放在 applicationDirectory 以外的 swf 檔。某些 swf 運作正常, 但少部份無法正常執行。後來在官網找到了解決方式: AIR security / Scripting between content in different domains

以下是測試的情況:

在工作目錄建立 box1 目錄, 裡面包含的檔案: BoxMain-app.xml, BoxMain.as, BoxMain.fla, BoxMain.swf, BoxMainBridge.as。
在工作目錄建立 box2 目錄, 裡面包含的檔案: BoxSub-app.xml, BoxSub.as, BoxSub.fla, BoxSub.swf。

BoxMain.swf 第一個按鈕用來載入文字檔, 第二個按鈕用來載入 BoxSub.swf。
BoxMain.as 的內容:
package  {
 import flash.display.MovieClip;
 import flash.display.SimpleButton;
 import flash.text.TextField;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.net.URLRequest;
 import flash.net.URLLoader;
 import flash.filesystem.File;
 import flash.display.Loader;
 
 public class BoxMain extends MovieClip {
  public  var y_btn    :SimpleButton;
  public  var o_btn    :SimpleButton;
  public  var info_txt :TextField;
  private var loader   :Loader;
  private var file     :File;
  
  public function BoxMain() {
   y_btn.addEventListener(MouseEvent.CLICK, yClick, false, 0, true);
   o_btn.addEventListener(MouseEvent.CLICK, oClick, false, 0, true);
   // 要取得 application 以外的目錄, 故不直接使用 File.applicationDirectory
   file = new File(File.applicationDirectory.nativePath);
   file = file.parent.resolvePath('text.txt');
  }

  public function getFile():File {
   return file;
  }
  public function getURL():String {
   return file.url;
  }

  private function yClick(event:MouseEvent = null):void {
   trace("file.url: " + file.url);
   var ur :URLRequest = new URLRequest( file.url );
   var ul :URLLoader  = new URLLoader;
   ul.addEventListener(Event.COMPLETE, ulComplete, false, 0, true);
   ul.load(ur);
  }

  private function ulComplete(event:Event):void {
   info_txt.text = event.currentTarget.data;
  }

  private function oClick(event:MouseEvent):void {
   if(loader) {
    return;
   }
   var swf_file :File   = new File( File.applicationDirectory.nativePath);
   var swf_url  :String = swf_file.parent.resolvePath('./box2/BoxSub.swf').url;
   // var swf_url  :String = 'http://dl.dropbox.com/u/9545926/qops.blogspot.tw/2012/BoxSub.swf'
   loader               = new Loader;
   loader.contentLoaderInfo.parentSandboxBridge = new BoxMainBridge(this); 
   this.addChild( loader );
   loader.x = o_btn.x;
   loader.load(new URLRequest( swf_url ));
  }
 }
}

BoxMainBridge.as 的內容:
package {
    import flash.filesystem.File;

    public class BoxMainBridge {
        private var main:Object;

        public function BoxMainBridge (main:Object) {
            this.main = main;
        }

        public function getFile():File {
            return main.getFile();
        }
        public function getURL():String {
            return main.getURL();
        }
    }
}

BoxSub.as 的內容:
package  {
 import flash.display.MovieClip;
 import flash.text.TextField;
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.net.URLLoader;
 import flash.filesystem.File;
 
 public class BoxSub extends MovieClip {
  public  var info_txt :TextField;
  private var bridge   :Object;
  
  public function BoxSub() {
   condition4();
  }

  private function condition1():void {
   // 被載入時,使用 File 就會發生 SecurityError
   var file :File       = new File(File.applicationDirectory.nativePath);
   file = file.parent.resolvePath('text.txt');

   var url  :String     = file.url;
   var ur   :URLRequest = new URLRequest(url);
   var ul   :URLLoader  = new URLLoader;
   ul.addEventListener(Event.COMPLETE, ulComplete, false, 0, true);
   ul.load(ur);
  }

  private function condition2():void {
   // 用完整的 URL 是 OK 的
   var url: String     = 'file:///C:/Users/shinder/Documents/My Dropbox/current_proj/sandbox_bridge/text.txt';
   trace( this, ": " + url);
   var ur : URLRequest = new URLRequest(url);
   var ul : URLLoader  = new URLLoader;
   ul.addEventListener(Event.COMPLETE, ulComplete, false, 0, true);
   ul.load(ur);
  }

  private function condition3():void {
   if(! loaderInfo.parentSandboxBridge) {
    return;
   }
   bridge = loaderInfo.parentSandboxBridge;
   // SecurityError: filePathAccess
   var file:File = bridge.getFile();
   var url:String = file.url;
   var ur:URLRequest = new URLRequest(url);
   var ul:URLLoader = new URLLoader;
   ul.addEventListener(Event.COMPLETE, ulComplete, false, 0, true);
   ul.load(ur);
  }

  private function condition4():void {
   if(! loaderInfo.parentSandboxBridge) {
    return;
   }
   bridge = loaderInfo.parentSandboxBridge;
   trace( "bridge.getURL(): " + bridge.getURL());
   var ur:URLRequest = new URLRequest(bridge.getURL());
   var ul:URLLoader = new URLLoader;
   ul.addEventListener(Event.COMPLETE, ulComplete, false, 0, true);
   ul.load(ur);
  }

  private function ulComplete(event:Event):void {
   info_txt.text = event.currentTarget.data;
  }
 } 
}

結論:
被載入的 BoxSub.swf 屬於不被信任的內容, 所以不能使用 File 類別 (condition1), 也不能透過 sandbox bridge 取得 File 物件 (condition3)。可以透過完整的 URL 取得文字檔內容 (condition2), 呼叫 sandbox bridge 取得 URL 是比較合理的做法。
另外, BoxSub.swf 若放在網路上是不能取得本地端檔案的內容的。
測試檔下載

1 則留言:

Shinder 提到...

如果 BoxSub.swf 放在網路上, BoxMain 可以先將 swf 下載存至本地端 applicationStorageDirectory 裡, 再載入。

FB 留言