Merge mozilla-central and birch
authorEd Morley <emorley@mozilla.com>
Mon, 22 Jul 2013 16:52:40 +0100
changeset 151710 d4398449bad81bea296f60c1f2789a6aa40ffadd
parent 151701 308e3cf5ba75fdf8ed3bdd3dc766410b708b98ef (current diff)
parent 151709 749593c0086bab53f89b432933007719dcc16cfd (diff)
child 151711 bda9723bdcccc835b45e179289a0a05a0468846c
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and birch
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "e04bb3527c33dd6771e63397a3b52a4b9a5fce4e", 
+    "revision": "4b4d244b83fe6eb64e0701b01a2b00a5970aeb49", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -143,17 +143,17 @@ static const char* sBluetoothDBusSignals
   "type='signal',interface='org.bluez.AudioSink'"
 };
 
 /**
  * DBus Connection held for the BluetoothCommandThread to use. Should never be
  * used by any other thread.
  *
  */
-static nsAutoPtr<RawDBusConnection> gThreadConnection;
+static nsRefPtr<RawDBusConnection> gThreadConnection;
 static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
 static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
 static Atomic<int32_t> sIsPairing;
 static nsString sAdapterPath;
 
 typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
 typedef bool (*FilterFunc)(const BluetoothValue&);
 typedef void (*SinkCallback)(DBusMessage*, void*);
@@ -1111,119 +1111,107 @@ AgentEventFilter(DBusConnection *conn, D
 
   return DBUS_HANDLER_RESULT_HANDLED;
 
 handle_error:
   NS_WARNING(NS_ConvertUTF16toUTF8(errorStr).get());
   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-static const DBusObjectPathVTable agentVtable = {
-  NULL, AgentEventFilter, NULL, NULL, NULL, NULL
+class RegisterAgentReplyHandler : public DBusReplyHandler
+{
+public:
+  RegisterAgentReplyHandler(const DBusObjectPathVTable* aAgentVTable)
+    : mAgentVTable(aAgentVTable)
+  {
+    MOZ_ASSERT(aAgentVTable);
+  }
+
+  void Handle(DBusMessage* aReply)
+  {
+    MOZ_ASSERT(!NS_IsMainThread()); // DBus thread
+
+    if (!aReply || (dbus_message_get_type(aReply) == DBUS_MESSAGE_TYPE_ERROR)) {
+      return;
+    }
+
+    nsRefPtr<RawDBusConnection> threadConnection = gThreadConnection;
+
+    if (!threadConnection.get()) {
+      BT_WARNING("%s: DBus connection has been closed.", __FUNCTION__);
+      return;
+    }
+
+    // There is no "RegisterAgent" function defined in device interface.
+    // When we call "CreatePairedDevice", it will do device agent registration
+    // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
+    if (!dbus_connection_register_object_path(threadConnection->GetConnection(),
+                                              KEY_REMOTE_AGENT,
+                                              mAgentVTable,
+                                              NULL)) {
+      BT_WARNING("%s: Can't register object path %s for remote device agent!",
+                 __FUNCTION__, KEY_REMOTE_AGENT);
+      return;
+    }
+
+    NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
+  }
+
+private:
+  const DBusObjectPathVTable* mAgentVTable;
 };
 
-// Local agent means agent for Adapter, not agent for Device. Some signals
-// will be passed to local agent, some will be passed to device agent.
-// For example, if a remote device would like to pair with us, then the
-// signal will be passed to local agent. If we start pairing process with
-// calling CreatePairedDevice, we'll get signal which should be passed to
-// device agent.
-static bool
-RegisterLocalAgent(const char* adapterPath,
-                   const char* agentPath,
-                   const char* capabilities)
+static bool RegisterAgent(const DBusObjectPathVTable* aAgentVTable)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
+  const char* agentPath = KEY_LOCAL_AGENT;
+  const char* capabilities = B2G_AGENT_CAPABILITIES;
+
+  // Local agent means agent for Adapter, not agent for Device. Some signals
+  // will be passed to local agent, some will be passed to device agent.
+  // For example, if a remote device would like to pair with us, then the
+  // signal will be passed to local agent. If we start pairing process with
+  // calling CreatePairedDevice, we'll get signal which should be passed to
+  // device agent.
   if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
-                                            agentPath,
-                                            &agentVtable,
+                                            KEY_LOCAL_AGENT,
+                                            aAgentVTable,
                                             NULL)) {
     BT_WARNING("%s: Can't register object path %s for agent!",
-                __FUNCTION__, agentPath);
-    return false;
-  }
-
-  DBusMessage* msg =
-    dbus_message_new_method_call("org.bluez", adapterPath,
-                                 DBUS_ADAPTER_IFACE, "RegisterAgent");
-  if (!msg) {
-    BT_WARNING("%s: Can't allocate new method call for agent!", __FUNCTION__);
-    return false;
-  }
-
-  if (!dbus_message_append_args(msg,
-                                DBUS_TYPE_OBJECT_PATH, &agentPath,
-                                DBUS_TYPE_STRING, &capabilities,
-                                DBUS_TYPE_INVALID)) {
-    BT_WARNING("%s: Couldn't append arguments to dbus message.", __FUNCTION__);
+               __FUNCTION__, KEY_LOCAL_AGENT);
     return false;
   }
 
