Webページ(親ページ)とそこからiframeで読み込んだ子ページ間でデータの授受を行う。
ただし、iframe内のコンテンツは親ページのドメイン外に存在する任意のサイトとする。
◆ 課題
解決する課題は2つある。
1. クロスドメイン制約への対応
XMLHttpRequestを投げる際などにも必ず気に掛ける点だろう。
Same Origin Policy(同一生成元ポリシー)を回避しなければならない。
今回は任意の外部サイトをiframeに取り込むことを前提としているため、
CORS(Cross-Origin Resource Sharing)もJSONP(JSON with padding)も今回は使えない。
2. iframe内での読み込み不許可ヘッダへの対応
HTTPのレスポンスヘッダに、iframe内からWebページが読み込まれるのを防止するオプション(X-Frame-Options)が付与されていればiframe内で表示はできない。
◆ 解決策
1. postMessageの利用
クロスドメイン制約への対応として、HTML5で用意されたpostMessageの利用を思いつく。
(親サイト)
<iframe id="ifrm" src="外部サイト"></iframe> <script type="text/javascript"> window.onload = function() { var ifrm = document.getElementById('ifrm').contentWindow ifrm.postMessage("hello", '外部サイト') }; </script>
(外部サイト)
<script type="text/javascript"> window.addEventListener('message', function(event) { alert(event.data) }, false); </script>
しかし、Chromeが許可しない。
※Safariでは警告なく、実施できた。
(エラー例)
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://親サイト') does not match the recipient window's origin ('http://外部サイト').
2. iframeを使わない
iframeを使う前提を変え、コンテンツをダウンロードし、外部サイトを親側で再現させる。つまり、前提を変える。
が、しかし、JavaScriptが生成する動的ページのリソースの管理は困難があるため、やはり、iframeは使いたい。前提は戻す。
が、しかし、JavaScriptが生成する動的ページのリソースの管理は困難があるため、やはり、iframeは使いたい。前提は戻す。
3. Google Chromeの拡張機能の利用
Chromeに限定されるが、Chromeの拡張機能を使えば対応ができそうである。
データの共有は、バックグラウンドで動作させるスクリプト内でセッションストレージを使えばいいだろう。
また、ヘッダの書き換えも実施できる。
◆ 拡張コード概要
chrome拡張を利用することにした。
拡張コードを有効にするには、最低限以下の3つのファイルを用意し、それらを適当なフォルダに入れ、Chromeブラウザの拡張機能からインポートすれば良い。
○ マニフェスト ファイル
拡張機能に関する情報を与える。
○ コンテンツ スクリプト
ブラウザで表示させるページで読み込むjsとは別空間で実行させるjsである。
このファイルは親サイトと、iframe内の外部サイト、両方に読み込まれる。
空間は分かれているため、コンテンツスクリプト内で利用しているjQueryなどのライブラリがサイトで利用しているバージョンと異なっていても問題は起きない。
○ バックグラウンド スクリプト
Chromeのバックエンド側で処理させるjsである。
表示コンテンツには取り込まれないが、コンテント ファイルとの間でメッセージ通信ができる。
簡易図で表すと下記のような感じである。
parent
+----------+
| |← contentScripts.js
| iframe |
| +----+ |
| | | |
| | |←-|-- contentScripts.js
| +----+ |
| |
+----------+
background.js
contentScripts.js、background.jsの名称はmanifest.json内で指定する。
◆ 試験
1. iframe内でマウスを操作。マウスオーバしたタグ要素に色がつくようにしている。
そのタグ要素でクリックすると、タグ名がセッションストレージへ保存される。
2. 親側でiframe外の要素をクリックする。
iframeで取得した要素がalert表示されれば成功である。
◆ コード例
○ manifest.json
○ contentScripts.js
○ background.js
◆ コード解説
1点、解説を加えておく。
contentScript.jsは親子両方に読み込まれるため、共通処理以外のjsコードは、親子用で条件を加えている。
○ manifest.json
○ contentScripts.js
○ background.js
◆ コード解説
1点、解説を加えておく。
contentScript.jsは親子両方に読み込まれるため、共通処理以外のjsコードは、親子用で条件を加えている。
// iframe用 if (window != parent) { ~snip~ } // 親用 else{ ~snip~ }