Now browsing the SCRAPBLOG weblog archives.

Firefox 4 対応アドオンのプレビュー用スクリーンショット作成手順

Firefox 4 ベータ版の完成度もだいぶ高くなってきて見た目も最終仕様に近づいてきたと思われるので、addons.mozilla.org で公開している Firefox 4 対応済み自作アドオンのプレビュー用スクリーンショットを順次 Firefox 4 ベースに差し替えていこうと思う。以下はプレビュー用スクリーンショットの作成手順メモ。

OS

以前は Windows XP (Royale Theme) + Firefox 3.6 でプレビュー用スクリーンショットを作成していたが、 Firefox 4 になってデザインが Windows 7 と相性が良くなったのと、自分の開発環境が Windows 7 ということもあり、今後は Windows 7 + Firefox 4 とする。

OSのテーマ

Windows 7 デフォルトの Aero テーマで、透明感を有効(Glass効果あり)にし、壁紙を白一色にする。透けたタイトルバーにデスクトップのアイコンが写り込むのを防ぐため、デスクトップ上で右クリック→「表示」→「デスクトップアイコンを表示」をオフにする。
次に、「デザインの詳細設定」でUIに使用するすべてのフォントを、メイリオから Segoe UI に変更する。メイリオも綺麗だが、 Segoe UI は大変美しく、小さいサイズでも可視性に優れている。さらに、アクティブタイトルバーのサイズを18から20に変更する。
次回以降の作業時のために、変更した状態となっている「未保存のテーマ」を右クリックして名前を付けて保存しておく。

Firefox のバージョン

Firefox 4.0b8pre(トランクビルド)だと、タイトルバーのアプリケーションボタンなどが「Firefox」ではなく「Minefield」になってしまうため、現時点で最新のベータ版である、 Firefox 4.0b7 をインストールする。自分はインストーラ版の Firefox 3.6 をインストールしており、追加でインストーラから Firefox 4.0b7 をインストールすると関連付けが変わったり若干面倒なことになりそうな予感がしたので、ZIPパッケージの Firefox 4.0b7 を探したが、見つからなかった。そこで、イレギュラーな方法であるが、Firefox ベータ版 – 各国語版のダウンロードから英語版の Firefox 4.0b7 インストーラをダウンロードし、インストーラの exe ファイルを 7-Zip で展開し、中にある「core」フォルダを取り出して「Mozilla Firefox 4.0b7」にリネームし、「C:Program Files」内に移動することでインストールした。なお、プレビュー画像の国際化対応のためUIを英語にしたいので、英語版の Firefox 4.0b7 をダウンロードする。

Firefox の設定

「”C:Program FilesMozilla Firefox 4.0b7firefox.exe” -p -no-remote」で Firefox を起動し、プロファイルマネージャにて常用プロファイルとは別のプレビュー画像作成用のクリーンなプロファイルを作って起動する。
各種ツールバーの表示は基本的にはデフォルト状態のまま変更しないようにする。つまり、メニューバーは非表示(代わりにアプリケーションボタン表示)、ナビゲーションツールバー表示、タブバーは上部に表示となる。ただし、ナビゲーションツールバー右端の「Feedback」ボタンはベータ版特有のものなので、ツールバーのカスタマイズで取り除いておく。アドオンバーは必要に応じて表示する。
また、GPU固有の問題かもしれないが、自分の環境ではハードウェアアクセラレーションを有効にするとなぜかテキストの描画が劣化するので、オプションの「Use hardware acceleration when available」をオフにする。

Firefox のウィンドウ

Firefox のウィンドウのサイズを addons.mozilla.org で許可されている最大のサイズである 700 x 525 にする。
やり方は、以下のコードをローケーションバーに貼り付けて移動するだけで良い。ただし、 JavaScript の詳細設定で「Move or resize existing windows」がオフになっていると効かないので注意。

javascript:window.resizeTo(700,525);

サイドバー系の拡張機能では以下のコードをエラーコンソールで実行し、サイドバーの幅を一意にする。

var win = Components.classes["@mozilla.org/appshell/window-mediator;1"].
          getService(Components.interfaces.nsIWindowMediator).
          getMostRecentWindow("navigator:browser");
win.document.getElementById("sidebar-box").width = 200;

スクリーンショットのキャプチャ

ここまでの手順で準備は完了で、あとはひたすらアドオンの色々な場面のスクリーンショットをキャプチャしていく。
スクリーンショットのキャプチャには WinShot を使用し、基本的にはアプリケーションのウィンドウ全体をキャプチャする。 Windows Aero 有効時はウィンドウの外側に影が付くが、画像サイズを700 x 525で統一するため、この影は省いてキャプチャすることとなる。ただ、ウィンドウ枠の四隅の角丸の外側が汚れたような見た目になってしまう問題点がある。

実際の例

以上の手順に従って作成した Firefox のスクリーンショットはこちらです。

TOP

persist 属性による属性値の永続化

属性の永続化とは?

XUL要素に persist 属性によって値を保存したい属性の名前を指定しておくと、その属性の値が次回XULドキュメントをロードした時に復元される。保存された属性の値は Firefox 終了時にローカルファイル(プロファイルフォルダ下の localstore.rdf)へ書き出されるので、 Firefox を終了しても保持される。

XULアプリで何らかの設定値を保存するには、 Preferences の仕組みを使って保存する方法が有名だが、UIの見た目に関する設定値(ウィンドウの位置や大きさ、ツールバーのボタンの並び順など)は属性の永続化によって保存される場合が多い。

具体的な例

Firefoxのウィンドウの位置や大きさは、前回開いたときの状態が復元される。これは属性の永続化によって実現されている。 browser.xul のソースを見ると、以下のように persist 属性によって保存したい属性のリストが指定されている。

browser.xul
<window id="main-window"
        ...
        persist="screenX screenY width height sizemode">

localstore.rdf には以下のように {XULドキュメントのURL}#{要素のid} をキーとしてして保存対象の各属性の値が保存される。

localstore.rdf
  <RDF:Description RDF:about="chrome://browser/content/browser.xul#main-window"
                   screenX="115"
                   screenY="87"
                   width="823"
                   height="645"
                   sizemode="normal" />

基本的な使い方

例として、以下のように persist 属性にて value 属性を永続化するようにした xul:label 要素を作成する。idの無い要素は属性の永続化ができないことに注意。

test.xul
<label id="testLabel" value="Test" persist="value" />

次に、以下のようなボタンを作って、 value 属性の値を動的に変更してみる。

test.xul
<button label="Write value"
        oncommand="document.getElementById('testLabel').setAttribute('value', 'xxx');" />

いったん Firefox を終了し、 localstore.rdf を開いてみると、以下のように変更後の foo 属性の値が保存されていることがわかる。

localstore.rdf
  <RDF:Description RDF:about="chrome://.../content/test.xul#testLabel"
                   value="xxx" />

引き続き Firefox を起動して test.xul を開いてみると、ラベルの文言が変更後の xxx になっていることがわかる。

次に、以下のようなボタンを追加し、ボタンをクリックして value 属性の値を削除してみる。

test.xul
<button label="Delete value"
        oncommand="document.getElementById('testLabel').removeAttribute('value');" />

Firefox を終了して localstore.rdf を見ると、 value 属性の値を保存していた部分が削除されていることがわかる。また、 Firefox を起動して test.xul を開いてみると、ラベルの文言が初期値の Test になっていることがわかる。

persist 属性の落とし穴

persist 属性には永続化したい属性の名前(複数ある場合はスペース区切りで連結)を指定するが、注意すべき点として、 persist 属性に指定した属性の名前に部分一致する属性までもが永続化の対象となることが挙げられる。

先ほどの persist="value" とした xul:label 要素の例で、以下のようなボタンを追加する。

test.xul
<button label="Write val"
        oncommand="document.getElementById('testLabel').setAttribute('val', 'xxx');" />

ボタンをクリックして val 属性の値に xxx をセットし、 Firefox を終了して localstore.rdf を見ると、永続化の対象外と思われた val 属性の値までもがちゃっかり保存さていることがわかる。

localstore.rdf
  <RDF:Description RDF:about="chrome://.../content/test.xul#testLabel"
                   val="xxx" />

これはFirefox 本体側のバグと思われる。
Bug 231333 – Unexpected persist when the persisted attribute name contains another

document.persist

document.persist メソッドによって、 persist 属性を使わずに属性の永続化をすることも可能である。

test.xul
<label id="testLabel" value="Test" />
<button label="Write value"
        oncommand="document.getElementById('testLabel').setAttribute('value', 'xxx');
                   document.persist('testLabel', 'value');" />

