B2G OS のアドオンを開発する

アドオンは Web ブラウザの世界ではよく知られたコンセプトであり、Firefox OS にもアドオンの仕組みが導入されることとなりました。アドオン 1 つでアプリ 1 つ、または複数、もしくは全アプリを拡張できます。この記事では自分で Firefox OS アドオンを作るためのガイドやヒント、Tips やその他の有益な情報を提供します。

脚注: Firefox OS アドオンは Chrome/Blink アドオンの基礎となるWebExtensionsの拡張機能モデルを利用しており、 互換性と機能の点においてアドオンの作成に多くの利点をもたらします。より詳細については鋭意執筆中の WebExtensions ドキュメントをご確認ください。

重要: アドオンは Firefox OS 2.5 以降でのみ使用可能であり、また拡張機能をデバッグするための最新の WebIDE サポートを得るために新しいビルドを端末にインストールする必要があります。Firefox OS アドオンの開発を始める前に、開発用端末が最新の利用可能なビルドにアップグレードされているかご確認ください。

アドオンの開発

アドオンはJavaScript, CSSやその他のファイルからなるアプリパッケージです。ただし、アドオンは独立したアプリとしては動作しません。アドオンのマニフェストはどのアプリに アドオンを適用するのかを定義するための特別な機能を含んでいます。アプリがアドオンのインストールされた Firefox OS 端末から起動されるとき、アドオンは manifest.json のフィールドで指定されたパターンにマッチするアプリに注入されます。

Firefox OS アドオンは、WebExtensions API を利用して開発する新しい Firefox アドオン群と同様の構文と構造になっています。WebExtension API は Chrome extensions のモデルをベースにしています。

簡単な例

Firefox OS アドオンの基本について説明するために、クリックすると消えるバナーをシステムアプリに追加する簡単なサンプルを紹介します。

firefox os screenshot showing add-on banner

これはとても基本的でありふれた例ですが、開発を始めるには充分なものです。Github 上のサンプルコードを確認のうえ、ローカル環境に clone もしくは ZIP をダウンロードし、WebIDE で自分の Firefox OS 端末にアドオンをインストールしてみてください。(Testing your add-on using WebIDE セクションもご覧ください。) アドオンはいずれ Firefox Marketplace で配布できるようになります。

Firefox OS アドオンはここに列挙したこと以上にたくさんのことができることに注意してください。WebExtensions のドキュメントにはこれから情報が充実していきます。

Firefox OS アドオンの構造

このセクションではサンプルアドオンの内容の各項目を順に説明していきます。ディレクトリ構成は以下のようになっています:

  • simple-addon/
    • manifest.json
    • update.webapp
    • css/
      • style.css
    • js/
      • index.js
    • icons/
      • 128.png
    • extension.zip

manifest.json

サンプルアドオンのディレクトリには、2 つのマニフェストファイルが含まれていることにお気付きでしょう。最初のものは manifest.json で、Chrome スタイルのマニフェスト構成をもち、CSS や JavaScript、アイコンファイルなどとともに extensions.zip パッケージのなかに配置されるものです。様々な指示を含めることができます (Chrome Manifest File Format をご覧ください) 。ただし、この例ではシンプルなものを利用することにしましょう:

{
  "manifest_version": 1,
  "name": "Add-on banner",
  "description": "Firefox OS add-on example",
  "version": "1.0",
  "author": "Chris Mills",
  "content_scripts": [{
    "matches": ["app://system.gaiamobile.org/index.html"],
    "css": ["css/style.css"],
    "js": ["js/index.js"]
  }],
  "icons": {
    "128": "/icons/128.png"
  }
}

多くのフィールドは説明するまでもありませんが、最後のいくつかを紹介します。

まずはじめに、content_scripts フィールドはアドオンが適用されるアプリに注入されるコードを参照します。CSS と JavaScript のファイルはそれぞれ cssjs フィールドでバスを指定します。matches フィールドはどのアプリにコードが注入されるのかを指定するパターンを含むものです。このパターンは多様な形式で書けます (Chrome Match Patterns をご覧ください)。いまはシンプルに app://system.gaiamobile.org/index.html と指定されており、システムアプリのみに限定されています。すべてのアプリに適用したい時には app://*/* を利用します。

脚注: 配列に複数の要素を含めることで、容易に複数のスクリプトやスタイルシートを参照することができます。例えば "css": ["css/style.css", "css/more.css"] のような形になります。

脚注: Firefox OS では、現在のところ Chrome の <all_urls> キーワードはサポートしていません。

マニフェストの最後の部分で、icons フィールドをインクルードしていますが、これについての詳細は次のセクションをご覧ください。

update.webapp

Note: You don't need the .webapp manifest if you are submitting add-ons to the Firefox Marketplace — you just need the .zip file.

update.webapp マニフェストは Firefox OS スタイルのマニフェストであり、基本的にはパッケージアプリと同様の簡易マニフェストです。 (Self-publishing packaged apps をご覧ください)

update.webapp ファイルは以下のようなものです:

{
  "name" : "Add-on banner",
  "description": "Firefox OS add-on example",
  "developer": { "name": "Chris Mills" },
  "package_path": "extension.zip",
  "icons": {
    "128": "/icons/128.png"
  }
}

やはり、これらのほとんどは説明するまでもありません。

おそらく、ここでいちばん重要なフィールドは package_path でしょう。これはエクステンションを含むパッケージのパスを指すものです。

ここで、manifest.json のときと同様に icons フィールドが含まれていることにお気付きでしょう。update.webapp は現時点でアイコンに関する情報をもつ必要がある唯一の場所ですが、仕様が変更になる可能性もあるため、いまのところ両方にフィールドを含めることを推奨しています。icons フィールドで指定されたアドオンのアイコンは Gaia Settings アプリ内で利用され、また、アドオンのホストが開始されれば Firefox Marketplace でも利用されます。

アイコンのインクルード

アイコンは少なくとも1つ含まれなければならず、マニフェストから参照しなければ、そのマニフェストは有効なものとはなりません。詳細については Manifest リファレンスのアイコンセクション をご覧ください。

CSS

重要: bug 1179536 によると、システムアプリ以外にスタイルシートを注入するのは動作しません。このアドオンはシステムアプリにのみ影響するため、チュートリアルは動作しますが、その他のアプリやwebページにスタイルを追加するには、JavaScriptを使ってスタイル変更する必要があります。

CSSについて、例ではなにも特殊なことはしていませんが、覚えておくべきことは、アドオンのクラス名やセレクタ名はそのアドオンを適用するアプリの CSS とコンフリクトさせるべきではないということです。

例えば、バナーのサンプルでは、バナーを fxos-banner というクラス名の <div> で囲いました。しかし本来はクラス名について、より一意性のあるコードとなるように考えるのが良いでしょう。

JavaScript

JavaScript についても、なにも特殊な機能は持たせていません (Github 上の JavaScript ソースをご覧ください)。JavaScript ソースは、manifest.json ファイルで指定された CSS に続いてアプリに注入されます。

脚注: アドオンのコードはアプリが起動し、マニフェストの指定とマッチする度に注入されます。また、アドオンが有効化される度に注入されます。アプリの起動時に アドオンが注入される場合、アプリの DOM を含めたすべてのものが初期化される前にアドオンの各ファイルが注入されます。アプリの初期化を待たずに即時注入するか、アプリの起動を待ってから注入す るか、起動処理タイミングの選択はアドオンの開発者に任されています。以下でより詳細について説明します。

その他の主な事柄は以下に記載されています。

window オブジェクト

アドオンはプロキシされた window オブジェクトを共有します。結果として、アドオンから window オブジェクトに書き込まれたものはアプリ側のコードでは利用できません。しかし、アプリ側で window オブジェクトにセットされたものはアドオンで利用できます。同様に、DOM も普段通りにアクセス可能です。

DOM へのコード注入

アプリの DOM を操作するのに JavaScript API を利用できます。

正しいタイミングでコードを注入する

アプリがロードされた後にアドオンが注入される場合に正しく処理されるように気をつけねばなりません。そのようなシナリオは、アプリがすでに起動している状態でアドオンが有効化されたときに起こります。その場合、DOMContentLoaded イベントがすでに発生してしまっているため、window.onload ハンドラは動作しません。

現在、この問題に対するうまい解決策はありません。一時的な対処として、DOMContentLoaded コールバックをセットする前に DOM がロードされているかどうかを確認することをおすすめします。このパターンはサンプルコードでも利用されています:

// If injecting into an app that was already running at the time
// the app was enabled, simply initialize it.
if (document.documentElement) {
  initialize();
}

// Otherwise, we need to wait for the DOM to be ready before
// starting initialization since add-ons are usually (always?)
// injected *before* `document.documentElement` is defined.
else {
  window.addEventListener('DOMContentLoaded', initialize);
}

function initialize() {
  // ...
}

複数の注入の防止

最後に、一つのアプリインスタンスに対して、何度もアドオンが注入されてしまうことを防ぐために、アドオンの初期化処理が既に終わっているか確認すると良いでしょう。以下に例を示します:

function initialize() {
  if (document.querySelector('.fxos-banner')) {
    // Already injected, abort.
    return;
  } else {
    var body = document.querySelector('body');
    var fxosBanner = document.createElement('div');
    fxosBanner.classList.add('fxos-banner');
    var bannerText = document.createElement('p');
    var closeBtn = document.createElement('button');

    fxosBanner.appendChild(bannerText);
    fxosBanner.appendChild(closeBtn);
    body.appendChild(fxosBanner);

    closeBtn.textContent = 'X';
    bannerText.textContent = 'Wow, you have an extension installed!';

    closeBtn.onclick = function() {
      fxosBanner.parentNode.removeChild(fxosBanner);
    }
  }
}

ここでは、バナーがすでに存在するかを確認するために if (document.querySelector('.fxos-banner')) を利用しています。もしそうであれば、関数から抜け出します。そうでなければ、querySelector() メソッドは null を返し、バナーを作成するコードブロックが実行されます。

アドオンのアプリマネジメント関数

