読者です 読者をやめる 読者になる 読者になる

anti scroll

ブラウザと小説の新しい関係を模索する

Firefox Add-on SDKでサクっとクロスドメインXHRする拡張を作る

プログラミング

Firefox拡張機能を作ろうと思って公式のサイトを見たら、ドキュメントは色々とあるんだけど何を読んだらいいのかよくわかりませんでした。

でもなんか「今はこれが一押し」みたいなリンクがあって、そこを辿っていったらAdd-on SDKというものに巡り合って、ちょっとみたら難しくなかったので、忘れないうちに書き残しておきます。

Add-on SDKのインストールは、単にsdkをダウンロードしてきて、

> unzip add-on-sdk.zip
> cd add-on-sdk
add-on-sdk> source bin/activate

あとはadd-on-sdk/binのパスを登録して、適当にディレクトリでも作って

>mkdir test-addon
>cd test-addon
test-addon>cfx init

とかしたらaddonに必要なソースが展開されます。

アドオンのエントリーポイントは「lib/main.js」で、そこにメイン関数と、クロスドメインリクエストを書きます。

今回は、特定のサイトを開いたらクロスドメイン通信をしてデータを引っ張ってきて、その結果を下にページ内容を書き換える、っていう良くあるパターンを書いてみました。


main.jsはこんな感じです。

// lib/main.js
var pageMod = require("page-mod");
var request = require("request");
var self = require("self");

exports.main = function(options, callbacks){
  pageMod.PageMod({
    include: "*.somedomain.com", // 特定のページだけ起動
    contentScriptWhen: "end", // ページ読み込みが終わったらcontentScriptを走らせる
    contentScriptFile: [ // ページ内容を操作するスクリプト
      self.data.url("jquery-1.7.min.js"), // 先にjqueryを読み込ませる
      self.data.url("content.js") // 実際のページ操作を書くスクリプト
    ],
    onAttach: function(worker){
      // クロスドメインにXHR
      request.Request({
	url : "http://some-service/some-api.json"
	content:{
	  loaded_url:worker.url // 引数に読み込まれたページのURLを渡してみる
	},
	// (注意)戻り値はSDK内製のResponseオブジェクト
	onComplete : function(response){
	  worker.postMessage(response.json);// workerを使ってcontent.jsに通知
	}
      }).get();
    }
  });

};

// 以下はよくわかんないけどサンプルにあったのを写しただけ
exports.onUnload = function(reason){
  console.log(reason);
};

PageModってのは、特定のURLをフックしてページを操作するオブジェクトです。

ロードしたページのURLがマッチするとPageModが起動し、ページへのアタッチ時にonAttachコールバックが呼ばれます。

コールバックの引数は、main.jsとコンテントスクリプトの通信を仲介するオブジェクト(worker)です。

このworkerにメッセージをポストすることで、main.jsからcontent.jsに指示を出すことができます。

ちなみにクロスドメインへのリクエストはセキュリティの関係でcontent.jsなどからは実行できないので、main.jsからXHRさせ、その結果をworkerを通じてcontent.jsに送信する流れになります。

main.jsからの通信をcontent.jsが受ける処理は次の通り。

// data/content.js
self.on("message", function onMessage(ret){
    $("#some-wrapper").html("hoge"); // なんかする。
});

ContentScriptの中ではselfという変数が対応するworkerとしてグローバルに定義されているので、こいつでself.on("message", function(msg){}); みたく待ち受けたらいいみたいです。

コンテキストスクリプトは、読み込んだページのコンテキストで動くので、あとは好き勝手にページの表示を変えるだけ。

ちなみに先にjqueryを読み込ませているので、jqueryオブジェクトを使って記述できます。楽チンですね。

content.jsと、jquery.jsは、dataディレクトリに放り込んでます。dataディレクトリに放り込んだスクリプトは、

self = require("self");
jqueryScript = self.data.url("jquery.js");

みたいにして取得できます。他にも画像ファイルとかが必要なら、同じくdataディレクトリに放り込んでおくことになるんだと思います。

で、完成したら最後にコマンドラインから

cfx xpi

で、拡張がコンパイルされ、この場合だと「test-addon.xpi」が出力されます(最初の一回はなんかエラーが出ますが、気にせずもう一回コンパイルすればちゃんと出力されます)。

あとついでですが、workerの読み込まれた先のURLについて、ドキュメントでは「contentURL」っていうプロパティとして設定されていますが、現在は「url」っていうプロパティ名に変わってます。