在Flash播放器運(yùn)行時(shí),將不同來源的資源劃分到獨(dú)立的沙箱(sandbox)內(nèi),不同沙箱之間不能彼此操作數(shù)據(jù)(除非目標(biāo)沙箱做過一些設(shè)置,授權(quán)其他沙箱可訪問),這就是Flash的跨沙箱問題。當(dāng)Flash文件(.swf) 和頁面(.html)不在同一個(gè)域名下時(shí),如果不經(jīng)過Flash內(nèi)部聲明System.allowDomain,html無法訪問flash定義的接口;不經(jīng)過html設(shè)置allowScriptAccess為’always’,F(xiàn)lash也無法調(diào)用頁面上的js函數(shù)。
那么如果html和flash都設(shè)置了互相可以訪問,是否Flash和html之間就可以互相訪問了呢?理論上是的,然而實(shí)際上卻不是。
在Chrome、Firefox等非IE瀏覽器上,是沒有問題的。在“純正”的IE6、IE7、IE8上也是正常的。但是在傲游、360瀏覽器、騰訊瀏覽器等基于IE的多標(biāo)簽瀏覽器中,刷新頁面的時(shí)候,F(xiàn)lash播放器還是會(huì)拋安全沙箱錯(cuò)誤。
點(diǎn)擊訪問測試頁面。
使用上面說的“基于IE的多標(biāo)簽瀏覽器”訪問,你會(huì)看到,第一次是正常的,刷新之后就不正常。如果你安裝的是debug版本的播放器,可以看到Flash運(yùn)行時(shí)發(fā)生了異常。
SecurityError: Error #2060: 安全沙箱沖突:ExternalInterface 調(diào)用者 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調(diào)用 Security.allowDomain("*"); start(); } private function start():void { //在基于IE的多標(biāo)簽瀏覽器中,這里運(yùn)行時(shí)可能出錯(cuò) ExternalInterface.call("alert", "Hi, flash is ready!"); ExternalInterface.addCallback(drawCircle, drawCircle); } private function drawCircle():void { TextField(getChildAt(0)).appendText( Draw a circle); graphics.beginFill(Math.random() * 0xFFFFFF, .5); graphics.drawCircle( Math.random() * stage.stageWidth, Math.random() * stage.stageHeight, 50); graphics.endFill(); } } }
似乎一旦swf是從緩存中讀取的,allowScriptAccess這個(gè)配置就不起作用?為了驗(yàn)證是不是緩存引起的,我們每次為swf文件地址后面加上隨機(jī)的數(shù)字,發(fā)現(xiàn)就不存在上面的問題了??梢娺@個(gè)問題確實(shí)是瀏覽器緩存造成的。
為swf文件動(dòng)態(tài)加時(shí)間戳或隨機(jī)數(shù),通過防止緩存可以回避掉這個(gè)問題。不過這不是一個(gè)很好的方案,因?yàn)檫@會(huì)極大增加服務(wù)器的壓力,并且導(dǎo)致頁面加載速度一直都很慢。
不過好消息是,目前有個(gè)比這個(gè)更好的方案:延遲Flash的初始化功能。通過將Flash的ExternalInterface.addCallback時(shí)機(jī)延后一些,就可以解決這個(gè)問題。
修改一下Flash的代碼,加一個(gè)setTimeout:
...(略) public class Main extends Sprite { public function Main():void { ...(略) //start(); setTimeout(start, 500); } ...(略) } }
測試修改后的效果
那么,延遲多少比較合適呢?如果太多,用戶會(huì)感覺到明顯的延遲;太少,一些性能較差的電腦上問題依然存在。根據(jù)我一年多總結(jié)的經(jīng)驗(yàn),500ms是比較合理的數(shù)字。目前阿里巴巴中國網(wǎng)站上使用的Flash應(yīng)用程序,如果有需要和js通信,都是延遲500ms初始化。
順便說一下,延遲500ms還有另外的一個(gè)作用。IE6中,F(xiàn)lash初始化的時(shí)候無法得到 stage.stageWidth正確的數(shù)字,返回是0(stageHeight也一樣)。延遲一點(diǎn)初始化就可以得到正確的數(shù)值了。