Bug 809661 - Need a speedy way to construct a thread list for SMS messages. r=sicking, a=blocking-basecamp

This commit is contained in:
Ben Turner 2012-11-14 11:49:56 -08:00
Родитель 84f9d5d059
Коммит 6910d3c122
17 изменённых файлов: 492 добавлений и 57 удалений

Просмотреть файл

@ -11,7 +11,7 @@ relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
PARALLEL_DIRS = interfaces src
DIRS = interfaces src
TEST_DIRS += tests
ifdef ENABLE_TESTS

Просмотреть файл

@ -8,7 +8,7 @@ interface nsIDOMEventListener;
interface nsIDOMMozSmsRequest;
interface nsIDOMMozSmsFilter;
[scriptable, builtinclass, uuid(1bee1224-56a2-4935-af7b-0011746306cb)]
[scriptable, builtinclass, uuid(caaf5c38-a730-4dbb-a0f0-12384bfac8e3)]
interface nsIDOMMozSmsManager : nsIDOMEventTarget
{
unsigned short getNumberOfMessagesForText(in DOMString text);
@ -29,6 +29,8 @@ interface nsIDOMMozSmsManager : nsIDOMEventTarget
nsIDOMMozSmsRequest markMessageRead(in long id, in boolean aValue);
nsIDOMMozSmsRequest getThreadList();
[implicit_jscontext] attribute jsval onreceived;
[implicit_jscontext] attribute jsval onsent;
[implicit_jscontext] attribute jsval ondeliverysuccess;

Просмотреть файл

@ -14,7 +14,7 @@
interface nsIDOMMozSmsFilter;
interface nsISmsRequest;
[scriptable, uuid(6ad55465-6937-4491-93ca-29dad9775d46)]
[scriptable, uuid(c2cb2af7-6b96-4915-bcc8-54ad705d6110)]
interface nsISmsDatabaseService : nsISupports
{
// Takes some information required to save the message and returns its id.
@ -30,4 +30,6 @@ interface nsISmsDatabaseService : nsISupports
void getNextMessageInList(in long listId, in nsISmsRequest request);
void clearMessageList(in long listId);
void markMessageRead(in long messageId, in boolean value, in nsISmsRequest request);
void getThreadList(in nsISmsRequest request);
};

Просмотреть файл

@ -6,7 +6,15 @@
interface nsIDOMMozSmsMessage;
[scriptable, builtinclass, uuid(97067327-64b9-4e26-848b-59e443c55db9)]
dictionary SmsThreadListItem
{
DOMString senderOrReceiver;
unsigned long long timestamp;
DOMString body;
unsigned long long unreadCount;
};
[scriptable, builtinclass, uuid(82a6d16d-cf33-4745-8662-8b5d441f512f)]
interface nsISmsRequest : nsISupports
{
/**
@ -37,4 +45,8 @@ interface nsISmsRequest : nsISupports
void notifyMessageMarkedRead(in boolean read);
void notifyMarkMessageReadFailed(in long error);
[implicit_jscontext]
void notifyThreadList(in jsval threadList);
void notifyThreadListFailed(in long error);
};

Просмотреть файл

@ -303,6 +303,20 @@ SmsManager::MarkMessageRead(int32_t aId, bool aValue,
return NS_OK;
}
NS_IMETHODIMP
SmsManager::GetThreadList(nsIDOMMozSmsRequest** aRequest)
{
nsCOMPtr<nsIDOMMozSmsRequest> req = SmsRequest::Create(this);
nsCOMPtr<nsISmsDatabaseService> smsDBService =
do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(smsDBService, NS_ERROR_FAILURE);
nsCOMPtr<nsISmsRequest> forwarder =
new SmsRequestForwarder(static_cast<SmsRequest*>(req.get()));
smsDBService->GetThreadList(forwarder);
req.forget(aRequest);
return NS_OK;
}
nsresult
SmsManager::DispatchTrustedSmsEventToSelf(const nsAString& aEventName, nsIDOMMozSmsMessage* aMessage)
{

Просмотреть файл

@ -9,11 +9,16 @@
#include "nsDOMString.h"
#include "nsContentUtils.h"
#include "nsIDOMSmsMessage.h"
#include "nsIScriptGlobalObject.h"
#include "nsPIDOMWindow.h"
#include "SmsCursor.h"
#include "SmsMessage.h"
#include "SmsManager.h"
#include "mozilla/dom/DOMError.h"
#include "SmsParent.h"
#include "jsapi.h"
#include "DictionaryHelpers.h"
#include "xpcpublic.h"
#define SUCCESS_EVENT_NAME NS_LITERAL_STRING("success")
#define ERROR_EVENT_NAME NS_LITERAL_STRING("error")
@ -145,12 +150,7 @@ SmsRequest::SetSuccess(nsIDOMMozSmsMessage* aMessage)
void
SmsRequest::SetSuccess(bool aResult)
{
NS_PRECONDITION(!mDone, "mDone shouldn't have been set to true already!");
NS_PRECONDITION(!mError, "mError shouldn't have been set!");
NS_PRECONDITION(mResult == JSVAL_NULL, "mResult shouldn't have been set!");
mResult.setBoolean(aResult);
mDone = true;
SetSuccess(aResult ? JSVAL_TRUE : JSVAL_FALSE);
}
void
@ -168,6 +168,17 @@ SmsRequest::SetSuccess(nsIDOMMozSmsCursor* aCursor)
}
}
void
SmsRequest::SetSuccess(const jsval& aResult)
{
NS_PRECONDITION(!mDone, "mDone shouldn't have been set to true already!");
NS_PRECONDITION(!mError, "mError shouldn't have been set!");
NS_PRECONDITION(JSVAL_IS_VOID(mResult), "mResult shouldn't have been set!");
mResult = aResult;
mDone = true;
}
bool
SmsRequest::SetSuccessInternal(nsISupports* aObject)
{
@ -445,6 +456,134 @@ SmsRequest::NotifyMarkMessageReadFailed(int32_t aError)
return NotifyError(aError);
}
NS_IMETHODIMP
SmsRequest::NotifyThreadList(const jsval& aThreadList, JSContext* aCx)
{
MOZ_ASSERT(aThreadList.isObject());
if (mParent) {
JSObject* array = const_cast<JSObject*>(&aThreadList.toObject());
uint32_t length;
bool ok = JS_GetArrayLength(aCx, array, &length);
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
ReplyThreadList reply;
InfallibleTArray<ThreadListItem>& ipcItems = reply.items();
if (length) {
ipcItems.SetCapacity(length);
for (uint32_t i = 0; i < length; i++) {
jsval arrayEntry;
ok = JS_GetElement(aCx, array, i, &arrayEntry);
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
MOZ_ASSERT(arrayEntry.isObject());
SmsThreadListItem item;
nsresult rv = item.Init(aCx, &arrayEntry);
NS_ENSURE_SUCCESS(rv, rv);
ThreadListItem* ipcItem = ipcItems.AppendElement();
ipcItem->senderOrReceiver() = item.senderOrReceiver;
ipcItem->timestamp() = item.timestamp;
ipcItem->body() = item.body;
ipcItem->unreadCount() = item.unreadCount;
}
}
return SendMessageReply(reply);
}
return NotifySuccess(aThreadList);
}
NS_IMETHODIMP
SmsRequest::NotifyThreadListFailed(int32_t aError)
{
if (mParent) {
return SendMessageReply(MessageReply(ReplyThreadListFail(aError)));
}
return NotifyError(aError);
}
void
SmsRequest::NotifyThreadList(const InfallibleTArray<ThreadListItem>& aItems)
{
MOZ_ASSERT(!mParent);
MOZ_ASSERT(GetOwner());
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS_VOID(rv);
NS_ENSURE_TRUE_VOID(sc);
JSContext* cx = sc->GetNativeContext();
MOZ_ASSERT(cx);
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
JSObject* ownerObj = sgo->GetGlobalJSObject();
NS_ENSURE_TRUE_VOID(ownerObj);
nsCxPusher pusher;
NS_ENSURE_TRUE_VOID(pusher.Push(cx, false));
JSAutoRequest ar(cx);
JSAutoCompartment ac(cx, ownerObj);
JSObject* array = JS_NewArrayObject(cx, aItems.Length(), nullptr);
NS_ENSURE_TRUE_VOID(array);
bool ok;
for (uint32_t i = 0; i < aItems.Length(); i++) {
const ThreadListItem& source = aItems[i];
nsString temp = source.senderOrReceiver();
jsval senderOrReceiver;
ok = xpc::StringToJsval(cx, temp, &senderOrReceiver);
NS_ENSURE_TRUE_VOID(ok);
JSObject* timestampObj = JS_NewDateObjectMsec(cx, source.timestamp());
NS_ENSURE_TRUE_VOID(timestampObj);
jsval timestamp = OBJECT_TO_JSVAL(timestampObj);
temp = source.body();
jsval body;
ok = xpc::StringToJsval(cx, temp, &body);
NS_ENSURE_TRUE_VOID(ok);
jsval unreadCount = JS_NumberValue(double(source.unreadCount()));
JSObject* elementObj = JS_NewObject(cx, nullptr, nullptr, nullptr);
NS_ENSURE_TRUE_VOID(elementObj);
ok = JS_SetProperty(cx, elementObj, "senderOrReceiver", &senderOrReceiver);
NS_ENSURE_TRUE_VOID(ok);
ok = JS_SetProperty(cx, elementObj, "timestamp", &timestamp);
NS_ENSURE_TRUE_VOID(ok);
ok = JS_SetProperty(cx, elementObj, "body", &body);
NS_ENSURE_TRUE_VOID(ok);
ok = JS_SetProperty(cx, elementObj, "unreadCount", &unreadCount);
NS_ENSURE_TRUE_VOID(ok);
jsval element = OBJECT_TO_JSVAL(elementObj);
ok = JS_SetElement(cx, array, i, &element);
NS_ENSURE_TRUE_VOID(ok);
}
NotifyThreadList(OBJECT_TO_JSVAL(array), cx);
}
} // namespace sms
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -17,24 +17,33 @@ namespace mozilla {
namespace dom {
namespace sms {
class SmsRequestChild;
class SmsRequestParent;
class MessageReply;
class ThreadListItem;
// We need this forwarder to avoid a QI to nsIClassInfo.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=775997#c51
class SmsRequestForwarder : public nsISmsRequest
{
NS_FORWARD_NSISMSREQUEST(mRealRequest->)
friend class SmsRequestChild;
public:
NS_DECL_ISUPPORTS
NS_FORWARD_NSISMSREQUEST(mRealRequest->)
SmsRequestForwarder(nsISmsRequest* aRealRequest) {
mRealRequest = aRealRequest;
}
private:
virtual
~SmsRequestForwarder() {}
private:
nsISmsRequest* GetRealRequest() {
return mRealRequest;
}
nsCOMPtr<nsISmsRequest> mRealRequest;
};
@ -66,6 +75,9 @@ public:
mParentAlive = false;
}
void
NotifyThreadList(const InfallibleTArray<ThreadListItem>& aItems);
private:
SmsRequest() MOZ_DELETE;
@ -100,6 +112,11 @@ private:
*/
void SetSuccess(nsIDOMMozSmsCursor* aCursor);
/**
* Set the object in a success state with the result being the given jsval.
*/
void SetSuccess(const jsval& aVal);
/**
* Set the object in an error state with the error type being aError.
*/

Просмотреть файл

@ -112,6 +112,13 @@ SmsDatabaseService::MarkMessageRead(int32_t aMessageId, bool aValue,
return NS_OK;
}
NS_IMETHODIMP
SmsDatabaseService::GetThreadList(nsISmsRequest* aRequest)
{
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
} // namespace sms
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -90,6 +90,13 @@ SmsDatabaseService::MarkMessageRead(int32_t aMessageId,
return NS_OK;
}
NS_IMETHODIMP
SmsDatabaseService::GetThreadList(nsISmsRequest* aRequest)
{
NS_ERROR("We should not be here!");
return NS_OK;
}
} // namespace sms
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -53,6 +53,10 @@ struct MarkMessageReadRequest
bool value;
};
struct GetThreadListRequest
{
};
union IPCSmsRequest
{
SendMessageRequest;
@ -61,6 +65,7 @@ union IPCSmsRequest
CreateMessageListRequest;
GetNextMessageInListRequest;
MarkMessageReadRequest;
GetThreadListRequest;
};
sync protocol PSms {

Просмотреть файл

@ -85,6 +85,24 @@ struct ReplyNoMessageInList
{
};
struct ThreadListItem
{
nsString senderOrReceiver;
uint64_t timestamp;
nsString body;
uint64_t unreadCount;
};
struct ReplyThreadList
{
ThreadListItem[] items;
};
struct ReplyThreadListFail
{
int32_t error;
};
union MessageReply
{
ReplyMessageSend;
@ -99,6 +117,8 @@ union MessageReply
ReplyGetNextMessage;
ReplyMarkeMessageRead;
ReplyMarkeMessageReadFail;
ReplyThreadList;
ReplyThreadListFail;
};
} // namespace sms

Просмотреть файл

@ -161,6 +161,14 @@ SmsRequestChild::Recv__delete__(const MessageReply& aReply)
case MessageReply::TReplyMarkeMessageReadFail:
mReplyRequest->NotifyMarkMessageReadFailed(aReply.get_ReplyMarkeMessageReadFail().error());
break;
case MessageReply::TReplyThreadList: {
SmsRequestForwarder* forwarder = static_cast<SmsRequestForwarder*>(mReplyRequest.get());
SmsRequest* request = static_cast<SmsRequest*>(forwarder->GetRealRequest());
request->NotifyThreadList(aReply.get_ReplyThreadList().items());
} break;
case MessageReply::TReplyThreadListFail:
mReplyRequest->NotifyThreadListFailed(aReply.get_ReplyThreadListFail().error());
break;
default:
MOZ_NOT_REACHED("Received invalid response parameters!");
return false;

Просмотреть файл

@ -179,6 +179,13 @@ SmsIPCService::MarkMessageRead(int32_t aMessageId,
return NS_OK;
}
NS_IMETHODIMP
SmsIPCService::GetThreadList(nsISmsRequest* aRequest)
{
SendRequest(GetThreadListRequest(), aRequest);
return NS_OK;
}
} // namespace sms
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -203,6 +203,8 @@ SmsParent::RecvPSmsRequestConstructor(PSmsRequestParent* aActor,
return actor->DoRequest(aRequest.get_GetNextMessageInListRequest());
case IPCSmsRequest::TMarkMessageReadRequest:
return actor->DoRequest(aRequest.get_MarkMessageReadRequest());
case IPCSmsRequest::TGetThreadListRequest:
return actor->DoRequest(aRequest.get_GetThreadListRequest());
default:
MOZ_NOT_REACHED("Unknown type!");
return false;
@ -347,6 +349,21 @@ SmsRequestParent::DoRequest(const MarkMessageReadRequest& aRequest)
return true;
}
bool
SmsRequestParent::DoRequest(const GetThreadListRequest& aRequest)
{
nsCOMPtr<nsISmsDatabaseService> smsDBService =
do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(smsDBService, true);
mSmsRequest = SmsRequest::Create(this);
nsCOMPtr<nsISmsRequest> forwarder = new SmsRequestForwarder(mSmsRequest);
nsresult rv = smsDBService->GetThreadList(forwarder);
NS_ENSURE_SUCCESS(rv, false);
return true;
}
} // namespace sms
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -103,6 +103,9 @@ protected:
bool
DoRequest(const MarkMessageReadRequest& aRequest);
bool
DoRequest(const GetThreadListRequest& aRequest);
};
} // namespace sms

