Bug 847738 - B2G MMS: provide nsIDOMMobileMessageManager.getMessage(). r=vicamo sr=sicking a=leo+
authorGene Lian <clian@mozilla.com>
Fri, 08 Mar 2013 11:46:16 +0800
changeset 135470 44b0df91e49d7a9944725d3c1151b63dc2226bc1
parent 135469 605348ff1ee6a4742d480dda503410aa99deb0bc
child 135471 a59af50c07e2a16bd35a2cef8c05ba273c592b09
push idunknown
push userunknown
push dateunknown
reviewersvicamo, sicking, leo
bugs847738
milestone22.0a1
Bug 847738 - B2G MMS: provide nsIDOMMobileMessageManager.getMessage(). r=vicamo sr=sicking a=leo+
dom/mms/src/ril/MmsService.js
dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl
dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
dom/mobilemessage/src/MmsMessage.cpp
dom/mobilemessage/src/MobileMessageCallback.cpp
dom/mobilemessage/src/MobileMessageCallback.h
dom/mobilemessage/src/MobileMessageManager.cpp
dom/mobilemessage/src/SmsMessage.cpp
dom/mobilemessage/src/SmsRequest.cpp
dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
dom/system/gonk/RadioInterfaceLayer.js
--- a/dom/mms/src/ril/MmsService.js
+++ b/dom/mms/src/ril/MmsService.js
@@ -914,72 +914,74 @@ MmsService.prototype = {
   },
 
   /**
    * Convert intermediate message to indexedDB savable object.
    *
    * @param intermediate
    *        Intermediate MMS message parsed from PDU.
    */
