merge b2g-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 27 Feb 2014 15:10:44 +0100
changeset 171318 f8e801b991bcc20c5eea6b9558b743ab15c73bf9
parent 171303 0b930fe64a1ed3441b5e1ffe9344c0053c7e95c4 (current diff)
parent 171317 dcfcbdb6f2bbd1297efbe3244a3ed85856f1c359 (diff)
child 171323 4cfb6c61b13789061152cd9b04950b0430ddf81d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
merge b2g-inbound to mozilla-central
b2g/config/emulator-ics/sources.xml
b2g/config/emulator-jb/sources.xml
b2g/config/emulator/sources.xml
b2g/config/gaia.json
b2g/config/hamachi/sources.xml
b2g/config/helix/sources.xml
b2g/config/inari/sources.xml
b2g/config/leo/sources.xml
b2g/config/mako/sources.xml
b2g/config/wasabi/sources.xml
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="022eadd5917615ff00c47eaaafa792b45e9c8a28"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "remote": "", 
         "branch": "", 
         "revision": ""
     }, 
-    "revision": "2761bef239957844e6126ef252b82b5f63b304e7", 
+    "revision": "cba6294f97cb705143dc4581410518901aa4aa59", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="97a5b461686757dbb8ecab2aac5903e41d2e1afe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="22d48b62df7901ad45044f66e15e7d8943884a06"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3a9fdec737cdfd41206a332970fea833ec4ca13a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="15e8982284c4560f9c74c2b9fe8bb361ebfe0cb6"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="52ca41d9fa6ef88e65d9da52e375716c68d48646"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -11812,17 +11812,19 @@ ICCIOHelperObject.prototype = {
                      " EF id = " + options.fileId.toString(16) +
                      " command = " + options.command.toString(16);
       if (options.sw1 && options.sw2) {
         errorMsg += "(" + options.sw1.toString(16) +
                     "/" + options.sw2.toString(16) + ")";
       }
       this.context.debug(errorMsg);
     }
-    onerror(requestError);
+    if (options.onerror) {
+      options.onerror(requestError);
+    }
   },
 };
 ICCIOHelperObject.prototype[ICC_COMMAND_SEEK] = null;
 ICCIOHelperObject.prototype[ICC_COMMAND_READ_BINARY] = function ICC_COMMAND_READ_BINARY(options) {
   this.processICCIOReadBinary(options);
 };
 ICCIOHelperObject.prototype[ICC_COMMAND_READ_RECORD] = function ICC_COMMAND_READ_RECORD(options) {
   this.processICCIOReadRecord(options);
@@ -12200,17 +12202,17 @@ ICCRecordHelperObject.prototype = {
 
     this.context.ICCIOHelper.updateLinearFixedEF({
       fileId: fileId,
       recordNumber: recordNumber,
       dataWriter: dataWriter,
       callback: onsuccess,
       onerror: onerror
     });
- },
+  },
 
   /**
    * Cache EF_ANR record size.
    */
   _anrRecordSize: null,
 
   /**
    * Read USIM/RUIM Phonebook EF_ANR.
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "Telephony.h"
 #include "mozilla/dom/TelephonyBinding.h"
+#include "mozilla/dom/Promise.h"
 
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 #include "nsIPermissionManager.h"
 
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -56,16 +57,54 @@ public:
   void
   Disconnect()
   {
     MOZ_ASSERT(mTelephony);
     mTelephony = nullptr;
   }
 };
 
+class Telephony::Callback : public nsITelephonyCallback
+{
+  nsRefPtr<Telephony> mTelephony;
+  nsRefPtr<Promise> mPromise;
+  uint32_t mServiceId;
+  nsString mNumber;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  Callback(Telephony* aTelephony, Promise* aPromise, uint32_t aServiceId,
+           const nsAString& aNumber)
+    : mTelephony(aTelephony), mPromise(aPromise), mServiceId(aServiceId),
+      mNumber(aNumber)
+  {
+    MOZ_ASSERT(mTelephony);
+  }
+
+  virtual ~Callback() {}
+
+  NS_IMETHODIMP
+  NotifyDialError(const nsAString& aError)
+  {
+    mPromise->MaybeReject(aError);
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  NotifyDialSuccess()
+  {
+    nsRefPtr<TelephonyCall> call =
+      mTelephony->CreateNewDialingCall(mServiceId, mNumber);
+
+    mPromise->MaybeResolve(call);
+    return NS_OK;
+  }
+};
+
 class Telephony::EnumerationAck : public nsRunnable
 {
   nsRefPtr<Telephony> mTelephony;
 
 public:
   EnumerationAck(Telephony* aTelephony)
   : mTelephony(aTelephony)
   {
@@ -75,18 +114,17 @@ public:
   NS_IMETHOD Run()
   {
     mTelephony->NotifyCallsChanged(nullptr);
     return NS_OK;
   }
 };
 
 Telephony::Telephony(nsPIDOMWindow* aOwner)
-  : nsDOMEventTargetHelper(aOwner),
-    mActiveCall(nullptr), mEnumerated(false)
+  : nsDOMEventTargetHelper(aOwner), mActiveCall(nullptr), mEnumerated(false)
 {
   if (!gTelephonyList) {
     gTelephonyList = new TelephonyList();
   }
 
   gTelephonyList->AppendElement(this);
 }
 
@@ -230,73 +268,62 @@ Telephony::HasDialingCall()
 bool
 Telephony::MatchActiveCall(TelephonyCall* aCall)
 {
   return (mActiveCall &&
           mActiveCall->CallIndex() == aCall->CallIndex() &&
           mActiveCall->ServiceId() == aCall->ServiceId());
 }
 
-already_AddRefed<TelephonyCall>
+already_AddRefed<Promise>
 Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber,
-                        bool aIsEmergency, ErrorResult& aRv)
+                        bool aIsEmergency)
 {
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (!global) {
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = new Promise(global);
+
   if (!IsValidNumber(aNumber) || !IsValidServiceId(aServiceId)) {
-    aRv.Throw(NS_ERROR_INVALID_ARG);
-    return nullptr;
+    promise->MaybeReject(NS_LITERAL_STRING("InvalidAccessError"));
+    return promise.forget();
   }
 
   // We only support one outgoing call at a time.
   if (HasDialingCall()) {
-    NS_WARNING("Only permitted to dial one call at a time!");
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return nullptr;
-  }
-
-  nsresult rv = mProvider->Dial(aServiceId, aNumber, aIsEmergency);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-    return nullptr;
+    promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError"));
+    return promise.forget();
   }
 
-  nsRefPtr<TelephonyCall> call = CreateNewDialingCall(aServiceId, aNumber);
-
-  // Notify other telephony objects that we just dialed.
-  for (uint32_t i = 0; i < gTelephonyList->Length(); i++) {
-    Telephony*& telephony = gTelephonyList->ElementAt(i);
-    if (telephony != this) {
-      nsRefPtr<Telephony> kungFuDeathGrip = telephony;
-      telephony->NoteDialedCallFromOtherInstance(aServiceId, aNumber);
-    }
+  nsCOMPtr<nsITelephonyCallback> callback =
+    new Callback(this, promise, aServiceId, aNumber);
+  nsresult rv = mProvider->Dial(aServiceId, aNumber, aIsEmergency, callback);
+  if (NS_FAILED(rv)) {
+    promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError"));
+    return promise.forget();
   }
 
-  return call.forget();
+  return promise.forget();
 }
 
 already_AddRefed<TelephonyCall>
 Telephony::CreateNewDialingCall(uint32_t aServiceId, const nsAString& aNumber)
 {
   nsRefPtr<TelephonyCall> call =
     TelephonyCall::Create(this, aServiceId, aNumber,
                           nsITelephonyProvider::CALL_STATE_DIALING);
   NS_ASSERTION(call, "This should never fail!");
 
   NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!");
 
   return call.forget();
 }
 
-void
-Telephony::NoteDialedCallFromOtherInstance(uint32_t aServiceId,
-                                           const nsAString& aNumber)
-{
-  // We don't need to hang on to this call object, it is held alive by mCalls.
-  nsRefPtr<TelephonyCall> call = CreateNewDialingCall(aServiceId, aNumber);
-}
-
 nsresult
 Telephony::NotifyCallsChanged(TelephonyCall* aCall)
 {
   return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall);
 }
 
 void
 Telephony::UpdateActiveCall(TelephonyCall* aCall, bool aIsActive)
@@ -377,36 +404,35 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(Telephony, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Telephony, nsDOMEventTargetHelper)
 
 NS_IMPL_ISUPPORTS1(Telephony::Listener, nsITelephonyListener)
+NS_IMPL_ISUPPORTS1(Telephony::Callback, nsITelephonyCallback)
 
 // Telephony WebIDL
 
-already_AddRefed<TelephonyCall>
-Telephony::Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId,
-                ErrorResult& aRv)
+already_AddRefed<Promise>
+Telephony::Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId)
 {
   uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId);
-  nsRefPtr<TelephonyCall> call = DialInternal(serviceId, aNumber, false, aRv);
-  return call.forget();
+  nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, false);
+  return promise.forget();
 }
 
-already_AddRefed<TelephonyCall>
+already_AddRefed<Promise>
 Telephony::DialEmergency(const nsAString& aNumber,
-                         const Optional<uint32_t>& aServiceId,
-                         ErrorResult& aRv)
+                         const Optional<uint32_t>& aServiceId)
 {
   uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId);
-  nsRefPtr<TelephonyCall> call = DialInternal(serviceId, aNumber, true, aRv);
-  return call.forget();
+  nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, true);
+  return promise.forget();
 }
 
 void
 Telephony::StartTone(const nsAString& aDTMFChar,
                      const Optional<uint32_t>& aServiceId,
                      ErrorResult& aRv)
 {
   uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId);
@@ -639,17 +665,17 @@ Telephony::EnumerateCallState(uint32_t a
 }
 
 NS_IMETHODIMP
 Telephony::SupplementaryServiceNotification(uint32_t aServiceId,
                                             int32_t aCallIndex,
                                             uint16_t aNotification)
 {
   nsRefPtr<TelephonyCall> associatedCall;
-  if (!mCalls.IsEmpty() && aCallIndex != -1) {
+  if (!mCalls.IsEmpty()) {
     associatedCall = GetCall(aServiceId, aCallIndex);
   }
 
   nsresult rv;
   switch (aNotification) {
     case nsITelephonyProvider::NOTIFICATION_REMOTE_HELD:
       rv = DispatchCallEvent(NS_LITERAL_STRING("remoteheld"), associatedCall);
       break;
@@ -670,21 +696,17 @@ Telephony::NotifyError(uint32_t aService
                        int32_t aCallIndex,
                        const nsAString& aError)
 {
   if (mCalls.IsEmpty()) {
     NS_ERROR("No existing call!");
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsRefPtr<TelephonyCall> callToNotify;
-
-  callToNotify = (aCallIndex == -1) ? GetOutgoingCall()
-                                    : GetCall(aServiceId, aCallIndex);
-
+  nsRefPtr<TelephonyCall> callToNotify = GetCall(aServiceId, aCallIndex);
   if (!callToNotify) {
     NS_ERROR("Don't call me with a bad call index!");
     return NS_ERROR_UNEXPECTED;
   }
 
   UpdateActiveCall(callToNotify, false);
 
   // Set the call state to 'disconnected' and remove it from the calls list.
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -3,16 +3,17 @@
 /* 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/. */
 
 #ifndef mozilla_dom_telephony_telephony_h__
 #define mozilla_dom_telephony_telephony_h__
 
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Promise.h"
 #include "mozilla/dom/telephony/TelephonyCommon.h"
 
 #include "nsITelephonyProvider.h"
 
 // Need to include TelephonyCall.h because we have inline methods that
 // assume they see the definition of TelephonyCall.
 #include "TelephonyCall.h"
 
