萬盛學電腦網

 萬盛學電腦網 >> 圖文處理 >> Flash教程 >> Flash在某些多標簽浏覽器中的“偽沙箱”問題

Flash在某些多標簽浏覽器中的“偽沙箱”問題

在Flash播放器運行時,將不同來源的資源劃分到獨立的沙箱(sandbox)內,不同沙箱之間不能彼此操作數據(除非目標沙箱做過一些設置,授 權其他沙箱可訪問),這就是Flash的跨沙箱問題。當Flash文件(.swf) 和頁面(.html)不在同一個域名下時,如果不經過Flash內部聲明System.allowDomain,html無法訪問flash定義的接口; 不經過html設置allowScriptAccess為’always’,Flash也無法調用頁面上的js函數。

那麼如果html和flash都設置了互相可以訪問,是否Flash和html之間就可以互相訪問了呢?理論上是的,然而實際上卻不是。

 

在Chrome、Firefox等非IE浏覽器上,是沒有問題的。在“純正”的IE6、IE7、IE8上也是正常的。但是在傲游、360浏覽器、騰訊浏覽器等基於IE的多標簽浏覽器中,刷新頁面的時候,Flash播放器還是會拋安全沙箱錯誤。

點擊訪問測試頁面。

使用上面說的“基於IE的多標簽浏覽器”訪問,你會看到,第一次是正常的,刷新之後就不正常。如果你安裝的是debug版本的播放器,可以看到Flash運行時發生了異常。

SecurityError: Error #2060: 安全沙箱沖突:ExternalInterface 調用者 http://pnq.cc/temp/test-dmm-crssdmn.swf 不能訪問 http://q.pnq.cc/works/test/test-dmm-crssmn.html。
at flash.external::ExternalInterface$/_initJS()
at flash.external::ExternalInterface$/call()
at Main/start()
at Main/init()
at Main()

Flash的源碼:

package
{
	import flash.display.Sprite;
	import flash.external.ExternalInterface;
	import flash.system.Security;
	import flash.text.TextField;

	/**
	 * Flash緩存造成的偽沙箱問題演示
	 * @author qhwa
	 */
	public class Main extends Sprite
	{

		public function Main():void
		{
			var tf:TextField = new TextField();
			tf.text = 'flash ready';
			tf.autoSize = 'left';
			addChild(tf);

			//允許被所有其他沙箱中的js或flash調用
			Security.allowDomain("*");

			start();
		}

		private function start():void
		{
			//在基於IE的多標簽浏覽器中,這裡運行時可能出錯
			ExternalInterface.call("alert", "Hi, flash is ready!");
			ExternalInterface.addCallback('drawCircle', drawCircle);
		}

		private function drawCircle():void
		{
			TextField(getChildAt(0)).appendText('nDraw a circle');

			graphics.beginFill(Math.random() * 0xFFFFFF, .5);
			graphics.drawCircle(
				Math.random() * stage.stageWidth,
				Math.random() * stage.stageHeight,
				50);
			graphics.endFill();
		}

	}

}

似乎一旦swf是從緩存中讀取的,allowScriptAccess這個配置就不起作用?為了驗證是不是緩存引起的,我們每次為swf文件地址後面加上隨機的數字,發現就不存在上面的問題了。可見這個問題確實是浏覽器緩存造成的。

為swf文件動態加時間戳或隨機數,通過防止緩存可以回避掉這個問題。不過這不是一個很好的方案,因為這會極大增加服務器的壓力,並且導致頁面加載速度一直都很慢。

不過好消息是,目前有個比這個更好的方案:延遲Flash的初始化功能。通過將Flash的ExternalInterface.addCallback時機延後一些,就可以解決這個問題。

修改一下Flash的代碼,加一個setTimeout:

	...(略)
	public class Main extends Sprite
	{

		public function Main():void
		{
			...(略)
			//start();
			setTimeout(start, 500);
		}

		...(略)

	}

}

測試修改後的效果

那麼,延遲多少比較合適呢?如果太多,用戶會感覺到明顯的延遲;太少,一些性能較差的電腦上問題依然存在。根據我一年多總結的經驗,500ms是比較合理的數字。目前阿裡巴巴中國網站上使用的Flash應用程序,如果有需要和js通信,都是延遲500ms初始化。

順便說一下,延遲500ms還有另外的一個作用。IE6中,Flash初始化的時候無法得到 stage.stageWidth正確的數字,返回是0(stageHeight也一樣)。延遲一點初始化就可以得到正確的數值了。

目前我還沒有發現比延遲初始化更好的解決方案,如果你有更好的辦法,歡迎交流!

copyright © 萬盛學電腦網 all rights reserved