fxosコードリーディングミートアップ#16 contacts api読んでみた
TRANSCRIPT
Contacts API読んでみた
株式会社グローバルサイバーグループ
マネージャ
藪下正美
はじめに
自己紹介
• 藪下正美
• 株式会社グローバルサイバーグループというところから来ました
• Firefox OSコミュニティから来ました
• Codezineに記事乗りました!
– http://codezine.jp/article/detail/8540
今日のおはなし
• Contacts APIとは
• 手始めにfindメソッドを追ってみる
• getAllメソッドを見てみる
• まとめ
Contacts APIとは
• 使い方のおはなしは#11でやったのでslideshareとか参照
– http://www.slideshare.net/aoitan/meetup11-contacts-api
手始めにFINDメソッドを追ってみる
まずざっとまとめ
• パーミッションチェック
• チェックがallowならsendAsyncMessageで 'Contacts:Find' メッセージを投げる
• Parentプロセスで動いているContactsServiceが 'Contacts:Find' メッセージを受信すると
• ContactDBを検索
• ContactDBの処理が終わったらChildプロセスに 'Contacts:Find:Return:OK' か'Contacts:Find:Return:KO' メッセージを送信
• ContactsManagerがメッセージを受信してDOMRequestのsuccessイベントかerrorイベントを発火
• 間にIPCとか挟まってるけど闇が深いので省略!
• 間にIndexedDBHelperとか挟まってるけどただのプロミスラッパーなので省略!
具体的なコードを見てみる(1)
• パーミッションチェック
• チェックがallowならsendAsyncMessageで 'Contacts:Find' メッセージを投げる
find: function(aOptions) {if (DEBUG) debug("find! " + JSON.stringify(aOptions));let request = this.createRequest();let options = { findOptions: aOptions };let allowCallback = function() {
cpmm.sendAsyncMessage("Contacts:Find", {requestID: this.getRequestId({
request: request,reason: "find"
}), options: options });}.bind(this)this.askPermission("find", request, allowCallback);return request;
},
具体的なコードを見てみる(2)
• Chromeプロセスで動いているContactsServiceが 'Contacts:Find' メッセージを受信すると
receiveMessage: function(aMessage) {if (DEBUG) debug("receiveMessage " + aMessage.name);let mm = aMessage.target;let msg = aMessage.data;let cursorList;
switch (aMessage.name) {case "Contacts:Find":
具体的なコードを見てみる(3)
• ContactDBを検索
• ContactDBの処理が終わったらChildプロセスに 'Contacts:Find:Return:OK' か'Contacts:Find:Return:KO' メッセージを送信
this._db.find(function(contacts) {
for (let i in contacts) {result.push(contacts[i]);
}if (DEBUG) debug("result:" + JSON.stringify(result));mm.sendAsyncMessage("Contacts:Find:Return:OK",
{requestID: msg.requestID, contacts: result});}.bind(this),function(aErrorMsg) {
mm.sendAsyncMessage("Contacts:Find:Return:KO", {requestID: msg.requestID,errorMsg: aErrorMsg });
}.bind(this), msg.options.findOptions);
具体的なコードを見てみる(4)
• ContactsManagerがメッセージを受信してDOMRequestのsuccessイベントかerrorイベントを発火
receiveMessage: function(aMessage) {if (DEBUG) debug("receiveMessage: " + aMessage.name);let msg = aMessage.json;let contacts = msg.contacts;
let req;switch (aMessage.name) {
case "Contacts:Find:Return:OK":req = this.getRequest(msg.requestID);if (req) {
let result = this._convertContacts(contacts);Services.DOMRequest.fireSuccess(req.request, result);
} else {if (DEBUG) debug("no request stored!" + msg.requestID);
}break;
GETALLメソッドを見てみる
まずざっとまとめ
• パーミッションチェック
• チェックがallowならsendAsyncMessageで 'Contacts:GetAll' メッセージを投げる
• Parentプロセスで動いているContactsServiceが 'Contacts:GetAll' メッセージを受信すると
• ContactDBを検索
• ContactDBのカーソルが回るたびにChildプロセスに 'Contacts:GetALl:Next' メッセージを送信する
• 処理失敗が起きたら 'Contacts:GetAll:Return:KO' メッセージを送信
• ContactsManagerがメッセージを受信してDOMCursorのsuccessイベントかerrorイベントを発火
具体的なコードを見てみる(1)
• パーミッションチェック
• チェックがallowならsendAsyncMessageで 'Contacts:GetAll' メッセージを投げる
getAll: function CM_getAll(aOptions) {if (DEBUG) debug("getAll: " + JSON.stringify(aOptions));let [cursorId, cursor] = this.createCursor();let allowCallback = function() {
cpmm.sendAsyncMessage("Contacts:GetAll", {cursorId: cursorId, findOptions: aOptions});
}.bind(this);this.askPermission("find", cursor, allowCallback);return cursor;
},
具体的なコードを見てみる(2)
• Parentプロセスで動いているContactsServiceが 'Contacts:GetAll' メッセージを受信すると
receiveMessage: function(aMessage) {if (DEBUG) debug("receiveMessage " + aMessage.name);let mm = aMessage.target;let msg = aMessage.data;let cursorList;
switch (aMessage.name) {(snip)
case "Contacts:GetAll":
具体的なコードを見てみる(3)
• ContactDBを検索
• ContactDBのカーソルが回るたびにChildプロセスに 'Contacts:GetALl:Next' メッセージを送信する
this._db.getAll(function(aContacts) {
try {mm.sendAsyncMessage("Contacts:GetAll:Next",
{cursorId: msg.cursorId, contacts: aContacts});if (aContacts === null) {
let cursorList = this._cursors.get(mm);let index = cursorList.indexOf(msg.cursorId);cursorList.splice(index, 1);
}} catch (e) {
if (DEBUG) debug("Child is dead, DB should stop sending contacts");throw e;
}}.bind(this),
具体的なコードを見てみる(4)
• 処理失敗が起きたら 'Contacts:GetAll:Return:KO' メッセージを送信
function(aErrorMsg) {mm.sendAsyncMessage("Contacts:GetAll:Return:KO",
{ requestID: msg.cursorId, errorMsg: aErrorMsg });},msg.findOptions, msg.cursorId);
break;
具体的なコードを見てみる(5)
• ContactsManagerがメッセージを受信してDOMCursorのsuccessイベントかerrorイベントを発火
case "Contacts:GetAll:Next":(snip)
this.nextTick(this._fireSuccessOrDone.bind(this, data.cursor, contact));(snip)
break;
(snip)
_fireSuccessOrDone: function(aCursor, aResult) {if (aResult == null) {
Services.DOMRequest.fireDone(aCursor);} else {
Services.DOMRequest.fireSuccess(aCursor, aResult);}
},
具体的なコードを見てみる(6)
• ContactsManagerがメッセージを受信してDOMCursorのsuccessイベントかerrorイベントを発火
case "Contacts:GetAll:Return:KO":req = this.getRequest(msg.requestID);if (req) {
Services.DOMRequest.fireError(req.cursor, msg.errorMsg);}break;
他のメソッド
• どうやら基本は同じ
• 多少プロパティを詰め直したりとかしてるAPIはあるけどやることはfindと同じ
• getAllだけはDOMCursorを扱う都合で多少違った
まとめ
ちょっと寄り道
なぜsendAsyncMessageが挟まっているのか(1)
• 簡単なプロセスの図
Chrome Content
Systemアプリ Contactsアプリ
ContacsServiceContactDB
ContactsManager
なぜsendAsyncMessageが挟まっているのか(2)
• こんな感じでFxOSのアプリはアプリのプロセスとシステムのプロセスがわかれている
• なので基本的に子プロセスは権限の必要な作業は親プロセスに依頼しないといけない
–非同期に動くメソッドは大体プロセス境界をまたいでいる
• そこでpostなんとかMessageやsendなんとかMessageの出番
• Gaiaで閉じているものだとiframe間の通信なのでpostMessageが使われる
• Gecko側に実体のあるものだとsendAsyncMessage
• 基本的に同じ使い勝手のもので文字列とかJSONオブジェクトしか投げられないのでコマンド文字列とかこねこねしていることが多い
全体像
• 全体図
Chrome Content
ContactsService ContactsManager
ContactDB
IndexedDB
sendAsyncMessage
ContentsManager
• メソッドの呼び出しを受けたらパーミッションチェックして親プロセスへのメッセージ送信
• 戻りメッセージを待ち受けてDOMRequestやDOMCursorのイベントを発火
ContactsService
• 子プロセスからのメッセージを待ち受けてDB処理
– find/save/remove/clearは成功時と失敗時のコールバックを与えてDBのメソッドを呼ぶ
• コールバックの中身は子プロセスに対するsendAsyncMessage
• 成功時は元のメッセージに 'Return:OK' を付けて投げる
• 失敗時は元のメッセージに 'Return:KO' を付けて投げる
– getAllはカーソルが返るのでカーソル一つ回すごとに 'Contacts:GetAll:Next' メッセージをsendAsyncMessageで飛ばす
• sim入れ替えの監視
ContactDB
• 連絡帳用のDB処理をまとめている
• 単にIndexedDBラッパー