@@ -29,16 +30,19 @@ class Telephony MOZ_FINAL : public nsDOM
    * Class Telephony doesn't actually inherit nsITelephonyListener.
    * Instead, it owns an nsITelephonyListener derived instance mListener
    * and passes it to nsITelephonyProvider. The onreceived events are first
    * delivered to mListener and then forwarded to its owner, Telephony. See
    * also bug 775997 comment #51.
    */
   class Listener;
 
+  class Callback;
+  friend class Callback;
+
   class EnumerationAck;
   friend class EnumerationAck;
 
   nsCOMPtr<nsITelephonyProvider> mProvider;
   nsRefPtr<Listener> mListener;
 
   TelephonyCall* mActiveCall;
   nsTArray<nsRefPtr<TelephonyCall> > mCalls;
@@ -61,23 +65,21 @@ public:
     return GetOwner();
   }
 
   // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   // WebIDL
-  already_AddRefed<TelephonyCall>
-  Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId,
-       ErrorResult& aRv);
+  already_AddRefed<Promise>
+  Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId);
 
-  already_AddRefed<TelephonyCall>
-  DialEmergency(const nsAString& aNumber, const Optional<uint32_t>& aServiceId,
-                ErrorResult& aRv);
+  already_AddRefed<Promise>
+  DialEmergency(const nsAString& aNumber, const Optional<uint32_t>& aServiceId);
 
   void
   StartTone(const nsAString& aDTMFChar, const Optional<uint32_t>& aServiceId,
             ErrorResult& aRv);
 
   void
   StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv);
 
@@ -165,27 +167,22 @@ private:
   ProvidedOrDefaultServiceId(const Optional<uint32_t>& aServiceId);
 
   bool
   HasDialingCall();
 
   bool
   MatchActiveCall(TelephonyCall* aCall);
 
-  already_AddRefed<TelephonyCall>
-  DialInternal(uint32_t aServiceId, const nsAString& aNumber,
-               bool isEmergency, ErrorResult& aRv);
+  already_AddRefed<Promise>
+  DialInternal(uint32_t aServiceId, const nsAString& aNumber, bool isEmergency);
 
   already_AddRefed<TelephonyCall>
   CreateNewDialingCall(uint32_t aServiceId, const nsAString& aNumber);
 
-  void
-  NoteDialedCallFromOtherInstance(uint32_t aServiceId,
-                                  const nsAString& aNumber);
-
   nsresult
   NotifyCallsChanged(TelephonyCall* aCall);
 
   nsresult
   DispatchCallEvent(const nsAString& aType, TelephonyCall* aCall);
 
   void
   EnqueueEnumerationAck();
--- a/dom/telephony/gonk/TelephonyProvider.js
+++ b/dom/telephony/gonk/TelephonyProvider.js
@@ -395,44 +395,54 @@ TelephonyProvider.prototype = {
     for (let i = 0; i < this._numClients; ++i) {
       promise = promise.then(this._enumerateCallsForClient.bind(this, i, aListener));
     }
     promise.then(function() {
       aListener.enumerateCallStateComplete();
     });
   },
 
