Content Scriptsでのクロスドメイン通信

Contents Scripsとクロスドメイン通信

 ChromeのExtentionの実装方法の一つに、Content Scriptsというものがあります。一言で言えば、FirefoxGreasemonkeyChrome版です。Content Scriptsを使用すると、任意のWebページに対して、JavaScriptを実行することができます。これにより、本来Webページにはない機能をWebページに追加することができます。


 ChromeのExtentionでは、普通のWebページ内のJavascriptとは違い、クロスドメイン通信が可能です。
 BackgroudPagesやポップアップなどからクロスドメイン通信をする場合は、XMLHttpRequestなり、jQueryやprototypeなどで用意されているHttpRequest用のオブジェクトを使用すれば可能です。
 一方、Contents Scriptsから直接クロスドメイン通信をすることはできません。しかし、BackgroundPagesを用意し、通信自体はBackgroudPagesに行わせるという回りくどい方法で実現可能です。
 具体的には、Content Scriptsからchrome.extention.sendRequestという関数を呼び出し、httpリクエストのパラーメータとレスポンスを処理するコールバックをBackgroundPagesに送ります。その後、BackgroundPagesは
クロスドメイン通信を行い、そのレスポンスをContentScriptsから受け取ったコールバックに渡して呼び出します。この一連の流れを下に図示しておきます。


ContentScriptsによるクロスドメイン通信概要図



 以下では、Google検索時にYahooによる検索も同時に行い、Googleの広告が表示される部分にYahooの検索結果を埋め込むという簡単なExtentionを例に、Contents Scriptsからのクロスドメイン通信を説明してみます。

サンプルExtention

サンプルのソースコード
(ソースコード中のYahoo検索 APIを利用するためのアプリケーションIDを記述している箇所を変更しなければ動きません)

Content Scriptsによるクロスドメイン通信のサンプルExtention

manifest.json
1 {
2   "name": "Double Search",
3   "version": "1.0",
4   "description": "Double Search",
5   "background_page": "background.html",
6   "content_scripts": [
7     {
8       "matches": ["http://www.google.co.jp/search*", "http://www.google.co.jp/search*"],
9       "js": ["prototype.js", "contentscript.js"],
10       "run_at" : "document_end"
11     }
12   ],
13   "permissions": [
14     "http://search.yahooapis.jp/WebSearchService/V1/webSearch*"
15   ]
16 }

 5行目でクロスドメイン通信を行うBackgroundPagesとして「background.html」を指定しています。
 6行目から12行目がContentScriptsを設定する上で重要な箇所です。7行目のmatchesでContentScriptsを動作させるサイトを指定しています。8行目で使用するJavascriptファイルを指定しています。ここで記述した順番にロードされます。contentscript.jsではJavascriptライブラリのprototype.jsを利用するので、contentscript.jsより先にprototype.jsを記述しておきます。9行目では動作タイミングを指定しています。
 13行目から15行目でクロスドメイン通信を行うサイトのURL(この例では、Yahoo検索 APIのURL)を指定しています。

contentscript.js(重要部分のみ)
13 function searchByYahoo() {
14 	var query = getQuery();
15 	
16 	chrome.extension.sendRequest(query, function(res) {
17 		console.log("get response");
18 		console.log(res);
省略
30 	});
31 }

14行目で、Google検索の検索結果ページからクエリを取得しています。
16行目では、chrome.extention.sendRequestを呼び出すことで、BackgroundPages(この例では「background.html」)へYahoo検索に使用するクエリと、Yahooの検索結果を処理するコールバックを渡しています。

background.html
1 <html>
2 <head>
3 <script type="text/javascript" src="prototype.js"></script>
4 <script>
5 chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
6 	console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
7 	
8 	new Ajax.Request("http://search.yahooapis.jp/WebSearchService/V1/webSearch", {
9 		method : "get",
10 		parameters : {"query" : request, "appid" : "appid"},
11 		onSuccess : function(res) {
12 			sendResponse(res.responseText);
13 		},
14 		onFailure : function(res) {
15 			sendResponse(null);
16 		}
17 	});
18 });
19 </script>
20 </head>
21 <body></body>
22 </html>

 ここで一番重要なのは、5行目で呼び出している関数chrome.extention.onRequest.addListenerです。この関数では、ContentScriptsからsendRequestを呼び出したときに呼び出すコールバックが引数となります。このコールバックの第一引数requestは、ContentScriptsから送られてきたメッセージ(chrome.extention.sendRequestの第一引数)、第3引数sendResponseは返信を処理するコールバック(chrome.extention.sendRequestの第三引数)となります。
 8から17行目で、実際にクロスドメイン通信を行っています。12行目で、sendResponseを呼び出して、クロスドメイン通信の相手サイトからのレスポンスの内容をContentScriptsへ送っています。ここではレスポンスのオブジェクトそのものではなく、テキストレスポンスを渡しています。これは、sendResponseには、JSONオブジェクトかundefiedしか引数として渡せないためです。もし、XMLレスポンスなどを渡すとsendResponseは動作しません。ここで、私は1時間くらいはまりました)。

最後に

特になし