-  convertFromIntermediateToSavable: function convertFromIntermediateToSavable(intermediate) {
+  convertIntermediateToSavable: function convertIntermediateToSavable(intermediate) {
     intermediate.type = "mms";
     intermediate.delivery = DELIVERY_NOT_DOWNLOADED;
     intermediate.timestamp = Date.now();
     intermediate.sender = null;
     if (intermediate.headers.from) {
       intermediate.sender = intermediate.headers.from.address;
     } else {
       record.sender = "anonymous";
     }
     intermediate.receivers = [];
     return intermediate;
   },
 
   /**
-   * Convert intermediate message to indexedDB savable object.
+   * Merge the retrieval confirmation into the savable message.
    *
    * @param intermediate
-   *        Intermediate MMS message parsed from PDU.
-   * @param record
-   *        Record stored in the database.
+   *        Intermediate MMS message parsed from PDU, which carries
+            the retrieval confirmation.
+   * @param savable
+   *        The indexedDB savable MMS message, which is going to be
+   *        merged with the extra retrieval confirmation.
    */
-  mergeRetrievalConfirmationIntoRecord: function mergeRetrievalConfirmationIntoRecord(intermediate, record) {
+  mergeRetrievalConfirmation: function mergeRetrievalConfirmation(intermediate, savable) {
     if (intermediate.headers["Date"]) {
-      record.timestamp = Date.parse(intermediate.headers["Date"]);
+      savable.timestamp = Date.parse(intermediate.headers["Date"]);
     }
     if (intermediate.headers.from) {
-      record.sender = intermediate.headers.from.address;
+      savable.sender = intermediate.headers.from.address;
     } else {
-      record.sender = "anonymous";
+      savable.sender = "anonymous";
     }
-    record.receivers = [];
+    savable.receivers = [];
     // We don't have Bcc in recevied MMS message.
     for each (let type in ["cc", "to"]) {
       if (intermediate.headers[type]) {
         if (intermediate.headers[type] instanceof Array) {
           for (let index in intermediate.headers[type]) {
-            record.receivers.push(intermediate.headers[type][index].address)
+            savable.receivers.push(intermediate.headers[type][index].address)
           }
         } else {
-          record.receivers.push(intermediate.headers[type].address);
+          savable.receivers.push(intermediate.headers[type].address);
         }
       }
     }
 
-    record.delivery = DELIVERY_RECEIVED;
+    savable.delivery = DELIVERY_RECEIVED;
     for (let field in intermediate.headers) {
-      record.headers[field] = intermediate.headers[field];
+      savable.headers[field] = intermediate.headers[field];
     }
     if (intermediate.parts) {
-      record.parts = intermediate.parts;
+      savable.parts = intermediate.parts;
     }
     if (intermediate.content) {
-      record.content = intermediate.content;
+      savable.content = intermediate.content;
     }
-    return record;
+    return savable;
   },
 
   /**
    * @param contentLocation
    *        X-Mms-Content-Location of the message.
    * @param callback [optional]
    *        A callback function that takes two arguments: one for X-Mms-Status,
    *        the other parsed MMS message.
@@ -1006,89 +1008,98 @@ MmsService.prototype = {
     let url = notification.headers["x-mms-content-location"].uri;
     // TODO: bug 810091 - don't download message twice on receiving duplicated
     //                     notification
 
     let transactionId = notification.headers["x-mms-transaction-id"];
     // For X-Mms-Report-Allowed
     let wish = notification.headers["x-mms-delivery-report"];
 
-    notification = this.convertFromIntermediateToSavable(notification);
+    let savableMessage = this.convertIntermediateToSavable(notification);
 
-    gMobileMessageDatabaseService.saveReceivedMessage(notification,
-      (function (rv, messageRecord) {
+    gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
+      (function (rv, domMessage) {
         // TODO: Bug 760065 - B2G MMS: Implement MMS DOM API
         // Connect to DOM API for notifing new comming MMS to Gaia.
         let success = Components.isSuccessCode(rv);
         if (!success) {
           // At this point we could send a message to content to notify the
           // user that storing an incoming MMS notify indication failed,
           // ost likely due to a full disk.
-          debug("Could not store MMS " + JSON.stringify(notification) +
+          debug("Could not store MMS " + JSON.stringify(savableMessage) +
                 ", error code " + rv);
           // Because MMSC will resend the notification indication once we don't
           // response the notification. Hope the end user will clean some space
           // for the resended notification indication.
           return;
         }
 
         let retrievalMode = RETRIEVAL_MODE_MANUAL;
         try {
           retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
         } catch (e) {}
 
         if (RETRIEVAL_MODE_AUTOMATIC !== retrievalMode) {
-          let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode ?
-              MMS.MMS_PDU_STATUS_REJECTED : MMS.MMS_PDU_STATUS_DEFERRED;
+          let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
+                        ? MMS.MMS_PDU_STATUS_REJECTED
+                        : MMS.MMS_PDU_STATUS_DEFERRED;
+
           // For X-Mms-Report-Allowed
-          let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish);
+          let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
+                                                    wish);
 
           let transaction = new NotifyResponseTransaction(transactionId,
                                                           mmsStatus,
                                                           reportAllowed);
           transaction.run();
         }
 
         this.retrieveMessage(url, (function responseNotify(mmsStatus,
-                                                           retrievedMsg) {
+                                                           retrievedMessage) {
           // `The absence of the field does not indicate any default
           // value.` So we go checking the same field in retrieved
           // message instead.
-          if ((wish == null) && retrievedMsg) {
-            wish = retrievedMsg.headers["x-mms-delivery-report"];
+          if ((wish == null) && retrievedMessage) {
+            wish = retrievedMessage.headers["x-mms-delivery-report"];
           }
-          let reportAllowed = this.getReportAllowed(
-            this.confSendDeliveryReport, wish);
+          let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
+                                                    wish);
 
           // Should update the retrievedStatus in databse.
-          debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
+          debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
 
           // If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry, we
           // should not store into database.
           if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) {
             let transaction =
-              new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed);
+              new NotifyResponseTransaction(transactionId,
+                                            mmsStatus,
+                                            reportAllowed);
             transaction.run();
           }
 
-          messageRecord = this.mergeRetrievalConfirmationIntoRecord(retrievedMsg, messageRecord);
-          gMobileMessageDatabaseService.saveReceivedMessage(messageRecord,
-            (function (rv, messageRecord) {
+          savableMessage = this.mergeRetrievalConfirmation(retrievedMessage,
+                                                           savableMessage);
+
+          gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
+            (function (rv, domMessage) {
               // TODO: Bug 760065 - B2G MMS: Implement MMS DOM API
               // Connect to DOM API for notifing new comming MMS to Gaia.
               let success = Components.isSuccessCode(rv);
               if (!success) {
                 // At this point we could send a message to content to
                 // notify the user that storing an incoming MMS failed, most
                 // likely due to a full disk.
-                debug("Could not store MMS " + messageRecord.id +
+                debug("Could not store MMS " + domMessage.id +
                       ", error code " + rv);
 
-                let transaction = new NotifyResponseTransaction(
-                  transactionId, MMS.MMS_PDU_STATUS_DEFERRED, reportAllowed);
+                let transaction =
+                  new NotifyResponseTransaction(transactionId,
+                                                MMS.MMS_PDU_STATUS_DEFERRED,
+                                                reportAllowed);
                 transaction.run();
                 return;
               }
             }).bind(this)
           );
         }).bind(this));
         return;
       }).bind(this)
@@ -1172,107 +1183,64 @@ MmsService.prototype = {
     message["deliveryStatusRequested"] = true;
     message["timestamp"] = Date.now();
     message["receivers"] = receivers;
 
     debug("createSavableFromParams: message: " + JSON.stringify(message));
     return message;
   },
 
-  createMmsMessageFromRecord: function createMmsMessageFromRecord(aRecord) {
-    debug("createMmsMessageFromRecord: aRecord: " + JSON.stringify(aRecord));
-
-    let headers = aRecord["headers"];
-    let subject = headers["subject"];
-    if (subject == undefined) {
-      subject = "";
-    }
-    let smil = "";
-    let attachments = [];
-    let parts = aRecord.parts;
-    if (parts) {
-      for (let i = 0; i < parts.length; i++) {
-        let part = parts[i];
-        let partHeaders = part["headers"];
-        let partContent = part["content"];
-        // Don't need to make the SMIL part if it's present.
-        if (partHeaders["content-type"]["media"] == "application/smil") {
-          smil = part.content;
-          continue;
-        }
-        attachments.push({
-          "id": partHeaders["content-id"],
-          "location": partHeaders["content-location"],
-          "content": partContent
-        });
-      }
-    }
-
-    debug("createMmsMessageFromRecord: attachments: " + JSON.stringify(attachments));
-    return gMobileMessageService.createMmsMessage(aRecord.id,
-                                                  aRecord.delivery,
-                                                  aRecord.deliveryStatus,
-                                                  aRecord.sender,
-                                                  aRecord.receivers,
-                                                  aRecord.timestamp,
-                                                  aRecord.read,
-                                                  subject,
-                                                  smil,
-                                                  attachments);
-  },
-
   // nsIMmsService
 
   send: function send(aParams, aRequest) {
     debug("send: aParams: " + JSON.stringify(aParams));
     if (aParams.receivers.length == 0) {
       aRequest.notifySendMmsMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
       return;
     }
 
     let self = this;
 
-    let sendTransactionCb = function sendTransactionCb(aIsSentSuccess, aMmsMessage) {
-      debug("sendTransactionCb: aIsSentSuccess: " + aIsSentSuccess);
+    let sendTransactionCb = function sendTransactionCb(aRecordId, aIsSentSuccess) {
+      debug("The success status of sending transaction: " + aIsSentSuccess);
       gMobileMessageDatabaseService
-        .setMessageDelivery(aMmsMessage.id,
+        .setMessageDelivery(aRecordId,
                             null,
                             aIsSentSuccess ? "sent" : "error",
                             aIsSentSuccess ? null : "error",
-                            function notifySetDeliveryResult(setDeliveryRv, record) {
+                            function notifySetDeliveryResult(aRv, aDomMessage) {
         debug("Marking the delivery state/staus is done. Notify sent or failed.");
         if (!aIsSentSuccess) {
           aRequest.notifySendMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-          Services.obs.notifyObservers(aMmsMessage, kMmsFailedObserverTopic, null);
+          Services.obs.notifyObservers(aDomMessage, kMmsFailedObserverTopic, null);
           return;
         }
-        aRequest.notifyMessageSent(aMmsMessage);
-        Services.obs.notifyObservers(aMmsMessage, kMmsSentObserverTopic, null);
+        aRequest.notifyMessageSent(aDomMessage);
+        Services.obs.notifyObservers(aDomMessage, kMmsSentObserverTopic, null);
       });
     };
 
     let savableMessage = this.createSavableFromParams(aParams);
     gMobileMessageDatabaseService
       .saveSendingMessage(savableMessage,
-                          function notifySendingResult(sendingRv, sendingRecord) {
+                          function notifySendingResult(aRv, aDomMessage) {
       debug("Saving sending message is done. Start to send.");
-      let mmsMessage = self.createMmsMessageFromRecord(sendingRecord);
-      Services.obs.notifyObservers(mmsMessage, kMmsSendingObserverTopic, null);
+      Services.obs.notifyObservers(aDomMessage, kMmsSendingObserverTopic, null);
       let sendTransaction;
       try {
-        sendTransaction = new SendTransaction(sendingRecord);
+        sendTransaction = new SendTransaction(savableMessage);
       } catch (e) {
-        debug("Fail to create a SendTransaction instance.");
-        sendTransactionCb(false, mmsMessage);
+        debug("Exception: fail to create a SendTransaction instance.");
+        sendTransactionCb(aDomMessage.id, false);
         return;
       }
       sendTransaction.run(function callback(aMmsStatus, aMsg) {
         let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK);
-        debug("The returned status of sendTransaction.run(): " + aMmsStatus);
-        sendTransactionCb(isSentSuccess, mmsMessage);
+        debug("The sending status of sendTransaction.run(): " + aMmsStatus);
+        sendTransactionCb(aDomMessage.id, isSentSuccess);
       });
     });
   },
 
   // nsIWapPushApplication
 
   receiveWapPush: function receiveWapPush(array, length, offset, options) {
     let data = {array: array, offset: offset};
--- a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
+++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
@@ -14,31 +14,31 @@ interface nsIDOMBlob;
 dictionary MmsParameters
 {
   jsval      receivers; // DOMString[]
   DOMString? subject;
   DOMString? smil;
   jsval      attachments; // MmsAttachment[]
 };
 
-[scriptable, builtinclass, uuid(f020e48e-84c7-11e2-939e-53a3106dde16)]
+[scriptable, builtinclass, uuid(4e3b73f6-8d45-11e2-a590-534c61399fe5)]
 interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
 {
   nsIDOMMozSmsSegmentInfo getSegmentInfoForText(in DOMString text);
 
   // The first parameter can be either a DOMString (only one number) or an array
   // of DOMStrings.
   // The method returns a SmsRequest object if one number has been passed.
   // An array of SmsRequest objects otherwise.
   jsval send(in jsval number, in DOMString message);
 
   nsIDOMDOMRequest sendMMS(in jsval parameters /* MmsParameters */);
 
   [binaryname(GetMessageMoz)]
-  nsIDOMMozSmsRequest getMessage(in long id);
+  nsIDOMDOMRequest getMessage(in long id);
 
   // The parameter can be either a message id or a SmsMessage.
   nsIDOMMozSmsRequest delete(in jsval param);
 
   nsIDOMMozSmsRequest getMessages(in nsIDOMMozSmsFilter filter, in boolean reverse);
 
   nsIDOMMozSmsRequest markMessageRead(in long id, in boolean aValue);
 
--- a/dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl
+++ b/dom/mobilemessage/interfaces/nsIDOMMozMmsMessage.idl
@@ -8,19 +8,24 @@ interface nsIDOMBlob;
 
 dictionary MmsAttachment
 {
   DOMString? id;
   DOMString? location;
   nsIDOMBlob content;
 };
 
-[scriptable, builtinclass, uuid(df002ad2-71d7-11e2-9f1c-af6fa139069f)]
+[scriptable, builtinclass, uuid(c9683d00-88a6-11e2-85cc-57a33dfbea3b)]
 interface nsIDOMMozMmsMessage : nsISupports
 {
+  /**
+   * |type| is always "mms".
+   */
+  readonly attribute DOMString type;
+
   readonly attribute long      id;
 
   /**
    * Should be "not-downloaded", "received", "sending", "sent" or "error".
    */
   readonly attribute DOMString state;
 
   [implicit_jscontext]
--- a/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
+++ b/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
@@ -1,17 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, builtinclass, uuid(59fc5ea8-33fe-40ba-890b-b9abaeb5ac26)]
+[scriptable, builtinclass, uuid(d4d848c4-88a6-11e2-b6da-6f252cbfa716)]
 interface nsIDOMMozSmsMessage : nsISupports
 {
+  /**
+   * |type| is always "sms".
+   */
+  readonly attribute DOMString type;
+
   readonly attribute long      id;
 
   /**
    * Should be "received", "sending", "sent" or "error".
    *
    * TODO Bug 850530 Please see the IDL proposal at Bug 760065.
    * We need to s/delivery/state in nsIDOMMozSmsMessage, which sounds
    * a better name and can be consistent with nsIDOMMozMmsMessage.
--- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
@@ -1,54 +1,58 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsIDOMMozSmsMessage;
-
 dictionary SmsThreadListItem
 {
   DOMString senderOrReceiver;
   unsigned long long timestamp;
   DOMString body;
   unsigned long long unreadCount;
 };
 
-[scriptable, builtinclass, uuid(18d7c4da-848f-11e2-a3a5-f749c4ba31b5)]
+[scriptable, builtinclass, uuid(edb1de12-8d58-11e2-b382-7bf132b20cb2)]
 interface nsIMobileMessageCallback : nsISupports
 {
   /**
    * All SMS related errors that could apply to SmsRequest objects.
    * Make sure to keep this list in sync with the list in:
    * embedding/android/GeckoSmsManager.java
    */
   const unsigned short SUCCESS_NO_ERROR = 0;
   const unsigned short NO_SIGNAL_ERROR  = 1;
   const unsigned short NOT_FOUND_ERROR  = 2;
   const unsigned short UNKNOWN_ERROR    = 3;
   const unsigned short INTERNAL_ERROR   = 4;
 
   /**
-   * |message| can be either |nsIDOMMozSmsMessage| or |nsIDOMMozMmsMessage|.
+   * |message| can be nsIDOMMoz{Mms,Sms}Message.
    */
   void notifyMessageSent(in nsISupports message);
   void notifySendMessageFailed(in long error);
 
-  void notifyMessageGot(in nsIDOMMozSmsMessage message);
+  /**
+   * |message| can be nsIDOMMoz{Mms,Sms}Message.
+   */
+  void notifyMessageGot(in nsISupports message);
   void notifyGetMessageFailed(in long error);
 
   void notifyMessageDeleted(in boolean deleted);
   void notifyDeleteMessageFailed(in long error);
 
+  /**
+   * |message| can be nsIDOMMoz{Mms,Sms}Message.
+   */
   void notifyMessageListCreated(in long listId,
-                                in nsIDOMMozSmsMessage message);
+                                in nsISupports message);
   void notifyReadMessageListFailed(in long error);
-  void notifyNextMessageInListGot(in nsIDOMMozSmsMessage message);
+  void notifyNextMessageInListGot(in nsISupports message);
   void notifyNoMessageInList();
 
   void notifyMessageMarkedRead(in boolean read);
   void notifyMarkMessageReadFailed(in long error);
 
   [implicit_jscontext]
   void notifyThreadList(in jsval threadList);
   void notifyThreadListFailed(in long error);
--- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
+++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 #include "nsISupports.idl"
 #include "nsIMobileMessageDatabaseService.idl"
 
-[scriptable, function, uuid(0bffae74-71db-11e2-962a-73cf64d6393e)]
+[scriptable, function, uuid(92986322-8d56-11e2-8816-73a531c493c2)]
 interface nsIRilMobileMessageDatabaseCallback : nsISupports
 {
   /**
-   * |aRecord| Object: the mobile-message database record
+   * |aDomMessage|: the nsIDOMMoz{Mms,Sms}Message
    */
-  void notify(in nsresult aRv, in jsval aRecord);
+  void notify(in nsresult aRv, in nsISupports aDomMessage);
 };
 
 [scriptable, uuid(a31b1716-8631-11e2-afaa-2fbd087f426e)]
 interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
 {
   /**
    * |aMessage| Object: should contain the following properties for internal use:
    *   - |type| DOMString: "sms" or "mms"
--- a/dom/mobilemessage/src/MmsMessage.cpp
+++ b/dom/mobilemessage/src/MmsMessage.cpp
@@ -202,16 +202,23 @@ MmsMessage::Create(int32_t              
                                                          aSubject,
                                                          aSmil,
                                                          attachments);
   message.forget(aMessage);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+MmsMessage::GetType(nsAString& aType)
+{
+  aType = NS_LITERAL_STRING("mms");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 MmsMessage::GetId(int32_t* aId)
 {
   *aId = mId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MmsMessage::GetState(nsAString& aState)
--- a/dom/mobilemessage/src/MobileMessageCallback.cpp
+++ b/dom/mobilemessage/src/MobileMessageCallback.cpp
@@ -30,18 +30,18 @@ MobileMessageCallback::MobileMessageCall
   : mDOMRequest(aDOMRequest)
 {
 }
 
 MobileMessageCallback::~MobileMessageCallback()
 {
 }
 
-NS_IMETHODIMP
-MobileMessageCallback::NotifyMessageSent(nsISupports *aMessage)
+nsresult
+MobileMessageCallback::NotifySuccess(nsISupports *aMessage)
 {
   nsresult rv;
   nsIScriptContext* scriptContext = mDOMRequest->GetContextForEventHandlers(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
 
   AutoPushJSContext cx(scriptContext->GetNativeContext());
   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
@@ -56,18 +56,18 @@ MobileMessageCallback::NotifyMessageSent
   nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
 
   rs->FireSuccess(mDOMRequest, wrappedMessage);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-MobileMessageCallback::NotifySendMessageFailed(int32_t aError)
+nsresult
+MobileMessageCallback::NotifyError(int32_t aError)
 {
   nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
 
   switch (aError) {
     case nsIMobileMessageCallback::NO_SIGNAL_ERROR:
       return rs->FireError(mDOMRequest, NS_LITERAL_STRING("NoSignalError"));
     case nsIMobileMessageCallback::NOT_FOUND_ERROR:
@@ -79,54 +79,66 @@ MobileMessageCallback::NotifySendMessage
     default: // SUCCESS_NO_ERROR is handled above.
       MOZ_ASSERT(false, "Unknown error value.");
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileMessageCallback::NotifyMessageGot(nsIDOMMozSmsMessage *aMessage)
+MobileMessageCallback::NotifyMessageSent(nsISupports *aMessage)
+{
+  return NotifySuccess(aMessage);
+}
+
+NS_IMETHODIMP
+MobileMessageCallback::NotifySendMessageFailed(int32_t aError)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return NotifyError(aError);
+}
+
+NS_IMETHODIMP
+MobileMessageCallback::NotifyMessageGot(nsISupports *aMessage)
+{
+  return NotifySuccess(aMessage);
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyGetMessageFailed(int32_t aError)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return NotifyError(aError);
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyMessageDeleted(bool aDeleted)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyDeleteMessageFailed(int32_t aError)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyMessageListCreated(int32_t aListId,
-                                                nsIDOMMozSmsMessage *aMessage)
+                                                nsISupports *aMessage)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyReadMessageListFailed(int32_t aError)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-MobileMessageCallback::NotifyNextMessageInListGot(nsIDOMMozSmsMessage *aMessage)
+MobileMessageCallback::NotifyNextMessageInListGot(nsISupports *aMessage)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifyNoMessageInList()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/mobilemessage/src/MobileMessageCallback.h
+++ b/dom/mobilemessage/src/MobileMessageCallback.h
@@ -23,15 +23,18 @@ public:
   NS_DECL_NSIMOBILEMESSAGECALLBACK
 
   MobileMessageCallback(DOMRequest* aDOMRequest);
 
 private:
   ~MobileMessageCallback();
 
   nsRefPtr<DOMRequest> mDOMRequest;
+
+  nsresult NotifySuccess(nsISupports *aMessage);
+  nsresult NotifyError(int32_t aError);
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_MobileMessageCallback_h
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -198,26 +198,28 @@ MobileMessageManager::SendMMS(const JS::
   nsresult rv = mmsService->Send(aParams, msgCallback);
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(aRequest);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMMozSmsRequest** aRequest)
+MobileMessageManager::GetMessageMoz(int32_t aId, nsIDOMDOMRequest** aRequest)
 {
-  nsCOMPtr<nsIDOMMozSmsRequest> req = SmsRequest::Create(this);
   nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService =
     do_GetService(MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mobileMessageDBService, NS_ERROR_FAILURE);
-  nsCOMPtr<nsIMobileMessageCallback> forwarder =
-    new SmsRequestForwarder(static_cast<SmsRequest*>(req.get()));
-  mobileMessageDBService->GetMessageMoz(aId, forwarder);
-  req.forget(aRequest);
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(GetOwner());
+  nsCOMPtr<nsIMobileMessageCallback> msgCallback = new MobileMessageCallback(request);
+  nsresult rv = mobileMessageDBService->GetMessageMoz(aId, msgCallback);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  request.forget(aRequest);
   return NS_OK;
 }
 
 nsresult
 MobileMessageManager::Delete(int32_t aId, nsIDOMMozSmsRequest** aRequest)
 {
   nsCOMPtr<nsIDOMMozSmsRequest> req = SmsRequest::Create(this);
   nsCOMPtr<nsIMobileMessageDatabaseService> mobileMessageDBService =
--- a/dom/mobilemessage/src/SmsMessage.cpp
+++ b/dom/mobilemessage/src/SmsMessage.cpp
@@ -131,16 +131,23 @@ SmsMessage::Create(int32_t aId,
 
 const SmsMessageData&
 SmsMessage::GetData() const
 {
   return mData;
 }
 
 NS_IMETHODIMP
+SmsMessage::GetType(nsAString& aType)
+{
+  aType = NS_LITERAL_STRING("sms");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 SmsMessage::GetId(int32_t* aId)
 {
   *aId = mData.id();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SmsMessage::GetDelivery(nsAString& aDelivery)
--- a/dom/mobilemessage/src/SmsRequest.cpp
+++ b/dom/mobilemessage/src/SmsRequest.cpp
@@ -365,23 +365,31 @@ SmsRequest::NotifySendMessageFailed(int3
   if (mParent) {
     return SendMessageReply(MessageReply(ReplyMessageSendFail(aError)));
   }
   return NotifyError(aError);
 
 }
 
 NS_IMETHODIMP
-SmsRequest::NotifyMessageGot(nsIDOMMozSmsMessage *aMessage)
+SmsRequest::NotifyMessageGot(nsISupports *aMessage)
 {
+  // We only support nsIDOMMozSmsMessage for SmsRequest.
+  nsCOMPtr<nsIDOMMozSmsMessage> message(do_QueryInterface(aMessage));
+  if (!message) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  SmsMessage* smsMessage = static_cast<SmsMessage*>(message.get());
+
   if (mParent) {
-    SmsMessageData data = SmsMessageData(static_cast<SmsMessage*>(aMessage)->GetData());
+    SmsMessageData data = SmsMessageData(smsMessage->GetData());
     return SendMessageReply(MessageReply(ReplyGetMessage(data)));
   }
-  return NotifySuccess<nsIDOMMozSmsMessage*>(aMessage);
+  return NotifySuccess<nsIDOMMozSmsMessage*>(smsMessage);
 
 }
 
 NS_IMETHODIMP
 SmsRequest::NotifyGetMessageFailed(int32_t aError)
 {
   if (mParent) {
     return SendMessageReply(MessageReply(ReplyGetMessageFail(aError)));
@@ -403,25 +411,32 @@ SmsRequest::NotifyDeleteMessageFailed(in
 {
   if (mParent) {
     return SendMessageReply(MessageReply(ReplyMessageDeleteFail(aError)));
   }
   return NotifyError(aError);
 }
 
 NS_IMETHODIMP
-SmsRequest::NotifyMessageListCreated(int32_t aListId,
-                                     nsIDOMMozSmsMessage *aMessage)
+SmsRequest::NotifyMessageListCreated(int32_t aListId, nsISupports *aMessage)
 {
+  // We only support nsIDOMMozSmsMessage for SmsRequest.
+  nsCOMPtr<nsIDOMMozSmsMessage> message(do_QueryInterface(aMessage));
+  if (!message) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  SmsMessage* smsMessage = static_cast<SmsMessage*>(message.get());
+
   if (mParent) {
-    SmsMessageData data = SmsMessageData(static_cast<SmsMessage*>(aMessage)->GetData());
+    SmsMessageData data = SmsMessageData(smsMessage->GetData());
     return SendMessageReply(MessageReply(ReplyCreateMessageList(aListId, data)));
   } else {
     nsCOMPtr<SmsCursor> cursor = new SmsCursor(aListId, this);
-    cursor->SetMessage(aMessage);
+    cursor->SetMessage(smsMessage);
     return NotifySuccess<nsIDOMMozSmsCursor*>(cursor);
   }
 }
 
 NS_IMETHODIMP
 SmsRequest::NotifyReadMessageListFailed(int32_t aError)
 {
   if (mParent) {
@@ -429,25 +444,33 @@ SmsRequest::NotifyReadMessageListFailed(
   }
   if (mCursor) {
     static_cast<SmsCursor*>(mCursor.get())->Disconnect();
   }
   return NotifyError(aError);
 }
 
 NS_IMETHODIMP
-SmsRequest::NotifyNextMessageInListGot(nsIDOMMozSmsMessage *aMessage)
+SmsRequest::NotifyNextMessageInListGot(nsISupports *aMessage)
 {
+  // We only support nsIDOMMozSmsMessage for SmsRequest.
+  nsCOMPtr<nsIDOMMozSmsMessage> message(do_QueryInterface(aMessage));
+  if (!message) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  SmsMessage* smsMessage = static_cast<SmsMessage*>(message.get());
+
   if (mParent) {
-    SmsMessageData data = SmsMessageData(static_cast<SmsMessage*>(aMessage)->GetData());
+    SmsMessageData data = SmsMessageData(smsMessage->GetData());
     return SendMessageReply(MessageReply(ReplyGetNextMessage(data)));
   }
   nsCOMPtr<SmsCursor> cursor = static_cast<SmsCursor*>(mCursor.get());
   NS_ASSERTION(cursor, "Request should have an cursor in that case!");
-  cursor->SetMessage(aMessage);
+  cursor->SetMessage(smsMessage);
   return NotifySuccess<nsIDOMMozSmsCursor*>(cursor);
 }
 
 NS_IMETHODIMP
 SmsRequest::NotifyNoMessageInList()
 {
   if (mParent) {
     return SendMessageReply(MessageReply(ReplyNoMessageInList()));
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -17,16 +17,17 @@ const DEBUG = false;
 const DB_NAME = "sms";
 const DB_VERSION = 8;
 const MESSAGE_STORE_NAME = "sms";
 const THREAD_STORE_NAME = "thread";
 const PARTICIPANT_STORE_NAME = "participant";
 const MOST_RECENT_STORE_NAME = "most-recent";
 
 const DELIVERY_SENDING = "sending";
+const DELIVERY_SENT = "sent";
 const DELIVERY_RECEIVED = "received";
 
 const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable";
 const DELIVERY_STATUS_SUCCESS = "success";
 const DELIVERY_STATUS_PENDING = "pending";
 const DELIVERY_STATUS_ERROR = "error";
 
 const MESSAGE_CLASS_NORMAL = "normal";
@@ -112,17 +113,17 @@ MobileMessageDatabaseService.prototype =
    * This object keeps the message lists associated with each search. Each
    * message list is stored as an array of primary keys.
    */
   messageLists: null,
 
   lastMessageListId: 0,
 
   /**
-   * Last sms object store key value in the database.
+   * Last sms/mms object store key value in the database.
    */
   lastMessageId: 0,
 
   /**
    * nsIObserver
    */
   observe: function observe() {},
 
@@ -553,29 +554,83 @@ MobileMessageDatabaseService.prototype =
         addThreadRequest.onerror = function () {
           // Error in fetching message records, check next most recent record.
           mostRecentCursor.continue();
         };
       });
     };
   },
 
