コンテンツにスキップするには Enter キーを押してください

webRequest APIをざっくり理解する。(あるいはChrome拡張の作り方)

昨日2月9日、Google Chrome 17の安定版がリリースされました。

このバージョンでの変更点の一つに、webRequest APIが正式に実装されたことがあります。
これまでこのAPIはexperimental(実験的機能)として実装されていましたが、今回晴れて正式なものになりました。

このAPIを使うと、Chromeが行う通信を監視して通信があるたびにイベントを実行したり、HTTPヘッダを書き換えたりすることができます。
Chrome 17では、User-Agentを他のブラウザに偽装することができる機能がDeveloper Toolsに付いたのですが、その機能もこのAPIを利用して実装されてい(ると思われ)ます。(Developer Tools自体もJavaScriptによって実装された“Webアプリ”なので、多分そのはずです)

HTTPヘッダをいじれるというと不安に思われるかも知れませんが、このAPIは普通のWebページで実行されるJavaScriptからは利用できません
利用できるのはChromeにインストールした拡張機能で、この機能を利用するというパーミッションを指定しているものからのみです。

では試しにひとつ簡単なChrome拡張を作ってみて、webRequest APIの使い方を簡単に紹介します。

今回は、ITmediaの画像を直接表示できるようにする拡張機能を作ってみます。
ITmediaの記事の画像は、サイト内(itmedia.co.jp)からのリファラがないとエラーになって表示することができません。
掲示板やTwitterなどからリンクを張っても、リファラがおかしいというエラーページに飛ばされてしまいます。
この不便を解消するために、直リンクでも画像を表示できるようにリファラをいじる拡張機能を作ってみることにしましょう。

まず適当な空のディレクトリを作り、その中にmanifest.jsonというファイルを作って、拡張機能の設定情報を記述します。

{
    "name": "Referrer Changer for ITmedia",
    "description": "ITmediaの記事画像を直リンクでも表示できるようにする",
    "version": "0.0.1",
    "background_page": "background.html",
    "permissions": [
        "webRequest",
        "webRequestBlocking",
        "*://image.itmedia.co.jp/*"
    ]
}

詳しい説明は省略しますので公式ドキュメントを読んでください。
ここで重要なのはpermissonsの部分です。
webRequest APIを利用するには、ここにwebRequestと、監視対象としたいURLのパターンを指定します。
HTTPヘッダを書き換えたり、リクエストをキャンセルするなど、リクエスト内容に変更を加える操作をする場合はwebRequestBlockingも必要になります。

次に拡張機能の本体を作ります。
manifest.jsonのbackground_pageで指定したファイルが本体ファイルになります。
この例ではbackground.htmlです。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
//あとで使う正規表現パターンを作っておく
var re = /^http:\/\/\[a-z0-9]+.itmedia\.co\.jp\//i;
//onBeforeSendHeaders(ヘッダを送信する前)イベントリスナを登録
chrome.webRequest.onBeforeSendHeaders.addListener(
	function(details){
		//フラグ変数
		var refererFound = false;
		for(var i=0; i<details.requestHeaders.length; i++){
			var header = details.requestHeaders&#91;i&#93;;
			if(header.name == "Referer"){
				//リファラがitmedia.co.jpでなければ書き換える
				if(!re.test(header.value)){
					header.value = "http://www.itmedia.co.jp/";
				}
				refererFound = true;
				break;
			}
		}
		//リファラなしの場合は追加
		if(!refererFound){
			details.requestHeaders.push({
				name: "Referer",
				value: "http://www.itmedia.co.jp/"
			});
		}
		//書き換えたヘッダを返却
		return {requestHeaders:details.requestHeaders};
	},
	{
		urls: &#91;
		    "*://image.itmedia.co.jp/*"
		&#93;,
		types:&#91;
		    "main_frame",
		    "sub_frame",
		    "xmlhttprequest"
		&#93;
	},
	&#91;
	    "requestHeaders",
	    "blocking"
	&#93;
);
</script>
</body>
</html>

コメントの指摘のとおり、コードを修正しました。

JavaScriptは別ファイルにして、HTMLからはそれを読み込むほうがセオリーには沿っているのですが、今回は特に分けるほどでもないので直接記述しました。

chrome.webRequest.onBeforeSendHeaders.addListenerで、ヘッダが送信される前に実行するイベントを登録します。
このメソッドは引数を3つ取ります。
第1引数はイベント発火時に実行するコールバック関数を指定します。この関数の引数にはヘッダの内容が渡されます。
第2引数はリクエストフィルタ(イベントを実行する条件)を指定します。urlsは監視対象のURLのパターン、typesはリクエストの種類です。今回はURLのホスト名が「image.itmedia.co.jp」であるページが、普通にタブで読み込まれたとき(main_frame)、iframe等の内部で読み込まれたとき(sub_frame)、XHRで読み込まれたとき(xmlhttprequest)にイベントが発動するように指定しました。
第3引数は実行時のオプションです。requestHeadersを指定するとコールバック関数の引数にHTTPリクエストヘッダの内容が渡されるようになります。blockingを指定するとイベントが同期的に実行され、リクエストはコールバック関数の処理が終わったあとで実行されるようになります。blockingを指定しなかった場合はイベントは非同期的に実行されるため、リクエスト内容に変更を加えることができません。

