[Firefox OS][勉強会]FxOSコードリーディングミートアップ#21でも話してきてました

 藪下@2課のガジェオタです。

 ぎょう(ry

 何やかんやで月一なんとか開催中のFxOSコードリーディングミートアップも#21で五回目ぐらいのセミナー回です。
 まあ主催者が話さなくてどうすんのってことで話してきました。


 名前がかわいらしい某.NET系で有名な勉強会で東京名古屋大阪とやってきた内容にそこを読むならこの辺にソースコードがあるよってところを追記しただけのものです。
 が、まあざっくりと全体を見ているのでなんとなく構造がつかめるんじゃないかなと思います。
 話が偏ってるのは藪下の知識の偏りに起因しているのでもっとちゃんと構造を話したいのが今後の課題。

 なんとなくFirefox OSの全体的な構造が知りたい方は見てみるとよろしいんじゃないかと思います。
 もちろん質問いただければお答えしますよ!

質問はこちら>http://www.gcg.bz/labo_blog/?page_id=945

[Firefox OS][勉強会]中国Firefox OS勉強会3rdで話してきてました

 藪下@2課のガジェオタです。

 業務多忙で割と身体的に弱ってきているのでそろそろリフレッシュしたいところに勉強会入れて自分を追いつめるスタイルの藪下です。

 さてもう一月近く経ってますが去る8/29に広島で中国Firefox OS勉強会3rdが開催されました。OWBも配られたりKDDIさんが一セッションやってたり地方開催でも何かと豪華だった中国3rdなんですが今回も登壇してきました。

 というわけで資料ペタリ。


 タイトル負けしてますがセンサについてつらつら20分話してきました。20分短い。。。
 藪下は50分に慣れてるので20分短くてつらかとでした。。。

 業務多忙で時間がなくて書きなぐった資料ですがセンサの使い方が知りたい方はざっと眺めてみてくださいな。個別のトピックは本ブログで書いてるのでそちらもよしなに。

[FxOS]FxOSコードリーディングミートアップ#16でLTしてきました

 藪下@2課のガジェオタです。

 FxOSコードリーディングミートアップ#16でLTやってきました。

 スライドをSlideshareに上げました。今回はContacts APIの中身とVibrator APIの中身とCodezineで記事を書いた顛末記ですよ。






[Firefox OS][イベント]中国Firefox OS勉強会 1stの資料と録画

 藪下@2課のガジェオタです。

 中国Firefox OS勉強会 1stで登壇しました。明けた今日まだ広島です。
 今回主催者と事前の見積もりで組み込み系のエンジニアがそれなりに来ると予想してました。どうも目論見通り需要を満たせたようなので一安心です。

 今回の資料はこちらです。

Firefox OSアーキテクチャクイックツアー
http://t.co/iPmeyz5nUC

 Ustreamで配信していたので録画もあります。

Ustream
http://t.co/0A4Iosu46E

 今回書いていることをそのまま読むより多くのことを話しているので録画で見てみてください。

[Firefox OS][WebAPI] Idle API読んでみた

 藪下@2課のガジェオタです。

 世間がFirefox 29とZTE Open CとFirefox OS v1.3で賑わっているなかどこふく風でAPIの実装を読んでみたお話です。

 前回addIdleObserverが見えないと書いたのですが、geckoのソースコードを覗いてみたら実装されてるっぽいです。

http://wiki.mozilla.org/WebAPI/IdleAPI

 ここにも書いてある通りaddIdleObserverはNavigatorの下にあるのでNavigatorを見てみます。
 まずはwebidlですね。

http://reading.fxos.org/source/xref/B2G/gecko/dom/webidl/Navigator.webidl#167

  /**
   * Navigator requests to add an idle observer to the existing window.
   */
  [Throws, Func="Navigator::HasIdleSupport"]
  void addIdleObserver(MozIdleObserver aIdleObserver);

  /**
   * Navigator requests to remove an idle observer from the existing window.
   */
  [Throws, Func="Navigator::HasIdleSupport"]
  void removeIdleObserver(MozIdleObserver aIdleObserver);

 [Func]属性がついてますね。Navigator::HasIdleSupportがtrueでないと見えないということを示しています。

 webidlについては以下のページに書かれています。全訳されてませんが[Func]属性の部分は訳しておきました。

 HasIdleSupportを見てみます。

http://reading.fxos.org/source/xref/B2G/gecko/dom/base/Navigator.cpp#2049

/* static */
bool
Navigator::HasIdleSupport(JSContext*  /* unused */, JSObject* aGlobal)
{
  if (!nsContentUtils::IsIdleObserverAPIEnabled()) {
    return false;
  }

  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
  return CheckPermission(win, "idle");
}

 nsContentUtils::IsIdleObserverAPIEnabled()とCheckPermission(win, “idle”)に依存してるのでそれぞれ見ていきます。

http://reading.fxos.org/source/xref/B2G/gecko/content/base/public/nsContentUtils.h#1837

  /**
   * Returns true if the idle observers API is enabled.
   */
  static bool IsIdleObserverAPIEnabled() { return sIsIdleObserverAPIEnabled; }

 sIsIdleObserverAPIEnabledが設定されているところを探します。

http://reading.fxos.org/source/xref/B2G/gecko/content/base/src/nsContentUtils.cpp#434

  sIsIdleObserverAPIEnabled = Preferences::GetBool("dom.idle-observers-api.enabled", true);

 ということで “dom.idle-observers-api.enabled” がtrueかどうかがnsContentUtils::IsIdleObserverAPIEnabled()の結果です。

 次にCheckPermission(win, “idle”)を追います。

http://reading.fxos.org/source/xref/B2G/gecko/dom/base/Navigator.cpp#1789

/* static */
bool
Navigator::CheckPermission(nsPIDOMWindow* aWindow, const char* aType)
{
  if (!aWindow) {
    return false;
  }

  nsCOMPtr<nsIPermissionManager> permMgr =
    services::GetPermissionManager();
  NS_ENSURE_TRUE(permMgr, false);

  uint32_t permission = nsIPermissionManager::DENY_ACTION;
  permMgr->TestPermissionFromWindow(aWindow, aType, &permission);
  return permission == nsIPermissionManager::ALLOW_ACTION;
}

 ということでパーミッションがALLOW_ACTIONかDENY_ACTIONかで見ているようなので設定しているところを探します。

http://reading.fxos.org/source/xref/B2G/gecko/dom/apps/src/PermissionsTable.jsm#209

                           "idle": {
                             app: DENY_ACTION,
                             privileged: DENY_ACTION,
                             certified: ALLOW_ACTION
                           },

 あれ、これなんか見たことあると思ったらMDNとmxrに書いてありました。

https://developer.mozilla.org/ja/docs/Web/Apps/App_permissions

関連情報
Firefox コード内では 許可設定一覧表 にすべての許可設定が定義されています。

https://mxr.mozilla.org/mozilla-central/source/dom/apps/src/PermissionsTable.jsm#36

 というわけでcertifiedのアプリにidleパーミッションをつけないといけないみたいです。
# MDNにもwikimoにも必要って書いてないし! ないし!

 サンプルコードを更新しました。

https://github.com/aoitan/gcg_labo/tree/master/api_reading/20140323_using_Idle_API

 マニフェストにpermissions属性を加えてます。もちろん許可するのはidleです。
 あとCSPに怒られたのでスクリプトをidle_test.jsに抜き出しました。

  "permissions": {
    "idle": {}
  },

 ビルドして動かしてみましょう。

Screenshot_from_2014-05-12 01_22_39

 とまあ動いたのですが、HasIdleSupportを見る限りパーミッションが設定されてないとやっぱりAPIが見えないはずなので、デスクトップとかfor Android的にはおかしいです。
# wikimoにはデスクトップとAndroidはデフォルト有効と書いてます。
https://wiki.mozilla.org/WebAPI
 これビルド対象合わせでパーミッションチェックしないといけない気がするのでちょっとパッチ書いてみようかと思います。

 今回Idle APIを追いかけましたが、実は書いた以上にソースコードを追いかけました。
 パーミッションマネージャを割と掘り下げたので機会があれば記事にします。お楽しみに。

[Firefox OS][FxOS] バックグラウンドタスクとアラームAPIを使ってみたお話その2

 藪下@2課のガジェオタです。

 関東Firefox OS勉強会5thで話したLTがもともと20分枠向けのアイデアだったのもあって駆け足で消化不良なことになってたりFxOS Gecko勉強会その2のセッションも資料作りからしてやっつけだったのでちゃんと書くシリーズの二回目です。
 資料はSlideShareに上がってます。




 全部ひとつのエントリに書いてしまうと長いので何度かにわけます。
 今回はアラームAPIの使い方についてです。

目次

アラームAPIの使い方

 アラームAPIを使う手順は大まかに以下のステップです。

  1. manifest.webappへの記述
  2. アラームの追加
  3. メッセージハンドラの登録

manifest.webappへの記述

 アラームを使うにはアラームを管理しているオブジェクトにアクセス出来なければなりません。
 そのためにmanifest.webappにパーミッションを設定する必要があります。

  "permissions": {
    "alarms": {}
  },

 更にアラームはメッセージとして飛んでくるのでメッセージの送信先もmanifest.webappに書いておきます。

  "messages": [
    { "alarm": "/index.html" }
  ]

アラームの追加

 navigatorにmozAlarmsというアラームを管理しているオブジェクトにアラームを追加します。
 mozAlarms.add()には

  • いつ発火するか
  • タイムゾーンを考慮するか
  • 発火したときに渡され るデータ

 を指定します。

var request = navigator.mozAlarms.add(
        new Date((new Date()) .getTime() + time),
        "ignoreTimezone",
        {message:"battery_log"});
request.onsuccess = function () {
    alarmId = this.result;
}

メッセージハンドラの登録

 メッセージとしてく るのでメッセージハ ンドラを登録します。
 今回はアラームなので “alarm”のハンドラで すよと指定しましょう。

navigator.mozSetMessageHandler( "alarm",
        function (mozAlarm) {
            なんかなんか
        }); 

 これで指定した発火時間になるとアラームのメッセージハンドラが呼ばれます。
 ハンドラの部分に次のアラームの設定とバッテリー状態の取得を書いておけばバッテリーログが取れますね。

 次回はバッテリーAPIの使い方です。

[Firefox OS][FxOS] バックグラウンドタスクとアラームAPIを使ってみたお話その1

 藪下@2課のガジェオタです。

 関東Firefox OS勉強会5thで話したLTがもともと20分枠向けのアイデアだったのもあって駆け足で消化不良なことになってたりFxOS Gecko勉強会その2のセッションも資料作りからしてやっつけだったのでちゃんと書くシリーズの一回目です。
 資料はSlideShareに上がってます。




 全部ひとつのエントリに書いてしまうと長いので何度かにわけます。
 今回はバックグラウンドタスクの作り方についてです。

目次

Firefox OSでのバックグラウンドタスクの作り方

 FxOSでのバックグラウンドタスクの作り方は簡単です。
 普通のアプリとの違いはmanifest.webappにアプリがバックグラウンドタスクとして動作することとバックグラウンドタスクとして動作するhtmlファイルのファイル名を書けばそのアプリがバックグラウンドタスクであることが指示できます。
 前者はpermissionsに、後者はbackground_pageに書きます。具体的には以下のとおり。

  "permissions": {
    "backgroundservice": {},
  },
  "background_page": "/index.html",

 仕様はここら辺に書かれています。

App manifest
https://developer.mozilla.org/id/docs/Apps/Manifest

App permissions
https://developer.mozilla.org/en-US/Apps/Developing/App_permissions

 App permissionsに書かれている通りbackfroundserviceはCertifiedな権限が必要です。これもマニフェストに書いておきましょう。

  "type": "certified",

 この辺りを追記したアプリを${B2G}/gaia/appsに入れて./build.sh gaiaすればバックグラウンド動作するアプリの出来上がりですよ。

 次回はアラームAPIについてです。

[Firefox OS][FxOS][Gaia] 日本語キーボードの出し方

北村です。

以前の記事で日本語キーボードの表示方法を書きましたが、最新のFirefox OSでまたまた変更されていたので、日本語キーボードを有効にする方法を書いてみます。

日本語キーボードの有効化
1.”B2G/gaia/Makefile”の「GAIA_KEYBOARD_LAYOUTS」に”jp-kanji”を追加
2.settingsアプリの「Keyboards」→「Selected keyboards」→「Add more keyboards」にて「Japanese-Kanji」にチェック

以上で日本語キーボードが有効になります。

今後も変更されるかもしれませんが。。。

[Firefox OS][FxOS][Gaia] 日本語キーボードの出し方と日本語IME用の辞書の作り方

 藪下@2課のガジェオタです。

 藪下はFxOSコードリーディングという会をやってるんですが、そこで水城珠洲という方が日本語キーボードが出せないというお話をされていたのでちょっと見てみました。日本語キーボードの出し方については水城さんが書いているので今回は日本語辞書の作り方を書いてみます。
 水城さんの記事はこちら。

Firefox OSでの日本語IMEを有効にする方法 – 水城珠洲の日記4th final
http://minashiro.net/computer/how-to-enable-japanese-ime-in-firefox-os/

 あとMDNの記事も置いときますね。

WebAPI/KeboardIME
https://wiki.mozilla.org/WebAPI/KeboardIME

日本語辞書の作り方

 で、今回の本題。Built-in日本語IMEで使う辞書の作り方です。
 Built-inな日本語IMEの辞書は${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/に入っています。
 ${B2G}/gaia/apps/keyboard/js/imes/jskanji/READMEに書いてある辞書の作り方を参考に今回はNAISTの辞書を使って日本語辞書を作ってみます。

IPA日本語辞書ファイルの入手

 README記載の方法では日本語辞書はIPAの日本語辞書から作ります。
 http://sourceforge.jp/projects/ipadic/このあたりからファイルを落としてきてdict以下に全展開しろと書いてあるんですが、今回はhttp://sourceforge.jp/projects/naist-jdic/releases/こちらを使います。

$ wget -v http://sourceforge.jp/projects/naist-jdic/downloads/31880/naist-jdic-0.4.3.tar.gz/

配置とビルド

 先ほどのNAIST Japanese Dictionaryの辞書ファイルを落としたら適当に展開してnaist-jdic.dic${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/ipadic/にコピー。

$ tar zxvf naist-jdic-0.4.3.tar.gz
$ mkdir -p ${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/ipadic/
$ cp naist-jdic-0.4.3/naist-jdic.dic ${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/ipadic/

 辞書ファイルが置けたら${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/makeするだけでJSONに変換してくれます。

$ cd ${B2G}/gaia/apps/keyboard/js/imes/jskanji/dict/
$ make

 後はいつものgaiaビルドです (辞書ファイルの転送だけでよさそうな気がしますが)。

$ cd ${B2G}
$ ./build.sh gaia

 転送も普通に行えばいいです。

$ ./flash.sh gaia

 できましたー。
 ChaSen辞書なので形式を合わせてごにょごにょしてあげればお好きな辞書が作って登録できますよー。

[Firefox OS][FxOS][Gecko] webidlでのAPIの追加

 藪下@2課のガジェオタです。

 しばらくブログサボってたんですが書くこと作っていかないとなーと思ってたところにいい感じの質問を貰ったのでやってみます。
 ご質問くださったリスナーの方はAPIを自前で作りたいとのことなのでAPIの追加について書きます。
 本ブログにはすでにAPIの追加について記事があるんですが、内容が少々古くなっているので最近のやり方について調べてみました。

目次

  • FxOSの大まかな構造
  • WebIDL
  • APIの追加
    • WebIDLの追加
    • バインディングエントリの追加
    • 実装の追加
  • テストアプリ
  • まとめ

FxOSの大まかな構造

 FxOSのAPIは大まかにいうと以下の構造になっています。

レイヤー 役割 使用言語
Gaia アプリ、UI HTML、JavaScript
Gecko ランタイム JavaScript、C/C++
Gonk OS、HAL C/C++

 今回の記事ではGecko層にAPIを追加するのが目的になります。
 リスナーさんからの質問ではデバイスの値をもっと精密に取りたいという話だったので次回以降の記事でGonk層も触ります。

WebIDL

 以前の記事ではAPIの追加にXPIDLを使っていたんですが、最近のFxOSではWebIDLというのを使います。
 WebIDLが何なのかざっくりと説明すると、ブラウザへの追加機能をJavaScriptなどとバインディングする方法をW3Cで定義したものです。JavaScriptなどへのAPIの提供の際にどのようなAPIを提供するのかの示し方を標準化したものとも言えます。
 WebIDLについてちゃんとした文書はW3Cを参照してください。

Web IDL(W3C):http://www.w3.org/TR/WebIDL/
Web IDL(日本語訳):http://www.hcn.zaq.ne.jp/___/WEB/WebIDL-ja.html

 FxOSでのちゃんとした情報についてはMDNを参照してください。

MDN(英語):https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings
MDN(日本語):https://developer.mozilla.org/ja/docs/Mozilla/WebIDL_bindings

 この記事を書いている時点で日本語の方は翻訳中でさわりの部分だけ日本語になっています。

APIの追加

 ここからはAPIの追加の仕方を説明します。まず大筋を説明すると

      WebIDLの追加
      バインディングエントリの追加
      実装の追加

 が必要になります。以下順番に説明します。

WebIDLの追加

 WebIDLはすべて gecko/dom/webidl 配下に置かれます。
 ここに新しくHelloAPI.webidlを置きます。中身は以下の通りです。

[Constructor()]
interface HelloAPI {
    DOMString hello();
};

 作ったHelloAPI.webidlをdom/webidl/WebIDL.mkのリストに追加します。webidl_filesに追記しましょう。

webidl_files = \
  AudioBuffer.webidl \
    :
  (snip)
    :
  XMLHttpRequestUpload.webidl \
  HelloAPI.webidl \
  $(NULL)

バインディングエントリの追加

 実際のコードとwebidlで定義した内容をバインディングするための設定を書きます。
 dom/bindings/Bindings.confに以下のような設定を追加します。詳しい内容はMDNを参照してもらうとして、ここでは実際のクラス名を指示しています。
 これに沿ってHelloAPIBinding.h/.cppが生成されます。

DOMInterfaces = {

'HelloAPI' : {
    'nativeType': 'HelloAPIImpl',
},

'mozAudioContext': {
    'nativeType': 'AudioContext',
    'implicitJSContext': [ 'createBuffer' ],
},
    :
  (snip)
    :
}

実装の追加

 今回はgecko/dom配下にhelloディレクトリを用意して実装ファイルを置いていきます。のつもりでしたがどうにもリンクが通せなかったのでずぼらしてHelloAPI.cppとHelloAPI.hをgecko/dom/baseに置きます。
 まずgecko/dom/baseにソースコードがあることを示さなければならないのでgecko/dom/base/Makefile.inにファイル名を追加します。

EXPORTS_NAMESPACES = mozilla/dom
EXPORTS_mozilla/dom = \
    :
  (snip)
    :
  HelloAPIImpl.h \
  $(NULL)

CPPSRCS =			\
    :
  (snip)
    :
	HelloAPIImpl.cpp \
	$(NULL)

 次にファイルの用意。HelloAPI.cppとHelloAPI.h、Makefile.inをgecko/dom/baseに配置します。
 詳しくはMDNを見てもらうとしてポイントだけ見ていきます。

HelloAPI.h

 まずはヘッダ全体。

#ifndef HELLO_API_H
#define HELLO_API_H

#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMWindow.h"
#include "mozilla/Attributes.h"
#include "nsAutoPtr.h"
#include "nsString.h"
#include "mozilla/ErrorResult.h"
#include <android/log.h>

namespace mozilla {
namespace dom {

class HelloAPIImpl : public nsISupports,
                     public nsWrapperCache
{
public:
    HelloAPIImpl(nsIDOMWindow* aWindow): mWindow(aWindow)
    {
        __android_log_print(ANDROID_LOG_INFO, "HelloAPIImpl", "HelloAPIImpl::HelloAPIImpl(nsIDOMWindow*) S");
        SetIsDOMBinding();
        __android_log_print(ANDROID_LOG_INFO, "HelloAPIImpl", "HelloAPIImpl::HelloAPIImpl(nsIDOMWindow*) E");
    }

    virtual ~HelloAPIImpl() {}

    static already_AddRefed<HelloAPIImpl>
    Constructor(nsISupports*& aGlobal, ErrorResult& rv)
    {
        return already_AddRefed<HelloAPIImpl>(new HelloAPIImpl(nullptr));
    }

    void Hello(nsString& ret)
    {
        // hello, world!を出力する
        nsString* tmp = new nsString((nsString::char_type*)L"hello, world!");
        ret = *tmp;
    }

    // WebIDL required
    JSObject* WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap);

    nsIDOMWindow* GetParentObject() const
    {
        return mWindow;
    }

    // nsISupports required
    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HelloAPIImpl)

    static bool HasSupport();
    void Shutdown();

private:
    nsCOMPtr<nsIDOMWindow> mWindow;
};

} // dom
} // mozilla

#endif // HELLO_API_H

 まずはclassの宣言ですが、追加するinterfaceに対応するclassはnsISupportsを継承することが多いようです。これはDOM APIに関係したオブジェクトのツリーを作ったり他のオブジェクトやメソッドに転送するための抽象化のようです。
 そしてオブジェクトを沢山作ったり素早く作ったりするのにパフォーマンスを気にするならnsWrapperCacheの継承が必要です。これは生成したオブジェクトをキャッシュする仕組みに組み込むためのものです。今回必要ないんですがMDNの説明で組み込んでいるのでとりあえず継承しときました。

class HelloAPIImpl : public nsISupports,
                     public nsWrapperCache

 少し下に飛びますがnsISupportsの提供するインターフェイスを実装する必要があるので用意していきます。
 nsISupportsについてはマクロが用意されているのでそれを使います。
 addRefやreleaseなどの基本的なヘルパ関数はこのあたりのマクロから展開されます。xxxBindings.h/cppでaddRefがないとか出てきたらこのマクロが展開されてません。マクロをpublicなメンバを各位置に書いているか、nsISupports.hを正しくインクルードしているかなど気にするといいでしょう。

    // nsISupports required
    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HelloAPIImpl)

 次にnsWrapperCacheが要求するインターフェイスを満たします。
 ずぼらしてヘッダだけでいけないかなと思っていた名残でいくつかの関数がクラス定義内に実装されてますが、WrapObjectは#includeの循環とかでヘッダ側に書けませんでした。実装はMDNにある通りxxxBindings::Wrapに転送するだけですがcpp側で説明します。

    // WebIDL required
    JSObject* WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap);

 DOMの親子関係を辿れるように親オブジェクトを取得する関数を用意します。これはnsISupportsやnsWrapperCacheに関係なく必要なようです。

    nsIDOMWindow* GetParentObject() const
    {
        return mWindow;
    }

 最後にwebidlで定義したinterfaceから転送されてくるメソッドを用意します。
 今回のHelloAPIではコンストラクタ属性を付けたのとhello()メソッドを宣言したのでConstructorとHelloの2メソッドが必要です。このあたりの命名規則がよくわかってないのですが、メソッド名の頭文字は大文字にされるようです。名前がおかしいとxxxBindings.cppのコンパイルでエラーが出るのでそれに合わせます。
 webidlでの宣言に対するcpp側のシグネチャについてはMDNを参照してください。ここでは使ったものだけ説明します。
 Constructorについてはグローバル (ルート) オブジェクト、Constructor属性で指定した型に対応する方の引数リスト、例外を返すための参照という決まりです。今回は無引数のConstructorなのでグローバル (ルート) オブジェクトとErrorResult&です。

    static already_AddRefed<HelloAPIImpl>
    Constructor(nsISupports*& aGlobal, ErrorResult& rv)
    {
        return already_AddRefed<HelloAPIImpl>(new HelloAPIImpl(nullptr));
    }

 hello()ではDOMStringを返却するよう宣言しています。cpp側では返り値ではなく引数経由で返します。DOMStringに相当するクラスがnsStringになるのでnsString&の引数を持つ関数を定義します。

    void Hello(nsString& ret)
    {
        // hello, world!を出力する
        nsString* tmp = new nsString((nsString::char_type*)L"hello, world!");
        ret = *tmp;
    }

HelloAPI.cpp

 次にソース全体。

#include "HelloAPIImpl.h"
#include "mozilla/dom/HelloAPIBinding.h"
#include "nsContentUtils.h"
#include <android/log.h>
#include "mozilla/Preferences.h"

namespace mozilla {
namespace dom {

NS_IMPL_CYCLE_COLLECTION_CLASS(HelloAPIImpl)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  NS_DROP_JS_OBJECTS(tmp, HelloAPIImpl);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(HelloAPIImpl)
NS_IMPL_CYCLE_COLLECTING_RELEASE(HelloAPIImpl)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HelloAPIImpl)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

JSObject* HelloAPIImpl::WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap)
{
    return HelloAPIBinding::Wrap(aCx, aScope, this, aTriedToWrap);
}

/* static */ bool HelloAPIImpl::HasSupport()
{
  return Preferences::GetBool("dom.helloApi.enabled", true);
}

void HelloAPIImpl::Shutdown()
{
}

} // dom
} // mozilla

 こちらでもnsISupportsとnsWrapperCacheの実装を行っていきます。
 これもマクロが用意されているので使っていきます。これはメンバ関数になるので名前空間で囲まれている場所に書きましょう。
 それぞれのマクロの説明は長くなるので省きますが、クラスに対しての細工とメンバ変数に対しての細工があるのでそれぞれ気を付けて書いていきます。基本的にクラス名が必要なものはメンバ関数の宣言部分。メンバ変数が必要なのはスマートポインタの管理コードなどです。
 nsWrapperCacheを継承する場合はNS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTIONあたりのマクロを書く必要があります。

