Now browsing the archives for the 'Jetpack' category.

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

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

Jetpack SDK 0.5 の Reuqest API

Jetpack SDK 0.5 で追加された Request API を使用すると、 XMLHttpRequest によってWebサーバとデータを送受信する処理をより簡単に実装できます。特に、レスポンスがJSONかXML形式であるようなWebサービスのAPIを利用する場合に重宝しそうです。

この記事では、 Request API を使い、Twitter でキーワード「Firefox 4」を含むツイートの検索結果をコンソールへ列挙する例を紹介します。

Twitter のAPI仕様

実装に入る前に、 Twitter の検索用APIの仕様を簡単に示しておきます。

リクエスト

キーワード xxx で検索する場合、以下のようなURLへGETメソッドで送信する。
http://search.twitter.com/search.json?q=xxx

レスポンス

キーワードにマッチしたツイートのデータが、下記のようなJSON形式で返る。

{
    "results": [
        { "text": "(1番目のツイートの内容)", "from_user": "(1番目のツイートの発言者)" ... },
        { "text": "(2番目のツイートの内容)", "from_user": "(2番目のツイートの発言者)" ... },
        { "text": "(3番目のツイートの内容)", "from_user": "(3番目のツイートの発言者)" ... },
        ...
    ]
}

実装

まず、 require 関数でモジュールをインポートします。

var requests = require("request");

次に、 Request APIの Request コンストラクタを用いて、 Request オブジェクトのインスタンスを生成します。コンストラクタの引数には、以下のプロパティを有するオブジェクトを渡します。

プロパティ 概要
url データ送信先のURL
onComplete データ受信時(XHRでいうところの readyState == 4)のコールバック処理
headers 必要に応じてリクエストヘッダ(User-Agent や Referer)をオブジェクト形式でセットする。例:
headers: { "User-Agent": "MyApp", Referer: "http://..." },
content 必要に応じてリクエストのパラメータをオブジェクト形式でセットする。
contentType 必要に応じてHTTPヘッダの Content-Type の値をセットする。
デフォルトでは application/x-www-form-urlencoded

生成した Request インスタンスの get メソッドを呼び出すと、GETメソッドでリクエストが送信されます。なお、 post メソッドを呼び出すと、POSTメソッドでリクエストが送信されます。

// Request インスタンスの生成
var request = requests.Request({
    url: "http://search.twitter.com/search.json",
    content: { q: "Firefox 4" },
    onComplete: function () {
        // ToDo
    }
});
// GETメソッドで送信
request.get();

Webサーバからのレスポンスが返って onComplete メソッドがコールバックされると、 Request インスタンスの response プロパティからレスポンス内容(Response オブジェクト)を取得することができるようになります。 Response オブジェクトは色々なプロパティを有し、 json プロパティや xml プロパティでレスポンスのボディ部をJSONあるいはXML形式でパースした結果を取得したり、 headers プロパティでレスポンスのヘッダ部の各フィールド値を取得したりすることが可能です。

今回はレスポンスのボディ部をJSON形式でパースし、前述のAPI仕様にあるとおり "results" プロパティでツイートのデータの配列を取得し、配列の各要素に対して "from_user", "text" プロパティで発言者と発言内容を取得します。

    onComplete: function () {
        var results = this.response.json.results;
        results.forEach(function(result) {
            console.log("user: " + result.from_user);
            console.log("text: " + result.text);
        });
    }

TOP

Jetpack SDK 0.4 の Page Worker API

HTTP(S)によりWebサーバとデータを送受信するには、 xhr API で XMLHttpRequest のインスタンスを生成する方法や、 Jetpack SDK 0.5 で追加された Request API を使用する方法があります。しかし、これらのAPIではWebサーバからのレスポンスがHTMLデータだった場合、特定のノードにある文字列を抽出したりする処理がやや困難となります。

一方、 Jetpack SDK 0.4 で追加された Page Worker API を用いると、不可視のフレーム(iframe 要素)内に指定したURLのHTMLドキュメントをロードし、パースされた結果をDOM操作することが可能となります。この記事では、 Page Worker API を用いて Wikipedia の Internet に関するページを不可視のフレームに読み込んで見出し(H2 > SPAN要素)を列挙する例を紹介します。

基本的な使い方

まず、 require 関数でモジュールをインポートします。

var pageWorker = require("page-worker");

Page Worker APIの Page コンストラクタを用いて、 Page オブジェクトのインスタンスを生成します。コンストラクタの引数には、以下のプロパティを有するオブジェクトを渡します。