-  createSmsMessageFromRecord: function createSmsMessageFromRecord(aMessageRecord) {
+  createDomMessageFromRecord: function createDomMessageFromRecord(aMessageRecord) {
     if (DEBUG) {
-      debug("createSmsMessageFromRecord: " + JSON.stringify(aMessageRecord));
+      debug("createDomMessageFromRecord: " + JSON.stringify(aMessageRecord));
     }
-    return gMobileMessageService.createSmsMessage(aMessageRecord.id,
-                                                  aMessageRecord.delivery,
-                                                  aMessageRecord.deliveryStatus,
-                                                  aMessageRecord.sender,
-                                                  aMessageRecord.receiver,
-                                                  aMessageRecord.body,
-                                                  aMessageRecord.messageClass,
-                                                  aMessageRecord.timestamp,
-                                                  aMessageRecord.read);
+    if (aMessageRecord.type == "sms") {
+      return gMobileMessageService.createSmsMessage(aMessageRecord.id,
+                                                    aMessageRecord.delivery,
+                                                    aMessageRecord.deliveryStatus,
+                                                    aMessageRecord.sender,
+                                                    aMessageRecord.receiver,
+                                                    aMessageRecord.body,
+                                                    aMessageRecord.messageClass,
+                                                    aMessageRecord.timestamp,
+                                                    aMessageRecord.read);
+    } else if (aMessageRecord.type == "mms") {
+      let headers = aMessageRecord["headers"];
+
+      let subject = headers["subject"];
+      if (subject == undefined) {
+        subject = "";
+      }
+
+      let smil = "";
+      let attachments = [];
+      let parts = aMessageRecord.parts;
+      if (parts) {
+        for (let i = 0; i < parts.length; i++) {
+          let part = parts[i];
+          let partHeaders = part["headers"];
+          let partContent = part["content"];
+          // Don't need to make the SMIL part if it's present.
+          if (partHeaders["content-type"]["media"] == "application/smil") {
+            smil = part.content;
+            continue;
+          }
+          attachments.push({
+            "id": partHeaders["content-id"],
+            "location": partHeaders["content-location"],
+            "content": partContent
+          });
+        }
+      }
+      if (DEBUG) {
+        debug("createDomMessageFromRecord: createMmsMessage: " + JSON.stringify({
+          id: aMessageRecord.id,
+          delivery: aMessageRecord.delivery,
+          deliveryStatus: aMessageRecord.deliveryStatus,
+          sender: aMessageRecord.sender,
+          receivers: aMessageRecord.receivers,
+          timestamp: aMessageRecord.timestamp,
+          read: aMessageRecord.read,
+          subject: subject,
+          smil: smil,
+          attachments: attachments
+        }));
+      }
+      return gMobileMessageService.createMmsMessage(aMessageRecord.id,
+                                                    aMessageRecord.delivery,
+                                                    aMessageRecord.deliveryStatus,
+                                                    aMessageRecord.sender,
+                                                    aMessageRecord.receivers,
+                                                    aMessageRecord.timestamp,
+                                                    aMessageRecord.read,
+                                                    subject,
+                                                    smil,
+                                                    attachments);
+    }
   },
 
   /**
    * Queue up passed message id, reply if necessary. 'aMessageId' = 0 for no
    * more messages, negtive for errors and valid otherwise.
    */
   onNextMessageInListGot: function onNextMessageInListGot(
       aMessageStore, aMessageList, aMessageId) {
@@ -597,67 +652,68 @@ MobileMessageDatabaseService.prototype =
       if (DEBUG) debug("Cursor.continue() not called yet");
       return;
     }
 
     // We assume there is only one request waiting throughout the message list
     // retrieving process. So we don't bother continuing to process further
     // waiting requests here. This assumption comes from SmsCursor::Continue()
     // implementation.
-    let smsRequest = aMessageList.requestWaiting;
+    let request = aMessageList.requestWaiting;
     aMessageList.requestWaiting = null;
 
     if (!aMessageList.results.length) {
       // No fetched results yet.
       if (!aMessageList.processing) {
         if (DEBUG) debug("No messages matching the filter criteria");
-        smsRequest.notifyNoMessageInList();
+        request.notifyNoMessageInList();
       }
       // No fetched results yet and still processing. Let's wait a bit more.
       return;
     }
 
     if (aMessageList.results[0] < 0) {
       // An previous error found. Keep the answer in results so that we can
       // reply INTERNAL_ERROR for further requests.
       if (DEBUG) debug("An previous error found");
-      smsRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+      request.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
       return;
     }
 
     let firstMessageId = aMessageList.results.shift();
     if (DEBUG) debug ("Fetching message " + firstMessageId);
 
     let getRequest = aMessageStore.get(firstMessageId);
     let self = this;
     getRequest.onsuccess = function onsuccess(event) {
-      let sms = self.createSmsMessageFromRecord(event.target.result);
+      let messageRecord = event.target.result;
+      let domMessage = self.createDomMessageFromRecord(messageRecord);
       if (aMessageList.listId >= 0) {
         if (DEBUG) {
           debug("notifyNextMessageInListGot - listId: "
                 + aMessageList.listId + ", messageId: " + firstMessageId);
         }
-        smsRequest.notifyNextMessageInListGot(sms);
+        request.notifyNextMessageInListGot(domMessage);
       } else {
         self.lastMessageListId += 1;
         aMessageList.listId = self.lastMessageListId;
         self.messageLists[self.lastMessageListId] = aMessageList;
         if (DEBUG) {
           debug("notifyMessageListCreated - listId: "
                 + aMessageList.listId + ", messageId: " + firstMessageId);
         }
-        smsRequest.notifyMessageListCreated(aMessageList.listId, sms);
+        request.notifyMessageListCreated(aMessageList.listId, domMessage);
       }
     };
     getRequest.onerror = function onerror(event) {
       if (DEBUG) {
         debug("notifyReadMessageListFailed - listId: "
               + aMessageList.listId + ", messageId: " + firstMessageId);
       }
-      smsRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+      request.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
     };
   },
 
   /**
    * Queue up {aMessageId, aTimestamp} pairs, find out intersections and report
    * to onNextMessageInListGot. Return true if it is still possible to have
    * another match.
    */