-  dial: function(aClientId, aNumber, aIsEmergency) {
+  isDialing: false,
+  dial: function(aClientId, aNumber, aIsEmergency, aTelephonyCallback) {
     if (DEBUG) debug("Dialing " + (aIsEmergency ? "emergency " : "") + aNumber);
 
+    if (this.isDialing) {
+      if (DEBUG) debug("Already has a dialing call. Drop.");
+      aTelephonyCallback.notifyDialError("InvalidStateError");
+      return;
+    }
+
     // we don't try to be too clever here, as the phone is probably in the
     // locked state. Let's just check if it's a number without normalizing
     if (!aIsEmergency) {
       aNumber = gPhoneNumberUtils.normalize(aNumber);
     }
 
+    // Validate the number.
     if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
       // Note: isPlainPhoneNumber also accepts USSD and SS numbers
       if (DEBUG) debug("Number '" + aNumber + "' is not viable. Drop.");
       let errorMsg = RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER];
-      Services.tm.currentThread.dispatch(
-        this.notifyCallError.bind(this, aClientId, -1, errorMsg),
-        Ci.nsIThread.DISPATCH_NORMAL);
+      aTelephonyCallback.notifyDialError(errorMsg);
       return;
     }
 
+    this.isDialing = true;
     this._getClient(aClientId).sendWorkerMessage("dial", {
       number: aNumber,
       isDialEmergency: aIsEmergency
-    }, (function(clientId, response) {
-      if (!response.success) {
-        this.notifyCallError(clientId, -1, response.errorMsg);
+    }, (function(response) {
+      this.isDialing = false;
+      if (response.success) {
+        aTelephonyCallback.notifyDialSuccess();
+      } else {
+        aTelephonyCallback.notifyDialError(response.errorMsg);
       }
       return false;
-    }).bind(this, aClientId));
+    }).bind(this));
   },
 
   hangUp: function(aClientId, aCallIndex) {
     this._getClient(aClientId).sendWorkerMessage("hangUp", { callIndex: aCallIndex });
   },
 
   startTone: function(aClientId, aDtmfChar) {
     this._getClient(aClientId).sendWorkerMessage("startTone", { dtmfChar: aDtmfChar });
--- a/dom/telephony/ipc/PTelephony.ipdl
+++ b/dom/telephony/ipc/PTelephony.ipdl
@@ -7,16 +7,34 @@
 include protocol PContent;
 include protocol PTelephonyRequest;
 include TelephonyTypes;
 
 namespace mozilla {
 namespace dom {
 namespace telephony {
 
+struct EnumerateCallsRequest
+{
+  // empty.
+};
+
+struct DialRequest
+{
+  uint32_t clientId;
+  nsString number;
+  bool isEmergency;
+};
+
+union IPCTelephonyRequest
+{
+  EnumerateCallsRequest;
+  DialRequest;
+};
+
 sync protocol PTelephony {
   manager PContent;
   manages PTelephonyRequest;
 
 child:
   NotifyCallError(uint32_t aClientId, int32_t aCallIndex, nsString aError);
 
   NotifyCallStateChanged(uint32_t aClientId, IPCCallStateData aData);
@@ -32,27 +50,24 @@ child:
 
 parent:
   /**
    * Sent when the child no longer needs to use PTelephony.
    */
   __delete__();
 
   /**
-   * Sent when the child makes an asynchronous request to the parent.  It's
-   * currently only for request call enumeration.
+   * Sent when the child makes an asynchronous request to the parent.
    */
-  PTelephonyRequest();
+  PTelephonyRequest(IPCTelephonyRequest request);
 
   RegisterListener();
 
   UnregisterListener();
 
-  DialCall(uint32_t aClientId, nsString aNumber, bool aIsEmergency);
-
   HangUpCall(uint32_t aClientId, uint32_t aCallIndex);
 
   AnswerCall(uint32_t aClientId, uint32_t aCallIndex);
 
   RejectCall(uint32_t aClientId, uint32_t aCallIndex);
 
   HoldCall(uint32_t aClientId, uint32_t aCallIndex);
 
--- a/dom/telephony/ipc/PTelephonyRequest.ipdl
+++ b/dom/telephony/ipc/PTelephonyRequest.ipdl
@@ -6,25 +6,44 @@
 
 include protocol PTelephony;
 include TelephonyTypes;
 
 namespace mozilla {
 namespace dom {
 namespace telephony {
 
+struct EnumerateCallsResponse
+{
+  // empty.
+};
+
+struct DialResponse
+{
+  // empty.
+};
+
+union IPCTelephonyResponse
+{
+  EnumerateCallsResponse;
+  DialResponse;
+};
+
 protocol PTelephonyRequest
 {
   manager PTelephony;
 
 child:
   NotifyEnumerateCallState(uint32_t aClientId, IPCCallStateData aData);
 
+  NotifyDialError(nsString aError);
+
+  NotifyDialSuccess();
+
   /**
-   * Sent when the asynchronous request has completed. It's currently only for
-   * request call enumeration.
+   * Sent when the asynchronous request has completed.
    */
-  __delete__();
+  __delete__(IPCTelephonyResponse aResponse);
 };
 
 } /* namespace telephony */
 } /* namespace dom */
 } /* namespace mozilla */
--- a/dom/telephony/ipc/TelephonyChild.cpp
+++ b/dom/telephony/ipc/TelephonyChild.cpp
@@ -19,17 +19,17 @@ TelephonyChild::TelephonyChild(nsITeleph
 
 void
 TelephonyChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mListener = nullptr;
 }
 
 PTelephonyRequestChild*
-TelephonyChild::AllocPTelephonyRequestChild()
+TelephonyChild::AllocPTelephonyRequestChild(const IPCTelephonyRequest& aRequest)
 {
   MOZ_CRASH("Caller is supposed to manually construct a request!");
 }
 
 bool
 TelephonyChild::DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor)
 {
   delete aActor;
@@ -104,34 +104,43 @@ TelephonyChild::RecvNotifySupplementaryS
                                               aNotification);
   return true;
 }
 
 /*******************************************************************************
  * TelephonyRequestChild
  ******************************************************************************/
 
-TelephonyRequestChild::TelephonyRequestChild(nsITelephonyListener* aListener)
-  : mListener(aListener)
+TelephonyRequestChild::TelephonyRequestChild(nsITelephonyListener* aListener,
+                                             nsITelephonyCallback* aCallback)
+  : mListener(aListener), mCallback(aCallback)
 {
-  MOZ_ASSERT(aListener);
 }
 
 void
 TelephonyRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mListener = nullptr;
+  mCallback = nullptr;
 }
 
 bool
-TelephonyRequestChild::Recv__delete__()
+TelephonyRequestChild::Recv__delete__(const IPCTelephonyResponse& aResponse)
 {
-  MOZ_ASSERT(mListener);
+  switch (aResponse.type()) {
+    case IPCTelephonyResponse::TEnumerateCallsResponse:
+      mListener->EnumerateCallStateComplete();
+      break;
+    case IPCTelephonyResponse::TDialResponse:
+      // Do nothing.
+      break;
+    default:
+      MOZ_CRASH("Unknown type!");
+  }
 
-  mListener->EnumerateCallStateComplete();
   return true;
 }
 
 bool
 TelephonyRequestChild::RecvNotifyEnumerateCallState(const uint32_t& aClientId,
                                                     const IPCCallStateData& aData)
 {
   MOZ_ASSERT(mListener);
@@ -141,8 +150,26 @@ TelephonyRequestChild::RecvNotifyEnumera
                                 aData.callState(),
                                 aData.number(),
                                 aData.isActive(),
                                 aData.isOutGoing(),
                                 aData.isEmergency(),
                                 aData.isConference());
   return true;
 }
+
+bool
+TelephonyRequestChild::RecvNotifyDialError(const nsString& aError)
+{
+  MOZ_ASSERT(mCallback);
+
+  mCallback->NotifyDialError(aError);
+  return true;
+}
+
+bool
+TelephonyRequestChild::RecvNotifyDialSuccess()
+{
+  MOZ_ASSERT(mCallback);
+
+  mCallback->NotifyDialSuccess();
+  return true;
+}
--- a/dom/telephony/ipc/TelephonyChild.h
+++ b/dom/telephony/ipc/TelephonyChild.h
@@ -20,17 +20,17 @@ public:
 
 protected:
   virtual ~TelephonyChild() {}
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PTelephonyRequestChild*
-  AllocPTelephonyRequestChild() MOZ_OVERRIDE;
+  AllocPTelephonyRequestChild(const IPCTelephonyRequest& aRequest) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyCallError(const uint32_t& aClientId, const int32_t& aCallIndex,
                       const nsString& aError) MOZ_OVERRIDE;
 
@@ -56,30 +56,38 @@ protected:
 
 private:
   nsCOMPtr<nsITelephonyListener> mListener;
 };
 
 class TelephonyRequestChild : public PTelephonyRequestChild
 {
 public:
-  TelephonyRequestChild(nsITelephonyListener* aListener);
+  TelephonyRequestChild(nsITelephonyListener* aListener,
+                        nsITelephonyCallback* aCallback);
 
 protected:
   virtual ~TelephonyRequestChild() {}
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
-  Recv__delete__() MOZ_OVERRIDE;
+  Recv__delete__(const IPCTelephonyResponse& aResponse) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyEnumerateCallState(const uint32_t& aClientId,
                                const IPCCallStateData& aData) MOZ_OVERRIDE;
 
+  virtual bool
+  RecvNotifyDialError(const nsString& aError) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvNotifyDialSuccess() MOZ_OVERRIDE;
+
 private:
   nsCOMPtr<nsITelephonyListener> mListener;
+  nsCOMPtr<nsITelephonyCallback> mCallback;
 };
 
 END_TELEPHONY_NAMESPACE
 
 #endif // mozilla_dom_telephony_TelephonyChild_h
