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

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

 ぎょう(ry

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


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

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

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

[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][Gecko]おや? Promiseって?

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

 Gecko触って遊んでて気づいたんですが、Geckoで使ってるPromiseとcontentプロセスで使っているPromiseは別物みたいです。

Promise – MDN
https://developer.mozilla.org/ja/docs/Core_JavaScript_1.5_Reference/Global_Objects/Promise

 ここを見るとcatch使えますね。実際適当なHTMLとJSこさえたら使えました。
 でもGeckoいじってAPI作ってテスト書いてたらcatchは関数ちゃうでと怒られる。なんでだと思ったらgecko/toolkit/modules/Promise.jsmにcatchがいなかったんですね。ちなみにmasterのHEADだと怒られないです。遊んでた環境が古くてまだ実装されてなかったみたいです。

 でまあcatchが実装されてるとかされてないとかは割とどうでもよくて、Geckoでは実装されてないものがコンテンツにぶら下がってる方にはあったので別物だったのかーとなった次第です。
 ちなみにGecko側のにはPromise.defer()があるけどコンテンツのにはなかったりもしました。

 ざっくり見た感じGeckoでよく

let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;

 されてるものはgecko/toolkit/modules/Promise.jsmで実装されていて、コンテンツ側で使うものはgecko/dom/promiseにいるみたいです。

 まだ整理されてないだけかもしれませんが、こういうこともあるんですね。

[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][Gecko]デバッグ情報付きのビルド

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

 ちょっとFirefox OSのGeckoのソースコード読んだりしていてスタックトレース取りたいなとか思うところがあったのでデバッグ情報付きのビルドを試してみました。
 今日の目次。

      デバッグビルド設定
      ビルド
      ディスアセンブル

デバッグビルド設定

 FxOSのビルド設定には.userconfigファイルを使います。
 以下MDNの解説ページです。

Customization with the .userconfig file
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Customization_with_the_.userconfig_file
#日本語がないので誰か訳して!

 最初のほうに書いてある通りビルド環境のルートに.userconfigを置いてその中に設定を書いてねということです。
 デバッグビルドは以下のフラグです。

export B2G_DEBUG=1

 最適化を抑えたい場合のフラグもあります。今回はつけません。

export B2G_NOOPT=1

ビルド

 ビルドはいつものgeckoビルド。

./build.sh gecko

 お目当てはlibxul.soあたりなので探してサイズ見てみましょうかね。

$ find -name libxul.so -ls
3957733  17456 -rw-r--r--  1 root       yabushita   17871052  8月  6 04:01 ./backup-keon/system/b2g/libxul.so
2640914  37960 -rwxr-xr-x  1 yabushita  yabushita   38869944  6月 11 04:42 ./gaia/xulrunner-sdk/bin/libxul.so
2637188  44820 -rwxr-xr-x  1 yabushita  yabushita   45894424  6月 11 04:41 ./gaia/xulrunner-sdk/sdk/lib/libxul.so
2640866  44820 -rwxr-xr-x  1 yabushita  yabushita   45894424  6月 11 04:42 ./gaia/xulrunner-sdk/lib/libxul.so
2492722      0 lrwxrwxrwx  1 yabushita  yabushita         31  9月 18 19:56 ./objdir-gecko/dist/bin/libxul.so -> ../../toolkit/library/libxul.so
2658747      0 lrwxrwxrwx  1 yabushita  yabushita         34  9月 18 19:56 ./objdir-gecko/dist/sdk/lib/libxul.so -> ../../../toolkit/library/libxul.so
2658991  17556 -rwxrwxr-x  1 yabushita  yabushita   17975724  9月 18 19:57 ./objdir-gecko/dist/b2g/libxul.so
2492723      0 lrwxrwxrwx  1 yabushita  yabushita         31  9月 18 19:56 ./objdir-gecko/dist/lib/libxul.so -> ../../toolkit/library/libxul.so
2495101 466732 -rwxrwxr-x  1 yabushita  yabushita  477927944  9月 18 19:56 ./objdir-gecko/toolkit/library/libxul.so
2115069  17556 -rwxr-xr-x  1 yabushita  yabushita   17975724  9月 18 19:57 ./out/target/product/keon/system/b2g/libxul.so

 おや?
 dist配下というかoutに入った奴はデバッグシンボルついてなさそうですね。
 なんでかすら? ビルドの後にstripしちゃったりなんかしちゃったり?

ディスアセンブル

 とりあえずtoolkit配下のはでっかくなってるのでobjdumpしてみましょう。-Sスイッチで混合モードですね。

$ prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-objdump -S objdir-gecko/toolkit/library/libxul.so > ~/Documents/FxOS/libxul-debug.asm

 ソースコード出てたところを一部抜粋。インライン展開うぜえ。。。

NS_IMETHODIMP
nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement)
{
  2e113c:	b537      	push	{r0, r1, r2, r4, r5, lr}
  2e113e:	4604      	mov	r4, r0
  2e1140:	20b1      	movs	r0, #177	; 0xb1
  2e1142:	460d      	mov	r5, r1
  2e1144:	f000 ffd8 	bl	2e20f8 <_ZN14nsCacheService4LockEN7mozilla9Telemetry2IDE>
    nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT));
    if (!mCacheEntry)                 return NS_ERROR_NOT_AVAILABLE;
  2e1148:	6920      	ldr	r0, [r4, #16]
  2e114a:	4c08      	ldr	r4, [pc, #32]	; (2e116c <_ZN22nsCacheEntryDescriptor15SetCacheElementEP11nsISupports+0x30>)
  2e114c:	b140      	cbz	r0, 2e1160 <_ZN22nsCacheEntryDescriptor15SetCacheElementEP11nsISupports+0x24>

    bool IsDoomed()          { return (mFlags & eDoomedMask) != 0; }
    bool IsEntryDirty()      { return (mFlags & eEntryDirtyMask) != 0; }
    bool IsDataDirty()       { return (mFlags & eDataDirtyMask) != 0; }
    bool IsMetaDataDirty()   { return (mFlags & eMetaDataDirtyMask) != 0; }
    bool IsStreamData()      { return (mFlags & eStreamDataMask) != 0; }
  2e114e:	6a83      	ldr	r3, [r0, #40]	; 0x28
  2e1150:	04d9      	lsls	r1, r3, #19
    if (mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_STREAM;
  2e1152:	bf48      	it	mi
  2e1154:	4c06      	ldrmi	r4, [pc, #24]	; (2e1170 <_ZN22nsCacheEntryDescriptor15SetCacheElementEP11nsISupports+0x34>)
  2e1156:	d403      	bmi.n	2e1160 <_ZN22nsCacheEntryDescriptor15SetCacheElementEP11nsISupports+0x24>

    return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
  2e1158:	4629      	mov	r1, r5
  2e115a:	f000 ff59 	bl	2e2010 <_ZN14nsCacheService15SetCacheElementEP12nsCacheEntryP11nsISupports>
  2e115e:	4604      	mov	r4, r0
  2e1160:	a801      	add	r0, sp, #4
  2e1162:	f7ff f90b 	bl	2e037c <_ZN22nsCacheServiceAutoLockD1Ev>
}

 混合モードでディスアセンブルできましたー。
 あとはdistとかoutに入ってくれたらいいんですけどなんで入らぬの? 教えて偉い人!

#2013/11/19追記
 rm -rf objdir-geckoしてあげると多分うまく行きます。
 .mozconfigとかを調べてみたのでそのエントリはまた後日。

おまけの宣伝

 あ、藪下の個人的な活動ですがFxOSコードリーディングという会を始めてみました。
 Firefox OS勉強会で知り合った人たちとFxOSのソースコードを読んで意見交換してます。
 藪下の成果は本ブログに書いていくつもりです。

FxOSコードリーディング
https://www.facebook.com/groups/1408800662676489/

[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層の構造あたりをかけたらいいなと思います。