@@ -971,17 +1027,18 @@ MobileMessageDatabaseService.prototype =
     }
     if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord));
 
     let self = this;
     function notifyResult(rv) {
       if (!aCallback) {
         return;
       }
-      aCallback.notify(rv, aMessageRecord);
+      let domMessage = self.createDomMessageFromRecord(aMessageRecord);
+      aCallback.notify(rv, domMessage);
     }
 
     this.newTxn(READ_WRITE, function(error, txn, stores) {
       if (error) {
         // TODO bug 832140 check event.target.errorCode
         notifyResult(Cr.NS_ERROR_FAILURE);
         return;
       }
@@ -1183,17 +1240,18 @@ MobileMessageDatabaseService.prototype =
     }
 
     let self = this;
     let messageRecord;
     function notifyResult(rv) {
       if (!callback) {
         return;
       }
-      callback.notify(rv, messageRecord);
+      let domMessage = self.createDomMessageFromRecord(messageRecord);
+      callback.notify(rv, domMessage);
     }
 
     this.newTxn(READ_WRITE, function (error, txn, messageStore) {
       if (error) {
         // TODO bug 832140 check event.target.errorCode
         notifyResult(Cr.NS_ERROR_FAILURE);
         return;
       }
@@ -1308,18 +1366,18 @@ MobileMessageDatabaseService.prototype =
         if (messageRecord.id != messageId) {
           if (DEBUG) {
             debug("Requested message ID (" + messageId + ") is " +
                   "different from the one we got");
           }
           aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR);
           return;
         }
-        let sms = self.createSmsMessageFromRecord(messageRecord);
-        aRequest.notifyMessageGot(sms);
+        let domMessage = self.createDomMessageFromRecord(messageRecord);
+        aRequest.notifyMessageGot(domMessage);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) {
           if (event.target)
             debug("Caught error on transaction", event.target.errorCode);
         }
         //TODO look at event.target.errorCode, pick appropriate error constant
@@ -1442,17 +1500,17 @@ MobileMessageDatabaseService.prototype =
         listId: -1,
         reverse: reverse,
         processing: true,
         stop: false,
         // Local contexts for multiple filter targets' case.
         contexts: null,
         // Result queues for multiple numbers filter's case.
         numberQueues: null,
-        // Pending createMessageList or getNextMessageInList SmsRequest.
+        // Pending createMessageList or getNextMessageInList request.
         requestWaiting: aRequest,
         results: []
       };
 
       let onNextMessageInListGotCb =
         self.onNextMessageInListGot.bind(self, messageStore, messageList);
 
       let singleFilterSuccessCb = function onsfsuccess(event) {
@@ -1753,18 +1811,18 @@ MobileMessageDatabaseService.prototype =
       };
 
       txn.oncomplete = function oncomplete(event) {
         if (DEBUG) debug("Transaction " + txn + " completed.");
         if (!messageRecord) {
           if (DEBUG) debug("Could not get message id " + messageId);
           aRequest.notifyReadMessageListFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
         }
-        let sms = self.createSmsMessageFromRecord(messageRecord);
-        aRequest.notifyNextMessageInListGot(sms);
+        let domMessage = self.createDomMessageFromRecord(messageRecord);
+        aRequest.notifyNextMessageInListGot(domMessage);
       };
 
       txn.onerror = function onerror(event) {
         //TODO check event.target.errorCode
         if (DEBUG) {
           debug("Error retrieving message id: " + messageId +
                 ". Error code: " + event.target.errorCode);
         }
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1480,28 +1480,16 @@ RadioInterfaceLayer.prototype = {
       sourcePort: message.header.originatorPort,
       destinationAddress: this.rilContext.iccInfo.msisdn,
       destinationPort: message.header.destinationPort,
     };
     WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
                                      0, options);
   },
 