persist 属性には前述のように部分一致した属性も永続化される問題があるため、こちらの方法を使ったほうが無難かもしれない。

TOP

Jetpack SDK 0.7 の Panel API

Jetpack SDK 0.7 では新たに Panel API が追加され、HTMLで記述されたGUIを表示可能なパネル型UIを追加するこが可能になりました。 Widget API で追加したボタン型UIと連携して、ボタンをクリックするとパネルを開くことも可能です。

基本的な使い方

Panel API を使うためには、まず panel モジュールをインポートします。なお、本記事のサンプルスクリプトでは exports.main の記載などを省略しています。

var panels = require("panel");

次に、 Panel コンストラクタを使ってパネル型UIを作ります。引数には、色々なプロパティを有するオブジェクトを渡します。下記の例では 幅200、高さ150ピクセルのパネル上に指定したURLをロードします。

var myPanel = panels.Panel({
    width : 200,
    height: 150,
    contentURL: "http://www.example.com/",
});

Panel コンストラクタで生成した panel オブジェクトの show メソッドを呼び出すと、パネルを開くことができます。メソッドの引数に何も指定しない場合は親ウィンドウの中央にパネルが開かれます。

myPanel.show();

widget API の Widget コンストラクタの引数オブジェクトの panel プロパティに panel オブジェクトをセットすることで、ボタン型UIをクリックしてパネルを開くこともできます。

const widgets = require("widget");
var button = widgets.Widget({
    label: "Test",
    image: "chrome://browser/skin/Secure24.png",
    panel: myPanel
});
widgets.add(button);

パネル型UIへ自前のHTMLをロードする

self API を使うと、パッケージの data フォルダ内に格納した自前のHTMLをパネル型UIへロードすることができます。

data/panel.html
<html>
<body>
    <p>Jetpack</p>
    <button onclick="say('Hello');">Say Hello</button>
    <button onclick="say('Bye');">Say Bye</button>
</body>
</html>
lib/main.js
const self = require("self");
var myPanel = panels.Panel({
    width : 200,
    height: 150,
    contentURL: self.data.url("panel.html"),
});

パネル型UIへコンテントスクリプトをロードする

引き続き、上記のパネルに配置されたボタンをクリックした際に、 Jetpack SDK の notifications API を使って通知を表示させるようにします。パネル型UIにロードされたHTMLからは直接 notifications API を呼び出すことはできず、2つの JavaScript コンテキスト間でメッセージを受け渡す、少し回りくどいやり方となります。

まず、 Panel コンストラクタの引数オブジェクトへ contentScriptURL プロパティを追加し、 self API を使って data フォルダ内に格納した panel.js のURLを配列で指定します。また、 contentScriptWhen プロパティに “ready” という値を指定することで、パネル内のHTMLロード完了時にスクリプトが実行されるようになります。

lib/main.js
const self = require("self");
var myPanel = panels.Panel({
    width : 200,
    height: 150,
    contentURL: self.data.url("panel.html"),
    contentScriptURL: [self.data.url("panel.js")],
    contentScriptWhen: "ready",
});

contentScriptURL プロパティによって読み込まれるスクリプト(コンテントスクリプト)は、HTMLに<script>タグで記述したスクリプトと異なる特殊な JavaScript コンテキストで実行されます。そのため、HTMLの window オブジェクトにアクセスするには明示的に window. とする必要があります。以下の例では window オブジェクト直下に say という関数を追加し、 button 要素の onclick 属性から呼び出し可能にしています。また、コンテントスクリプトでは特殊な変数 panel の sendMessage メソッドによって main.js 側の JavaScript コンテキストへ文字列(あるいはJSON文字列化可能なオブジェクトなど)を送ることができます。

data/panel.js
window.say = function(text) {
    panel.sendMessage(text);
};

panel オブジェクトの sendMessage で送られた文字列(あるいはJSON文字列化可能なオブジェクトなど)は、 main.js 側の JavaScript コンテキスト内の panel オブジェクトの onMessage コールバック関数によって受け取ることができます。以下の例では受け取った文字列を notifications API を使って通知として表示します。

lib/main.js
const self = require("self");
var myPanel = panels.Panel({
    width : 200,
    height: 150,
    contentURL: self.data.url("panel.html"),
    contentScriptURL: [self.data.url("panel.js")],
    contentScriptWhen: "ready",
    onMessage: function(message, callback) {
        require("notifications").notify({
            title: "Message from Panel",
            text: message
        });
    }
});