プロパティ 概要
content 不可視のフレームにロードするURL、またはHTMLソース
onReady ロード完了時のコールバック処理
allow 不可視のフレーム内でのスクリプト実行を許可するかどうかなどを指定するためのオプション。
allow: { script: false } でスクリプトの実行を不許可に設定することが可能。

生成した Page インスタンスを add メソッドへ渡すと、不可視のフレーム内への読み込みが開始されます。

var page = pageWorker.Page({
    content: "http://en.wikipedia.org/wiki/Internet",
    onReady: function() {
        // ToDo
    }
});
pageWorker.add(page);

不可視のフレームへの読み込みが完了すると、 Page インスタンスの window および document プロパティ経由で不可視のフレーム内のDOMへアクセスすることができるようになります。以下の例では、不可視のフレームへの読み込み完了時、 HTMLのURLおよびタイトルを取得してコンソールへ表示し、さらにH2要素直下にあるSPAN要素の中身の文字列を列挙して表示します。

    onReady: function() {
        var url = this.window.location.href;
        var title = this.document.title;
        console.log(url + "
" + title);
        var elts = this.document.querySelectorAll("h2 > span");
        Array.forEach(elts, function(elt) {
            console.log(elt.textContent);
        });
        pageWorker.remove(this);
    }

なお、 onReady の最後で Page オブジェクトのインスタンス自身を PageWorker の remove メソッドへ渡すことで、 window および document プロパティが削除され、不可視のフレーム内でのページ読み込みに使用されたメモリが解放されます。

TOP

Jetpack SDK 0.4 の Simple Storage API

拡張機能の設定値のような少量のデータを保存する際には Preferences Service API を使用しますが、より多くのデータを永続的に(Firefox を終了しても保持されるように)保存するには、 Simple Storage API が便利です。

基本的な使い方

はじめに require 関数でライブラリをインポートします。

var simpleStorage = require("simple-storage");

もっとも単純な方法は、 storage プロパティへ直接値をセットする方式です。以下は “test” という文字列を保存した後、値を取り出す例です。

simpleStorage.storage = "test";
console.log(simpleStorage.storage);    // test

文字列だけでなく、数値、配列、オブジェクトなどをセットして保存可能です。

simpleStorage.storage = { "foo": 100, "bar": 200 };
console.log(simpleStorage.storage.foo);    // 100

上記は以下のように書くことも可能です。

simpleStorage.storage.foo = 100;
simpleStorage.storage.bar = 200;

データの保存先

Simple Storage APIを使って保存したデータは、プロファイルフォルダ配下の jetpack{パッケージマニフェストのID}simple-storagestore.json へJSON形式で保存されます。また、ファイルへの出力タイミングは Jetpack SDK 0.5 時点では Firefox 終了時のみですが、将来的には Firefox 使用中も最大で5分に1回出力される仕様となるようです。

データ保存可能サイズ

ひとつの拡張機能が保存できるデータサイズは5MBまでとなっています。 Simple Storage API の quotaUsage プロパティにて現在のデータ使用量(5MBを1.0とした割合)を調べたり、 onOverQuota プロパティにてデータ使用量が最大値を超過したときのコールバック処理を追加したりすることも可能です。ただし、 Jetpack SDK 0.5 時点では正常に動作しないようです。

console.log(simpleStorage.quotaUsage);
simpleStorage.onOverQuota = function() {
    // データ使用量が最大値を超過したときのコールバック処理
};

TOP

Jetpack SDK 0.4 の Widget と Private Browsing API 使用例

この記事では、 Jetpack 0.4 で新たに追加された4つの標準APIのうち、 WidgetPrivate Browsing を使用して実際に機能を開発する手順を解説します。なお、解説はメインプログラムの作成手順以降となります。 Jetpack SDK 自体の基本的な使い方については、 はじめての Jetpack SDK 0.2 を参照してください。

完成イメージ

プライベートブラウジングを開始/停止するためのボタンを有する Jetpack 拡張機能を、以下のように3段階に分けて実装します。

  • フェーズ1: Widget API を使ってUIを追加する
  • フェーズ2: Private Browsing API を使ってボタン押下時にプライベートブラウジングを開始/停止する
  • フェーズ3: Simple Dialog API (自作ライブラリ)を使ってプライベートブラウジング停止確認ダイアログを表示する

フェーズ1: Widget API を使ってUIを追加する

一般的な拡張機能では Firefox のメニューバー、ツールバー、ステータスバーなど様々な箇所にUIを追加することが可能で、それゆえUIの一貫性が保たれなくなる問題があります。 Jetpack では Widget API を使ってUIを追加することで、すべての拡張機能のUIの一貫性が保たれるようになります。現在のところ、 Widget API ではステータスバー上部に現れる拡張機能専用のぶっといバーにボタン型UIが追加可能ですが、このUI仕様は今後変更される予定です(参考)。

フェーズ1では、 Widget API を使って非常に単純なボタン型UIを追加します。まず、 main.js 内に以下のように記述して Widget API のモジュールを読み込みます。

const widgets = require("widget");

引き続き main.js へ以下のような main 関数を記述します。

exports.main = function(options, callbacks) {
    var button = widgets.Widget({
        label: "Start/Stop Private Browsing",
        image: "chrome://browser/skin/Privacy-48.png",
        onClick: function(event) {
            // ToDo
        }
    });
    widgets.add(button);
};

はじめに Widget API の Widget(options) コンストラクタを用いてボタンのインスタンスを生成します。コンストラクタの引数 options には下記の3つのプロパティを指定します。

プロパティ 詳細
label ボタンに対する説明の文言。画面に表示されないが、アクセシビリティの観点から指定が必要。
image ボタンのアイコン画像のURL。アイコン画像は24×24ピクセルにリサイズされる。
onClick ボタン押下時に実行するコールバック関数。

なお、 content プロパティに HTML をセットすることで、単純なボタンではなく凝ったUIを作ることも可能です(参考)。

生成したボタンのインスタンスは、 Widget API の add メソッドを用いて実際に追加します。ここまでで、ひとまず「cfx run -a firefox」コマンドで動作確認し、ボタンが表示されることを確認してください。なお、ボタンが配置されるバーは Ctrl+Shift+U で表示/非表示可能です。

フェーズ2: Private Browsing API を使ってボタン押下時にプライベートブラウジングを開始/停止する

フェーズ2では、 Private Browsing API を使ってボタン押下時にプライベートブラウジングを開始/停止できるようにします。はじめに main.js の冒頭部分に以下の内容を追加し、 Private Browsing API モジュールを読み込みます。

const privateBrowsing = require("private-browsing");

さらに、 Widget(options) コンストラクタの onClick オプションを以下のように修正します。

        onClick: function(event) {
            privateBrowsing.active = !privateBrowsing.active;
        }

Private Browsing API の active プロパティは真偽値で、現在プライベートブラウジング中かどうかを調べたり、値を変更することでプライベートブラウジングを開始/停止することができます。

フェーズ3: Simple Dialog API を使ってプライベートブラウジング停止確認ダイアログを表示する

Private Browsing API にはプライベートブラウジング開始/停止の直前/直後に呼び出すコールバック関数を追加/削除するためのメソッドが用意されています。フェーズ3では、プライベートブラウジング停止直前に確認ダイアログを表示する機能を実装します。

まず、 main.js の冒頭部分に以下の内容を追加し、自作ライブラリである Simple Dialog API モジュールを読み込みます。

const simpleDialog = require("simple-dialog");

Simple Dialog API の詳細は はじめての Jetpack SDK 0.2 の「自作ライブラリの作成」の項を参照してください。記載されているソースコードをコピペして main.js と同一フォルダ内に「simple-dialog.js」として格納してください。

次に、 main 関数内の最後に以下の内容を追加します。

    privateBrowsing.onBeforeStop = function(cancel) {
        var yes = simpleDialog.confirmYesNo("Do you want to stop private browsing?");
        if (!yes)
            cancel();
    };

Private Browsing API の onBeforeStop プロパティへ、プライベートブラウジング停止直前に呼び出されるコールバック関数をセットします。コールバック関数内では Simple Dialog API の confirmYesNo メソッドを使って確認メッセージを表示し、ユーザが「いいえ」ボタンを押したらコールバック関数の引数 cancel を実行し、停止処理をキャンセルします。

なお、 onBeforeStop.add(callback) メソッドを使ってコールバック関数 callback を追加したり、 onBeforeStop.add([callback1, callback2, ...]) のようにして複数のコールバック関数を一括して追加することも可能です。

動作確認

ボタンを押下することで、プライベートブラウジングが開始/停止されることを確認してください。また、停止直前に停止確認ダイアログが表示され、「いいえ」ボタンを押下するとプライベートブラウジングが停止されないことを確認してください。

TOP