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オーバーレイが実質なくなったので、再起動不要が主流