--- a/dom/telephony/ipc/TelephonyIPCProvider.cpp
+++ b/dom/telephony/ipc/TelephonyIPCProvider.cpp
@@ -112,32 +112,40 @@ TelephonyIPCProvider::UnregisterListener
   mListeners.RemoveElement(aListener);
 
   if (!mListeners.Length()) {
     mPTelephonyChild->SendUnregisterListener();
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-TelephonyIPCProvider::EnumerateCalls(nsITelephonyListener *aListener)
+nsresult
+TelephonyIPCProvider::SendRequest(nsITelephonyListener *aListener,
+                                  nsITelephonyCallback *aCallback,
+                                  const IPCTelephonyRequest& aRequest)
 {
   // Life time of newly allocated TelephonyRequestChild instance is managed by
   // IPDL itself.
-  TelephonyRequestChild* actor = new TelephonyRequestChild(aListener);
-  mPTelephonyChild->SendPTelephonyRequestConstructor(actor);
+  TelephonyRequestChild* actor = new TelephonyRequestChild(aListener, aCallback);
+  mPTelephonyChild->SendPTelephonyRequestConstructor(actor, aRequest);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TelephonyIPCProvider::Dial(uint32_t aClientId, const nsAString& aNumber,
-                           bool aIsEmergency)
+TelephonyIPCProvider::EnumerateCalls(nsITelephonyListener *aListener)
 {
-  mPTelephonyChild->SendDialCall(aClientId, nsString(aNumber), aIsEmergency);
-  return NS_OK;
+  return SendRequest(aListener, nullptr, EnumerateCallsRequest());
+}
+
+NS_IMETHODIMP
+TelephonyIPCProvider::Dial(uint32_t aClientId, const nsAString& aNumber,
+                           bool aIsEmergency, nsITelephonyCallback *aCallback)
+{
+  return SendRequest(nullptr, aCallback,
+                     DialRequest(aClientId, nsString(aNumber), aIsEmergency));
 }
 
 NS_IMETHODIMP
 TelephonyIPCProvider::HangUp(uint32_t aClientId, uint32_t aCallIndex)
 {
   mPTelephonyChild->SendHangUpCall(aClientId, aCallIndex);
   return NS_OK;
 }
--- a/dom/telephony/ipc/TelephonyIPCProvider.h
+++ b/dom/telephony/ipc/TelephonyIPCProvider.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/dom/telephony/TelephonyCommon.h"
 #include "mozilla/Attributes.h"
 #include "nsIObserver.h"
 #include "nsITelephonyProvider.h"
 
 BEGIN_TELEPHONY_NAMESPACE
 
+struct IPCTelephonyRequest;
 class PTelephonyChild;
 
 class TelephonyIPCProvider MOZ_FINAL : public nsITelephonyProvider
                                      , public nsITelephonyListener
                                      , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
@@ -29,13 +30,17 @@ public:
 
 protected:
   virtual ~TelephonyIPCProvider();
 
 private:
   nsTArray<nsCOMPtr<nsITelephonyListener> > mListeners;
   PTelephonyChild* mPTelephonyChild;
   uint32_t mDefaultServiceId;
+
+  nsresult SendRequest(nsITelephonyListener *aListener,
+                       nsITelephonyCallback *aCallback,
+                       const IPCTelephonyRequest& aRequest);
 };
 
 END_TELEPHONY_NAMESPACE
 
 #endif // mozilla_dom_telephony_TelephonyIPCProvider_h
--- a/dom/telephony/ipc/TelephonyParent.cpp
+++ b/dom/telephony/ipc/TelephonyParent.cpp
@@ -28,25 +28,35 @@ TelephonyParent::ActorDestroy(ActorDestr
   // an error here to avoid sending a message to the dead process.
   mActorDestroyed = true;
 
   // Try to unregister listener if we're still registered.
   RecvUnregisterListener();
 }
 
 bool
-TelephonyParent::RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor)
+TelephonyParent::RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor,
+                                                  const IPCTelephonyRequest& aRequest)
 {
   TelephonyRequestParent* actor = static_cast<TelephonyRequestParent*>(aActor);
 
-  return actor->DoRequest();
+  switch (aRequest.type()) {
+    case IPCTelephonyRequest::TEnumerateCallsRequest:
+      return actor->DoRequest(aRequest.get_EnumerateCallsRequest());
+    case IPCTelephonyRequest::TDialRequest:
+      return actor->DoRequest(aRequest.get_DialRequest());
+    default:
+      MOZ_CRASH("Unknown type!");
+  }
+
+  return false;
 }
 
 PTelephonyRequestParent*
-TelephonyParent::AllocPTelephonyRequestParent()
+TelephonyParent::AllocPTelephonyRequestParent(const IPCTelephonyRequest& aRequest)
 {
   TelephonyRequestParent* actor = new TelephonyRequestParent();
   // Add an extra ref for IPDL. Will be released in
   // TelephonyParent::DeallocPTelephonyRequestParent().
   NS_ADDREF(actor);
 
   return actor;
 }
@@ -87,29 +97,16 @@ TelephonyParent::RecvUnregisterListener(
     do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   NS_ENSURE_TRUE(provider, true);
 
   mRegistered = !NS_SUCCEEDED(provider->UnregisterListener(this));
   return true;
 }
 
 bool
-TelephonyParent::RecvDialCall(const uint32_t& aClientId,
-                              const nsString& aNumber,
-                              const bool& aIsEmergency)
-{
-  nsCOMPtr<nsITelephonyProvider> provider =
-    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
-  NS_ENSURE_TRUE(provider, true);
-
-  provider->Dial(aClientId, aNumber, aIsEmergency);
-  return true;
-}
-
-bool
 TelephonyParent::RecvHangUpCall(const uint32_t& aClientId,
                                 const uint32_t& aCallIndex)
 {
   nsCOMPtr<nsITelephonyProvider> provider =
     do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   NS_ENSURE_TRUE(provider, true);
 
   provider->HangUp(aClientId, aCallIndex);
@@ -367,50 +364,67 @@ TelephonyParent::SupplementaryServiceNot
   return SendNotifySupplementaryService(aClientId, aCallIndex, aNotification)
       ? NS_OK : NS_ERROR_FAILURE;
 }
 
 /*******************************************************************************
  * TelephonyRequestParent
  ******************************************************************************/
 
