Jetpack Library 事始め

2,841 views
2,793 views

Published on

Mozilla 勉強会@東京 5th の発表資料

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,841
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
8
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Jetpack Library 事始め

  1. 1. Jetpack Library 事始め 2011/01/16 あすかぜ(海津智宏) @asukaze55
  2. 2. 自己紹介 http://www.asukaze.net/ RemoveTabs,Tablocという、小さな拡張を公開しています。 Add-on SDK(Jetpack SDK) の開発状況を追いかけて、メモをまとめています。 Mozilla とは関係ないですが、Cassava Editor という CSV エディタを公開しています。 2010/12/01 に転職しました。転職先の会社は、Mozilla とは商売敵かもしれません :) ヒキコモリ体質のため、Mozilla の勉強会等に参加するのは初めてです。皆様よろしくお願いします。
  3. 3. Add-on SDK (Jetpack SDK) とは? 現在開発中の、新しい拡張開発キット。 XULなどの知識を必要とせず、HTML, JavaScript, CSSの知識だけで拡張が開発できるかも。 API は Firefox のバージョンに非依存。拡張を再パッケージングするだけで Firefox の新しいバージョンに対応できるはず。 開発した拡張は再起動不要でインストール/アンインストール可能。 サードパーティが自由に API を追加できる。
  4. 4. History Jetpack Prototype 開発終了 Jetpack SDK Add-on SDK Jetpack Reboot 0.1~0.9 β1 feature-stable 2009/05 2010/03 2010/12
  5. 5. PrototypeとSDK Jetpack Jetpack Jetpack Jetpack Jetpack Jetpack API (Jetpack Prototype 拡張) Core Core Core Firefox Firefox Jetpack Prototype Add-on SDK
  6. 6. Add-on SDK 1.0b1 の API(addon-kit) ページコンテンツの変更 ウィンドウ操作・タブ操作 ウィジェット表示・パネル表示 コンテキストメニュー表示 選択範囲の取得 HTTP リクエスト送信・DOM 操作 クリップボードの取得・更新 アラートメッセージ表示 プライベートブラウジングの開始・終了 ストレージへの情報保存 以上!
  7. 7. Add-on SDK 1.0b1 の API(addon-kit) 標準 API でGreaseMonkey+α のことはできる。 が、XUL ベースの拡張に比べると、できることは限られている。 ブラウザ自体のUIを拡張できるのはウィジェットとコンテキストメニューのみ 低レベル API を使って自作ライブラリを作れば、なんでもできる!
  8. 8. Add-on SDK の低レベル API(api-utils) Components オブジェクトへのアクセス {Cc, Ci, Cu, Cr, Cm, components} = require(“chrome”); tabbrowser要素やwindow要素へのアクセス require(“tab-browser”) require(“window-utils”) API 提供用のヘルパ関数 require(“unload”).ensure() require(“api-utils”).publicConstructor() require(“errors”). catchAndLog()
  9. 9. できることの例 ブラウザ内の任意の場所に XUL 要素を追加できる。 ブラウザ内の任意の場所にイベントリスナを仕掛けられる。
  10. 10. 考慮すべきこと 拡張が再起動不要となるようにする Firefox, SDK のバージョンに非依存な API を提供する addon-kit の API と似た使い方を提供する construct/destroy モデル EventEmitterモデル エラー発生時のスタックトレース出力 その他
  11. 11. 1) 再起動不要 拡張が無効化/アンインストールされたときに状態を元に戻すのはライブラリの責任。 ブラウザに対する変更を全て覚えておき、終了時に元に戻す。 api-utils内の unload モジュールを使うと、終了時の処理を登録できる。 イベントの伝播は bootstrap.js -> harness -> cuddlefish-> unload という流れ。
  12. 12. unload モジュール function Foo() { // 変更処理 require("unload").ensure(this); } Foo.prototype = { unload: function(reason) { // 復帰処理 } }; function bar() { // 変更処理 require("unload").when( function(reason){ // 復帰処理 }); } ensure 関数にオブジェクトを登録する。 終了時には、オブジェクトごとに 1 回ずつ unload 関数が実行される。 when関数にコールバック関数を登録する。 複数回 when を呼び出すと、その回数分 処理が実行される。
  13. 13. MyExtension 2.0 MyExtension 1.0 変えちゃダメ MyLibrary 2.0 2) バージョン非依存 MyLibrary 1.0 変わらないはず addon-kit 2.x addon-kit 1.0b1 変わるかも api-utils 2.x api-utils 1.0b1 変わるかも Firefox 5.x Firefox 4.0
  14. 14. 2) バージョン非依存 Firefox の内部構造に依存しない、汎用的な API を提供する 今後の Firefox の変化を先取りし、あらかじめ備えておく E10S 対応!!
  15. 15. E10S (Electrolysis) とは? firefox.exe 「プロセス分離」のコードネーム。 今後、ブラウザ本体と Web ページのコンテンツと Jetpack 拡張は、それぞれ別プロセスで動作するようになる。 content process ←メッセージ通信-> jetpack process × 直接アクセスはできない
  16. 16. コンテンツプロセスの分離を考慮した API varpageSourceItem = contextMenu.Item({ label: "Edit Page Source", contentScript: 'on("click", function (node, data) {' + ' postMessage(document.URL);' + '});', onMessage: function (pageURL){ editSource(pageURL); } }); 別プロセスで動作するスクリプト メッセージ通信 プロセス間で関数オブジェクトは受け渡しできないので、 コンテンツプロセスで動作する部分はスクリプトを文字列もしくは URLで受け渡す。 JSON 形式に変換可能なオブジェクトはメッセージ通信で受け渡し可能。
  17. 17. content モジュール for (window in require("window-utils").windowIterator()) { require("content").Worker({ window: window, contentScript: 'postMessage("message!");', onMessage: function(data) { console.log(data); } }); } Worker として登録したスクリプトは対象の window 上で動作する。 オプションは page-mod や context-menu と同様。
  18. 18. Jetpack プロセスの分離を考慮した API cfx run に 「--e10s」オプションを付加することで試行できる。 SDK 本体の対応もこれから。今後の動向に注目。 self-e10s-adapter などは参考になる > cfx run --e10s -b "C:Program FilesMinefieldfirefox.exe" … Error: Module 'widget' requires chrome privileges and has no e10s adapter. OK …
  19. 19. いまここ 拡張が再起動不要となるようにする Firefox, SDK のバージョンに非依存な API を提供する addon-kit の API と似た使い方を提供する construct/destroy モデル EventEmitterモデル エラー発生時のスタックトレース出力 その他
  20. 20. 3-1) construct/destroy モデル widgets.add(widgets.Widget({ label: …, image: …, onClick: … })); widgets.remove(w); widgets.Widget({ label: …, image: …, onClick: … }); w.destroy(); 時代は construct/destroy モデル! add/ removeモデルはもう古い! コンストラクタの中で初期化処理まで実行する。 明示的に変更を元に戻す時は destroy メソッドを使う。
  21. 21. コンストラクタ作成時に便利な関数 new ClassName({…}) でも ClassName({…}) だけでもオブジェクトを作れるようにする。 require(“api-utils”).publicConstructor() オプションの内容をチェックする。 require(“api-utils”).validateOptions() Array もしくは スカラ値を受け取るプロパティを作る。 require(“collection”).addCollectionProperty()
  22. 22. 3-2) EventEmitterモデル tabs.onOpen =function(){…}; tabs.onReady = function(){…}; tabs.onActivate =function(){ …}; tabs.onDeactivate = function(){ …}; tabs.on(‘Open’,function(){…}); tabs.on(‘Ready’, function(){…}); tabs.on(‘Activate’, function(){ …}); tabs.on(‘Deactivate’, function(){ …}); 時代は EventEmitterモデル! onXXXプロパティはもう古い! EventEmitterを使うことで、全てのイベントについて _emit() でのイベント送信、on() でのイベント受信に統一できる。
  23. 23. events モジュール const { EventEmitter } = require("events"); var object = EventEmitter.compose({ constructor: function() {…}, fire: function() { this._emit('event1'); } })(); object.on('event1', function(){ console.log("Event Fired!"); }); object.fire(); EventEmitter.compose()() は Trait という形のオブジェクトを返す。 _emit() など 「_」から始まるメソッドは、this からしか呼び出せない。
  24. 24. 3-3) スタックトレース出力 実行時エラーの発生時、放っておくと何も言わずに処理が終了してしまう。きちんとスタックトレースを出力するように注意する。 EventEmitterを使っていれば、自動的にやってくれるので問題ない。 それ以外でコールバック関数に処理を渡すときは、require(“errors”).catchAndLog() を使う。
  25. 25. いまここ 拡張が再起動不要となるようにする Firefox, SDK のバージョンに非依存な API を提供する addon-kit の API と似た使い方を提供する construct/destroy モデル EventEmitterモデル エラー発生時のスタックトレース出力 その他
  26. 26. 配布 https://builder.addons.mozilla.org/にライブラリとして登録する。 が、、、Add-on Builder ではライブラリを検索できない! 編集画面の「Use Library」で名前を検索できるのが唯一の検索手段 今後の発展を祈ります。
  27. 27. 参考にすべきライブラリ addon-kit api-utils 本家の API がつい最近までコロコロ変わっていたので、サードパーティで十分な品質のライブラリはまだできていない(と予想。なにぶん検索できないのでよくわかりませんが)。 ベータ版が出て、API が固まりつつある今が参入時?
  28. 28. ライブラリ例
  29. 29. ロケーションバーアイコン API ロケーションバーの中にアイコンを表示するAPI を作ってみる
  30. 30. API 設計 パッケージ名:location-bar モジュール名: location-bar クラス名: Icon コンストラクタ: label, tooltip, contentURL, onClick プロパティ: なし
  31. 31. 使い方 const locationBar = require("location-bar"); const tabs = require("tabs"); locationBar.Icon({ label: "Mozilla website", contentURL: "http://www.mozilla.org/favicon.ico", onClick: function() { tabs.open("http://www.mozilla.org/"); } }); locationBar.Icon({ label: "Google website", contentURL: "http://www.google.com/favicon.ico", onClick: function() { tabs.open("http://www.google.com/"); } });
  32. 32. 動作 アイコン追加時: 全てのウィンドウにアイコンを追加する アイコン削除時: 全てのウィンドウからアイコンを削除する ウィンドウ追加時: ウィンドウに全てのアイコンを追加する ウィンドウ削除時、拡張アンロード時: ウィンドウから全てのアイコンを削除する
  33. 33. コンストラクタ:オプションチェック const apiUtils = require("api-utils"); const { EventEmitter } = require("events"); const windowUtils = require("window-utils"); var icons = []; exports.Icon = EventEmitter.compose({ constructor: function Icon(options) { options = apiUtils.validateOptions(options, { label: { is: ["string"] }, tooltip: { is: ["null", "undefined", "string"] }, contentURL: { is: ["string"] }, onClick: { is: ["null", "undefined", "function"] } });
  34. 34. コンストラクタ:初期化 this.id = "locationbaricon:" + require("xpcom").makeUuid().toString(); this._label = options.label; this.tooltip = ("tooltip" in options) ? options.tooltip : this._label this.contentURL = options.contentURL; if ("onClick" in options) { this.on("click", options.onClick); } icons.push(this); for (window in windowUtils.windowIterator()) { addItem(window, this); } },
  35. 35. destroy destroy : function(){ varidx = icons.indexOf(this); if (idx > -1) { icons.splice(idx, 1); } for (window in windowUtils.windowIterator()) { removeItem(window, this); } } });
  36. 36. アイコンの追加 function addItem(window, icon) { var doc = window.document; varurlbar = doc.getElementById('urlbar'); if(urlbar) { var button = doc.createElement('toolbarbutton'); button.setAttribute("id", icon.id); button.setAttribute("tooltiptext", icon.tooltip); button.style.listStyleImage = 'url(' + icon.contentURL + ')'; button.addEventListener('click', function() icon._emit('click'), false); vargoButton = doc.getElementById('urlbar-go-button'); urlbar.insertBefore(button, goButton); } }
  37. 37. アイコンの削除 function removeItem(window, icon) { var doc = window.document; varurlbar = doc.getElementById('urlbar'); if(urlbar) { var button = doc.getElementById(icon.id); urlbar.removeChild(button); } }
  38. 38. ウィンドウの監視 varwindowManager = { init: function () { varwindowTracker = new (windowUtils.WindowTracker)(this); require("unload").ensure(windowTracker); }, onTrack: function (window) { for(vari=0; i<icons.length; i++){ addItem(window, icons[i]); } }, onUntrack: function (window) { for(vari=0; i<icons.length; i++){ removeItem(window, icons[i]); } } } windowManager.init();
  39. 39. まとめ 自作ライブラリを作ることによって、Jetpackの可能性は大きく広がる。 XUL の構造まで把握できる人がライブラリを作り、一般開発者の人がそのライブラリを活用してサクッと拡張を開発する、といった分担ができるようになることを期待します。
  40. 40. Thank you!

×