NS_IMPL_CYCLE_COLLECTION_CLASS(HelloAPIImpl)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  NS_DROP_JS_OBJECTS(tmp, HelloAPIImpl);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HelloAPIImpl)
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(HelloAPIImpl)
NS_IMPL_CYCLE_COLLECTING_RELEASE(HelloAPIImpl)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HelloAPIImpl)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

 WrapObjectの実装です。前述したとおりxxxBindings::Wrapへの転送のみ行ってます。

JSObject* HelloAPIImpl::WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap)
{
    return HelloAPIBinding::Wrap(aCx, aScope, this, aTriedToWrap);
}

 他の関数はwindowオブジェクトやwindow.navigatorにぶら下げたい場合に使うので実装していますが、new Xxx()して利用する場合には必要ないので割愛します。

テストアプリ

 追加したインターフェイスが使用できるならnew HelloAPI()したオブジェクトのhello()を呼べばhello, world!という文字列が得られるはずです。
 ということでテストアプリ。

<!DOCUTYPE html>
<html>
<head>
    <meta charset=UTF-8>
    <title>hello test</title>
    <script>
        function log(elem, log) {
            elem.appendChild(document.createTextNode(log));
            elem.appendChild(document.createElement("br"));
        }

        function helloPrint() {
            var elem = document.getElementById("hoge");
            var helloApi = new HelloAPI(window);
            if (helloApi == null) {
                log(elem, "helloApi is null\n");
            } else {
                var hello = helloApi.hello();
                if (hello == null) {
                    log(elem, "hello is null\n");
                    for (var key in hello) {
                        log(elem, hello[key] + "\n");
                    }
                } else {
                    log(elem, hello + "\n");
                }
            }
        }
    </script>