すべての AppsMgmt の関数はアプリで動作するのと同じようにアドオン上でも動作します。後者については webapps-manager パーミッションがマニフェストで指定されている特権アプリに注入されたアドオンでのみ利用可能であることに注意してください。

これらの関数に加えて、アドオンが有効化、無効化される際のコールバックとして onenabledstatechange が用意されています。このイベントはすべてのアドオンに対して発火するため、初期化やクリーンアップの処理の前にどのアドオンが有効化・無効化されたのかを確認する必要があるでしょう

navigator.mozApps.mgmt.addEventListener('enabledstatechange', function(event) {
  var app = event.application;
  if (app.manifestURL === 'https://origin.of.manifest/manifest.webapp') {
    var wasEnabled = app.enabled;
    // do something with this information
  }
});

重要bug 1214155 にあるように、navigator.mozApps.mgmt.onenabledstatechange = function() {...} を通じて有効なステートリスナーを追加できません。つまり上で述べている addEventListener を使わないといけません、

extension.zip

脚注: extension.zip ファイルは、どのようにシステムが動作するのかといった説明のためにデモ用のリポジトリの中に配置されています。実際には、 Zip ファイルをディレクトリの中に含める必要はなく、WebIDE がアドオンをインストールする際に自動生成します。Firefox Marketplace がアドオンの掲載を始める際にも、同様にする予定となっています。

extension.zip アーカイブはエクステンションのためのコードを含み、それらは update.webapppackage_path フィールドで指定されています。このため、Gecko がインストールするべきコードを見つけることができるのです。アーカイブのなかは以下のようになっています:

  • css/
    • style.css
  • js/
    • index.js
  • icons/
    • 128.png
  • manifest.json

このように manifest.json ファイルはアーカイブのなかに配置され、どのファイルをどのアプリに注入するのかを参照するために提供されます。

WebIDE を利用したアドオンのテスト

Mozilla の WebIDE ツールはデスクトップ版の Firefox ではじめから利用可能です。これを利用してアドオンを端末にインストールするには、以下のステップに従います:

  1. Firefox 43 かそれ以降のバージョンの Firefox がインストールされているか確認してください (執筆時点では Nightly バージョンが必要でした)。WebIDE でのアドオン開発はこのバージョン以降でのみサポートされています。
  2. ブラウザを開き、WebIDE ボタンを押下する、もしくはメニューから Tools > Web Developer > WebIDE を選択して WebIDE ツールを開きます。
  3. 端末のリモートデバッグが有効になっているかを確認します。(Settings アプリ > Developer > "Debugging via USB" の項目で "ADB and DevTools" を選択します。)
  4. USB ケーブルを利用し端末を PC に接続します。同時に2台以上の端末が接続されていないようにしてください。
  5. WebIDE の UI 上で、Select Runtime オプションを押下し、USB Devices に列挙されている端末のなかから接続された端末を選択します。
  6. 現時点では、Allow USB debugging connection? というプロンプトが端末上に表示されます。Allow を選択してください。(訳注: 開発者向けビルドの場合プロンプト無しでインストール可能になっている場合もあります)
  7. Open App オプションから Open Packaged App... を選択します。
  8. ファイルの選択画面で update.webapp マニフェストファイルを含むディレクトリまで遷移し、Open を押下します。
  9. 警告もエラーも報告されなければ、"Play" ボタンを押下してアドオンを端末にインストールすることができます。(Install and Run)
  10. 動作しているアドオンを確認するには、Settings アプリ > Add-ons > Add-on example > 上部のチェックボックスにチェックしてアドオンを有効化します。

アドオンをデバッグする

Please note due to バグ 1185464 にある通り、現在 WebIDE を使ってデバッグすることはできないことに気をつけてください。

アドオンの設定

Settings アプリ > Add-ons から端末上のアドオンを管理することができます。この項目ではインストールされたアドオンが列挙され、それぞれをタップしてそれぞれの詳細について確認することができます。

firefox os screenshot showing a list of installed add-ons in the settings appinformation screen for an individual addon, with a list of apps this add-on affects, and controls to disable and delete the add-on

アドオンの有効化・無効化、そして削除

標準では、アドオンは Firefox Marketplace からインストールされると有効になります。WebIDE からインストールされた場合は、最初は無効化された状態になります。

各アドオンのページ (Settings app > Add-ons 以下にあります) の上部のチェックボックスから、手動でアドオンを有効化、無効化することができます。また、これを自動化したい場合は navigator.mozApps.mgmt.setEnabled() 関数を利用することが可能です (この Github 上の setEnabled() 利用例 をご覧ください)。

各アドオンのページにある Delete ボタンをタップすることでアドオンを削除することができます。

パーミッション

アドオンはホストアプリからすべてのパーミッションを継承します。アドオンのマニフェスト (update.webapp) でパーミッションを要求しても効果はなく、ホストアプリで利用できない API はアドオンにも提供されません。

ドキュメントのタグと貢献者

 このページの貢献者: chrisdavidmills, hamasaki, Uemmra3
 最終更新者: chrisdavidmills,