前回はWebアプリの検索ボックスとコンテキストメニューの拡張を通して、OmniboxとContext Menusについて詳細を解説しました。今回は、任意のWebサイトでJavaScriptを実行できるContent Scriptsについて解説していきたいと思います。
ほかのWebサイトと連携する
みなさんは、Webアプリを使っていてほかのWebサイトと連携して欲しいと思ったことはありませんか。例えば、あるWebサイトから情報を抜き出して欲しかったり、もしくはユーザーインフェースを変更・追加して直接Webアプリを操作したかったりといった内容です。それらは、Content Scriptsの仕組みを利用することで実現可能です。Content Scriptsは、対象のWebサイトで任意のJavaScriptを実行することができます。
今回の機能追加では、GoogleマップのWebサイトと連携して目的地を設定できるようにしてみます。具体的には、Googleマップの検索ボタンの隣にOdometerの「目的地設定」ボタンを追加します。「目的地設定」ボタンをクリックすると、Googleマップで表示している緯度・経度を使ってOdometerの目的地を設定します。
Webアプリの構成
今回は、Webアプリを構成するファイルにcontent_script.jsを追加しています。これは、Googleマップのサイト上で実行されるJavaScriptになります。そのほかにもodometer.jsとbackground.js、そしてmanifest.jsonを変更しています。
Content Scriptsを利用するには、マニフェストファイルで "content_scripts" 以下の項目を設定します。"matches" で、対象のWebサイトを指定します。ここでは、Googleマップのドメインを2つ指定しています。また、実行するJavaScriptファイルとして "js" に今回追加したcontent_script.jsを指定しています。ここで設定した項目以外にもJavaScriptの実行タイミングの指定などさまざまな設定項目があります。
表1 マニフェストファイルのContent Scripts関連項目
フィールド名 | 説明 |
content_scripts | 対象のWebサイトで任意のJavaScriptやCSSを適用する設定項目をオブジェクト配列で指定 |
matches | 対象のWebサイトのURLを配列で指定
"http://example.com/*"(マッチパターン) |
css | 挿入するCSSファイルを配列で指定 |
js | 実行するJavaScriptファイルを配列で指定 |
run_at | JavaScriptを実行するタイミングを指定
"document_start", "document_idle"(デフォルト), "document_end" の3つが指定可能 |
all_frames | すべてのフレーム要素でスクリプトを実行するかどうかを指定(デフォルト:false) |
include_globs | "matches" からさらに適用するパターンを指定 |
exclude_globs | "matches" からさらに除外するパターンを指定 |
Content Scripts
Content Scriptsは、対象のWebサイトで任意のJavaScriptを実行することや、任意のCSSを挿入することができます。挿入されたJavaScriptは、対象のWebサイトのDOMツリーに自由にアクセスすることができます。ただし、挿入されたJavaScriptから元のWebサイトのJavaScriptへ直接アクセスすることはできません。例えば、元のWebサイトであるライブラリを読み込んでいるからといって、挿入されたJavaScriptからそのライブラリを利用することはできません。そのライブラリを利用するためには、改めてContent Scriptsで読み込む必要があります。また、挿入されたJavaScriptからはクロスドメイン通信などの特権を必要とするものも利用することができません。その場合、バックグラウンドページと通信してバックグラウンドページから呼び出す必要があります。バックグラウンドページとその通信方法については、過去の連載記事で解説していますのでそちらを参照してください。
Content Scriptsのセキュリティ
Content Scriptsは、非常に強力な機能であるがゆえに、その適用範囲やセキュリティには留意しなければなりません。例えば、すべてのWebサイトでContent Scriptsが適用されるのであれば、その必要性と機能について詳細な説明がなければユーザーは不安に思います。もしかしたらあなたのWebアプリをインストールしないかもしれません。そのため、Content Scriptsを利用する場合は、適用範囲を必要最小限にとどめ、その上で十分な説明をするようにしてください。また、Content Scriptsで挿入するJavaScriptに脆弱性があると、自身のWebアプリだけではなく対象のWebサイトにも被害が及ぶため、セキュリティには十分に注意してください。
Chrome 14からは、マニフェストファイルでスクリプトを実行できる場所を制御できるようになります。例えばマニフェストファイルで次のように指定すると、スクリプトの読み込み元を自身のサイトに制限することができます。
Googleマップにボタンを追加してOdometerを呼び出す
今回は、Googleマップの検索ボタンの隣にOdometerの「目的地設定」ボタンを追加します。Googleマップのサイトを表示した場合、content_script.jsが挿入されますので、スクリプト内でサイトのページ内に要素を追加しています。
要素の追加は、通常のDOM操作で行います。ここでは、document.createElementメソッドとappendChildメソッドなどを使って元の検索ボタンと同じ見た目になるように、同じ構成でボタンを作成しています。クラス名やスタイルなどは、元の検索ボタンで使われているものをそのまま指定しています。また、この目的地設定ボタンはOdometerの機能だと分りやすいようにOdometerのアイコンを追加しています。また、目的地設定ボタンをクリックされた際にGoogleマップの座標を取得するにはページのJavaScriptにアクセスする必要があります。ここでは、あまりスマートではありませんが次のようにスクリプト要素を追加してJavaScriptを実行して、sessionStorage経由で座標を取得しています。sessionStorageなどのWeb Storageについては過去の連載記事で解説しています。
挿入するスクリプト要素の中で、ページのgApplicationオブジェクトを操作して座標を取得し、sessionStorageに格納しています。元のスクリプトでは、挿入後にsessionStorageを監視して座標が取得できた場合にchrome.extension.sendRequestメソッドを使ってバックグラウンドページへ目的地設定のためのリクエストを送信しています。監視はsetTimeoutメソッドを使って0.5秒ごとにgetLatLngメソッドを呼び出して最大5回まで確認するようにしています。バックグラウンドページへは、アクションの内容を "set_destination_from_websites" として緯度、経度を渡しています。
バックグラウンドページでは、アクション内容の "set_destination_from_websites" をcase句で判断してsetDestinationOnAppメソッドを呼び出しています。setDestinationOnAppメソッドでは、前回と同じようにOdometerのメイン画面が既に開いているか確認し、開いていればメイン画面のWindowオブジェクトを取得してsetDestinationメソッドを実行しています。
メイン画面のsetDestinationメソッドでは、地図上のマーカーの作成など表示に関わる部分を更新しています。以降は、従来の操作プロセスと 同様の動きになるので解説は割愛します。
動的にJavaScriptを挿入する
Content Scriptsのようにあらかじめ対象としたURLに対してJavaScriptファイルを挿入するのではなく、任意のタイミングで動的にJavaScriptやCSSファイルを挿入する方法もあります。chrome.tabs以下のexecuteScriptメソッドとinsertCSSメソッドを利用します。これらのメソッドを利用するには、マニフェストファイルで以下のような指定が必要です。
"permissions" に、"tabs" と対象のURLをあらかじめ指定する必要があります。サンプルではすべてのhttpサイトを指定していますが、Webアプリに合わせて個別に指定してください。
実際にJavaScriptやCSSを挿入する場合のサンプルコードです。executeScriptメソッドとinserCSSメソッドは、第1引数で対象のタブIDを指定しています。タブIDはnullを指定すると現在表示されているタブを対象とします。第2引数でcode、もしくはfileを指定することができます。
表2 chrome.tabs
メソッド/プロパティ | 説明 |
executeScript(tabId, details, callback) | 指定のタブにJavaScriptを挿入する |
insertCSS(tabId, details, callback) | 指定のタブにCSSを挿入する |
表3 details
プロパティ | 説明 |
code | JavaScriptコード、もしくはスタイルの文字列を指定 |
file | JavaScriptファイル、もしくはCSSファイルのURLを指定 |
allFrames | すべてのフレーム要素でスクリプトを実行するかどうかを指定(デフォルト:false) |
まとめ
これで、Googleマップのサイトから直接Odometerの目的地を設定できるようになりました。Content Scriptsを使えば、今回紹介したGoogleマップ以外にもほかの地図サイトやいろいろなサイトとも連携することができるようになります。Content Scriptsは自由度が非常に高いので面白いアイディアを実現しやすいと思います。是非活用してみてください。
今回は、Content Scriptsの詳細を解説しました。次回も引き続きさまざまなAPIを解説していきたいと思います。