Panel

TOP

Jetpack SDK 0.7 の Notifications API

Jetpack SDK 0.7 では新たに Notifications API が追加され、Firefox のダウンロード完了通知などでお馴染みのスライド式の通知UIを表示することが可能になりました。 Notifications API を使うためには、まず notifcations モジュールをインポートします。

const notifications = require("notifications");

通知を表示するためには notify メソッドを呼び出します。引数には、以下のプロパティを有するオブジェクトを渡します。

プロパティ 概要
title
text
通知に表示する文字列。
iconURL 通知に表示する画像のURL。 self APIを使って自パッケージ内の data フォルダに格納した画像を指定することも可能。
data onClick の引数として渡される文字列。
onClick 通知をクリックした際の処理。引数に data プロパティの値が渡される。
notifications.notify({
    title: "Jetpack",
    text: "This is a notification.",
    iconURL: "chrome://browser/skin/Geolocation-64.png",
    data: "test",
    onClick: function(data) {
        console.log(data);
    },
});

notification

TOP

Jetpack SDK 0.7 の Clipboard API

Jetpack SDK 0.7 では新たに Clipboard API が追加され、クリップボードへのコピー、およびコピーされたデータの取得が可能になりました。 Clipboard API を使うためには、まず clipboard モジュールをインポートします。

const clipboard = require("clipboard");

クリップボードへのコピー

クリップボードへ文字列をコピーするには、 set メソッドを使います。

clipboard.set("hello");

set メソッドの第2引数へデータ形式を指定して、HTMLソースをコピーすることも可能です。現在対応しているデータ形式は text, html のみですが、将来的には画像やファイルなどの形式にも対応するようです。第2引数を省略した場合は text 形式となります。

clipboard.set("<em>hello</em>", "html");

クリップボード内のデータ取得

クリップボード内のデータを取得するには、 get メソッドを使います。

var data = clipboard.get();

第2引数へデータ形式を指定した場合、その形式のデータを取得できます。第2引数を省略した場合は text 形式となります。

var data = clipboard.get("html");

クリップボード内のデータ形式を調べる

現在クリップボードへコピーされているデータの形式を調べるには、 currentFlavors プロパティを使います。データ形式はひとつとは限りませんので、戻り値は配列となります。

var types = clipboard.currentFlavors;

TOP

nsITransactionManager を使ったトランザクション管理

拡張機能やXULアプリにて、ユーザの操作に対する「元に戻す」「やり直し」機能を実装する際、 nsITransactionManager が便利です。例えば Firefox 本体ではブックマークの追加/削除/移動などが nsITransactionManager によってトランザクション管理されています。

基本形

ユーザがボタンをクリックすると、金額が加算されて合計金額がテキストボックスに表示されるような、簡単なXULアプリを作ってみます。

bank.xul
<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      title="Bank" onload="init();">

    <script type="application/x-javascript" src="bank.js" />

    <textbox id="total" />
    <hbox>
        <button label="Deposit $1"   oncommand="deposit(1);" />
        <button label="Deposit $10"  oncommand="deposit(10);" />
        <button label="Deposit $100" oncommand="deposit(100);" />
    </hbox>

</page>

引き続き JavaScript で機能を実装します。合計金額を gTotalMoney というグローバル変数に保持し、合計金額をテキストボックスに表示するための updateUI 関数を作っておきます。 onload イベント発生時に呼び出される init 関数では、 updateUI で合計金額の初期値「$0」を表示します。
ボタンをクリックしたときに呼び出される deposit 関数では、引数に渡された金額を gTotalMoney に加算した後、 updateUI で合計金額の表示を更新します。

bank.js
var gTotalMoney = 0;

function init() {
    updateUI();
}

function updateUI() {
    document.getElementById("total").value = "$" + gTotalMoney.toString();
}

function deposit(aMoney) {
    gTotalMoney += aMoney;
    updateUI();
}

トランザクション管理

では、上記のアプリでボタンをクリックした後、その処理を元に戻す/やり直しできるようにします。

はじめに、一連のトランザクションを管理するための nsITransactionManager インスタンスであるグローバル変数 gTxnManager およびその初期化処理を追加します。