コールバック関数の中身では、引数で渡されたリクエストヘッダ配列からリファラ情報を探し出し、リファラのドメインがitmedia.co.jpでなければITmediaのトップページに書き換えています。
そもそもリファラ情報がない場合も情報を追加しています。

最後に書き換えたヘッダを返却して、処理は終了します。

ではこの拡張機能をChromeにインストールしてみましょう。
画面右上のスパナマークからオプションを開き、拡張機能のタブを開きます。
「デベロッパーモード」のチェックボックスにチェックを入れると「パッケージ化されていない拡張機能を読み込む」というボタンが出現するのでこれをクリックし、先ほど作ったjsonとhtmlファイルの入ったディレクトリを指定します。
ここで何もエラーが出なければ、インストールは完了しています。

ではちゃんと動作するかどうか、実際のページを見てみましょう。

http://image.itmedia.co.jp/l/im/nl/articles/1202/08/l_ky_miku_0207_302.jpg

拡張機能をインストールしていないブラウザから上のリンクに飛ぶとエラーページに飛んでしまうはずです。
拡張機能をインストールしたChromeで、エラーページに飛ばず、ちゃんと画像が表示されれば成功です。

今回紹介したのはwebRequest APIのごく一部です。
実際にはonBeforeSendHeaders以外にもいろんなタイミングでイベントをセットできます。
すべてを説明していくわけにも行きませんので、詳しいことは公式ドキュメントをご覧ください。

APIが正式版になったため、このAPIを利用した拡張機能もChrome Web Storeで公開できるようになりました。
リクエストヘッダやレスポンスヘッダをいじれるようになったことで、今まで実現が難しかった高度な拡張が実現できるようになりました。
もしかするとFireMobileSimulatorのChrome版のような拡張機能も登場してくるかもしれません。(技術的には可能なはずです)
今後が楽しみなAPIですね。


5 Comments

  1. teramako 2012/02/15

    //書き換えたヘッダを返却
    return details;

    の部分ですが、
    return { requestHeaders: details.requestHeaders };

    ではないでしょうか。

  2. タカ 2012/02/16

    英語苦手なので引数の意味など、とても参考になりました。ありがとうこざいます。
    ただコードが私の環境ではError: Invalid value for argument 4. …(省略)と出てリファラが反映されてなかったので調べた所
    //書き換えたヘッダを返却
    return details;

    return {requestHeaders:details.requestHeaders};
    に変更したところ正常に動作しました。

  3. […] 期待を持っていたGoogleChromeの拡張機能である「webRequest」がいつの間にか正式にリリースされていました。これによってリクエスト/レスポンスヘッダをスクリプトから変更する事が可能に。Web Requests – Google Chrome Extensions – Google Code下記サイトがこの上なく参考になりました。webRequest APIをざっくり理解する。(あるいはChrome拡張の作り方) | mzsm.meこの機能を使ってニコニコ動画の視聴ページ(/watch)から動画をダウンロード&時報ブロックの拡張を作ってみました。自分も今までいくつかのニコニコダウンロード拡張を使っていたのですが、外部サービス経由でユーザーが増えるとダウンタイムが増えたり、ファイル名が”smile.mp4″固定だったり・・と。どうにも使い勝手が悪かった。その理由がニコニコの動画サーバーから送られてくるこのレスポンスヘッダ。 Content-Disposition: inline; filename="smile.mp4"ファイル名が”smile.mp4″なのはまさにこれが原因です。また、URLを新しいタブで開いてもダウンロード開始せずにブラウザ内で再生されるのは”inline”が原因です。動画保存に手間をかけさせたいという運営の努力の痕が見えます。これを上書きできるwebRequestApiの前では残念ながら無力ですけど。作った拡張を例に、そんな素晴らしいwebRequestApiを解説してみます。動画ファイルにアクセスする際動画IDがヒストリーに含まれたクッキーが必要なのですが、クッキーの処理はブラウザがやってくれるので今回は関係ありません。先ずmanifest.jsonについて。permissionsに”webRequest”を追加するのはもちろんですが、アクセス先のホストのマッチパターンも書きます。このホストが拡張のインストール時に表示されます。res.nimg.jpは後述の時報ブロックの為。 manifest.json JavaScript […]

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください