Android版Firefoxアドオン開発の基礎を学ぶ
デスクトップ版Firefox向けアドオン開発暦7年
※パソコンが高スペックならAndroidエミュレータでもいける?
		ソース編集
		 ▼
		インストーラ作成
		 ▼
		MicroSDカードへコピー
	
		MicroSDカードを装着
		 ▼
		file:///mnt/sdcard/ をFirefoxで開く
		 ▼
		インストール
	
		ソース編集
		 ▼
		インストーラ作成
		 ▼
		Dropboxで共有
	
		Web版DropboxをFirefoxで開く
		 ▼
		インストール
	
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>viewsource@xuldev.org</em:id>
    <em:type>2</em:type>
    <em:name>View Source</em:name>
    <em:version>0.1</em:version>
    <em:bootstrap>true</em:bootstrap>
    <em:description>Adds 'View Source' menu.</em:description>
    <em:creator>Gomita</em:creator>
    <em:targetApplication>
      <Description>
        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
        <em:minVersion>14.0</em:minVersion>
        <em:maxVersion>17.0a1</em:maxVersion>
      </Description>
    </em:targetApplication>
  </Description>
</RDF>
function install(data, reason) {
    // アドオンをインストール時に実行する処理
}
function uninstall(data, reason) {
    // アドオンを削除時に実行する処理
}
function startup(data, reason) {
    // アドオンを起動時(有効化時)に実行する処理
}
function shutdown(data, reason) {
    // アドオンを終了時(無効化時)に実行する処理
}
とりあえずServices.jsmをインポート
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Firefoxウィンドウの監視①
function startup(data, reason) {
    // アドオン起動時、すでに開いているウィンドウを取得
    var winEnum = Services.wm.getEnumerator("navigator:browser");
    
    while (winEnum.hasMoreElements()) {
        var win = winEnum.getNext().QueryInterface(Ci.nsIDOMWindow);
        if (win)
            // ウィンドウに対する処理
            loadIntoWindow(win);
    }
    
    // 今後開かれるウィンドウを監視
    Services.wm.addListener(windowListener);
}
Firefoxウィンドウの監視②
var windowListener = {
    onOpenWindow: function(aWindow) {
        // 新しいウィンドウが開かれたら…
        var win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
                  getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
        
        // UIReadyイベントを監視
        win.addEventListener("UIReady", function() {
            win.removeEventListener("UIReady", arguments.callee, false);
            if (win)
                // UIReadyイベント発生後、ウィンドウに対する処理
                loadIntoWindow(win);
        }, false);
    },
    onCloseWindow: function(aWindow) {},
    onWindowTitleChange: function(aWindow) {},
};
	Firefoxウィンドウの監視を解除
function shutdown(data, reason) {
    // Firefox自体の終了時は何もしなくていい
    if (reason == APP_SHUTDOWN)
        return;
    
    // 今後開かれるウィンドウへの監視を終了
    Services.wm.removeListener(windowListener);
    
    // 現在開かれているウィンドウに対する処理
    var winEnum = Services.wm.getEnumerator("navigator:browser");
    while (winEnum.hasMoreElements()) {
        var win = winEnum.getNext().QueryInterface(Ci.nsIDOMWindow);
        if (win)
            unloadFromWindow(win);
    }
}
Firefoxウィンドウに対するUI生成処理
var gMenuId;
function loadIntoWindow(aWindow) {
    // メニュー項目を追加
    gMenuId = aWindow.NativeWindow.menu.add("View Source", null, function() {
        viewSource(aWindow);
    });
}
	NativeWindow.menu.add … メニュー項目を追加するAPI
Firefoxウィンドウに対するUI削除処理
function unloadFromWindow(aWindow) {
    // メニュー項目を削除
    aWindow.NativeWindow.menu.remove(gMenuId);
}
	NativeWindow.menu.remove … メニュー項目を削除するAPI
ソースを表示する処理
function viewSource(aWindow) {
    // 現在のxul:browser要素を取得
    var browser = aWindow.BrowserApp.selectedBrowser;
    
    // 現在のURL
    var url = browser.currentURI.spec;
    
    // view-source:を付加したURLを新しいタブで開く
    aWindow.BrowserApp.addTab("view-source:" + url);
}
	BrowserApp.addTab … 新しいタブを開くAPI
デスクトップ版Firefox
モバイル版Firefox
<deck id="browsers" />
デスクトップ版Firefox
モバイル版Firefox - デスクトップ版よりも綺麗にモジュール化されている
アドオンからはタブブラウザ操作用APIとして利用
BrowserApp.tabs // Tabオブジェクトの配列
BrowserApp.selectedTab // 現在選択しているTabオブジェクト
BrowserApp.selectedBrowser // 現在選択しているxul:browser要素
BrowserApp.addTab() // タブを開く
BrowserApp.closeTab() // タブを閉じる
BrowserApp.selectTab() // タブを選択する
BrowserApp.deck // xul:deck要素
など
Native UIの機能を呼び出すAPI
NativeWindow.menu.add() // メニュー項目を追加
NativeWindow.contextmenus.add() // コンテキストメニュー項目を追加
NativeWindow.toast.show() // トースト型通知を表示
NativeWindow.doorhanger.show() // ドアハンガー型通知を表示
など
Javaで実装された機能を呼び出す、より低レベルなAPI
内部的にはnsIAndroidBridge::handleGeckoMessage
sendMessageToJava({
    gecko: { type: "ToggleChrome:Show" }
})
	sendMessageToJava({
   gecko: { type: "Tab:Select", tabID: aTab.id }
})
	など
chrome.manifest と locale フォルダを追加
localeパッケージを追加
locale viewsource en-US locale/en-US/ locale viewsource ja locale/ja/
・locale/en-US/main.properties
menu=View Source
・locale/ja/main.properties
menu=ソースを表示
main.properties のchrome:URL
chrome://viewsource/locale/main.properties
ローカライズされた文字列を取得する関数
var gStringBundle;
function getString(aName) {
    if (!gStringBundle) {
        var uri = "chrome://viewsource/locale/main.properties";
        gStringBundle = Services.strings.createBundle(uri);
    }
    return gStringBundle.GetStringFromName(aName);
}
function loadIntoWindow(aWindow) {
    gMenuId = aWindow.NativeWindow.menu.add("View Source", null, function() {
        viewSource(aWindow);
    });
}
	▼
function loadIntoWindow(aWindow) {
    gMenuId = aWindow.NativeWindow.menu.add(getString("menu"), null, function() {
        viewSource(aWindow);
    });
}
chrome://global/content/console.xul
function log(aMsg) {
    Services.console.logStringMessage(aMsg);
}
	
function alert(aMsg) {
    Services.prompt.alert(null, "My Add-on", aMsg);
}
	
		ツールバーがNative UIなので不可
		将来的に NativeWindow.toolbar.add のようなAPIが追加されるかも?
	
		オーバーレイして独自ツールバーを追加するなど、一応可能
		ただでさえ画面が狭いので、現実的でない
	
		chrome.manifestでcontentパッケージを追加して実現可能
		aboout:addons やabout:downloads のようにXHTMLによる構築が主流?
	
		
	
		
	
		可能
		nsILocalFile のインスタンスを作るか、FileUtils.jsm モジュールを利用
	
		<em:bootstrap>true</em:bootstrap> を削除すれば可能
		XULオーバーレイが実質なくなったので、再起動不要が主流