bank.js
var gTotalMoney = 0;
var gTxnManager;

function init() {
    gTxnManager = Components.classes["@mozilla.org/transactionmanager;1"].
                  createInstance(Components.interfaces.nsITransactionManager);
    updateUI();
}

トランザクション管理をして元に戻す/やり直し可能にするためには、個々の処理を nsITransaction インタフェースを実装したオブジェクトとして記述する必要があります。今回の場合は預金処理を表す DepositTxn クラスを以下のような設計で作ります。

メンバ 概要
_money コンストラクタの引数に渡された金額を内部的に保持するためのプロパティ。
doTransaction このトランザクションを実行する際に呼び出されるメソッド。
合計金額へ _money 分だけ加算する。
undoTransaction このトランザクションを元に戻す際に呼び出されるメソッド。
合計金額から _money 分だけ減算する。
redoTransaction このトランザクションをやり直しする際に呼び出されるメソッド。
通常は doTransaction と同じ処理を実行すればよい。
merge
isTransient
詳細不明。
function DepositTxn(aMoney) {
    this._money = aMoney;
}

DepositTxn.prototype = {
    doTransaction  : function() { gTotalMoney += this._money; },
    undoTransaction: function() { gTotalMoney -= this._money; },
    redoTransaction: function() this.doTransaction(),
    merge: function() false,
    get isTransient() false,
};

deposit 関数を書き換えて、合計金額を直接変更する代わりに、預金処理トランザクションクラスのインスタンスを生成して gTxnManagerdoTransaction メソッドへ渡します。こうすることで、内部的に DepositTxn インスタンスの doTransaction が呼び出されて合計金額への加算が行われ、なおかつその処理が元に戻す処理のスタックへ追加され、必要に応じて元に戻すことが可能となります。

function deposit(aMoney) {
    var txn = new DepositTxn(aMoney);
    gTxnManager.doTransaction(txn);
    updateUI();
}

次に、アプリのUIへ元に戻す/やり直しするためのボタンを追加します。

bank.xul
    <hbox>
        <button id="undoButton" label="Undo" oncommand="undo();" />
        <button id="redoButton" label="Redo" oncommand="redo();" />
    </hbox>

それぞれのボタン押下時の処理として、 nsITransactionManager の undoTransaction および redoTransaction メソッドを呼び出します。すると、元に戻す/やり直し処理のスタックの最後尾にあるトランザクション(nsITransaction インスタンス)の undoTransaction および redoTransaction メソッドが実行されます。その後、合計金額の表示を更新します。

bank.js
function undo() {
    gTxnManager.undoTransaction();
    updateUI();
}

function redo() {
    gTxnManager.redoTransaction();
    updateUI();
}

バッチ処理

例えば複数のブックマークを選択して削除した後に元に戻すと、複数のブックマークが一括して復元されます。このように、一連のトランザクションをバッチ処理として実行する場合の元に戻す/やり直しを実装します。

はじめに、アプリのUIへ複数の金額を一括して預金するためのボタンを追加します。

bank.xul
        <button label="Deposit $1+$10+$100" oncommand="depositSet([1,10,100]);" />

depositSet 関数は引数に渡された金額の配列すべてについて合計金額へ加算します。このとき、 nsITransactionManager の beginBatch メソッドを呼んでから doTransaction で個々の金額についての預金処理を実行し、最後に endBatch を呼ぶようにします。これにより、一連の預金処理がグループ化され、元に戻す際に一括して元に戻す処理が実行されます。

bank.js
function depositSet(aMoneys) {
    gTxnManager.beginBatch();
    aMoneys.forEach(function(money) {
        var txn = new DepositTxn(money);
        gTxnManager.doTransaction(txn);
    });
    gTxnManager.endBatch();
    updateUI();
}

元に戻す/やり直しスタックのリセット

nsITransactionManager の clear メソッドによって、元に戻す/やり直しスタックをいったんリセットすることができます。このメソッドを使って預金処理を確定させるボタンを作ってみます。

bank.xul
        <button id="completeButton" label="Complete" oncommand="complete();" />
bank.js
function complete() {
    gTxnManager.clear();
}

元に戻す/やり直しスタック内の個数

nsITransactionManager の numberOfUndoItems, numberOfRedoItems プロパティで元に戻す/やり直しスタック内の個数を調べることができます。これを使って元に戻す/やり直し可能なときだけボタンを押下可能にするようにしてみます。

