Rocho.J

人脑是不可靠的, 随时记录感悟并且经常重复!

 

[转]Cross Frame: 與不同網域的 Frame 做互動 ---- 转自:http://www.josephj.com/entry.php?id=338

為何要用不同網域的 Iframe?

通常較具規模的網站,會考慮這樣的安全性問題:當要使用到第三方所提供的 JavaScript 時,得放置在與頁面完全不同網域的 Iframe 中。例如在 Yahoo! 工作時,生活+  UrMap合作的地圖就是放在另一個網域的 Iframe 中的。若不這樣子做,UrMap 網站的 JavaScript 可以隨時竄改 Yahoo! 網頁的內容、甚至危害到使用者。

用不同網域的 Iframe 來放置第三方程式碼的情形,簡單來說就是:「我不信任你的程式、但我需要你的程式成為我網頁中的一部分功能」(除了 Iframe,另外還有 ADSafe、Caja、Flash 等方式可以做到,但後三者都有一定的門檻。)

Same Origin Policy

將第三方網頁或程式碼、用不同網域的 Iframe 放到你的網頁上是一件非常簡單的事情 (<iframe src="xxx"></iframe> 人人都會用)。但問題在於當兩者網域不同時,JavaScript 是沒有辦法透過 parent, window.frames, top, opener 等方式存取的。這就是所謂 Same Origin Policy 的限制。

不能互動,就缺乏了彈性與應用的機會。例如剛剛提到 Yahoo! 生活+ 與 UrMap 的合作,生活+ 這邊會希望在頁面上點選了「餐廳」的連結時、就用 AJAX 取得伺服器的餐廳經緯度資訊、傳遞到放置地圖不同網域的 Iframe 中、Iframe 接到訊息後就以 UrMap API 動態更新網頁地圖上的座標點。而這樣的行為也會因 Same Origin Policy 無法直接達成。

Cross Document Messaging

WHATWG 提出了 Cross Document Messaging(也是 HTML 5 的 Web Messaging)來解決此一問題。簡單來說,你可以丟任何 String 到任何你網頁上的 Window 物件,但是該 Window 物件要不要處理此 String,就是它自己的判斷(會一併提供來源 Window 的 domain)。範例程式碼如下:

A 網頁 (http://yahoo.com/a.html) 放置了一個 name=urmap 的 iframe:

<iframe name="urmap" src="http://yahoomashup.com/urmap.html"></iframe>

A 網頁想要傳遞 Hello World 的訊息給此 Iframe:

<script>
/* 我是 yahoo.com */
window.frames['urmap'].postMessage("Hello World!", "*");
</script>

 

Iframe 裡的 B 網頁 (http://yahoomashup.com/urmap.html) 可以這樣接到資料:

/* 我是 yahoomashup.com */
window.onmessage = function (e) {
    if (e.origin == "yahoo.com") { // 當來自 yahoo.com 時...
        alert(e.data); // 顯示 "Hello World!"
    }
}

 

好消息是目前大多數的主流瀏覽器支援此方式,包括 Firefox 3, Google Chrome, Opera, IE8, IE9 皆可。壞消息則是 IE6 與 IE7 沒有此功能。

Iframe In Iframe Hack

如果此主題有任何 A-Grade 瀏覽器無法使用,那也沒寫這篇文章的必要。只提供半套作法就遜掉了(這也是我對大多數 HTML 5 的新技術感到冷感的原因,IE[6-8] must die 也只是開發者肝苦下喊爽的產物,現實世界是不能不管 IE 的使用者的)其實早在 2007 年就有一些非正規 Cross Frame 的解法。這篇文章是目前解說最詳細的作法:Cross-Domain Communication with IFrames

其實原理相當簡單,就是當 A 網頁要傳遞訊息給 B 網頁的(在 Iframe 中)時,先動態產生一個與 B 網頁同網域的 Iframe,此動態 Iframe 內的 JavaScript 即可利用 window.frames 取得 B 網頁及做任何修改。另外還可透過修改生成 Iframe 中的 URL hash (#)、讓 JavaScript 可以依照不同參數做動作。這個作法的缺點是,你必須另外在接收端的網域放置一支 Proxy 的 HTML 檔、作為動態 Iframe 的網址。

YAHOO.util.CrossFrame 函式庫

雖然原理很簡單,但是要能夠有系統的溝通、寫程式就是一大挑戰了、中間的小細節其實是很多的。幸好 Yahoo! 的前端工程師 Julien Lecomte 用 YUI 2 寫了一個 CrossFrame Utility

 

YAHOO.util.CrossFrame.send(
    "http://www.y.com/proxy.html",
    "frames['mashup']",
    "message"
);

YAHOO.util.CrossFrame.onMessageEvent.subscribe(
    function (type, args, obj) {
        var message = args[0];
        var domain = args[1];
        // Do something with the incoming message...
    }
);

 

看到了嗎?這邊其實就是用 YAHOO.util.CrossFrame.send() 取代 HTML5 的 window.postMessage()、用 YAHOO.util.CrossFrame.onMessageEvent 取代 window.onmessage 事件。單一 API 介面、所有瀏覽器都能用!

用 YUI 3 改寫為 Y.CrossFrame

因為公司內用的是 YUI 3,有時間的話當然希望改寫。另外就是 Julien 當時寫那篇文章的時間點是 2007 年,postMessage 只有 Opera 有實作,所以 Julien 對於 Opera 以外瀏覽器都採用 Iframe in Iframe 的作法,效能差上許多。也因此我改為以 postMessage 為主、Iframe in Iframe 為輔的作法較有效率

 

Y.CrossFrame.postMessage(
    "Hello World!",       // 要傳遞的訊息
    "frames['urmap']",  // 要傳遞的視窗(請用 String)
    {proxy:"http://yahoomashup.com/b.html"}  //  不支援 postMessage 的瀏覽器會用到的 proxy 網址
);

Y.Global.on("crossframe:message", function (message, domain, url) {
alert(message);
});

 

目前測試 IE6, IE7, IE8, FF3, Chrome, Opera 都沒有問題(模擬器像是 IE Tester 無法使用)。希望對您有所幫助 :)

posted on 2013-12-20 15:58  RJ  阅读(244)  评论(0)    收藏  举报

导航