-  DBusError err;
-  dbus_error_init(&err);
-
-  DBusMessage* reply =
-    dbus_connection_send_with_reply_and_block(gThreadConnection->GetConnection(),
-                                              msg, -1, &err);
-  dbus_message_unref(msg);
-
-  if (!reply) {
-    if (dbus_error_is_set(&err)) {
-      if (!strcmp(err.name, "org.bluez.Error.AlreadyExists")) {
-        LOG_AND_FREE_DBUS_ERROR(&err);
-#ifdef DEBUG
-        BT_WARNING("Agent already registered, still returning true");
-#endif
-      } else {
-        LOG_AND_FREE_DBUS_ERROR(&err);
-        BT_WARNING("%s: Can't register agent!", __FUNCTION__);
-        return false;
-      }
-    }
-  } else {
-    dbus_message_unref(reply);
-  }
-
-  dbus_connection_flush(gThreadConnection->GetConnection());
-  return true;
-}
-
-static bool
-RegisterAgent()
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-
-  if (!RegisterLocalAgent(NS_ConvertUTF16toUTF8(sAdapterPath).get(),
-                          KEY_LOCAL_AGENT,
-                          B2G_AGENT_CAPABILITIES)) {
-    return false;
-  }
-
-  // There is no "RegisterAgent" function defined in device interface.
-  // When we call "CreatePairedDevice", it will do device agent registration
-  // for us. (See maemo.org/api_refs/5.0/beta/bluez/adapter.html)
-  if (!dbus_connection_register_object_path(gThreadConnection->GetConnection(),
-                                            KEY_REMOTE_AGENT,
-                                            &agentVtable,
-                                            NULL)) {
-    BT_WARNING("%s: Can't register object path %s for remote device agent!",
-               __FUNCTION__, KEY_REMOTE_AGENT);
-
-    return false;
-  }
+  nsRefPtr<RegisterAgentReplyHandler> handler = new RegisterAgentReplyHandler(aAgentVTable);
+  MOZ_ASSERT(handler.get());
+
+  bool success = dbus_func_args_async(gThreadConnection->GetConnection(), -1,
+                                      RegisterAgentReplyHandler::Callback, handler.get(),
+                                      NS_ConvertUTF16toUTF8(sAdapterPath).get(),
+                                      DBUS_ADAPTER_IFACE, "RegisterAgent",
+                                      DBUS_TYPE_OBJECT_PATH, &agentPath,
+                                      DBUS_TYPE_STRING, &capabilities,
+                                      DBUS_TYPE_INVALID);
+  NS_ENSURE_TRUE(success, false);
+
+  handler.forget();
 
   return true;
 }
 
 class PrepareAdapterRunnable : public nsRunnable
 {
 public:
-  nsresult Run()
+  NS_IMETHOD Run()
   {
+    static const DBusObjectPathVTable sAgentVTable = {
+      NULL, AgentEventFilter, NULL, NULL, NULL, NULL
+    };
+
     MOZ_ASSERT(!NS_IsMainThread());
     nsTArray<uint32_t> uuids;
 
     uuids.AppendElement(BluetoothServiceClass::HANDSFREE_AG);
     uuids.AppendElement(BluetoothServiceClass::HEADSET_AG);
     uuids.AppendElement(BluetoothServiceClass::OBJECT_PUSH);
 
     // TODO/qdot: This needs to be held for the life of the bluetooth connection
@@ -1232,22 +1220,21 @@ public:
 
     if (!BluetoothDBusService::AddReservedServicesInternal(uuids, handles)) {
       NS_WARNING("Failed to add reserved services");
 #ifdef MOZ_WIDGET_GONK
       return NS_ERROR_FAILURE;
 #endif
     }
 
-    if(!RegisterAgent()) {
+    if(!RegisterAgent(&sAgentVTable)) {
       NS_WARNING("Failed to register agent");
       return NS_ERROR_FAILURE;
     }
 
-    NS_DispatchToMainThread(new PrepareProfileManagersRunnable());
     return NS_OK;
   }
 };
 
 class PrepareAdapterTask : public nsRunnable
 {
 public:
   PrepareAdapterTask(const nsAString& aPath)
--- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
+++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
@@ -8,31 +8,32 @@ dictionary SmsThreadListItem
 {
   unsigned long long id;
   DOMString senderOrReceiver;
   unsigned long long timestamp;
   DOMString body;
   unsigned long long unreadCount;
 };
 
-[scriptable, builtinclass, uuid(e73baef1-7a9f-48c1-8b04-20d9d16c4974)]
+[scriptable, builtinclass, uuid(a22d9aae-ee0a-11e2-949e-e770d0d3883f)]
 interface nsIMobileMessageCallback : nsISupports
 {
   /**
    * All SMS related errors.
    * 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;
   const unsigned short NO_SIM_CARD_ERROR         = 5;
   const unsigned short RADIO_DISABLED_ERROR      = 6;
+  const unsigned short INVALID_ADDRESS_ERROR     = 7;
 
   /**
    * |message| can be nsIDOMMoz{Mms,Sms}Message.
    */
   void notifyMessageSent(in nsISupports message);
   void notifySendMessageFailed(in long error);
 
   /**
--- a/dom/mobilemessage/src/MobileMessageCallback.cpp
+++ b/dom/mobilemessage/src/MobileMessageCallback.cpp
@@ -86,16 +86,19 @@ MobileMessageCallback::NotifyError(int32
       mDOMRequest->FireError(NS_LITERAL_STRING("InternalError"));
       break;
     case nsIMobileMessageCallback::NO_SIM_CARD_ERROR:
       mDOMRequest->FireError(NS_LITERAL_STRING("NoSimCardError"));
       break;
     case nsIMobileMessageCallback::RADIO_DISABLED_ERROR:
       mDOMRequest->FireError(NS_LITERAL_STRING("RadioDisabledError"));
       break;
+    case nsIMobileMessageCallback::INVALID_ADDRESS_ERROR:
+      mDOMRequest->FireError(NS_LITERAL_STRING("InvalidAddressError"));
+      break;
     default: // SUCCESS_NO_ERROR is handled above.
       MOZ_CRASH("Should never get here!");
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -1544,54 +1544,65 @@ MmsService.prototype = {
   },
 
   /**
    * A utility function to convert the MmsParameters dictionary object
    * to a database-savable message.
    *
    * @param aParams
    *        The MmsParameters dictionay object.
+   * @param aMessage (output)
+   *        The database-savable message.
+   * Return the error code by veryfying if the |aParams| is valid or not.
    *
    * Notes:
    *
    * OMA-TS-MMS-CONF-V1_3-20110913-A section 10.2.2 "Message Content Encoding":
    *
    * A name for multipart object SHALL be encoded using name-parameter for Content-Type
    * header in WSP multipart headers. In decoding, name-parameter of Content-Type SHALL
    * be used if available. If name-parameter of Content-Type is not available, filename
    * parameter of Content-Disposition header SHALL be used if available. If neither
    * name-parameter of Content-Type header nor filename parameter of Content-Disposition
    * header is available, Content-Location header SHALL be used if available.
    */
-  createSavableFromParams: function createSavableFromParams(aParams) {
+  createSavableFromParams: function createSavableFromParams(aParams, aMessage) {
     if (DEBUG) debug("createSavableFromParams: aParams: " + JSON.stringify(aParams));
-    let message = {};
+
+    let isAddrValid = true;
     let smil = aParams.smil;
 
-    // |message.headers|
-    let headers = message["headers"] = {};
+    // |aMessage.headers|
+    let headers = aMessage["headers"] = {};
     let receivers = aParams.receivers;
     if (receivers.length != 0) {
       let headersTo = headers["to"] = [];
       for (let i = 0; i < receivers.length; i++) {
         let normalizedAddress = PhoneNumberUtils.normalize(receivers[i], false);
         if (DEBUG) debug("createSavableFromParams: normalize phone number " +
                          "from " + receivers[i] + " to " + normalizedAddress);
 
         headersTo.push({"address": normalizedAddress, "type": "PLMN"});
+
+        // Check if the address is valid to send MMS.
+        if (!PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
+          if (DEBUG) debug("Error! Address is invalid to send MMS: " +
+                           normalizedAddress);
+          isAddrValid = false;
+        }
       }
     }
     if (aParams.subject) {
       headers["subject"] = aParams.subject;
     }
 
-    // |message.parts|
+    // |aMessage.parts|
     let attachments = aParams.attachments;
     if (attachments.length != 0 || smil) {
-      let parts = message["parts"] = [];
+      let parts = aMessage["parts"] = [];
 
       // Set the SMIL part if needed.
       if (smil) {
         let part = {
           "headers": {
             "content-type": {
               "media": "application/smil",
               "params": {
@@ -1636,32 +1647,69 @@ MmsService.prototype = {
           },
           "content": content
         };
         parts.push(part);
       }
     }
 
     // The following attributes are needed for saving message into DB.
-    message["type"] = "mms";
-    message["deliveryStatusRequested"] = true;
-    message["timestamp"] = Date.now();
-    message["receivers"] = receivers;
-    message["sender"] = this.getMsisdn();
+    aMessage["type"] = "mms";
+    aMessage["deliveryStatusRequested"] = true;
+    aMessage["timestamp"] = Date.now();
+    aMessage["receivers"] = receivers;
+    aMessage["sender"] = this.getMsisdn();
 
-    if (DEBUG) debug("createSavableFromParams: message: " + JSON.stringify(message));
-    return message;
+    if (DEBUG) debug("createSavableFromParams: aMessage: " +
+                     JSON.stringify(aMessage));
+
+    return isAddrValid ? Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR
+                       : Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
   },
 
   // nsIMmsService
 
   send: function send(aParams, aRequest) {
     if (DEBUG) debug("send: aParams: " + JSON.stringify(aParams));
-    if (aParams.receivers.length == 0) {
-      aRequest.notifySendMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+
+    // Note that the following sanity checks for |aParams| should be consistent
+    // with the checks in SmsIPCService.GetSendMmsMessageRequestFromParams.
+
+    // Check if |aParams| is valid.
+    if (aParams == null || typeof aParams != "object") {
+      if (DEBUG) debug("Error! 'aParams' should be a non-null object.");
+      throw Cr.NS_ERROR_INVALID_ARG;
+      return;
+    }
+
+    // Check if |receivers| is valid.
+    if (!Array.isArray(aParams.receivers)) {
+      if (DEBUG) debug("Error! 'receivers' should be an array.");
+      throw Cr.NS_ERROR_INVALID_ARG;
+      return;
+    }
+
+    // Check if |subject| is valid.
+    if (aParams.subject != null && typeof aParams.subject != "string") {
+      if (DEBUG) debug("Error! 'subject' should be a string if passed.");
+      throw Cr.NS_ERROR_INVALID_ARG;
+      return;
+    }
+
+    // Check if |smil| is valid.
+    if (aParams.smil != null && typeof aParams.smil != "string") {
+      if (DEBUG) debug("Error! 'smil' should be a string if passed.");
+      throw Cr.NS_ERROR_INVALID_ARG;
+      return;
+    }
+
+    // Check if |attachments| is valid.
+    if (!Array.isArray(aParams.attachments)) {
+      if (DEBUG) debug("Error! 'attachments' should be an array.");
+      throw Cr.NS_ERROR_INVALID_ARG;
       return;
     }
 
     let self = this;
 
     let sendTransactionCb = function sendTransactionCb(aDomMessage, aErrorCode) {
       if (DEBUG) debug("The error code of sending transaction: " + aErrorCode);
 
@@ -1678,59 +1726,65 @@ MmsService.prototype = {
         .setMessageDelivery(aDomMessage.id,
                             null,
                             isSentSuccess ? DELIVERY_SENT : DELIVERY_ERROR,
                             isSentSuccess ? null : DELIVERY_STATUS_ERROR,
                             function notifySetDeliveryResult(aRv, aDomMessage) {
         if (DEBUG) debug("Marking the delivery state/staus is done. Notify sent or failed.");
         // TODO bug 832140 handle !Components.isSuccessCode(aRv)
         if (!isSentSuccess) {
-          if (DEBUG) debug("Send MMS fail. aParams.receivers = " +
-                           JSON.stringify(aParams.receivers));
+          if (DEBUG) debug("Sending MMS failed.");
           aRequest.notifySendMessageFailed(aErrorCode);
           Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
           return;
         }
 
-        if (DEBUG) debug("Send MMS successful. aParams.receivers = " +
-                         JSON.stringify(aParams.receivers));
+        if (DEBUG) debug("Sending MMS succeeded.");
 
         // Notifying observers the MMS message is sent.
         self.broadcastSentMessageEvent(aDomMessage);
 
         // Return the request after sending the MMS message successfully.
         aRequest.notifyMessageSent(aDomMessage);
       });
     };
 
-    let savableMessage = this.createSavableFromParams(aParams);
+    let savableMessage = {};
+    let errorCode = this.createSavableFromParams(aParams, savableMessage);
     gMobileMessageDatabaseService
       .saveSendingMessage(savableMessage,
                           function notifySendingResult(aRv, aDomMessage) {
       if (DEBUG) debug("Saving sending message is done. Start to send.");
 
       // TODO bug 832140 handle !Components.isSuccessCode(aRv)
       Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null);
 
+      if (errorCode !== Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR) {
+        if (DEBUG) debug("Error! The params for sending MMS are invalid.");
+        sendTransactionCb(aDomMessage, errorCode);
+        return;
+      }
+
       // For radio disabled error.
       if (gMmsConnection.radioDisabled) {
         if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
         sendTransactionCb(aDomMessage,
                           Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR);
         return;
       }
 
       // For SIM card is not ready.
       if (gRadioInterface.rilContext.cardState != "ready") {
         if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
         sendTransactionCb(aDomMessage,
                           Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR);
         return;
       }
 
+      // This is the entry point starting to send MMS.
       let sendTransaction;
       try {
         sendTransaction = new SendTransaction(aDomMessage.id, savableMessage);
       } catch (e) {
         if (DEBUG) debug("Exception: fail to create a SendTransaction instance.");
         sendTransactionCb(aDomMessage,
                           Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
         return;
--- a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
@@ -1225,17 +1225,17 @@ MobileMessageDatabaseService.prototype =
     }
     aMessage.deliveryIndex = [aMessage.delivery, timestamp];
 
     return this.saveRecord(aMessage, threadParticipants, aCallback);
   },
 
   saveSendingMessage: function saveSendingMessage(aMessage, aCallback) {
     if ((aMessage.type != "sms" && aMessage.type != "mms") ||
-        (aMessage.type == "sms" && !aMessage.receiver) ||
+        (aMessage.type == "sms" && aMessage.receiver == undefined) ||
         (aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
         aMessage.deliveryStatusRequested == undefined ||
         aMessage.timestamp == undefined) {
       if (aCallback) {
         aCallback.notify(Cr.NS_ERROR_FAILURE, null);
       }
       return;
     }
--- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
@@ -216,17 +216,17 @@ GetSendMmsMessageRequestFromParams(const
 }
 
 NS_IMETHODIMP
 SmsIPCService::Send(const JS::Value& aParameters,
                     nsIMobileMessageCallback *aRequest)
 {
   SendMmsMessageRequest req;
   if (!GetSendMmsMessageRequestFromParams(aParameters, req)) {
-    return NS_ERROR_UNEXPECTED;
+    return NS_ERROR_INVALID_ARG;
   }
   return SendRequest(SendMessageRequest(req), aRequest);
 }
 
 NS_IMETHODIMP
 SmsIPCService::Retrieve(int32_t aId, nsIMobileMessageCallback *aRequest)
 {
   return SendRequest(RetrieveMessageRequest(aId), aRequest);
--- a/dom/mobilemessage/tests/marionette/manifest.ini
+++ b/dom/mobilemessage/tests/marionette/manifest.ini
@@ -29,10 +29,11 @@ qemu = true
 [test_bug814761.js]
 [test_strict_7bit_encoding.js]
 [test_incoming_max_segments.js]
 [test_outgoing_max_segments.js]
 [test_update_thread_record_in_delete.js]
 [test_massive_incoming_delete.js]
 [test_getsegmentinfofortext.js]
 [test_phone_number_normalization.js]
+[test_invalid_address.js]
 [test_mmsmessage_attachments.js]
 [test_getthreads.js]
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/tests/marionette/test_invalid_address.js
@@ -0,0 +1,142 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+
+const MMS_MAX_LENGTH_SUBJECT = 40;
+
+SpecialPowers.addPermission("sms", true, document);
+SpecialPowers.setBoolPref("dom.sms.enabled", true);
+
+let tasks = {
+  // List of test fuctions. Each of them should call |tasks.next()| when
+  // completed or |tasks.finish()| to jump to the last one.
+  _tasks: [],
+  _nextTaskIndex: 0,
+
+  push: function push(func) {
+    this._tasks.push(func);
+  },
+
+  next: function next() {
+    let index = this._nextTaskIndex++;
+    let task = this._tasks[index];
+    try {
+      task();
+    } catch (ex) {
+      ok(false, "test task[" + index + "] throws: " + ex);
+      // Run last task as clean up if possible.
+      if (index != this._tasks.length - 1) {
+        this.finish();
+      }
+    }
+  },
+
+  finish: function finish() {
+    this._tasks[this._tasks.length - 1]();
+  },
+
+  run: function run() {
+    this.next();
+  }
+};
+
+let mozMobileMessage;
+
+function getAllMessages(callback, filter, reverse) {
+  if (!filter) {
+    filter = new MozSmsFilter;
+  }
+  let messages = [];
+  let request = mozMobileMessage.getMessages(filter, reverse || false);
+  request.onsuccess = function(event) {
+    if (request.result) {
+      messages.push(request.result);
+      request.continue();
+      return;
+    }
+
+    window.setTimeout(callback.bind(null, messages), 0);
+  }
+}
+
+function deleteAllMessages() {
+  getAllMessages(function deleteAll(messages) {
+    let message = messages.shift();
+    if (!message) {
+      ok(true, "all messages deleted");
+      tasks.next();
+      return;
+    }
+
+    let request = mozMobileMessage.delete(message.id);
+    request.onsuccess = deleteAll.bind(null, messages);
+    request.onerror = function (event) {
+      ok(false, "failed to delete all messages");
+      tasks.finish();
+    }
+  });
+}
+
+function testInvalidAddressForSMS(aInvalidAddr)  {
+  log("mozMobileMessage.send(...) should get 'InvalidAddressError' error " +
+      "when attempting to send SMS to: " + aInvalidAddr);
+
+  let request = mozMobileMessage.send(aInvalidAddr, "Test");
+
+  request.onerror = function(event) {
+    log("Received 'onerror' DOMRequest event.");
+    let error = event.target.error;
+    ok(error instanceof DOMError, "should be a valid DOMError object");
+    ok(error.name === "InvalidAddressError", "should be 'InvalidAddressError'");
+    tasks.next();
+  };
+}
+
+function testInvalidAddressForMMS(aInvalidAddrs)  {
+  log("mozMobileMessage.sendMMS(...) should get 'InvalidAddressError' error " +
+      "when attempting to send MMS to: " + aInvalidAddrs);
+
+  let request = mozMobileMessage.sendMMS({
+    subject: "Test",
+    receivers: aInvalidAddrs,
+    attachments: [],
+  });
+
+  request.onerror = function(event) {
+    log("Received 'onerror' DOMRequest event.");
+    let error = event.target.error;
+    ok(error instanceof DOMError, "should be a valid DOMError object");
+    ok(error.name === "InvalidAddressError", "should be 'InvalidAddressError'");
+    tasks.next();
+  };
+}
+
+tasks.push(function () {
+  log("Verifying initial state.");
+
+  mozMobileMessage = window.navigator.mozMobileMessage;
+  ok(mozMobileMessage instanceof MozMobileMessageManager);
+
+  tasks.next();
+});
+
+// Test sending SMS to invalid addresses.
+tasks.push(testInvalidAddressForSMS.bind(this, "&%&"));
+tasks.push(testInvalidAddressForSMS.bind(this, ""));
+
+// Test sending MMS to invalid addresses.
+tasks.push(testInvalidAddressForMMS.bind(this, ["&%&"]));
+tasks.push(testInvalidAddressForMMS.bind(this, [""]));
+tasks.push(testInvalidAddressForMMS.bind(this, ["123", "&%&", "456"]));
+
+tasks.push(deleteAllMessages);
+
+// WARNING: All tasks should be pushed before this!!!
+tasks.push(function cleanUp() {
+  SpecialPowers.removePermission("sms", document);
+  SpecialPowers.clearUserPref("dom.sms.enabled");
+  finish();
+});
+
+tasks.run();
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -3151,17 +3151,21 @@ RadioInterface.prototype = {
       function notifyResult(rv, domMessage) {
 
         // TODO bug 832140 handle !Components.isSuccessCode(rv)
         Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null);
 
         // If the radio is disabled or the SIM card is not ready, just directly
         // return with the corresponding error code.
         let errorCode;
-        if (!this._radioEnabled) {
+        if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
+          if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " +
+                                options.number);
+          errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
+        } else if (!this._radioEnabled) {
           if (DEBUG) this.debug("Error! Radio is disabled when sending SMS.");
           errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
         } else if (this.rilContext.cardState != "ready") {
           if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS.");
           errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
         }
         if (errorCode) {
           gMobileMessageDatabaseService
@@ -3179,22 +3183,18 @@ RadioInterface.prototype = {
 
         // Keep current SMS message info for sent/delivered notifications
         options.envelopeId = this.createSmsEnvelope({
           request: request,
           sms: domMessage,
           requestStatusReport: options.requestStatusReport
         });
 
-        if (PhoneNumberUtils.isPlainPhoneNumber(options.number)) {
-          this.worker.postMessage(options);
-        } else {
-          if (DEBUG) this.debug('Number ' + options.number + ' is not sendable.');
-          this.handleSmsSendFailed(options);
-        }
+        // This is the entry point starting to send SMS.
+        this.worker.postMessage(options);
 
       }.bind(this));
   },
 
   registerDataCallCallback: function registerDataCallCallback(callback) {
     if (this._datacall_callbacks) {
       if (this._datacall_callbacks.indexOf(callback) != -1) {
         throw new Error("Already registered this callback!");
--- a/embedding/android/GeckoSmsManager.java
+++ b/embedding/android/GeckoSmsManager.java
@@ -303,16 +303,17 @@ public class GeckoSmsManager
    */
   public final static int kNoError                = 0;
   public final static int kNoSignalError          = 1;
   public final static int kNotFoundError          = 2;
   public final static int kUnknownError           = 3;
   public final static int kInternalError          = 4;
   public final static int kNoSimCardError         = 5;
   public final static int kRadioDisabledError     = 6;
+  public final static int kInvalidAddressError    = 7;
 
   private final static int kMaxMessageSize    = 160;
 
   private final static Uri kSmsContentUri     = Uri.parse("content://sms");
   private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
 
   private final static int kSmsTypeInbox      = 1;
   private final static int kSmsTypeSentbox    = 2;
--- a/ipc/dbus/DBusThread.cpp
+++ b/ipc/dbus/DBusThread.cpp
@@ -481,28 +481,28 @@ public:
 
     return NS_OK;
   }
 
 private:
   DBusThread* mConnection;
 };
 
-static StaticAutoPtr<DBusThread> gDBusThread;
-static StaticRefPtr<nsIThread>   gDBusServiceThread;
+static StaticRefPtr<DBusThread> gDBusThread;
+static StaticRefPtr<nsIThread>  gDBusServiceThread;
 
 // Startup/Shutdown utility functions
 
 bool
 StartDBus()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   NS_ENSURE_TRUE(!gDBusThread, true);
 
-  nsAutoPtr<DBusThread> dbusThread(new DBusThread());
+  nsRefPtr<DBusThread> dbusThread(new DBusThread());
 
   bool eventLoopStarted = dbusThread->Initialize();
   NS_ENSURE_TRUE(eventLoopStarted, false);
 
   nsresult rv;
 
   if (!gDBusServiceThread) {
     nsIThread* dbusServiceThread;
@@ -516,17 +516,17 @@ StartDBus()
 #endif
 
   nsRefPtr<nsIRunnable> pollTask(new DBusPollTask(dbusThread));
   NS_ENSURE_TRUE(pollTask, false);
 
   rv = gDBusServiceThread->Dispatch(pollTask, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, false);
 
-  gDBusThread = dbusThread.forget();
+  gDBusThread = dbusThread;
 
   return true;
 }
 
 bool
 StopDBus()
 {
   MOZ_ASSERT(!NS_IsMainThread());
--- a/ipc/dbus/RawDBusConnection.cpp
+++ b/ipc/dbus/RawDBusConnection.cpp
@@ -6,20 +6,22 @@
 
 #include "RawDBusConnection.h"
 #include <dbus/dbus.h>
 
 using namespace mozilla::ipc;
 
 bool RawDBusConnection::sDBusIsInit(false);
 
-RawDBusConnection::RawDBusConnection() {
+RawDBusConnection::RawDBusConnection()
+{
 }
 
-RawDBusConnection::~RawDBusConnection() {
+RawDBusConnection::~RawDBusConnection()
+{
 }
 
 nsresult RawDBusConnection::EstablishDBusConnection()
 {
   if (!sDBusIsInit) {
     dbus_bool_t success = dbus_threads_init_default();
     NS_ENSURE_TRUE(success == TRUE, NS_ERROR_FAILURE);
     sDBusIsInit = true;
--- a/ipc/dbus/RawDBusConnection.h
+++ b/ipc/dbus/RawDBusConnection.h
@@ -9,33 +9,34 @@
 
 #include <string.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string>
 #include <stdlib.h>
 #include "nscore.h"
 #include "mozilla/Scoped.h"
+#include <mozilla/RefPtr.h>
 #include <mozilla/Mutex.h>
 
 struct DBusConnection;
 
 namespace mozilla {
 namespace ipc {
 
-class RawDBusConnection
+class RawDBusConnection : public detail::RefCounted<RawDBusConnection, detail::AtomicRefCount>
 {
   struct ScopedDBusConnectionPtrTraits : ScopedFreePtrTraits<DBusConnection>
   {
     static void release(DBusConnection* ptr);
   };
 
 public:
   RawDBusConnection();
-  ~RawDBusConnection();
+  virtual ~RawDBusConnection();
   nsresult EstablishDBusConnection();
   DBusConnection* GetConnection() {
     return mConnection;
   }
 
 protected:
   Scoped<ScopedDBusConnectionPtrTraits> mConnection;
 
--- a/mobile/android/base/GeckoSmsManager.java
+++ b/mobile/android/base/GeckoSmsManager.java
@@ -290,21 +290,25 @@ public class GeckoSmsManager
   public final static String ACTION_SMS_SENT      = "org.mozilla.gecko.SMS_SENT";
   public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED";
 
   /*
    * Make sure that the following error codes are in sync with |ErrorType| in:
    * dom/mobilemessage/src/Types.h
    * The error code are owned by the DOM.
    */
-  public final static int kNoError       = 0;
-  public final static int kNoSignalError = 1;
-  public final static int kNotFoundError = 2;
-  public final static int kUnknownError  = 3;
-  public final static int kInternalError = 4;
+  public final static int kNoError             = 0;
+  public final static int kNoSignalError       = 1;
+  public final static int kNotFoundError       = 2;
+  public final static int kUnknownError        = 3;
+  public final static int kInternalError       = 4;
+  public final static int kNoSimCardError      = 5;
+  public final static int kRadioDisabledError  = 6;
+  public final static int kInvalidAddressError = 7;
+
 
   private final static int kMaxMessageSize    = 160;
 
   private final static Uri kSmsContentUri     = Uri.parse("content://sms");
   private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
 
   private final static int kSmsTypeInbox      = 1;
   private final static int kSmsTypeSentbox    = 2;