bank.js
function updateUI() {
    document.getElementById("total").value = "$" + gTotalMoney.toString();
    var canUndo = gTxnManager.numberOfUndoItems > 0;
    var canRedo = gTxnManager.numberOfRedoItems > 0;
    function setDisabled(id, disabled) {
        var elt = document.getElementById(id);
        if (disabled)
            elt.setAttribute("disabled", "true");
        else
            elt.removeAttribute("disabled");
    }
    setDisabled("undoButton", !canUndo);
    setDisabled("redoButton", !canRedo);
    setDisabled("completeButton", !canUndo && !canRedo);
}

トランザクションの監視

nsITransactionManager の AddListener に nsITransactionListener インタフェースを実装したオブジェクトを渡して、トランザクションを監視することができます。 RemoveListener で監視を終了するのを忘れずに。

bank.xul
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      title="Bank" onload="init();" onunload="uninit();">
bank.js
function init() {
    gTxnManager = Components.classes["@mozilla.org/transactionmanager;1"].
                  createInstance(Components.interfaces.nsITransactionManager);
    gTxnManager.AddListener(gTxnListener);
    updateUI();
}

function uninit() {
    gTxnManager.RemoveListener(gTxnListener);
}

nsITransactionListener は多くのメソッドを持ち、元に戻す/やり直し処理の直前/直後などにコールバック処理を設定することができます。今回は元に戻す処理の直前に確認のダイアログを表示し、「キャンセル」ボタンを押下することで中止できるようにします。以下のように willUndo メソッドで true を返すと、処理を中止することができます。

var gTxnListener = {
    willDo: function() {},
    didDo: function() {},
    willUndo: function(aMgr, aTxn) {
        if (!window.confirm("Would you like to undo?"))
            return true;
    },
    didUndo: function() {},
    willRedo: function() {},
    didRedo: function() {},
    willBeginBatch: function() {},
    didBeginBatch: function() {},
    willEndBatch: function() {},
    didEndBatch: function() {},
    willMerge: function() {},
    didMerge: function() {},
};

TOP

while ループ条件中の in 演算子の不思議な挙動

JavaScript で単純なキーバリュー型のオブジェクトから、新しい一意のキーを生成する関数を作った(HTML にしたソースコードはこちら)。

function func() {
    var obj = {1:1, 2:2, 3:3};
    var i = 0;
    do { i++; } while (i in obj);
    return i;
}
alert(func());
alert(func());
alert(func());

これを Firefox 3.6 で実行すると、なぜか結果が 4, 4, 2 だったり 4, 2, 2 だったりと毎回結果が変わる不思議な動作となる。 JavaScript のJIT機能を無効にした場合や、IEなどの他ブラウザでは想定どおり 4, 4, 4 となる。不思議なことに while 文の条件を while (i in obj === true) にした場合も 4, 4, 4 となる。

while ループの条件中でこういう in の使い方をするときは以下のいずれかの方式にしたほうが安全っぽい。

while (obj[i] !== undefined)
while (obj.hasOwnProperty(i))

TOP

Jetpack SDK 0.5 の Tabs API

Jetpack SDK 0.5 で追加された Tabs API を使用すると、指定したURLを新しいタブやウィンドウで開いたり、各タブ内に表示されたページのDOMへアクセスしたり、タブの開閉やページの読み込みなどのイベントに対してコールバック処理を追加したりすることが可能です。

Tabs API を使用するには、はじめに require 関数でモジュールをインポートします。

var tabs = require("tabs");

タブを開く

Tabs API の open メソッドで、指定したURLを新しいタブやウィンドウで開くことが可能です。似た機能を持ったAPIとして、 tab-browser APIaddTab メソッドもありますが、 Tabs API の open メソッドを使用することが推奨されています。

open メソッドの引数に直接URLを指定した場合、新しいタブへ開くと同時にフォーカスします。

tabs.open("http://www.example.com/");

一方、引数を下記のようなオブジェクト形式とした場合、指定したURLを新しいバックグラウンドのタブで開きます。

tabs.open({
    url: "http://www.example.com/",
    inBackground: true
});

各タブの情報を取得する・タブ内のページのDOMへアクセスする