</head>
<body onload="helloPrint()">
    <p id="hoge">
    </p>
</body>
</heml>

 結果のスクショ。

結果のスクショ

 あれ?

まとめ

 webidl書いてビルドエラー見ながらAPIの実体書いてnsISupportsとかnsWrapperCacheのマクロペタペタすればとりあえずはAPI追加できました。
 ちなみに藪下は結構長いことnewでエラーになるようってなってましたがコンストラクタ属性宣言するだけでした。コンストラクタ属性つけない場合はどっかのオブジェクトにぶら下げましょう (この辺マクロいっぱい出てきてちょっとよくわかりませんでした)。

 最後ちょっと (だいぶ?) ミスったとか結局ディレクトリ作ってソース入れてMakefile書いたときのリンク時に.o見つけてくれないとか未解決部分があるのでまたそのうち調べたいと思いますが、とりあえずちょっとAPI追加するだけならそんなに大変ではないようです。
#藪下はビルドの仕組み調べてさまよってましたが。。。

 近いうちにgonk層も触ってみようかと思っているのでその際にこのあたりももう一度触れます。コールバックメソッドを受け取るプロパティを持ったインターフェイスとかはまだちゃんと説明を読んでないのでそこら辺とgonk層の構造あたりをかけたらいいなと思います。