-NS_IMPL_ISUPPORTS1(TelephonyRequestParent, nsITelephonyListener)
+NS_IMPL_ISUPPORTS2(TelephonyRequestParent,
+                   nsITelephonyListener,
+                   nsITelephonyCallback)
 
 TelephonyRequestParent::TelephonyRequestParent()
   : mActorDestroyed(false)
 {
 }
 
 void
 TelephonyRequestParent::ActorDestroy(ActorDestroyReason why)
 {
   // The child process could die before this asynchronous notification, in which
   // case ActorDestroy() was called and mActorDestroyed is set to true. Return
   // an error here to avoid sending a message to the dead process.
   mActorDestroyed = true;
 }
 
 bool
-TelephonyRequestParent::DoRequest()
+TelephonyRequestParent::DoRequest(const EnumerateCallsRequest& aRequest)
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   nsCOMPtr<nsITelephonyProvider> provider =
     do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
   if (provider) {
     rv = provider->EnumerateCalls(this);
   }
 
   if (NS_FAILED(rv)) {
     return NS_SUCCEEDED(EnumerateCallStateComplete());
   }
 
   return true;
 }
 
+bool
+TelephonyRequestParent::DoRequest(const DialRequest& aRequest)
+{
+  nsCOMPtr<nsITelephonyProvider> provider =
+    do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
+  if (provider) {
+    provider->Dial(aRequest.clientId(), aRequest.number(),
+                   aRequest.isEmergency(), this);
+  } else {
+    return NS_SUCCEEDED(NotifyDialError(NS_LITERAL_STRING("InvalidStateError")));
+  }
+
+  return true;
+}
+
 // nsITelephonyListener
 
 NS_IMETHODIMP
 TelephonyRequestParent::CallStateChanged(uint32_t aClientId,
                                          uint32_t aCallIndex,
                                          uint16_t aCallState,
                                          const nsAString& aNumber,
                                          bool aIsActive,
@@ -427,17 +441,17 @@ TelephonyRequestParent::ConferenceCallSt
   MOZ_CRASH("Not a TelephonyParent!");
 }
 
 NS_IMETHODIMP
 TelephonyRequestParent::EnumerateCallStateComplete()
 {
   NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
 
-  return Send__delete__(this) ? NS_OK : NS_ERROR_FAILURE;
+  return Send__delete__(this, EnumerateCallsResponse()) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 TelephonyRequestParent::EnumerateCallState(uint32_t aClientId,
                                            uint32_t aCallIndex,
                                            uint16_t aCallState,
                                            const nsAString& aNumber,
                                            bool aIsActive,
@@ -477,8 +491,28 @@ TelephonyRequestParent::NotifyError(uint
 
 NS_IMETHODIMP
 TelephonyRequestParent::SupplementaryServiceNotification(uint32_t aClientId,
                                                          int32_t aCallIndex,
                                                          uint16_t aNotification)
 {
   MOZ_CRASH("Not a TelephonyParent!");
 }
+
+// nsITelephonyCallback
+
+NS_IMETHODIMP
+TelephonyRequestParent::NotifyDialError(const nsAString& aError)
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return (SendNotifyDialError(nsString(aError)) &&
+          Send__delete__(this, DialResponse())) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::NotifyDialSuccess()
+{
+  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
+
+  return (SendNotifyDialSuccess() &&
+          Send__delete__(this, DialResponse())) ? NS_OK : NS_ERROR_FAILURE;
+}
--- a/dom/telephony/ipc/TelephonyParent.h
+++ b/dom/telephony/ipc/TelephonyParent.h
@@ -24,37 +24,34 @@ public:
 
 protected:
   virtual ~TelephonyParent() {}
 
   virtual void
   ActorDestroy(ActorDestroyReason why);
 
   virtual bool
-  RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor) MOZ_OVERRIDE;
+  RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor, const IPCTelephonyRequest& aRequest) MOZ_OVERRIDE;
 
   virtual PTelephonyRequestParent*
-  AllocPTelephonyRequestParent() MOZ_OVERRIDE;
+  AllocPTelephonyRequestParent(const IPCTelephonyRequest& aRequest) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPTelephonyRequestParent(PTelephonyRequestParent* aActor) MOZ_OVERRIDE;
 
   virtual bool
   Recv__delete__() MOZ_OVERRIDE;
 
   virtual bool
   RecvRegisterListener() MOZ_OVERRIDE;
 
   virtual bool
   RecvUnregisterListener() MOZ_OVERRIDE;
 
   virtual bool
-  RecvDialCall(const uint32_t& aClientId, const nsString& aNumber, const bool& aIsEmergency) MOZ_OVERRIDE;
-
-  virtual bool
   RecvHangUpCall(const uint32_t& aClientId, const uint32_t& aCallIndex) MOZ_OVERRIDE;
 
   virtual bool
   RecvAnswerCall(const uint32_t& aClientId, const uint32_t& aCallIndex) MOZ_OVERRIDE;
 
   virtual bool
   RecvRejectCall(const uint32_t& aClientId, const uint32_t& aCallIndex) MOZ_OVERRIDE;
 
@@ -96,32 +93,37 @@ protected:
 
 private:
   bool mActorDestroyed;
   bool mRegistered;
 };
 
 class TelephonyRequestParent : public PTelephonyRequestParent
                              , public nsITelephonyListener
+                             , public nsITelephonyCallback
 {
   friend class TelephonyParent;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYLISTENER
+  NS_DECL_NSITELEPHONYCALLBACK
 
 protected:
   TelephonyRequestParent();
   virtual ~TelephonyRequestParent() {}
 
   virtual void
   ActorDestroy(ActorDestroyReason why);
 
 private:
   bool mActorDestroyed;
 
   bool
-  DoRequest();
+  DoRequest(const EnumerateCallsRequest& aRequest);
+
+  bool
+  DoRequest(const DialRequest& aRequest);
 };
 
 END_TELEPHONY_NAMESPACE
 
 #endif /* mozilla_dom_telephony_TelephonyParent_h */
--- a/dom/telephony/nsITelephonyProvider.idl
+++ b/dom/telephony/nsITelephonyProvider.idl
@@ -127,28 +127,44 @@ interface nsITelephonyListener : nsISupp
    *        Error name. Possible values are addError and removeError.
    * @param message
    *        Detailed error message from RIL.
    */
   void notifyConferenceError(in AString name,
                              in AString message);
 };
 
+[scriptable, uuid(c095aa82-aacb-4e53-a787-56a89c3f638e)]
+interface nsITelephonyCallback : nsISupports
+{
+  /**
+   * Called when a dial request fails.
+   * @param error
+   *        Error from RIL.
+   */
+  void notifyDialError(in AString error);
+
+  /**
+   * Called when a dial request succeeds.
+   */
+  void notifyDialSuccess();
+};
+
 %{C++
 #define TELEPHONY_PROVIDER_CID \
   { 0x9cf8aa52, 0x7c1c, 0x4cde, { 0x97, 0x4e, 0xed, 0x2a, 0xa0, 0xe7, 0x35, 0xfa } }
 #define TELEPHONY_PROVIDER_CONTRACTID \
   "@mozilla.org/telephony/telephonyprovider;1"
 %}
 
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(4ff3ecb7-b024-4752-9dd6-c3623c6e6b8a)]
+[scriptable, uuid(b16ca98f-994f-4ae1-8c2d-e7b18e08d1f3)]
 interface nsITelephonyProvider : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_CONNECTING = 3;
   const unsigned short CALL_STATE_CONNECTED = 4;
   const unsigned short CALL_STATE_HOLDING = 5;
