diff --git a/dom/mobilemessage/MobileMessageCursorCallback.cpp b/dom/mobilemessage/MobileMessageCursorCallback.cpp index 8816f716652..dbbcebe3320 100644 --- a/dom/mobilemessage/MobileMessageCursorCallback.cpp +++ b/dom/mobilemessage/MobileMessageCursorCallback.cpp @@ -8,11 +8,94 @@ #include "nsIDOMDOMRequest.h" #include "nsIDOMMozSmsMessage.h" #include "nsIMobileMessageCallback.h" -#include "DOMCursor.h" #include "nsServiceManagerUtils.h" // for do_GetService namespace mozilla { namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(MobileMessageCursor, DOMCursor, + mPendingResults) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MobileMessageCursor) +NS_INTERFACE_MAP_END_INHERITING(DOMCursor) + +NS_IMPL_ADDREF_INHERITED(MobileMessageCursor, DOMCursor) +NS_IMPL_RELEASE_INHERITED(MobileMessageCursor, DOMCursor) + +MobileMessageCursor::MobileMessageCursor(nsPIDOMWindow* aWindow, + nsICursorContinueCallback* aCallback) + : DOMCursor(aWindow, aCallback) +{ +} + +NS_IMETHODIMP +MobileMessageCursor::Continue() +{ + // We have originally: + // + // DOMCursor::Continue() + // +-> DOMCursor::Continue(ErrorResult& aRv) + // + // Now it becomes: + // + // MobileMessageCursor::Continue() + // +-> DOMCursor::Continue() + // +-> MobileMessageCursor::Continue(ErrorResult& aRv) + // o-> DOMCursor::Continue(ErrorResult& aRv) + return DOMCursor::Continue(); +} + +void +MobileMessageCursor::Continue(ErrorResult& aRv) +{ + // An ordinary DOMCursor works in following flow: + // + // DOMCursor::Continue() + // +-> DOMCursor::Reset() + // +-> nsICursorContinueCallback::HandleContinue() + // +-> nsIMobileMessageCursorCallback::NotifyCursorResult() + // +-> DOMCursor::FireSuccess() + // + // With no pending result, we call to |DOMCursor::Continue()| as usual. + if (!mPendingResults.Length()) { + DOMCursor::Continue(aRv); + return; + } + + // Otherwise, reset current result and fire a success event with the last + // pending one. + Reset(); + + nsresult rv = FireSuccessWithNextPendingResult(); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + } +} + +nsresult +MobileMessageCursor::FireSuccessWithNextPendingResult() +{ + // We're going to pop the last element from mPendingResults, so it must not + // be empty. + MOZ_ASSERT(mPendingResults.Length()); + + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(GetOwner()))) { + return NS_ERROR_FAILURE; + } + + JSContext* cx = jsapi.cx(); + JS::Rooted val(cx); + nsresult rv = + nsContentUtils::WrapNative(cx, mPendingResults.LastElement(), &val); + NS_ENSURE_SUCCESS(rv, rv); + + mPendingResults.RemoveElementAt(mPendingResults.Length() - 1); + + FireSuccess(val); + return NS_OK; +} + namespace mobilemessage { NS_IMPL_CYCLE_COLLECTION(MobileMessageCursorCallback, mDOMCursor) @@ -55,21 +138,29 @@ MobileMessageCursorCallback::NotifyCursorError(int32_t aError) } NS_IMETHODIMP -MobileMessageCursorCallback::NotifyCursorResult(nsISupports* aResult) +MobileMessageCursorCallback::NotifyCursorResult(nsISupports** aResults, + uint32_t aSize) { MOZ_ASSERT(mDOMCursor); + // We should only be notified with valid results. Or, either + // |NotifyCursorDone()| or |NotifyCursorError()| should be called instead. + MOZ_ASSERT(aResults && *aResults && aSize); + // There shouldn't be unexpected notifications before |Continue()| is called. + nsTArray>& pending = mDOMCursor->mPendingResults; + MOZ_ASSERT(pending.Length() == 0); - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mDOMCursor->GetOwner()))) { - return NS_ERROR_FAILURE; + // Push pending results in reversed order. + pending.SetCapacity(pending.Length() + aSize); + while (aSize) { + --aSize; + pending.AppendElement(aResults[aSize]); } - JSContext* cx = jsapi.cx(); - JS::Rooted wrappedResult(cx); - nsresult rv = nsContentUtils::WrapNative(cx, aResult, &wrappedResult); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = mDOMCursor->FireSuccessWithNextPendingResult(); + if (NS_FAILED(rv)) { + NotifyCursorError(nsIMobileMessageCallback::INTERNAL_ERROR); + } - mDOMCursor->FireSuccess(wrappedResult); return NS_OK; } diff --git a/dom/mobilemessage/MobileMessageCursorCallback.h b/dom/mobilemessage/MobileMessageCursorCallback.h index 460291063a6..890de232694 100644 --- a/dom/mobilemessage/MobileMessageCursorCallback.h +++ b/dom/mobilemessage/MobileMessageCursorCallback.h @@ -6,6 +6,8 @@ #ifndef mozilla_dom_mobilemessage_MobileMessageCursorCallback_h #define mozilla_dom_mobilemessage_MobileMessageCursorCallback_h +#include "mozilla/Attributes.h" +#include "mozilla/dom/DOMCursor.h" #include "nsIMobileMessageCursorCallback.h" #include "nsCycleCollectionParticipant.h" #include "nsCOMPtr.h" @@ -16,12 +18,46 @@ class nsICursorContinueCallback; namespace mozilla { namespace dom { -class DOMCursor; class MobileMessageManager; namespace mobilemessage { +class MobileMessageCursorCallback; +} // namespace mobilemessage -class MobileMessageCursorCallback : public nsIMobileMessageCursorCallback +class MobileMessageCursor MOZ_FINAL : public DOMCursor +{ + friend class mobilemessage::MobileMessageCursorCallback; + +public: + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MobileMessageCursor, DOMCursor) + + MobileMessageCursor(nsPIDOMWindow* aWindow, + nsICursorContinueCallback* aCallback); + + // Override XPIDL continue function to suppress -Werror,-Woverloaded-virtual. + NS_IMETHOD + Continue(void) MOZ_OVERRIDE; + + virtual void + Continue(ErrorResult& aRv) MOZ_OVERRIDE; + +private: + // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor + ~MobileMessageCursor() {} + +private: + // List of read-ahead results in reversed order. + nsTArray> mPendingResults; + + nsresult + FireSuccessWithNextPendingResult(); +}; + +namespace mobilemessage { + +class MobileMessageCursorCallback MOZ_FINAL : public nsIMobileMessageCursorCallback { friend class mozilla::dom::MobileMessageManager; @@ -37,12 +73,13 @@ public: } private: - virtual ~MobileMessageCursorCallback() + // MOZ_FINAL suppresses -Werror,-Wdelete-non-virtual-dtor + ~MobileMessageCursorCallback() { MOZ_COUNT_DTOR(MobileMessageCursorCallback); } - nsRefPtr mDOMCursor; + nsRefPtr mDOMCursor; }; } // namespace mobilemessage diff --git a/dom/mobilemessage/MobileMessageManager.cpp b/dom/mobilemessage/MobileMessageManager.cpp index c5f62a1a784..b812baca855 100644 --- a/dom/mobilemessage/MobileMessageManager.cpp +++ b/dom/mobilemessage/MobileMessageManager.cpp @@ -439,9 +439,10 @@ MobileMessageManager::GetMessages(const MobileMessageFilter& aFilter, return nullptr; } - cursorCallback->mDOMCursor = new DOMCursor(GetOwner(), continueCallback); + cursorCallback->mDOMCursor = + new MobileMessageCursor(GetOwner(), continueCallback); - nsRefPtr cursor = cursorCallback->mDOMCursor; + nsRefPtr cursor(cursorCallback->mDOMCursor); return cursor.forget(); } @@ -491,9 +492,10 @@ MobileMessageManager::GetThreads(ErrorResult& aRv) return nullptr; } - cursorCallback->mDOMCursor = new DOMCursor(GetOwner(), continueCallback); + cursorCallback->mDOMCursor = + new MobileMessageCursor(GetOwner(), continueCallback); - nsRefPtr cursor = cursorCallback->mDOMCursor; + nsRefPtr cursor(cursorCallback->mDOMCursor); return cursor.forget(); } diff --git a/dom/mobilemessage/gonk/MobileMessageDB.jsm b/dom/mobilemessage/gonk/MobileMessageDB.jsm index 0f853ac7340..90a7199c36b 100644 --- a/dom/mobilemessage/gonk/MobileMessageDB.jsm +++ b/dom/mobilemessage/gonk/MobileMessageDB.jsm @@ -3807,7 +3807,7 @@ GetMessagesCursor.prototype = { } let domMessage = self.mmdb.createDomMessageFromRecord(event.target.result); - self.callback.notifyCursorResult(domMessage); + self.callback.notifyCursorResult([domMessage], 1); }; getRequest.onerror = function(event) { if (DEBUG) { @@ -3888,7 +3888,7 @@ GetThreadsCursor.prototype = { threadRecord.body, threadRecord.unreadCount, threadRecord.lastMessageType); - self.callback.notifyCursorResult(thread); + self.callback.notifyCursorResult([thread], 1); }; getRequest.onerror = function(event) { if (DEBUG) { diff --git a/dom/mobilemessage/interfaces/nsIMobileMessageCursorCallback.idl b/dom/mobilemessage/interfaces/nsIMobileMessageCursorCallback.idl index 004333d9a79..4072c3d3f6b 100644 --- a/dom/mobilemessage/interfaces/nsIMobileMessageCursorCallback.idl +++ b/dom/mobilemessage/interfaces/nsIMobileMessageCursorCallback.idl @@ -4,10 +4,11 @@ #include "nsISupports.idl" -[scriptable, builtinclass, uuid(8fd0dba2-032e-4190-a751-07cc3782e93e)] +[scriptable, builtinclass, uuid(134a6958-543b-46e2-b419-4631a2314164)] interface nsIMobileMessageCursorCallback : nsISupports { void notifyCursorError(in long error); - void notifyCursorResult(in nsISupports result); + void notifyCursorResult([array, size_is(size)] in nsISupports results, + in uint32_t size); void notifyCursorDone(); }; diff --git a/dom/mobilemessage/ipc/SmsChild.cpp b/dom/mobilemessage/ipc/SmsChild.cpp index 56b2552dbd9..54672e72510 100644 --- a/dom/mobilemessage/ipc/SmsChild.cpp +++ b/dom/mobilemessage/ipc/SmsChild.cpp @@ -293,22 +293,17 @@ MobileMessageCursorChild::RecvNotifyResult(const MobileMessageCursorData& aData) { MOZ_ASSERT(mCursorCallback); - nsCOMPtr result; switch(aData.type()) { - case MobileMessageCursorData::TMmsMessageData: - result = new MmsMessage(aData.get_MmsMessageData()); + case MobileMessageCursorData::TMobileMessageArrayData: + DoNotifyResult(aData.get_MobileMessageArrayData().messages()); break; - case MobileMessageCursorData::TSmsMessageData: - result = new SmsMessage(aData.get_SmsMessageData()); - break; - case MobileMessageCursorData::TThreadData: - result = new MobileMessageThread(aData.get_ThreadData()); + case MobileMessageCursorData::TThreadArrayData: + DoNotifyResult(aData.get_ThreadArrayData().threads()); break; default: MOZ_CRASH("Received invalid response parameters!"); } - mCursorCallback->NotifyCursorResult(result); return true; } @@ -338,6 +333,48 @@ MobileMessageCursorChild::HandleContinue() return NS_OK; } +void +MobileMessageCursorChild::DoNotifyResult(const nsTArray& aDataArray) +{ + const uint32_t length = aDataArray.Length(); + MOZ_ASSERT(length); + + AutoFallibleTArray autoArray; + NS_ENSURE_TRUE_VOID(autoArray.SetCapacity(length)); + + AutoFallibleTArray, 1> messages; + NS_ENSURE_TRUE_VOID(messages.SetCapacity(length)); + + for (uint32_t i = 0; i < length; i++) { + nsCOMPtr message = CreateMessageFromMessageData(aDataArray[i]); + NS_ENSURE_TRUE_VOID(messages.AppendElement(message)); + NS_ENSURE_TRUE_VOID(autoArray.AppendElement(message.get())); + } + + mCursorCallback->NotifyCursorResult(autoArray.Elements(), length); +} + +void +MobileMessageCursorChild::DoNotifyResult(const nsTArray& aDataArray) +{ + const uint32_t length = aDataArray.Length(); + MOZ_ASSERT(length); + + AutoFallibleTArray autoArray; + NS_ENSURE_TRUE_VOID(autoArray.SetCapacity(length)); + + AutoFallibleTArray, 1> threads; + NS_ENSURE_TRUE_VOID(threads.SetCapacity(length)); + + for (uint32_t i = 0; i < length; i++) { + nsCOMPtr thread = new MobileMessageThread(aDataArray[i]); + NS_ENSURE_TRUE_VOID(threads.AppendElement(thread)); + NS_ENSURE_TRUE_VOID(autoArray.AppendElement(thread.get())); + } + + mCursorCallback->NotifyCursorResult(autoArray.Elements(), length); +} + } // namespace mobilemessage } // namespace dom } // namespace mozilla diff --git a/dom/mobilemessage/ipc/SmsChild.h b/dom/mobilemessage/ipc/SmsChild.h index d541d3ea508..0445f70c529 100644 --- a/dom/mobilemessage/ipc/SmsChild.h +++ b/dom/mobilemessage/ipc/SmsChild.h @@ -129,6 +129,13 @@ protected: virtual bool Recv__delete__(const int32_t& aError) MOZ_OVERRIDE; + +private: + void + DoNotifyResult(const nsTArray& aData); + + void + DoNotifyResult(const nsTArray& aData); }; } // namespace mobilemessage diff --git a/dom/mobilemessage/ipc/SmsParent.cpp b/dom/mobilemessage/ipc/SmsParent.cpp index 3762c196679..c5171beb6ab 100644 --- a/dom/mobilemessage/ipc/SmsParent.cpp +++ b/dom/mobilemessage/ipc/SmsParent.cpp @@ -843,40 +843,59 @@ MobileMessageCursorParent::NotifyCursorError(int32_t aError) } NS_IMETHODIMP -MobileMessageCursorParent::NotifyCursorResult(nsISupports* aResult) +MobileMessageCursorParent::NotifyCursorResult(nsISupports** aResults, + uint32_t aSize) { + MOZ_ASSERT(aResults && *aResults && aSize); + // The child process could die before this asynchronous notification, in which // case ActorDestroy() was called and mContinueCallback is now null. Return an // error here to avoid sending a message to the dead process. NS_ENSURE_TRUE(mContinueCallback, NS_ERROR_FAILURE); - nsCOMPtr iSms = do_QueryInterface(aResult); - if (iSms) { - SmsMessage* message = static_cast(aResult); - return SendNotifyResult(MobileMessageCursorData(message->GetData())) - ? NS_OK : NS_ERROR_FAILURE; - } - - nsCOMPtr iMms = do_QueryInterface(aResult); - if (iMms) { - MmsMessage* message = static_cast(aResult); - ContentParent* parent = static_cast(Manager()->Manager()); - MmsMessageData data; - if (!message->GetData(parent, data)) { - return NS_ERROR_FAILURE; - } - return SendNotifyResult(MobileMessageCursorData(data)) - ? NS_OK : NS_ERROR_FAILURE; - } - - nsCOMPtr iThread = do_QueryInterface(aResult); + nsCOMPtr iThread = + do_QueryInterface(aResults[0]); if (iThread) { - MobileMessageThread* thread = static_cast(aResult); - return SendNotifyResult(MobileMessageCursorData(thread->GetData())) + nsTArray threads; + + for (uint32_t i = 0; i < aSize; i++) { + nsCOMPtr iThread = + do_QueryInterface(aResults[i]); + NS_ENSURE_TRUE(iThread, NS_ERROR_FAILURE); + + MobileMessageThread* thread = + static_cast(iThread.get()); + threads.AppendElement(thread->GetData()); + } + + return SendNotifyResult(MobileMessageCursorData(ThreadArrayData(threads))) ? NS_OK : NS_ERROR_FAILURE; } - MOZ_CRASH("Received invalid response parameters!"); + ContentParent* parent = static_cast(Manager()->Manager()); + nsTArray messages; + for (uint32_t i = 0; i < aSize; i++) { + nsCOMPtr iSms = do_QueryInterface(aResults[i]); + if (iSms) { + SmsMessage* sms = static_cast(iSms.get()); + messages.AppendElement(sms->GetData()); + continue; + } + + nsCOMPtr iMms = do_QueryInterface(aResults[i]); + if (iMms) { + MmsMessage* mms = static_cast(iMms.get()); + MmsMessageData mmsData; + NS_ENSURE_TRUE(mms->GetData(parent, mmsData), NS_ERROR_FAILURE); + messages.AppendElement(mmsData); + continue; + } + + return NS_ERROR_FAILURE; + } + + return SendNotifyResult(MobileMessageCursorData(MobileMessageArrayData(messages))) + ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP diff --git a/dom/mobilemessage/ipc/SmsTypes.ipdlh b/dom/mobilemessage/ipc/SmsTypes.ipdlh index a3450a258a3..3ec0ed0a655 100644 --- a/dom/mobilemessage/ipc/SmsTypes.ipdlh +++ b/dom/mobilemessage/ipc/SmsTypes.ipdlh @@ -99,11 +99,20 @@ struct ThreadData MessageType lastMessageType; }; +struct MobileMessageArrayData +{ + MobileMessageData[] messages; +}; + +struct ThreadArrayData +{ + ThreadData[] threads; +}; + union MobileMessageCursorData { - MmsMessageData; - SmsMessageData; - ThreadData; + MobileMessageArrayData; + ThreadArrayData; }; struct DeletedMessageInfoData diff --git a/dom/mobilemessage/tests/marionette/mmdb_head.js b/dom/mobilemessage/tests/marionette/mmdb_head.js index 6537783cdd8..f6ace2c4cad 100644 --- a/dom/mobilemessage/tests/marionette/mmdb_head.js +++ b/dom/mobilemessage/tests/marionette/mmdb_head.js @@ -303,9 +303,9 @@ function createMmdbCursor(aMmdb, aMethodName) { deferred.reject([aRv, results]); }, - notifyCursorResult: function(aResult) { - ok(true, "notifyCursorResult: " + aResult.id); - results.push(aResult); + notifyCursorResult: function(aResults, aSize) { + ok(true, "notifyCursorResult: " + aResults.map(function(aElement) { return aElement.id; })); + results = results.concat(aResults); cursor.handleContinue(); },