зеркало из https://github.com/mozilla/gecko-dev.git
Bug 809661 - Need a speedy way to construct a thread list for SMS messages. r=sicking, a=blocking-basecamp
This commit is contained in:
Родитель
84f9d5d059
Коммит
6910d3c122
|
@ -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", ×tamp);
|
||||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче