2013年1月17日木曜日

Chrome extension 入門一歩先

本格的にChrome extensionを作ってみました。

で、色々忘れないように備忘録的に書いておこうかと。

ちなみにHello Worldは他に譲ります。概念的な部分をもしかしたら厳密には違うかもしれませんが、書き記しておきます。

まずはmanifest.json。バージョン2について


忘れちゃいけないことを列挙します。
  • "manifest_version":2がバージョン2であることの宣言
  • permissionで、tabsなどは不要
  • インラインで書かれたjsやcssは動かないので、使う時はmanifestの中で、それぞれのスコープ宣言部でインクルードする
  • 通信permissionやmatchesは"http://*/*"のような書き方が出来る

ここからが大事。Chrome extensionの世界観


extensionはブラウザを改造するものです。ブラウザはURLからHTML等を取得し、ドキュメントを描画するものです。extensionはブラウザの動作に働きかけるために以下の3つのスコープを持っています。
  • アイコン表示とクリック時に表示されるポップアップなどのUIプロセス
  • extensionが有効化されている間、常駐しているバックグラウンドプロセス
  • コンテンツとともに動作するコンテントスクリプトプロセス

extensionはユーザーに有効化されると、ブラウザとは別のプロセスとして動作します。この基本がバックグラウンドプロセスであり、全ての中心であると考えるとデザインしやすいと思います。

このプロセスはユーザーとのやり取りもコンテンツとのやり取りも行いませんが、extensionが有効である限り常にロードされており、変数などが保持されます。ページ毎のコンテントスクリプトプロセスやイベントドリブンなUIプロセスはバックグラウンドプロセスとchrome.extension.getBackgroundPage();で直接スコープを読みだしたり、メッセージを使ってより対話的な情報交換を行うことで、ブラウジングとUIを結びつける高度なアプリケーションを作成できるようになります。

スコープの読み出し例

/* manifest.json */
{
    ...
    "background": {
        "scripts": ["background.js"]
    },
    ...
    "browser_action": {
        ...
        "default_popup": "popup.html"
    }
}
/* background.jsにvar resultがグローバル変数として宣言されている場合、popup.html内のスクリプトタグでインクルードされている(ここではそう記述されているとする)popup.js内ではこのresultの内容を以下のように参照可能 */
var bg = chrome.extension.getBackgroundPage();
var bg_result = bg.result;

メッセージ利用例

/* manifest.json */
{
    ...
    "background": {
        "scripts": ["background.js"]
    },
    ...
    "content_scripts": [{
        "matches": ["http://*/*", "https://*/*"],
        "js": "contentscript.js",
        "run_at": "document_end"
    }],...
    ...
}
/* background.jsにvar resultがグローバル変数として宣言されている場合、contentscript.js内ではこのresultの内容を以下のように参照可能 */
/* contentscript.js */
...
//以下でメッセージをやり取りする接続(パイプ)を作成する
var port = chrome.extension.connect({"name": "message_pipe"});
//以下でメッセージを送信して
port.postMessage({"dataA": "A", "dataB": "B", "dataC": "C"});
//以下でbackground.jsからの返信を受け取る
port.onMessage.addListener(function(msg){
    //msgにはbackground.jsからの返信オブジェクトが入っており、resultA~resultCが参照できる
    var reA = msg.resultA;
    var reB = msg.resultB;
    var reC = msg.resultC;
});
/* background.js */
...
//接続された時に以下が動くので
chrome.extension.onConnect.addListener(function(port){
    //メッセージのリスナを登録しておく
    port.onMessage.addListener(function(msg){
        //msgにはcontentscript.jsからの送信オブジェクトが入っており、dataA~dataCが参照できる
        var reA = msg.dataA + " result";
        var reB = msg.dataB + " result";
        var reC = msg.dataC + " result";
        //contentscript.jsに返信
        port.postMessage({"resultA": reA, "resultB": reB, "resultC": reC});
    });
});

バックグラウンドプロセスは省略可能で、例えばアイコンクリックでどこからか、何らかの情報を取得し、ポップアップに何かを表示するだけのextensionであればバックグラウンドプロセスを宣言する必要はないかもしれません。

2つめは今出ましたアイコンとポップアップなどのインタラクティブUI部分です。具体的には以下の3種類があります。
  • アイコンとポップアップのブラウザアクション
  • アドレスバー内に表示するページアクション
  • 別タブで専用のページを表示するオプションページ

これらはそれぞれmanifesut.json内に"browser_action", "page_action", "options_page"という名前で宣言し、関連するファイルを結びつけます。

UI系のプロセスはスコープがmanifest内で宣言したファイルまでで、それを越えることができません。タブに表示されている通常のコンテンツの情報を取得するには上で紹介した方法を用いてバックグラウンドプロセスを介して情報を取得する必要があります。直接コンテントスクリプトとやり取りすることは恐らく難しいか出来ないと思われます。動作するタイミングの関係でバックグラウンドプロセス(もしくはローカルストレージ)を介するほうが無難だと思います。

最後はコンテントスクリプトプロセスです。ブラウズするドキュメントに対し、manifest.json内run_atのタイミングで関連付けされたjsファイルを読み込みます。このスコープはダウンロードされたコンテンツですので、documentオブジェクトは実際ダウンロードされたコンテンツを参照します。

このプロセスの中でコンテンツ内の必要な情報をバックグラウンドプロセス(もしくはローカルストレージ)に渡すというのがコンテンツ解析系のextensionでは基本的な動きになるのではないでしょうか?


以上。

0 件のコメント:

コメントを投稿