Просмотреть файл

@ -14,8 +14,9 @@ const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d
const DEBUG = false;
const DB_NAME = "sms";
const DB_VERSION = 3;
const DB_VERSION = 4;
const STORE_NAME = "sms";
const MOST_RECENT_STORE_NAME = "most-recent";
const DELIVERY_SENT = "sent";
const DELIVERY_RECEIVED = "received";
@ -52,6 +53,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
const GLOBAL_SCOPE = this;
function numberFromMessage(message) {
return message.delivery == DELIVERY_SENT ? message.receiver : message.sender;
}
/**
* SmsDatabaseService
*/
@ -175,6 +180,10 @@ SmsDatabaseService.prototype = {
objectStore = event.target.transaction.objectStore(STORE_NAME);
self.upgradeSchema2(objectStore);
break;
case 3:
if (DEBUG) debug("Upgrade to version 4. Add quick threads view.")
self.upgradeSchema3(event.target.transaction);
break;
default:
event.target.transaction.abort();
callback("Old database version: " + event.oldVersion, null);
@ -182,7 +191,7 @@ SmsDatabaseService.prototype = {
}
currentVersion++;
}
};
}
request.onerror = function (event) {
//TODO look at event.target.Code and change error constant accordingly
callback("Error opening database!", null);
@ -200,15 +209,22 @@ SmsDatabaseService.prototype = {
* @param callback
* Function to call when the transaction is available. It will
* be invoked with the transaction and the 'sms' object store.
* @param objectStores
* Function to call when the transaction is available. It will
* be invoked with the transaction and the 'sms' object store.
*/
newTxn: function newTxn(txn_type, callback) {
newTxn: function newTxn(txn_type, callback, objectStores) {
if (!objectStores) {
objectStores = [STORE_NAME];
}
if (DEBUG) debug("Opening transaction for objectStores: " + objectStores);
this.ensureDB(function (error, db) {
if (error) {
if (DEBUG) debug("Could not open database: " + error);
callback(error);
return;
}
let txn = db.transaction([STORE_NAME], txn_type);
let txn = db.transaction(objectStores, txn_type);
if (DEBUG) debug("Started transaction " + txn + " of type " + txn_type);
if (DEBUG) {
txn.oncomplete = function oncomplete(event) {
@ -220,9 +236,18 @@ SmsDatabaseService.prototype = {
debug("Error occurred during transaction: " + event.target.errorCode);
};
}
if (DEBUG) debug("Retrieving object store", STORE_NAME);
let store = txn.objectStore(STORE_NAME);
callback(null, txn, store);
let stores;
if (objectStores.length == 1) {
if (DEBUG) debug("Retrieving object store " + objectStores[0]);
stores = txn.objectStore(objectStores[0]);
} else {
stores = [];
for each (let storeName in objectStores) {
if (DEBUG) debug("Retrieving object store " + storeName);
stores.push(txn.objectStore(storeName));
}
}
callback(null, txn, stores);
});
},
@ -233,8 +258,8 @@ SmsDatabaseService.prototype = {
* TODO full text search on body???
*/
createSchema: function createSchema(db) {
// This objectStore holds the main SMS data.
let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" });
objectStore.createIndex("id", "id", { unique: true });
objectStore.createIndex("delivery", "delivery", { unique: false });
objectStore.createIndex("sender", "sender", { unique: false });
objectStore.createIndex("receiver", "receiver", { unique: false });
@ -246,7 +271,6 @@ SmsDatabaseService.prototype = {
* Upgrade to the corresponding database schema version.
*/
upgradeSchema: function upgradeSchema(objectStore) {
// For now, the only possible upgrade is to version 2.
objectStore.createIndex("read", "read", { unique: false });
},
@ -265,6 +289,29 @@ SmsDatabaseService.prototype = {
}
},
upgradeSchema3: function upgradeSchema2(transaction) {
// Delete redundant "id" index.
let objectStore = transaction.objectStore(STORE_NAME);
if (objectStore.indexNames.contains("id")) {
objectStore.deleteIndex("id");
}
/**
* This objectStore can be used to quickly construct a thread view of the
* SMS database. Each entry looks like this:
*
* { senderOrReceiver: <String> (primary key),
* id: <Number>,
* timestamp: <Date>,
* body: <String>,
* unreadCount: <Number> }
*
*/
objectStore = db.createObjectStore(MOST_RECENT_STORE_NAME,
{ keyPath: "senderOrReceiver" });
objectStore.createIndex("timestamp", "timestamp");
},
/**
* Helper function to make the intersection of the partial result arrays
* obtained within createMessageList.
@ -354,12 +401,43 @@ SmsDatabaseService.prototype = {
this.lastKey += 1;
message.id = this.lastKey;
if (DEBUG) debug("Going to store " + JSON.stringify(message));
this.newTxn(READ_WRITE, function(error, txn, store) {
this.newTxn(READ_WRITE, function(error, txn, stores) {
if (error) {
return;
}
let request = store.put(message);
});
// First add to main objectStore.
stores[0].put(message);
let number = numberFromMessage(message);
// Next update the other objectStore.
stores[1].get(number).onsuccess = function(event) {
let mostRecentEntry = event.target.result;
if (mostRecentEntry) {
let needsUpdate = false;
if (mostRecentEntry.timestamp <= message.timestamp) {
mostRecentEntry.timestamp = message.timestamp;
mostRecentEntry.body = message.body;
needsUpdate = true;
}
if (!message.read) {
mostRecentEntry.unreadCount++;
needsUpdate = true;
}
if (needsUpdate) {
event.target.source.put(mostRecentEntry);
}
} else {
event.target.source.add({ senderOrReceiver: number,
timestamp: message.timestamp,
body: message.body,
unreadCount: message.read ? 0 : 1 });
}
}
}, [STORE_NAME, MOST_RECENT_STORE_NAME]);
// We return the key that we expect to store in the db
return message.id;
},
@ -499,33 +577,103 @@ SmsDatabaseService.prototype = {
deleteMessage: function deleteMessage(messageId, aRequest) {
let deleted = false;
let self = this;
this.newTxn(READ_WRITE, function (error, txn, store) {
this.newTxn(READ_WRITE, function (error, txn, stores) {
if (error) {
aRequest.notifyDeleteMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
return;
}
let request = store.count(messageId);
request.onsuccess = function onsuccess(event) {
let count = event.target.result;
if (DEBUG) debug("Count for messageId " + messageId + ": " + count);
deleted = (count == 1);
if (deleted) {
store.delete(messageId);
}
txn.onerror = function onerror(event) {
if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
//TODO look at event.target.errorCode, pick appropriate error constant
aRequest.notifyDeleteMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
};
const smsStore = stores[0];
const mruStore = stores[1];
let deleted = false;
txn.oncomplete = function oncomplete(event) {
if (DEBUG) debug("Transaction " + txn + " completed.");
aRequest.notifyMessageDeleted(deleted);
};
txn.onerror = function onerror(event) {
if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
//TODO look at event.target.errorCode, pick appropriate error constant
aRequest.notifyDeleteMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
smsStore.get(messageId).onsuccess = function(event) {
let message = event.target.result;
if (message) {
if (DEBUG) debug("Deleting message id " + messageId);
// First actually delete the message.
event.target.source.delete(messageId).onsuccess = function(event) {
deleted = true;
// Then update unread count and most recent message.
let number = numberFromMessage(message);
mruStore.get(number).onsuccess = function(event) {
// This must exist.
let mostRecentEntry = event.target.result;
if (!message.read) {
mostRecentEntry.unreadCount--;
}
if (mostRecentEntry.id == messageId) {
// This sucks, we have to find a new most-recent message.
message = null;
// Check most recent sender.
smsStore.index("sender").openCursor(number, "prev").onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
message = cursor.value;
}
};
// Check most recent receiver.
smsStore.index("receiver").openCursor(number, "prev").onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
if (!message || cursor.value.timeStamp > message.timestamp) {
message = cursor.value;
}
}
// If we found a new message then we need to update the data
// in the most-recent store. Otherwise we can delete it.
if (message) {
mostRecentEntry.id = message.id;
mostRecentEntry.timestamp = message.timestamp;
mostRecentEntry.body = message.body;
if (DEBUG) {
debug("Updating mru entry: " +
JSON.stringify(mostRecentEntry));
}
mruStore.put(mostRecentEntry);
}
else {
if (DEBUG) {
debug("Deleting mru entry for number '" + number + "'");
}
mruStore.delete(number);
}
};
} else if (!message.read) {
// Shortcut, just update the unread count.
if (DEBUG) {
debug("Updating unread count for number '" + number + "': " +
(mostRecentEntry.unreadCount + 1) + " -> " +
mostRecentEntry.unreadCount);
}
mruStore.put(mostRecentEntry);
}
};
};
} else if (DEBUG) {
debug("Message id " + messageId + " does not exist");
}
};
});
}, [STORE_NAME, MOST_RECENT_STORE_NAME]);
},
createMessageList: function createMessageList(filter, reverse, aRequest) {
@ -556,7 +704,7 @@ SmsDatabaseService.prototype = {
if (DEBUG) {
debug("These messages match the " + filter + " filter: " +
filteredKeys[filter]);
}
}
return;
}
// The cursor primaryKey is stored in its corresponding partial array
@ -726,18 +874,20 @@ SmsDatabaseService.prototype = {
markMessageRead: function markMessageRead(messageId, value, aRequest) {
if (DEBUG) debug("Setting message " + messageId + " read to " + value);
this.newTxn(READ_WRITE, function (error, txn, store) {
this.newTxn(READ_WRITE, function (error, txn, stores) {
if (error) {
if (DEBUG) debug(error);
aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
return;
}
let getRequest = store.get(messageId);
getRequest.onsuccess = function onsuccess(event) {
txn.onerror = function onerror(event) {
if (DEBUG) debug("Caught error on transaction ", event.target.errorCode);
aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
};
stores[0].get(messageId).onsuccess = function onsuccess(event) {
let message = event.target.result;
if (DEBUG) debug("Message ID " + messageId + " not found");
if (!message) {
if (DEBUG) debug("Message ID " + messageId + " not found");
aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR);
return;
}
@ -758,26 +908,50 @@ SmsDatabaseService.prototype = {
}
message.read = value ? FILTER_READ_READ : FILTER_READ_UNREAD;
if (DEBUG) debug("Message.read set to: " + value);
let putRequest = store.put(message);
putRequest.onsuccess = function onsuccess(event) {
event.target.source.put(message).onsuccess = function onsuccess(event) {
if (DEBUG) {
debug("Update successfully completed. Message: " +
JSON.stringify(event.target.result));
}
let checkRequest = store.get(message.id);
checkRequest.onsuccess = function onsuccess(event) {
aRequest.notifyMessageMarkedRead(event.target.result.read);
};
}
};
// Now update the unread count.
let number = numberFromMessage(message);
stores[1].get(number).onsuccess = function(event) {
let mostRecentEntry = event.target.result;
mostRecentEntry.unreadCount += value ? -1 : 1;
if (DEBUG) {
debug("Updating unreadCount for '" + number + "': " +
(value ?
mostRecentEntry.unreadCount + 1 :
mostRecentEntry.unreadCount - 1) +
" -> " + mostRecentEntry.unreadCount);
}
event.target.source.put(mostRecentEntry).onsuccess = function(event) {
aRequest.notifyMessageMarkedRead(message.read);
};
};
};
};
}, [STORE_NAME, MOST_RECENT_STORE_NAME]);
},
getThreadList: function getThreadList(aRequest) {
if (DEBUG) debug("Getting thread list");
this.newTxn(READ_ONLY, function (error, txn, store) {
if (error) {
if (DEBUG) debug(error);
aRequest.notifyThreadListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
return;
}
txn.onerror = function onerror(event) {
if (DEBUG) debug("Caught error on transaction ", event.target.errorCode);
aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
aRequest.notifyThreadListFailed(Ci.nsISmsRequest.INTERNAL_ERROR);
};
});
store.index("timestamp").mozGetAll().onsuccess = function(event) {
aRequest.notifyThreadList(event.target.result);
};
}, [MOST_RECENT_STORE_NAME]);
}
};
XPCOMUtils.defineLazyGetter(SmsDatabaseService.prototype, "mRIL", function () {

Просмотреть файл

@ -22,7 +22,8 @@ dictionaries = [
[ 'CameraPosition', 'nsIDOMCameraManager.idl' ],
[ 'CameraSelector', 'nsIDOMCameraManager.idl' ],
[ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ],
[ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ]
[ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ],
[ 'SmsThreadListItem', 'nsISmsRequest.idl' ]
]
# include file names