-  createSmsMessageFromRecord: function createSmsMessageFromRecord(aRecord) {
-    return gMobileMessageService.createSmsMessage(aRecord.id,
-                                                  aRecord.delivery,
-                                                  aRecord.deliveryStatus,
-                                                  aRecord.sender,
-                                                  aRecord.receiver,
-                                                  aRecord.body,
-                                                  aRecord.messageClass,
-                                                  aRecord.timestamp,
-                                                  aRecord.read);
-  },
-
   portAddressedSmsApps: null,
   handleSmsReceived: function handleSmsReceived(message) {
     debug("handleSmsReceived: " + JSON.stringify(message));
 
     // FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers
     if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
       message.fullData = new Uint8Array(message.fullData);
     }
@@ -1535,18 +1523,17 @@ RadioInterfaceLayer.prototype = {
     let mwi = message.mwi;
     if (mwi) {
       mwi.returnNumber = message.sender;
       mwi.returnMessage = message.fullBody;
       this._sendVoicemailMessage("RIL:VoicemailNotification", mwi);
       return true;
     }
 
-    let notifyReceived = function notifyReceived(rv, record) {
-      let sms = this.createSmsMessageFromRecord(record);
+    let notifyReceived = function notifyReceived(rv, domMessage) {
       let success = Components.isSuccessCode(rv);
 
       // Acknowledge the reception of the SMS.
       message.rilMessageType = "ackSMS";
       if (!success) {
         message.result = RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED;
       }
       this.worker.postMessage(message);
@@ -1565,29 +1552,40 @@ RadioInterfaceLayer.prototype = {
         sender: message.sender,
         receiver: message.receiver,
         body: message.fullBody,
         messageClass: message.messageClass,
         timestamp: message.timestamp,
         read: false
       });
 
-      Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
+      Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
     }.bind(this);
 
     if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) {
       message.id = gMobileMessageDatabaseService.saveReceivedMessage(message,
                                                                      notifyReceived);
     } else {
       message.id = -1;
       message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
       message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
       message.read = false;
 
-      notifyReceived(Cr.NS_OK, message);
+      let domMessage =
+        gMobileMessageService.createSmsMessage(message.id,
+                                               message.delivery,
+                                               message.deliveryStatus,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.messageClass,
+                                               message.timestamp,
+                                               message.read);
+
+      notifyReceived(Cr.NS_OK, domMessage);
     }
 
     // SMS ACK will be sent in notifyReceived. Return false here.
     return false;
   },
 
   /**
    * Local storage for sent SMS messages.
@@ -1611,63 +1609,60 @@ RadioInterfaceLayer.prototype = {
     if (!options) {
       return;
     }
 
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      DOM_MOBILE_MESSAGE_DELIVERY_SENT,
                                                      options.sms.deliveryStatus,
-                                                     function notifyResult(rv, record) {
-      let sms = this.createSmsMessageFromRecord(record);
+                                                     function notifyResult(rv, domMessage) {
       //TODO bug 832140 handle !Components.isSuccessCode(rv)
       gSystemMessenger.broadcastMessage("sms-sent",
                                         {id: options.sms.id,
                                          delivery: DOM_MOBILE_MESSAGE_DELIVERY_SENT,
                                          deliveryStatus: options.sms.deliveryStatus,
                                          sender: message.sender || null,
                                          receiver: options.sms.receiver,
                                          body: options.sms.body,
                                          messageClass: options.sms.messageClass,
                                          timestamp: options.sms.timestamp,
                                          read: true});
 
       if (!options.requestStatusReport) {
         // No more used if STATUS-REPORT not requested.
         delete this._sentSmsEnvelopes[message.envelopeId];
       } else {
-        options.sms = sms;
+        options.sms = domMessage;
       }
 
-      options.request.notifyMessageSent(sms);
-
-      Services.obs.notifyObservers(sms, kSmsSentObserverTopic, null);
+      options.request.notifyMessageSent(domMessage);
+      Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null);
     }.bind(this));
   },
 
   handleSmsDelivery: function handleSmsDelivery(message) {
     debug("handleSmsDelivery: " + JSON.stringify(message));
 
     let options = this._sentSmsEnvelopes[message.envelopeId];
     if (!options) {
       return;
     }
     delete this._sentSmsEnvelopes[message.envelopeId];
 
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      options.sms.delivery,
                                                      message.deliveryStatus,
-                                                     function notifyResult(rv, record) {
-      let sms = this.createSmsMessageFromRecord(record);
+                                                     function notifyResult(rv, domMessage) {
       //TODO bug 832140 handle !Components.isSuccessCode(rv)
       let topic = (message.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS)
                   ? kSmsDeliverySuccessObserverTopic
                   : kSmsDeliveryErrorObserverTopic;
-      Services.obs.notifyObservers(sms, topic, null);
+      Services.obs.notifyObservers(domMessage, topic, null);
     }.bind(this));
   },
 
   handleSmsSendFailed: function handleSmsSendFailed(message) {
     debug("handleSmsSendFailed: " + JSON.stringify(message));
 
     let options = this._sentSmsEnvelopes[message.envelopeId];
     if (!options) {
@@ -1681,21 +1676,20 @@ RadioInterfaceLayer.prototype = {
         error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR;
         break;
     }
 
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
                                                      null,
                                                      DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
                                                      RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
-                                                     function notifyResult(rv, record) {
-      let sms = this.createSmsMessageFromRecord(record);
+                                                     function notifyResult(rv, domMessage) {
       //TODO bug 832140 handle !Components.isSuccessCode(rv)
       options.request.notifySendMessageFailed(error);
-      Services.obs.notifyObservers(sms, kSmsFailedObserverTopic, null);
+      Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null);
     }.bind(this));
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     let data = this.rilContext.data;
@@ -2684,25 +2678,24 @@ RadioInterfaceLayer.prototype = {
       type: "sms",
       receiver: number,
       body: message,
       deliveryStatusRequested: options.requestStatusReport,
       timestamp: Date.now()
     };
 
     let id = gMobileMessageDatabaseService.saveSendingMessage(sendingMessage,
-                                                              function notifyResult(rv, record) {
-      let sms = this.createSmsMessageFromRecord(record);
+                                                              function notifyResult(rv, domMessage) {
       //TODO bug 832140 handle !Components.isSuccessCode(rv)
-      Services.obs.notifyObservers(sms, kSmsSendingObserverTopic, null);
+      Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
 
       // Keep current SMS message info for sent/delivered notifications
       options.envelopeId = this.createSmsEnvelope({
           request: request,
-          sms: sms,
+          sms: domMessage,
           requestStatusReport: options.requestStatusReport
       });
 
       if (PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
         this.worker.postMessage(options);
       } else {
         debug('Number ' + options.number + ' is not sendable.');
         this.handleSmsSendFailed(options);