@@ -176,17 +192,17 @@ interface nsITelephonyProvider : nsISupp
    * returns false.
    */
   void enumerateCalls(in nsITelephonyListener listener);
 
   /**
    * Functionality for making and managing phone calls.
    */
   void dial(in unsigned long clientId, in DOMString number,
-            in boolean isEmergency);
+            in boolean isEmergency, in nsITelephonyCallback callback);
   void hangUp(in unsigned long clientId, in unsigned long callIndex);
 
   void startTone(in unsigned long clientId, in DOMString dtmfChar);
   void stopTone(in unsigned long clientId);
 
   void answerCall(in unsigned long clientId, in unsigned long callIndex);
   void rejectCall(in unsigned long clientId, in unsigned long callIndex);
   void holdCall(in unsigned long clientId, in unsigned long callIndex);
--- a/dom/telephony/test/marionette/test_audiomanager_phonestate.js
+++ b/dom/telephony/test/marionette/test_audiomanager_phonestate.js
@@ -44,28 +44,29 @@ function checkEventCallState(event, call
   is(call, event.call, "event.call");
   is(call.state, state, "call state");
 }
 
 function dial(number) {
   log("Make an outgoing call: " + number);
 
   let deferred = Promise.defer();
-  let call = telephony.dial(number);
 
-  ok(call);
-  is(call.number, number);
-  is(call.state, "dialing");
+  telephony.dial(number).then(call => {
+    ok(call);
+    is(call.number, number);
+    is(call.state, "dialing");
 
-  call.onalerting = function onalerting(event) {
-    call.onalerting = null;
-    log("Received 'onalerting' call event.");
-    checkEventCallState(event, call, "alerting");
-    deferred.resolve(call);
-  };
+    call.onalerting = function onalerting(event) {
+      call.onalerting = null;
+      log("Received 'onalerting' call event.");
+      checkEventCallState(event, call, "alerting");
+      deferred.resolve(call);
+    };
+  });
 
   return deferred.promise;
 }
 
 function answer(call) {
   log("Answering the incoming call.");
 
   let deferred = Promise.defer();
--- a/dom/telephony/test/marionette/test_conference.js
+++ b/dom/telephony/test/marionette/test_conference.js
@@ -101,28 +101,29 @@ function receivedPending(received, pendi
     nextAction();
   }
 }
 
 function dial(number) {
   log("Make an outgoing call: " + number);
 
   let deferred = Promise.defer();
-  let call = telephony.dial(number);
 
-  ok(call);
-  is(call.number, number);
-  is(call.state, "dialing");
+  telephony.dial(number).then(call => {
+    ok(call);
+    is(call.number, number);
+    is(call.state, "dialing");
 
-  call.onalerting = function onalerting(event) {
-    call.onalerting = null;
-    log("Received 'onalerting' call event.");
-    checkEventCallState(event, call, "alerting");
-    deferred.resolve(call);
-  };
+    call.onalerting = function onalerting(event) {
+      call.onalerting = null;
+      log("Received 'onalerting' call event.");
+      checkEventCallState(event, call, "alerting");
+      deferred.resolve(call);
+    };
+  });
 
   return deferred.promise;
 }
 
 // Answering an incoming call could trigger conference state change.
 function answer(call, conferenceStateChangeCallback) {
   log("Answering the incoming call.");
 
--- a/dom/telephony/test/marionette/test_crash_emulator.js
+++ b/dom/telephony/test/marionette/test_crash_emulator.js
@@ -4,22 +4,23 @@
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let outNumber = "5555551111";
 let outgoingCall;
 
 function dial() {
   log("Make an outgoing call.");
-  outgoingCall = telephony.dial(outNumber);
-
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'alerting' call event.");
-    answer();
-  };
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'alerting' call event.");
+      answer();
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
     // just some code to keep call active for awhile
--- a/dom/telephony/test/marionette/test_dsds_normal_call.js
+++ b/dom/telephony/test/marionette/test_dsds_normal_call.js
@@ -68,29 +68,30 @@ function checkAll(active, calls, callLis
   return checkEmulatorCallList(callList);
 }
 
 function dial(number, serviceId) {
   serviceId = typeof serviceId !== "undefined" ? serviceId : 0;
   log("Make an outgoing call: " + number + ", serviceId: " + serviceId);
 
   let deferred = Promise.defer();
-  let call = telephony.dial(number, serviceId);
 
-  ok(call);
-  is(call.number, number);
-  is(call.state, "dialing");
+  telephony.dial(number).then(call => {
+    ok(call);
+    is(call.number, number);
+    is(call.state, "dialing");
 
-  call.onalerting = function onalerting(event) {
-    call.onalerting = null;
-    log("Received 'onalerting' call event.");
-    is(call.serviceId, serviceId);
-    checkEventCallState(event, call, "alerting");
-    deferred.resolve(call);
-  };
+    call.onalerting = function onalerting(event) {
+      call.onalerting = null;
+      log("Received 'onalerting' call event.");
+      is(call.serviceId, serviceId);
+      checkEventCallState(event, call, "alerting");
+      deferred.resolve(call);
+    };
+  });
 
   return deferred.promise;
 }
 
 function remoteDial(number) {
   log("Simulating an incoming call.");
 
   let deferred = Promise.defer();
--- a/dom/telephony/test/marionette/test_emergency.js
+++ b/dom/telephony/test/marionette/test_emergency.js
@@ -6,38 +6,41 @@ MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "911";
 let outgoing;
 let calls;
 
 function dial() {
   log("Make an emergency call.");
 
-  outgoing = telephony.dialEmergency(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dialEmergency(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  //ok(telephony.calls === calls); // bug 717414
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    //ok(telephony.calls === calls); // bug 717414
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
+      is(outgoing.emergency, true);
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + "        : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + "        : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the emergency call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoing.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_emergency_badNumber.js
+++ b/dom/telephony/test/marionette/test_emergency_badNumber.js
@@ -1,46 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "not a valid emergency number";
-let outgoing;
-let calls;
 
 function dial() {
   log("Make an outgoing call to an invalid number.");
 
-  outgoing = telephony.dialEmergency(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dialEmergency(number).then(null, cause => {
+    log("Received promise 'reject'");
 
-  is(outgoing, telephony.active);
-  //ok(telephony.calls === calls); // bug 717414
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
-
-  outgoing.onerror = function onerror(event) {
-    log("Received 'error' event.");
-    is(event.call, outgoing);
-    ok(event.call.error);
-    is(event.call.error.name, "BadNumberError");
-
+    is(telephony.active, null);
     is(telephony.calls.length, 0);
-    is(telephony.active, null);
+    is(cause, "BadNumberError");
 
     emulator.run("gsm list", function(result) {
       log("Initial call list: " + result);
       is(result[0], "OK");
       cleanUp();
     });
-  };
+  });
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_emergency_label.js
+++ b/dom/telephony/test/marionette/test_emergency_label.js
@@ -21,38 +21,40 @@ function createGoldenCallListResult0(num
   let padPattern = "          ";
   let pad = padPattern.substring(0, padPattern.length - number.length);
   return "outbound to  " + number + pad + " : " + state;
 }
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
-    is(outgoing.emergency, emergency);
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
+      is(outgoing.emergency, emergency);
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], createGoldenCallListResult0(number, "ringing"));
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], createGoldenCallListResult0(number, "ringing"));
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoing.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_incoming_already_connected.js
+++ b/dom/telephony/test/marionette/test_incoming_already_connected.js
@@ -7,37 +7,39 @@ MARIONETTE_HEAD_JS = 'head.js';
 let outNumber = "5555551111";
 let inNumber = "5555552222";
 let outgoingCall;
 let incomingCall;
 let gotOriginalConnected = false;
 
 function dial() {
   log("Make an outgoing call.");
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + outNumber + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + outNumber + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
--- a/dom/telephony/test/marionette/test_incoming_already_held.js
+++ b/dom/telephony/test/marionette/test_incoming_already_held.js
@@ -7,37 +7,39 @@ MARIONETTE_HEAD_JS = 'head.js';
 let outNumber = "5555551111";
 let inNumber = "5555552222";
 let outgoingCall;
 let incomingCall;
 let gotOriginalConnected = false;
 
 function dial() {
   log("Make an outgoing call.");
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + outNumber + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + outNumber + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
--- a/dom/telephony/test/marionette/test_multiple_hold.js
+++ b/dom/telephony/test/marionette/test_multiple_hold.js
@@ -91,38 +91,40 @@ function holdCall() {
   };
   incomingCall.hold();
 }
 
 // With one call on hold, make outgoing call
 function dial() {
   log("Making an outgoing call (while have one call already held).");
 
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 2);
-  is(telephony.calls[0], incomingCall);
-  is(telephony.calls[1], outgoingCall);
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 2);
+    is(telephony.calls[0], incomingCall);
+    is(telephony.calls[1], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "inbound from " + inNumber + " : held");
-      is(result[1], "outbound to  " + outNumber + " : ringing");
-      is(result[2], "OK");
-      answerOutgoing();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "inbound from " + inNumber + " : held");
+        is(result[1], "outbound to  " + outNumber + " : ringing");
+        is(result[2], "OK");
+        answerOutgoing();
+      });
+    };
+  });
 }
 
 // Have the outgoing call answered
 function answerOutgoing() {
   log("Answering the outgoing/2nd call");
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
--- a/dom/telephony/test/marionette/test_outgoing_already_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_already_held.js
@@ -104,39 +104,41 @@ function holdCall(){
   };
   incomingCall.hold();
 }
 
 // With one call on hold, make outgoing call
 function dial() {
   log("Making an outgoing call (while have one call already held).");
 
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 2);
-  is(telephony.calls[0], incomingCall);
-  is(telephony.calls[1], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 2);
+    is(telephony.calls[0], incomingCall);
+    is(telephony.calls[1], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "inbound from " + inNumber + " : held");
-      is(result[1], "outbound to  " + outNumber + " : ringing");
-      is(result[2], "OK");
-      answerOutgoing();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "inbound from " + inNumber + " : held");
+        is(result[1], "outbound to  " + outNumber + " : ringing");
+        is(result[2], "OK");
+        answerOutgoing();
+      });
+    };
+  });
 }
 
 // Have the outgoing call answered
 function answerOutgoing() {
   log("Answering the outgoing/2nd call");
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
--- a/dom/telephony/test/marionette/test_outgoing_answer_hangup.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_hangup.js
@@ -6,38 +6,40 @@ MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555552368";
 let outgoing;
 let calls;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  //ok(telephony.calls === calls); // bug 717414
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    //ok(telephony.calls === calls); // bug 717414
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoing.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_outgoing_answer_local_hangup.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_local_hangup.js
@@ -5,38 +5,40 @@ MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let outgoingCall;
 let outNumber = "5555551111";
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'alerting' call event.");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'alerting' call event.");
 
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + outNumber + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + outNumber + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoingCall.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
@@ -25,29 +25,29 @@ function setRadioEnabled(enabled) {
 
   return deferred.promise;
 }
 
 function dial(number) {
   log("Make an outgoing call.");
 
   let deferred = Promise.defer();
-  let call = telephony.dial(number);
-
-  ok(call);
-  is(call.number, number);
-  is(call.state, "dialing");
+  telephony.dial(number).then(call => {
+    ok(call);
+    is(call.number, number);
+    is(call.state, "dialing");
 
-  call.onalerting = function(event) {
-    log("Received 'onalerting' call event.");
-    call.onalerting = null;
-    is(call, event.call);
-    is(call.state, "alerting");
-    deferred.resolve(call);
-  };
+    call.onalerting = function(event) {
+      log("Received 'onalerting' call event.");
+      call.onalerting = null;
+      is(call, event.call);
+      is(call.state, "alerting");
+      deferred.resolve(call);
+    };
+  });
 
   return deferred.promise;
 }
 
 function remoteAnswer(call) {
   log("Remote answering the call.");
 
   let deferred = Promise.defer();
--- a/dom/telephony/test/marionette/test_outgoing_badNumber.js
+++ b/dom/telephony/test/marionette/test_outgoing_badNumber.js
@@ -2,40 +2,46 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "****5555552368****";
 let outgoing;
 
+
 function dial() {
   log("Make an outgoing call to an invalid number.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  // Note: The number is valid from the view of phone and the call could be
+  // dialed out successfully. However, it will later receive the BadNumberError
+  // from network side.
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onerror = function onerror(event) {
-    log("Received 'error' event.");
-    is(event.call, outgoing);
-    ok(event.call.error);
-    is(event.call.error.name, "BadNumberError");
+    outgoing.onerror = function onerror(event) {
+      log("Received 'error' event.");
+      is(event.call, outgoing);
+      ok(event.call.error);
+      is(event.call.error.name, "BadNumberError");
 
-    emulator.run("gsm list", function(result) {
-      log("Initial call list: " + result);
-      is(result[0], "OK");
-      cleanUp();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Initial call list: " + result);
+        is(result[0], "OK");
+        cleanUp();
+      });
+    };
+  });
 }
 
 function cleanUp() {
   finish();
 }
 
 startTest(function() {
   dial();
--- a/dom/telephony/test/marionette/test_outgoing_busy.js
+++ b/dom/telephony/test/marionette/test_outgoing_busy.js
@@ -5,37 +5,39 @@ MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555552368";
 let outgoing;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      busy();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        busy();
+      });
+    };
+  });
 }
 
 function busy() {
   log("The receiver is busy.");
 
   outgoing.onerror = function onerror(event) {
     log("Received 'error' call event.");
     is(outgoing, event.call);
--- a/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
+++ b/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
@@ -41,37 +41,39 @@ function setRadioEnabled(enabled, callba
   request.onerror = function onerror() {
     ok(false, "setRadioEnabled should be ok");
   };
 }
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + "        : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + "        : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoing.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_outgoing_hangup_alerting.js
+++ b/dom/telephony/test/marionette/test_outgoing_hangup_alerting.js
@@ -6,35 +6,37 @@ MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555552368";
 let outgoing;
 let calls;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  //ok(telephony.calls === calls); // bug 717414
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    //ok(telephony.calls === calls); // bug 717414
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'alerting' call event.");
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      hangUp();
-    });
-  };
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'alerting' call event.");
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        hangUp();
+      });
+    };
+  });
 }
 
 function hangUp() {
   log("Hang up the outgoing call.");
 
   let gotDisconnecting = false;
 
   outgoing.ondisconnecting = function ondisconnecting(event) {
--- a/dom/telephony/test/marionette/test_outgoing_hangup_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_hangup_held.js
@@ -6,37 +6,39 @@ MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555552368";
 let outgoing;
 let calls;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
-  is(outgoing, telephony.active);
-  //ok(telephony.calls === calls); // bug 717414
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
+    is(outgoing, telephony.active);
+    //ok(telephony.calls === calls); // bug 717414
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoing.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_outgoing_hold_resume.js
+++ b/dom/telephony/test/marionette/test_outgoing_hold_resume.js
@@ -6,37 +6,39 @@ MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555557777";
 let connectedCalls;
 let outgoingCall;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoingCall = telephony.dial(number);
-  ok(outgoingCall);
-  is(outgoingCall.number, number);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, number);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoingCall.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_outgoing_onstatechange.js
+++ b/dom/telephony/test/marionette/test_outgoing_onstatechange.js
@@ -5,41 +5,43 @@ MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let outgoingCall;
 let outNumber = "5555551111";
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onstatechange = function statechangering(event) {
-    log("Received 'onstatechange' call event.");
+    outgoingCall.onstatechange = function statechangering(event) {
+      log("Received 'onstatechange' call event.");
 
-    is(outgoingCall, event.call);
-    let expectedStates = ["dialing", "alerting"];
-    ok(expectedStates.indexOf(event.call.state) != -1);
+      is(outgoingCall, event.call);
+      let expectedStates = ["dialing", "alerting"];
+      ok(expectedStates.indexOf(event.call.state) != -1);
 
-    if (event.call.state == "alerting") {
-      emulator.run("gsm list", function(result) {
-        log("Call list is now: " + result);
-        is(result[0], "outbound to  " + outNumber + " : ringing");
-        is(result[1], "OK");
-        answer();
-      });
-    }
-  };
+      if (event.call.state == "alerting") {
+        emulator.run("gsm list", function(result) {
+          log("Call list is now: " + result);
+          is(result[0], "outbound to  " + outNumber + " : ringing");
+          is(result[1], "OK");
+          answer();
+        });
+      }
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoingCall.onstatechange = function onstatechangeanswer(event) {
--- a/dom/telephony/test/marionette/test_outgoing_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_radio_off.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let connection;
-let outgoing;
 
 function receivedPending(received, pending, nextAction) {
   let index = pending.indexOf(received);
   if (index != -1) {
     pending.splice(index, 1);
   }
   if (pending.length === 0) {
     nextAction();
@@ -45,39 +44,31 @@ function setRadioEnabled(enabled, callba
 function dial(number) {
   // Verify initial state before dial.
   ok(telephony);
   is(telephony.active, null);
   ok(telephony.calls);
   is(telephony.calls.length, 0);
 
   log("Make an outgoing call.");
-  outgoing = telephony.dial(number);
 
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(null, cause => {
+    log("Received promise 'reject'");
 
-  is(telephony.active, outgoing);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
-
-  outgoing.onerror = function onerror(event) {
-    log("Received 'error' event.");
-    is(event.call, outgoing);
-    ok(event.call.error);
-    is(event.call.error.name, "RadioNotAvailable");
+    is(telephony.active, null);
+    is(telephony.calls.length, 0);
+    is(cause, "RadioNotAvailable");
 
     emulator.run("gsm list", function(result) {
       log("Initial call list: " + result);
       is(result[0], "OK");
 
       setRadioEnabled(true, cleanUp);
     });
-  };
+  });
 }
 
 function cleanUp() {
   finish();
 }
 
 startTestWithPermissions(['mobileconnection'], function() {
   connection = navigator.mozMobileConnections[0];
--- a/dom/telephony/test/marionette/test_outgoing_reject.js
+++ b/dom/telephony/test/marionette/test_outgoing_reject.js
@@ -5,37 +5,39 @@ MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let number = "5555552368";
 let outgoing;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoing = telephony.dial(number);
-  ok(outgoing);
-  is(outgoing.number, number);
-  is(outgoing.state, "dialing");
+  telephony.dial(number).then(call => {
+    outgoing = call;
+    ok(outgoing);
+    is(outgoing.number, number);
+    is(outgoing.state, "dialing");
 
-  is(outgoing, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoing);
+    is(outgoing, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoing);
 
-  outgoing.onalerting = function onalerting(event) {
-    log("Received 'onalerting' call event.");
-    is(outgoing, event.call);
-    is(outgoing.state, "alerting");
+    outgoing.onalerting = function onalerting(event) {
+      log("Received 'onalerting' call event.");
+      is(outgoing, event.call);
+      is(outgoing.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + number + " : ringing");
-      is(result[1], "OK");
-      reject();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + number + " : ringing");
+        is(result[1], "OK");
+        reject();
+      });
+    };
+  });
 }
 
 function reject() {
   log("Reject the outgoing call on the other end.");
   // We get no "disconnecting" event when the remote party rejects the call.
 
   outgoing.ondisconnected = function ondisconnected(event) {
     log("Received 'disconnected' call event.");
--- a/dom/telephony/test/marionette/test_outgoing_remote_hangup_held.js
+++ b/dom/telephony/test/marionette/test_outgoing_remote_hangup_held.js
@@ -4,38 +4,40 @@
 MARIONETTE_TIMEOUT = 60000;
 
 let outNumber = "5555551111";
 let outgoingCall;
 
 function dial() {
   log("Make an outgoing call.");
 
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  is(outgoingCall, telephony.active);
-  is(telephony.calls.length, 1);
-  is(telephony.calls[0], outgoingCall);
+    is(outgoingCall, telephony.active);
+    is(telephony.calls.length, 1);
+    is(telephony.calls[0], outgoingCall);
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'alerting' call event.");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'alerting' call event.");
 
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + outNumber + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + outNumber + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
 
   outgoingCall.onconnected = function onconnected(event) {
--- a/dom/telephony/test/marionette/test_swap_held_and_active.js
+++ b/dom/telephony/test/marionette/test_swap_held_and_active.js
@@ -9,37 +9,39 @@ let inNumber = "5555552222";
 let outgoingCall;
 let incomingCall;
 let gotOriginalConnected = false;
 let gotHeld = false;
 let gotConnected = false;
 
 function dial() {
   log("Make an outgoing call.");
-  outgoingCall = telephony.dial(outNumber);
-  ok(outgoingCall);
-  is(outgoingCall.number, outNumber);
-  is(outgoingCall.state, "dialing");
+  telephony.dial(outNumber).then(call => {
+    outgoingCall = call;
+    ok(outgoingCall);
+    is(outgoingCall.number, outNumber);
+    is(outgoingCall.state, "dialing");
 
-  outgoingCall.onalerting = function onalerting(event) {
-    log("Received 'alerting' call event.");
+    outgoingCall.onalerting = function onalerting(event) {
+      log("Received 'alerting' call event.");
 
-    is(outgoingCall, event.call);
-    is(outgoingCall.state, "alerting");
-    is(outgoingCall, telephony.active);
-    is(telephony.calls.length, 1);
-    is(telephony.calls[0], outgoingCall);
+      is(outgoingCall, event.call);
+      is(outgoingCall.state, "alerting");
+      is(outgoingCall, telephony.active);
+      is(telephony.calls.length, 1);
+      is(telephony.calls[0], outgoingCall);
 
-    emulator.run("gsm list", function(result) {
-      log("Call list is now: " + result);
-      is(result[0], "outbound to  " + outNumber + " : ringing");
-      is(result[1], "OK");
-      answer();
-    });
-  };
+      emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        is(result[0], "outbound to  " + outNumber + " : ringing");
+        is(result[1], "OK");
+        answer();
+      });
+    };
+  });
 }
 
 function answer() {
   log("Answering the outgoing call.");
 
   // We get no "connecting" event when the remote party answers the call.
   outgoingCall.onconnected = function onconnectedOut(event) {
     log("Received 'connected' call event for the original outgoing call.");
--- a/dom/webidl/Telephony.webidl
+++ b/dom/webidl/Telephony.webidl
@@ -11,21 +11,21 @@ interface Telephony : EventTarget {
    * |serviceId| to indicate the target telephony service. If not specified,
    * the implementation MUST use the default service.
    *
    * Possible values of |serviceId| are 0 ~ (number of services - 1), which is
    * simply the index of a service. Get number of services by acquiring
    * |navigator.mozMobileConnections.length|.
    */
 
-  [Throws]
-  TelephonyCall dial(DOMString number, optional unsigned long serviceId);
+  // Promise<TelephonyCall>
+  Promise dial(DOMString number, optional unsigned long serviceId);
 
-  [Throws]
-  TelephonyCall dialEmergency(DOMString number, optional unsigned long serviceId);
+  // Promise<TelephonyCall>
+  Promise dialEmergency(DOMString number, optional unsigned long serviceId);
 
   [Throws]
   void startTone(DOMString tone, optional unsigned long serviceId);
 
   [Throws]
   void stopTone(optional unsigned long serviceId);
 
   [Throws]