Tabs API の activeTab プロパティによって現在のタブ(Tab オブジェクト)を取得したり、 Tabs API のオブジェクト自体を for ~ in ループで回すことで全ウィンドウ内の全タブ(Tab オブジェクト)を列挙したりすることが可能です。 Tab オブジェクトは、 title プロパティでタブのタイトルやURLを取得したり、 contentDocument プロパティでタブ内に表示されたページのDOMへアクセスしたりすることが可能です。

下記は、現在のタブのタイトルとURLを取得し、現在のタブ内に表示されたページのDOMの window.alert メソッドを使って表示させる例です。

var tab = tabs.activeTab;
tab.contentWindow.alert(tab.title + "
" + tab.location);

Tab オブジェクトの面白い機能として、 thumbnail プロパティでタブ内に表示されたページのサムネイル画像を html:canvas 要素として取得することが可能です。下記は、全ウィンドウ内の全タブについてサムネイル画像を取得して data: URL へ変換し、それらを表示するHTMLを新しいウィンドウで開く例です。

var html = "";
for (var tab in tabs) {
    html += '<img src="' + tab.thumbnail.toDataURL() + '"/>';
}
tabs.open({
    url: "data:text/html,<html><body>" + html + "</body></html>";,
    inNewWindow: true
});

タブを閉じる・移動する

Tab オブジェクトには、タブを閉じる close、タブを同一ウィンドウ内の別の位置へ移動する move、タブへフォーカスする activate などのメソッドもあります。下記は、現在のタブを閉じる例です。

tabs.activeTab.close();

タブ関連イベントへのコールバック処理

onOpen(タブを開いたとき), onClose(タブを閉じたとき), onActivate(アクティブなタブが変化したとき), onReady(タブ内のページのDOMツリー構築時 = DOMContentLoaded イベント発生時), onPaint(タブ内のページで再描画発生時 = MozAfterPaint イベント発生時)などに対してコールバック処理を追加することができます。下記は、タブ内のページのDOMツリー構築完了時に、そのページのURLをコンソールへ表示する例です。

tabs.onReady = function(tab) {
    console.log("onReady: " + tab.location.href);
};

TOP

Jetpack SDK 0.5 の Unload API

Jetpack SDK の Unload API を使用すると、以下の例のように Firefox の終了や拡張機能の無効化などのシグナルを検知してコールバック処理を実行することが可能となります。

require("unload").when(function(reason) {
    switch (reason) {
        case "disable": 
            // 拡張機能が無効化される直前に実行する処理
            break;
        case "uninstall": 
            // 拡張機能が削除される直前に実行する処理
            break;
        case "shutdown": 
            // アプリケーション終了直前に実行する処理
            break;
    }
});

Firefox 3.6 以下ではアドオンマネージャで該当する拡張機能の「無効化」または「削除」ボタンを押下して Firefox を終了または再起動する直前にコールバック処理が実行されるのに対して、 Firefox 4.0b2 以上では「無効化」または「削除」ボタンを押下した直後にコールバック処理が実行されます。

TOP

Jetpack SDK 0.5 の Selection API

Jetpack SDK 0.5 で追加された Selection API を使用すると、選択範囲の取得や変更などが可能となります。

Selection API を使用するためには、はじめに require 関数でモジュールをインポートします。

var selection = require("selection");

選択範囲の取得

Selection API の text および html プロパティで選択範囲の文字列あるいはHTMLソースを取得・変更することが可能です。また、Ctrlキーを押下しながら複数の範囲を選択した場合、 contigious プロパティが false となります。この時、 Selection API のオブジェクト自体に対して for ~ in ループで個々のサブ選択範囲(Selection オブジェクト)を取得することが可能です。

以下は、選択範囲が複数ある場合を考慮し、全ての選択範囲のHTMLソースをコンソールへ出力する例です。

if (selection.contiguous) {
    // 選択範囲がひとつだけの場合
    console.log(selection.html);
}
else {
    // 複数の選択範囲がある場合
    for (var subsel in selection) {
        console.log(subsel.html);
    }
}

範囲を選択したときのコールバック処理

onSelect プロパティによって、範囲を選択したときのコールバック処理を追加することが可能です。以下は、範囲を選択した時に、その選択範囲を <span> タグで囲み、蛍光ペンで着色したような表示にする例です。

selection.onSelect = function() {
    selection.html = '<span style="background-color: yellow;">' + selection.html + '</span>';
};

TOP