merge b2g-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 05 Mar 2014 13:24:51 +0100
changeset 171972 714c8927d6afe94183f270a8606b5052e3e1dc99
parent 171945 7f7d0399102a8703d675007a3c61465ba3a4c4d3 (current diff)
parent 171971 7ea33602118db17cbca545ca41cda19fd974a04b (diff)
child 171973 c6123402c5f6bc949c0c37dacad1c3a913758cf1
child 171979 260334e5c5aa470082ebd9047fc6b1abc6850eea
child 171986 2d0534a0b50fc0fec77897226ef06e730b673276
child 172011 a510eca01cff45e92b4abc457a4d8d15ae4b5058
push id26343
push usercbook@mozilla.com
push dateWed, 05 Mar 2014 12:27:10 +0000
treeherdermozilla-central@714c8927d6af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge b2g-inbound to mozilla-central
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
js/xpconnect/src/XPCJSRuntime.cpp
mobile/android/chrome/content/MemoryObserver.js
xpcom/base/nsIMemoryInfoDumper.idl
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryInfoDumper.cpp
xpcom/base/nsMemoryInfoDumper.h
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
--- 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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <!-- 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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <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"/>
new file mode 100644
--- /dev/null
+++ b/b2g/config/emulator-kk/config.json
@@ -0,0 +1,32 @@
+{
+    "config_version": 2,
+    "tooltool_manifest": "releng-emulator-kk.tt",
+    "mock_target": "mozilla-centos6-x86_64",
+    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git", "libxml2"],
+    "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
+    "build_targets": ["droid", "package-emulator", "package-tests"],
+    "upload_files": [
+        "{workdir}/out/target/product/generic/*.tar.bz2",
+        "{workdir}/out/target/product/generic/tests/*.zip",
+        "{workdir}/out/emulator.tar.gz",
+        "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
+        "{workdir}/sources.xml"
+    ],
+    "public_upload_files": [
+        "{workdir}/out/target/product/generic/*.tar.bz2",
+        "{workdir}/out/target/product/generic/tests/*.zip",
+        "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
+        "{workdir}/sources.xml"
+    ],
+    "upload_platform": "emulator-kk",
+    "gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
+    "gaia": {
+        "l10n": {
+            "vcs": "hgtool",
+            "root": "https://hg.mozilla.org/gaia-l10n"
+        }
+    },
+    "b2g_manifest": "emulator-kk.xml",
+    "b2g_manifest_branch": "master",
+    "b2g_manifest_intree": true
+}
new file mode 100644
--- /dev/null
+++ b/b2g/config/emulator-kk/releng-emulator-kk.tt
@@ -0,0 +1,2 @@
+[
+]
--- 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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <!-- 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": "61e8a88f301aea77f22abdde19a85f0af27ab7f4", 
+    "revision": "03dfc866c15f5322d13a542b722eeb1accb1c429", 
     "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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <!-- 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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <!-- 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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <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="6781459a49642ca0eb7ec3e95667808d5d77b656"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="4bb8b32d8f1a3a64bfac8f2da7878dc63e613ca5"/>
   <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="b2f773d8320d30648b89767dfe5b25ef94bc7e62"/>
   <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/bluetooth/BluetoothHidManager.cpp
+++ b/dom/bluetooth/BluetoothHidManager.cpp
@@ -38,19 +38,25 @@ BluetoothHidManager::Observe(nsISupports
     return NS_OK;
   }
 
   MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 BluetoothHidManager::BluetoothHidManager()
-  : mConnected(false)
-  , mController(nullptr)
 {
+  Reset();
+}
+
+void
+BluetoothHidManager::Reset()
+{
+  mConnected = false;
+  mController = nullptr;
 }
 
 bool
 BluetoothHidManager::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
--- a/dom/bluetooth/BluetoothProfileController.cpp
+++ b/dom/bluetooth/BluetoothProfileController.cpp
@@ -5,47 +5,76 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 
 #include "BluetoothA2dpManager.h"
 #include "BluetoothHfpManager.h"
 #include "BluetoothHidManager.h"
+#include "BluetoothUtils.h"
 
-#include "BluetoothUtils.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "nsComponentManagerUtils.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 #define BT_LOGR_PROFILE(mgr, msg, ...)               \
   do {                                               \
     nsCString name;                                  \
     mgr->GetName(name);                              \
     BT_LOGR("[%s] " msg, name.get(), ##__VA_ARGS__); \
   } while(0)
 
+#define CONNECTION_TIMEOUT_MS 15000
+
+class CheckProfileStatusCallback : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+
+  CheckProfileStatusCallback(BluetoothProfileController* aController)
+    : mController(aController)
+  {
+    MOZ_ASSERT(aController);
+  }
+
+  virtual ~CheckProfileStatusCallback()
+  {
+    mController = nullptr;
+  }
+
+private:
+  nsRefPtr<BluetoothProfileController> mController;
+};
+
 BluetoothProfileController::BluetoothProfileController(
                                    bool aConnect,
                                    const nsAString& aDeviceAddress,
                                    BluetoothReplyRunnable* aRunnable,
                                    BluetoothProfileControllerCallback aCallback,
                                    uint16_t aServiceUuid,
                                    uint32_t aCod)
   : mConnect(aConnect)
   , mDeviceAddress(aDeviceAddress)
   , mRunnable(aRunnable)
   , mCallback(aCallback)
+  , mCurrentProfileFinished(false)
   , mSuccess(false)
   , mProfilesIndex(-1)
 {
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
   MOZ_ASSERT(aRunnable);
   MOZ_ASSERT(aCallback);
 
+  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  MOZ_ASSERT(mTimer);
+
+  mCheckProfileStatusCallback = new CheckProfileStatusCallback(this);
   mProfiles.Clear();
 
   /**
    * If the service uuid is not specified, either connect multiple profiles
    * based on Cod, or disconnect all connected profiles.
    */
   if (!aServiceUuid) {
     mTarget.cod = aCod;
@@ -58,16 +87,20 @@ BluetoothProfileController::BluetoothPro
   }
 }
 
 BluetoothProfileController::~BluetoothProfileController()
 {
   mProfiles.Clear();
   mRunnable = nullptr;
   mCallback = nullptr;
+
+  if (mTimer) {
+    mTimer->Cancel();
+  }
 }
 
 void
 BluetoothProfileController::AddProfileWithServiceClass(
                                                    BluetoothServiceClass aClass)
 {
   BluetoothProfileManagerBase* profile;
   switch (aClass) {
@@ -159,44 +192,71 @@ BluetoothProfileController::SetupProfile
 
   // A device which supports HID should claim that it's a peripheral and it's
   // either a keyboard, a pointing device, or both.
   if (isPeripheral && (isKeyboard || isPointingDevice)) {
     AddProfile(BluetoothHidManager::Get());
   }
 }
 
+NS_IMPL_ISUPPORTS1(CheckProfileStatusCallback, nsITimerCallback)
+
+NS_IMETHODIMP
+CheckProfileStatusCallback::Notify(nsITimer* aTimer)
+{
+  MOZ_ASSERT(mController);
+  // Continue on the next profile since we haven't got the callback after
+  // timeout.
+  mController->GiveupAndContinue();
+
+  return NS_OK;
+}
+
 void
 BluetoothProfileController::Start()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   MOZ_ASSERT(mProfilesIndex == -1);
+  MOZ_ASSERT(mTimer);
   NS_ENSURE_TRUE_VOID(mProfiles.Length() > 0);
 
   ++mProfilesIndex;
   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
 
+  if (mTimer) {
+    mTimer->InitWithCallback(mCheckProfileStatusCallback, CONNECTION_TIMEOUT_MS,
+                             nsITimer::TYPE_ONE_SHOT);
+  }
+
   if (mConnect) {
     mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
   } else {
     mProfiles[mProfilesIndex]->Disconnect(this);
   }
 }
 
 void
 BluetoothProfileController::Next()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
+  MOZ_ASSERT(mTimer);
 
+  mCurrentProfileFinished = false;
   if (++mProfilesIndex < (int)mProfiles.Length()) {
     BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
 
+    if (mTimer) {
+      mTimer->InitWithCallback(mCheckProfileStatusCallback,
+                               CONNECTION_TIMEOUT_MS,
+                               nsITimer::TYPE_ONE_SHOT);
+    }
+
     if (mConnect) {
       mProfiles[mProfilesIndex]->Connect(mDeviceAddress, this);
     } else {
       mProfiles[mProfilesIndex]->Disconnect(this);
     }
     return;
   }
 
@@ -212,35 +272,60 @@ BluetoothProfileController::Next()
   }
   mCallback();
 }
 
 void
 BluetoothProfileController::OnConnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mTimer);
+
   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
     NS_ConvertUTF16toUTF8(aErrorStr).get());
 
+  mCurrentProfileFinished = true;
+  if (mTimer) {
+    mTimer->Cancel();
+  }
+
   if (!aErrorStr.IsEmpty()) {
     BT_WARNING(NS_ConvertUTF16toUTF8(aErrorStr).get());
   } else {
     mSuccess = true;
   }
 
   Next();
 }
 
 void
 BluetoothProfileController::OnDisconnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mTimer);
+
   BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "<%s>",
     NS_ConvertUTF16toUTF8(aErrorStr).get());
 
+  mCurrentProfileFinished = true;
+  if (mTimer) {
+    mTimer->Cancel();
+  }
+
   if (!aErrorStr.IsEmpty()) {
     BT_WARNING(NS_ConvertUTF16toUTF8(aErrorStr).get());
   } else {
     mSuccess = true;
   }
 
   Next();
 }
+
+void
+BluetoothProfileController::GiveupAndContinue()
+{
+  MOZ_ASSERT(!mCurrentProfileFinished);
+  MOZ_ASSERT(mProfilesIndex < (int)mProfiles.Length());
+
+  BT_LOGR_PROFILE(mProfiles[mProfilesIndex], ERR_OPERATION_TIMEOUT);
+  mProfiles[mProfilesIndex]->Reset();
+  Next();
+}
--- a/dom/bluetooth/BluetoothProfileController.h
+++ b/dom/bluetooth/BluetoothProfileController.h
@@ -3,18 +3,19 @@
 /* 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_bluetooth_bluetoothprofilecontroller_h__
 #define mozilla_dom_bluetooth_bluetoothprofilecontroller_h__
 
 #include "BluetoothUuid.h"
+#include "mozilla/RefPtr.h"
 #include "nsAutoPtr.h"
-#include "mozilla/RefPtr.h"
+#include "nsITimer.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 /*
  * Class of Device(CoD): 32-bit unsigned integer
  *
  *  31   24  23    13 12     8 7      2 1 0
  * |       | Major   | Major  | Minor  |   |
@@ -97,16 +98,21 @@ public:
   void OnConnect(const nsAString& aErrorStr);
 
   /**
    * It is invoked after a profile has tried to drop the connection.
    * An error string is returned when it fails.
    */
   void OnDisconnect(const nsAString& aErrorStr);
 
+  /**
+   * It is invoked after a profile has reached timeout, reset mProfiles.
+   */
+  void GiveupAndContinue();
+
 private:
   // Setup data member mProfiles
   void SetupProfiles(bool aAssignServiceClass);
 
   // Add profiles into array with/without checking connection status
   void AddProfile(BluetoothProfileManagerBase* aProfile,
                   bool aCheckConnected = false);
 
@@ -116,22 +122,26 @@ private:
   // Connect/Disconnect next profile in the array
   void Next();
 
   const bool mConnect;
   nsString mDeviceAddress;
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
   BluetoothProfileControllerCallback mCallback;
 
+  bool mCurrentProfileFinished;
   bool mSuccess;
   int8_t mProfilesIndex;
   nsTArray<BluetoothProfileManagerBase*> mProfiles;
 
   // Either CoD or BluetoothServiceClass is assigned.
   union {
     uint32_t cod;
     BluetoothServiceClass service;
   } mTarget;
+
+  nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsITimerCallback> mCheckProfileStatusCallback;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothProfileManagerBase.h
+++ b/dom/bluetooth/BluetoothProfileManagerBase.h
@@ -15,16 +15,17 @@
 #define ERR_ALREADY_CONNECTED           "AlreadyConnectedError"
 #define ERR_ALREADY_DISCONNECTED        "AlreadyDisconnectedError"
 #define ERR_CONNECTION_FAILED           "ConnectionFailedError"
 #define ERR_DISCONNECTION_FAILED        "DisconnectionFailedError"
 #define ERR_NO_AVAILABLE_RESOURCE       "NoAvailableResourceError"
 #define ERR_REACHED_CONNECTION_LIMIT    "ReachedConnectionLimitError"
 #define ERR_SERVICE_CHANNEL_NOT_FOUND   "DeviceChannelRetrievalError"
 #define ERR_UNKNOWN_PROFILE             "UnknownProfileError"
+#define ERR_OPERATION_TIMEOUT           "OperationTimeout"
 
 #include "BluetoothCommon.h"
 #include "nsIObserver.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 class BluetoothProfileController;
 
 class BluetoothProfileManagerBase : public nsIObserver
@@ -60,17 +61,22 @@ public:
   /**
    * If it establishes/releases a connection successfully, the error string
    * will be empty. Otherwise, the error string shows the failure reason.
    */
   virtual void OnConnect(const nsAString& aErrorStr) = 0;
   virtual void OnDisconnect(const nsAString& aErrorStr) = 0;
 
   /**
-   * Returns string of profile name
+   * Clean up profile resources and set mController as null.
+   */
+  virtual void Reset() = 0;
+
+  /**
+   * Returns string of profile name.
    */
   virtual void GetName(nsACString& aName) = 0;
 };
 
 #define BT_DECL_PROFILE_MGR_BASE                                                 \
 public:                                                                          \
   NS_DECL_ISUPPORTS                                                              \
   NS_DECL_NSIOBSERVER                                                            \
@@ -80,12 +86,13 @@ public:                                 
   virtual void OnUpdateSdpRecords(const nsAString& aDeviceAddress) MOZ_OVERRIDE; \
   virtual void GetAddress(nsAString& aDeviceAddress) MOZ_OVERRIDE;               \
   virtual bool IsConnected() MOZ_OVERRIDE;                                       \
   virtual void Connect(const nsAString& aDeviceAddress,                          \
                        BluetoothProfileController* aController) MOZ_OVERRIDE;    \
   virtual void Disconnect(BluetoothProfileController* aController) MOZ_OVERRIDE; \
   virtual void OnConnect(const nsAString& aErrorStr) MOZ_OVERRIDE;               \
   virtual void OnDisconnect(const nsAString& AErrorStr) MOZ_OVERRIDE;            \
+  virtual void Reset() MOZ_OVERRIDE;
 
 END_BLUETOOTH_NAMESPACE
 
 #endif  //#ifndef mozilla_dom_bluetooth_bluetoothprofilemanagerbase_h__
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -470,35 +470,42 @@ BluetoothService::StartStopBluetooth(boo
   }
 
   if (!aStart) {
     BluetoothProfileManagerBase* profile;
     profile = BluetoothHfpManager::Get();
     NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
     if (profile->IsConnected()) {
       profile->Disconnect(nullptr);
+    } else {
+      profile->Reset();
     }
 
     profile = BluetoothOppManager::Get();
     NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
     if (profile->IsConnected()) {
       profile->Disconnect(nullptr);
     }
 
     profile = BluetoothA2dpManager::Get();
     NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
     if (profile->IsConnected()) {
       profile->Disconnect(nullptr);
+    } else {
+      profile->Reset();
     }
 
     profile = BluetoothHidManager::Get();
     NS_ENSURE_TRUE(profile, NS_ERROR_FAILURE);
     if (profile->IsConnected()) {
       profile->Disconnect(nullptr);
+    } else {
+      profile->Reset();
     }
+
   }
 
   if (!mBluetoothThread) {
     mBluetoothThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
                                           NS_LITERAL_CSTRING("Bluetooth"),
                                           LazyIdleThread::ManualShutdown);
   }
 
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -193,16 +193,22 @@ BluetoothA2dpManager::Observe(nsISupport
   }
 
   MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 BluetoothA2dpManager::BluetoothA2dpManager()
 {
+  Reset();
+}
+
+void
+BluetoothA2dpManager::Reset()
+{
   ResetA2dp();
   ResetAvrcp();
 }
 
 static void
 AvStatusToSinkString(btav_connection_state_t aStatus, nsAString& aState)
 {
   nsAutoString state;
@@ -587,33 +593,35 @@ BluetoothA2dpManager::Connect(const nsAS
   NS_ENSURE_TRUE_VOID(sBtA2dpInterface);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
                       sBtA2dpInterface->connect(&remoteAddress));
 }
 
 void
 BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mController);
+
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     if (aController) {
       aController->OnDisconnect(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     }
     return;
   }
 
   if (!mA2dpConnected) {
     if (aController) {
       aController->OnDisconnect(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
     }
     return;
   }
 
   MOZ_ASSERT(!mDeviceAddress.IsEmpty());
-  MOZ_ASSERT(!mController);
 
   mController = aController;
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(mDeviceAddress, &remoteAddress);
   if (sBtA2dpInterface) {
     NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
                         sBtA2dpInterface->disconnect(&remoteAddress));
@@ -643,16 +651,17 @@ BluetoothA2dpManager::OnDisconnect(const
   /**
    * On the one hand, notify the controller that we've done for outbound
    * connections. On the other hand, we do nothing for inbound connections.
    */
   NS_ENSURE_TRUE_VOID(mController);
 
   nsRefPtr<BluetoothProfileController> controller = mController.forget();
   controller->OnDisconnect(aErrorStr);
+  Reset();
 }
 
 /* HandleSinkPropertyChanged update sink state in A2dp
  *
  * Possible values: "disconnected", "connecting", "connected", "playing"
  *
  * 1. "disconnected" -> "connecting"
  *    Either an incoming or outgoing connection attempt ongoing
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
@@ -53,16 +53,17 @@ public:
   void GetAlbum(nsAString& aAlbum);
   uint32_t GetDuration();
   ControlPlayStatus GetPlayStatus();
   uint32_t GetPosition();
   uint64_t GetMediaNumber();
   uint64_t GetTotalMediaNumber();
   void GetTitle(nsAString& aTitle);
   void GetArtist(nsAString& aArtist);
+
 private:
   class SinkPropertyChangedHandler;
   BluetoothA2dpManager();
   bool Init();
   void HandleShutdown();
   void NotifyConnectionStatusChanged();
 
   nsString mDeviceAddress;
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -1548,8 +1548,14 @@ BluetoothOppManager::OnConnect(const nsA
   MOZ_ASSERT(false);
 }
 
 void
 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(false);
 }
+
+void
+BluetoothOppManager::Reset()
+{
+  MOZ_ASSERT(false);
+}
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -1241,25 +1241,25 @@ BluetoothHfpManager::Connect(const nsASt
   mDeviceAddress = aDeviceAddress;
   mController = aController;
 }
 
 void
 BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mController);
 
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
   bt_bdaddr_t deviceBdAddress;
   StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
     sBluetoothHfpInterface->disconnect(&deviceBdAddress));
 
-  MOZ_ASSERT(!mController);
   mController = aController;
 }
 
 void
 BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
@@ -129,17 +129,16 @@ private:
   friend class CloseScoTask;
   friend class RespondToBLDNTask;
   friend class MainThreadTask;
 
   BluetoothHfpManager();
   bool Init();
   bool InitHfpInterface();
   void DeinitHfpInterface();
-  void Reset();
 
   void HandleShutdown();
   void HandleVolumeChanged(const nsAString& aData);
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 
   void NotifyConnectionStateChanged(const nsAString& aType);
   void NotifyDialer(const nsAString& aCommand);
 
--- a/dom/bluetooth/bluez/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothA2dpManager.cpp
@@ -41,16 +41,22 @@ BluetoothA2dpManager::Observe(nsISupport
   }
 
   MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 BluetoothA2dpManager::BluetoothA2dpManager()
 {
+  Reset();
+}
+
+void
+BluetoothA2dpManager::Reset()
+{
   ResetA2dp();
   ResetAvrcp();
 }
 
 bool
 BluetoothA2dpManager::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -225,16 +231,17 @@ BluetoothA2dpManager::OnDisconnect(const
   /**
    * On the one hand, notify the controller that we've done for outbound
    * connections. On the other hand, we do nothing for inbound connections.
    */
   NS_ENSURE_TRUE_VOID(mController);
 
   nsRefPtr<BluetoothProfileController> controller = mController.forget();
   controller->OnDisconnect(aErrorStr);
+  Reset();
 }
 
 /* HandleSinkPropertyChanged update sink state in A2dp
  *
  * Possible values: "disconnected", "connecting", "connected", "playing"
  *
  * 1. "disconnected" -> "connecting"
  *    Either an incoming or outgoing connection attempt ongoing
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -337,17 +337,17 @@ Call::IsActive()
 {
   return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
 }
 #endif // MOZ_B2G_RIL
 
 /**
  *  BluetoothHfpManager
  */
-BluetoothHfpManager::BluetoothHfpManager() : mController(nullptr)
+BluetoothHfpManager::BluetoothHfpManager()
 {
 #ifdef MOZ_B2G_RIL
   mPhoneType = PhoneType::NONE;
 #endif // MOZ_B2G_RIL
 
   Reset();
 }
 
@@ -379,34 +379,33 @@ BluetoothHfpManager::Reset()
   for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
     sCINDItems[i].activated = true;
   }
 
 #ifdef MOZ_B2G_RIL
   mCCWA = false;
   mCLIP = false;
   mDialingRequestProcessed = true;
-#endif
-  mCMEE = false;
-  mCMER = false;
-  mConnectScoRequest = false;
-  mSlcConnected = false;
-  mIsHsp = false;
-  mReceiveVgsFlag = false;
 
-#ifdef MOZ_B2G_RIL
   // We disable BSIR by default as it requires OEM implement BT SCO + SPEAKER
   // output audio path in audio driver. OEM can enable BSIR by setting
   // mBSIR=true here.
   //
   // Please see Bug 878728 for more information.
   mBSIR = false;
 
   ResetCallArray();
 #endif
+  mCMEE = false;
+  mCMER = false;
+  mConnectScoRequest = false;
+  mSlcConnected = false;
+  mIsHsp = false;
+  mReceiveVgsFlag = false;
+  mController = nullptr;
 }
 
 bool
 BluetoothHfpManager::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -1931,8 +1930,9 @@ BluetoothHfpManager::OnDisconnect(const 
    */
   NS_ENSURE_TRUE_VOID(mController);
 
   nsRefPtr<BluetoothProfileController> controller = mController.forget();
   controller->OnDisconnect(aErrorStr);
 }
 
 NS_IMPL_ISUPPORTS1(BluetoothHfpManager, nsIObserver)
+
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -148,17 +148,16 @@ private:
   friend class BluetoothHfpManagerObserver;
 
   BluetoothHfpManager();
   void HandleShutdown();
   void HandleVolumeChanged(const nsAString& aData);
 
   bool Init();
   void Notify(const hal::BatteryInformation& aBatteryInfo);
-  void Reset();
 #ifdef MOZ_B2G_RIL
   void ResetCallArray();
   uint32_t FindFirstCall(uint16_t aState);
   uint32_t GetNumberOfCalls(uint16_t aState);
   PhoneType GetPhoneType(const nsAString& aType);
 #endif
 
   void NotifyConnectionStatusChanged(const nsAString& aType);
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -1604,8 +1604,14 @@ BluetoothOppManager::OnConnect(const nsA
   MOZ_ASSERT(false);
 }
 
 void
 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(false);
 }
+
+void
+BluetoothOppManager::Reset()
+{
+  MOZ_ASSERT(false);
+}
--- a/dom/bluetooth/tests/marionette/head.js
+++ b/dom/bluetooth/tests/marionette/head.js
@@ -4,22 +4,23 @@
  * 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/. */
 
 let Promise =
   SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
 
 let bluetoothManager;
 
-/* Get mozSettings value specified by @aKey.
+/**
+ * Get mozSettings value specified by @aKey.
  *
  * Resolve if that mozSettings value is retrieved successfully, reject
  * otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   The corresponding mozSettings value of the key.
  * Reject params: (none)
  *
  * @param aKey
  *        A string.
  *
  * @return A deferred promise.
  */
@@ -34,21 +35,22 @@ function getSettings(aKey) {
   request.addEventListener("error", function() {
     ok(false, "getSettings(" + aKey + ")");
     deferred.reject();
   });
 
   return deferred.promise;
 }
 
-/* Set mozSettings values.
+/**
+ * Set mozSettings values.
  *
  * Resolve if that mozSettings value is set successfully, reject otherwise.
  *
- * Forfill params: (none)
+ * Fulfill params: (none)
  * Reject params: (none)
  *
  * @param aSettings
  *        An object of format |{key1: value1, key2: value2, ...}|.
  *
  * @return A deferred promise.
  */
 function setSettings(aSettings) {
@@ -62,53 +64,56 @@ function setSettings(aSettings) {
   request.addEventListener("error", function() {
     ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
     deferred.reject();
   });
 
   return deferred.promise;
 }
 
-/* Get mozSettings value of 'bluetooth.enabled'.
+/**
+ * Get mozSettings value of 'bluetooth.enabled'.
  *
  * Resolve if that mozSettings value is retrieved successfully, reject
  * otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   A boolean value.
  * Reject params: (none)
  *
  * @return A deferred promise.
  */
 function getBluetoothEnabled() {
   return getSettings("bluetooth.enabled");
 }
 
-/* Set mozSettings value of 'bluetooth.enabled'.
+/**
+ * Set mozSettings value of 'bluetooth.enabled'.
  *
  * Resolve if that mozSettings value is set successfully, reject otherwise.
  *
- * Forfill params: (none)
+ * Fulfill params: (none)
  * Reject params: (none)
  *
  * @param aEnabled
  *        A boolean value.
  *
  * @return A deferred promise.
  */
 function setBluetoothEnabled(aEnabled) {
   let obj = {};
   obj["bluetooth.enabled"] = aEnabled;
   return setSettings(obj);
 }
 
-/* Push required permissions and test if |navigator.mozBluetooth| exists.
+/**
+ * Push required permissions and test if |navigator.mozBluetooth| exists.
  * Resolve if it does, reject otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   bluetoothManager -- an reference to navigator.mozBluetooth.
  * Reject params: (none)
  *
  * @param aPermissions
  *        Additional permissions to push before any test cases.  Could be either
  *        a string or an array of strings.
  *
  * @return A deferred promise.
@@ -146,43 +151,45 @@ function ensureBluetoothManager(aPermiss
     } else {
       deferred.reject();
     }
   });
 
   return deferred.promise;
 }
 
-/* Wait for one named BluetoothManager event.
+/**
+ * Wait for one named BluetoothManager event.
  *
  * Resolve if that named event occurs.  Never reject.
  *
- * Forfill params: the DOMEvent passed.
+ * Fulfill params: the DOMEvent passed.
  *
  * @return A deferred promise.
  */
 function waitForManagerEvent(aEventName) {
   let deferred = Promise.defer();
 
   bluetoothManager.addEventListener(aEventName, function onevent(aEvent) {
     bluetoothManager.removeEventListener(aEventName, onevent);
 
     ok(true, "BluetoothManager event '" + aEventName + "' got.");
     deferred.resolve(aEvent);
   });
 
   return deferred.promise;
 }
 
-/* Convenient function for setBluetoothEnabled and waitForManagerEvent
+/**
+ * Convenient function for setBluetoothEnabled and waitForManagerEvent
  * combined.
  *
  * Resolve if that named event occurs.  Reject if we can't set settings.
  *
- * Forfill params: the DOMEvent passed.
+ * Fulfill params: the DOMEvent passed.
  * Reject params: (none)
  *
  * @return A deferred promise.
  */
 function setBluetoothEnabledAndWait(aEnabled) {
   let promises = [];
 
   // Bug 969109 -  Intermittent test_dom_BluetoothManager_adapteradded.js
@@ -193,21 +200,22 @@ function setBluetoothEnabledAndWait(aEna
   // installing the event handler *before* we ever enable/disable Bluetooth. Or
   // we might just miss those events and get a timeout error.
   promises.push(waitForManagerEvent(aEnabled ? "enabled" : "disabled"));
   promises.push(setBluetoothEnabled(aEnabled));
 
   return Promise.all(promises);
 }
 
-/* Get default adapter.
+/**
+ * Get default adapter.
  *
  * Resolve if that default adapter is got, reject otherwise.
  *
- * Forfill params: a BluetoothAdapter instance.
+ * Fulfill params: a BluetoothAdapter instance.
  * Reject params: a DOMError, or null if if there is no adapter ready yet.
  *
  * @return A deferred promise.
  */
 function getDefaultAdapter() {
   let deferred = Promise.defer();
 
   let request = bluetoothManager.getDefaultAdapter();
@@ -232,17 +240,18 @@ function getDefaultAdapter() {
   request.onerror = function(aEvent) {
     ok(false, "Failed to get default adapter.");
     deferred.reject(aEvent.target.error);
   };
 
   return deferred.promise;
 }
 
-/* Flush permission settings and call |finish()|.
+/**
+ * Flush permission settings and call |finish()|.
  */
 function cleanUp() {
   SpecialPowers.flushPermissions(function() {
     // Use ok here so that we have at least one test run.
     ok(true, "permissions flushed");
 
     finish();
   });
new file mode 100644
--- /dev/null
+++ b/dom/events/test/marionette/head.js
@@ -0,0 +1,146 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
+
+let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
+
+let _pendingEmulatorCmdCount = 0;
+
+/**
+ * Send emulator command with safe guard.
+ *
+ * We should only call |finish()| after all emulator command transactions
+ * end, so here comes with the pending counter.  Resolve when the emulator
+ * gives positive response, and reject otherwise.
+ *
+ * Fulfill params:
+ *   result -- an array of emulator response lines.
+ * Reject params:
+ *   result -- an array of emulator response lines.
+ *
+ * @return A deferred promise.
+ */
+function runEmulatorCmdSafe(aCommand) {
+  let deferred = Promise.defer();
+
+  ++_pendingEmulatorCmdCount;
+  runEmulatorCmd(aCommand, function(aResult) {
+    --_pendingEmulatorCmdCount;
+
+    ok(true, "Emulator response: " + JSON.stringify(aResult));
+    if (Array.isArray(aResult) &&
+        aResult[aResult.length - 1] === "OK") {
+      deferred.resolve(aResult);
+    } else {
+      deferred.reject(aResult);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Get emulator sensor values of a named sensor.
+ *
+ * Fulfill params:
+ *   result -- an array of emulator sensor values.
+ * Reject params: (none)
+ *
+ * @param aSensorName
+ *        A string name of the sensor.  Availables are: "acceleration"
+ *        "magnetic-field", "orientation", "temperature", "proximity".
+ *
+ * @return A deferred promise.
+ */
+function getEmulatorSensorValues(aSensorName) {
+  return runEmulatorCmdSafe("sensor get " + aSensorName)
+    .then(function(aResult) {
+      // aResult = ["orientation = 0:0:0", "OK"]
+      return aResult[0].split(" ")[2].split(":").map(function(aElement) {
+        return parseInt(aElement, 10);
+      });
+    });
+}
+
+/**
+ * Convenient alias function for getting orientation sensor values.
+ */
+function getEmulatorOrientationValues() {
+  return getEmulatorSensorValues("orientation");
+}
+
+/**
+ * Set emulator orientation sensor values.
+ *
+ * Fulfill params: (none)
+ * Reject params: (none)
+ *
+ * @param aAzimuth
+ * @param aPitch
+ * @param aRoll
+ *
+ * @return A deferred promise.
+ */
+function setEmulatorOrientationValues(aAzimuth, aPitch, aRoll) {
+  let cmd = "sensor set orientation " + aAzimuth + ":" + aPitch + ":" + aRoll;
+  return runEmulatorCmdSafe(cmd);
+}
+
+/**
+ * Wait for a named window event.
+ *
+ * Resolve if that named event occurs.  Never reject.
+ *
+ * Forfill params: the DOMEvent passed.
+ *
+ * @param aEventName
+ *        A string event name.
+ *
+ * @return A deferred promise.
+ */
+function waitForWindowEvent(aEventName) {
+  let deferred = Promise.defer();
+
+  window.addEventListener(aEventName, function onevent(aEvent) {
+    window.removeEventListener(aEventName, onevent);
+
+    ok(true, "Window event '" + aEventName + "' got.");
+    deferred.resolve(aEvent);
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Flush permission settings and call |finish()|.
+ */
+function cleanUp() {
+  waitFor(function() {
+    SpecialPowers.flushPermissions(function() {
+      // Use ok here so that we have at least one test run.
+      ok(true, "permissions flushed");
+
+      finish();
+    });
+  }, function() {
+    return _pendingEmulatorCmdCount === 0;
+  });
+}
+
+/**
+ * Basic test routine helper.
+ *
+ * This helper does nothing but clean-ups.
+ *
+ * @param aTestCaseMain
+ *        A function that takes no parameter.
+ */
+function startTestBase(aTestCaseMain) {
+  Promise.resolve()
+    .then(aTestCaseMain)
+    .then(cleanUp, function() {
+      ok(false, 'promise rejects during test.');
+      cleanUp();
+    });
+}
new file mode 100644
--- /dev/null
+++ b/dom/events/test/marionette/manifest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+b2g = true
+browser = false
+qemu = true
+
+[test_sensor_orientation.js]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/marionette/test_sensor_orientation.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 120000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+function doTest(aAzimuth, aPitch, aRoll) {
+  log("Testing [azimuth, pitch, roll] = " + Array.slice(arguments));
+
+  return setEmulatorOrientationValues(aAzimuth, aPitch, aRoll)
+    .then(() => waitForWindowEvent("deviceorientation"))
+    .then(function(aEvent) {
+      is(aEvent.alpha, aAzimuth, "azimuth");
+      is(aEvent.beta,  aPitch, "pitch");
+      is(aEvent.gamma, aRoll, "roll");
+    });
+}
+
+function testAllPermutations() {
+  const angles = [-180, -90, 0, 90, 180];
+  let promise = Promise.resolve();
+  for (let i = 0; i < angles.length; i++) {
+    for (let j = 0; j < angles.length; j++) {
+      for (let k = 0; k < angles.length; k++) {
+        promise =
+          promise.then(doTest.bind(null, angles[i], angles[j], angles[k]));
+      }
+    }
+  }
+  return promise;
+}
+
+startTestBase(function() {
+  let origValues;
+
+  return Promise.resolve()
+
+    // Retrieve original status.
+    .then(() => getEmulatorOrientationValues())
+    .then(function(aValues) {
+      origValues = aValues;
+      is(typeof origValues, "object", "typeof origValues");
+      is(origValues.length, 3, "origValues.length");
+    })
+
+    // Test original status
+    .then(() => doTest.apply(null, origValues))
+
+    .then(testAllPermutations)
+
+    // Restore original status.
+    .then(() => setEmulatorOrientationValues.apply(null, origValues));
+});
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/tests/marionette/head.js
@@ -0,0 +1,399 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
+
+const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
+const SETTINGS_KEY_DATA_ROAMING_ENABLED = "ril.data.roaming_enabled";
+const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
+
+let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
+
+let _pendingEmulatorCmdCount = 0;
+
+/**
+ * Send emulator command with safe guard.
+ *
+ * We should only call |finish()| after all emulator command transactions
+ * end, so here comes with the pending counter.  Resolve when the emulator
+ * gives positive response, and reject otherwise.
+ *
+ * Fulfill params:
+ *   result -- an array of emulator response lines.
+ * Reject params:
+ *   result -- an array of emulator response lines.
+ *
+ * @param aCommand
+ *        A string command to be passed to emulator through its telnet console.
+ *
+ * @return A deferred promise.
+ */
+function runEmulatorCmdSafe(aCommand) {
+  let deferred = Promise.defer();
+
+  ++_pendingEmulatorCmdCount;
+  runEmulatorCmd(aCommand, function(aResult) {
+    --_pendingEmulatorCmdCount;
+
+    ok(true, "Emulator response: " + JSON.stringify(aResult));
+    if (Array.isArray(aResult) && aResult[0] === "OK") {
+      deferred.resolve(aResult);
+    } else {
+      deferred.reject(aResult);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Get mozSettings value specified by @aKey.
+ *
+ * Resolve if that mozSettings value is retrieved successfully, reject
+ * otherwise.
+ *
+ * Fulfill params:
+ *   The corresponding mozSettings value of the key.
+ * Reject params: (none)
+ *
+ * @param aKey
+ *        A string.
+ * @param aAllowError [optional]
+ *        A boolean value.  If set to true, an error response won't be treated
+ *        as test failure.  Default: false.
+ *
+ * @return A deferred promise.
+ */
+function getSettings(aKey, aAllowError) {
+  let deferred = Promise.defer();
+
+  let request = navigator.mozSettings.createLock().get(aKey);
+  request.addEventListener("success", function(aEvent) {
+    ok(true, "getSettings(" + aKey + ") - success");
+    deferred.resolve(aEvent.target.result[aKey]);
+  });
+  request.addEventListener("error", function() {
+    ok(aAllowError, "getSettings(" + aKey + ") - error");
+    deferred.reject();
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Set mozSettings values.
+ *
+ * Resolve if that mozSettings value is set successfully, reject otherwise.
+ *
+ * Fulfill params: (none)
+ * Reject params: (none)
+ *
+ * @param aSettings
+ *        An object of format |{key1: value1, key2: value2, ...}|.
+ * @param aAllowError [optional]
+ *        A boolean value.  If set to true, an error response won't be treated
+ *        as test failure.  Default: false.
+ *
+ * @return A deferred promise.
+ */
+function setSettings(aSettings, aAllowError) {
+  let deferred = Promise.defer();
+
+  let request = navigator.mozSettings.createLock().set(aSettings);
+  request.addEventListener("success", function() {
+    ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
+    deferred.resolve();
+  });
+  request.addEventListener("error", function() {
+    ok(aAllowError, "setSettings(" + JSON.stringify(aSettings) + ")");
+    deferred.reject();
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Set mozSettings value with only one key.
+ *
+ * Resolve if that mozSettings value is set successfully, reject otherwise.
+ *
+ * Fulfill params: (none)
+ * Reject params: (none)
+ *
+ * @param aKey
+ *        A string key.
+ * @param aValue
+ *        An object value.
+ * @param aAllowError [optional]
+ *        A boolean value.  If set to true, an error response won't be treated
+ *        as test failure.  Default: false.
+ *
+ * @return A deferred promise.
+ */
+function setSettings1(aKey, aValue, aAllowError) {
+  let settings = {};
+  settings[aKey] = aValue;
+  return setSettings(settings, aAllowError);
+}
+
+/**
+ * Convenient MozSettings getter for SETTINGS_KEY_DATA_ENABLED.
+ */
+function getDataEnabled(aAllowError) {
+  return getSettings(SETTINGS_KEY_DATA_ENABLED, aAllowError);
+}
+
+/**
+ * Convenient MozSettings setter for SETTINGS_KEY_DATA_ENABLED.
+ */
+function setDataEnabled(aEnabled, aAllowError) {
+  return setSettings1(SETTINGS_KEY_DATA_ENABLED, aEnabled, aAllowError);
+}
+
+/**
+ * Convenient MozSettings getter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
+ */
+function getDataRoamingEnabled(aAllowError) {
+  return getSettings(SETTINGS_KEY_DATA_ROAMING_ENABLED, aAllowError);
+}
+
+/**
+ * Convenient MozSettings setter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
+ */
+function setDataRoamingEnabled(aEnabled, aAllowError) {
+  return setSettings1(SETTINGS_KEY_DATA_ROAMING_ENABLED, aEnabled, aAllowError);
+}
+
+/**
+ * Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS.
+ */
+function getDataApnSettings(aAllowError) {
+  return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError);
+}
+
+/**
+ * Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS.
+ */
+function setDataApnSettings(aApnSettings, aAllowError) {
+  return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError);
+}
+
+let mobileConnection;
+
+/**
+ * Push required permissions and test if
+ * |navigator.mozMobileConnections[<aServiceId>]| exists. Resolve if it does,
+ * reject otherwise.
+ *
+ * Fulfill params:
+ *   mobileConnection -- an reference to navigator.mozMobileMessage.
+ *
+ * Reject params: (none)
+ *
+ * @param aAdditonalPermissions [optional]
+ *        An array of permission strings other than "mobileconnection" to be
+ *        pushed. Default: empty string.
+ * @param aServiceId [optional]
+ *        A numeric DSDS service id. Default: 0.
+ *
+ * @return A deferred promise.
+ */
+function ensureMobileConnection(aAdditionalPermissions, aServiceId) {
+  let deferred = Promise.defer();
+
+  aAdditionalPermissions = aAdditionalPermissions || [];
+  aServiceId = aServiceId || 0;
+
+  if (aAdditionalPermissions.indexOf("mobileconnection") < 0) {
+    aAdditionalPermissions.push("mobileconnection");
+  }
+  let permissions = [];
+  for (let perm of aAdditionalPermissions) {
+    permissions.push({ "type": perm, "allow": 1, "context": document });
+  }
+
+  SpecialPowers.pushPermissions(permissions, function() {
+    ok(true, "permissions pushed: " + JSON.stringify(permissions));
+
+    // Permission changes can't change existing Navigator.prototype
+    // objects, so grab our objects from a new Navigator.
+    let ifr = document.createElement("iframe");
+    ifr.addEventListener("load", function load() {
+      ifr.removeEventListener("load", load);
+
+      mobileConnection =
+        ifr.contentWindow.navigator.mozMobileConnections[aServiceId];
+
+      if (mobileConnection) {
+        log("navigator.mozMobileConnections[" + aServiceId + "] is instance of " +
+            mobileConnection.constructor);
+      } else {
+        log("navigator.mozMobileConnections[" + aServiceId + "] is undefined");
+      }
+
+      if (mobileConnection instanceof MozMobileConnection) {
+        deferred.resolve(mobileConnection);
+      } else {
+        deferred.reject();
+      }
+    });
+
+    document.body.appendChild(ifr);
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Wait for one named MobileConnection event.
+ *
+ * Resolve if that named event occurs.  Never reject.
+ *
+ * Fulfill params: the DOMEvent passed.
+ *
+ * @param aEventName
+ *        A string event name.
+ *
+ * @return A deferred promise.
+ */
+function waitForManagerEvent(aEventName) {
+  let deferred = Promise.defer();
+
+  mobileConnection.addEventListener(aEventName, function onevent(aEvent) {
+    mobileConnection.removeEventListener(aEventName, onevent);
+
+    ok(true, "MobileConnection event '" + aEventName + "' got.");
+    deferred.resolve(aEvent);
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Set data connection enabling state and wait for "datachange" event.
+ *
+ * Resolve if data connection state changed to the expected one.  Never reject.
+ *
+ * Fulfill params: (none)
+ *
+ * @param aEnabled
+ *        A boolean state.
+ *
+ * @return A deferred promise.
+ */
+function setDataEnabledAndWait(aEnabled) {
+  let deferred = Promise.defer();
+
+  let promises = [];
+  promises.push(waitForManagerEvent("datachange"));
+  promises.push(setDataEnabled(aEnabled));
+  Promise.all(promises).then(function keepWaiting() {
+    // To ignore some transient states, we only resolve that deferred promise
+    // when the |connected| state equals to the expected one and never rejects.
+    let connected = mobileConnection.data.connected;
+    if (connected == aEnabled) {
+      deferred.resolve();
+      return;
+    }
+
+    return waitForManagerEvent("datachange").then(keepWaiting);
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Set voice/data roaming emulation and wait for state change.
+ *
+ * Fulfill params: (none)
+ *
+ * @param aRoaming
+ *        A boolean state.
+ *
+ * @return A deferred promise.
+ */
+function setEmulatorRoamingAndWait(aRoaming) {
+  function doSetAndWait(aWhich, aRoaming) {
+    let promises = [];
+    promises.push(waitForManagerEvent(aWhich + "change"));
+
+    let cmd = "gsm " + aWhich + " " + (aRoaming ? "roaming" : "home");
+    promises.push(runEmulatorCmdSafe(cmd));
+    return Promise.all(promises)
+      .then(() => is(mobileConnection[aWhich].roaming, aRoaming,
+                     aWhich + ".roaming"));
+  }
+
+  // Set voice registration state first and then data registration state.
+  return doSetAndWait("voice", aRoaming)
+    .then(() => doSetAndWait("data", aRoaming));
+}
+
+let _networkManager;
+
+/**
+ * Get internal NetworkManager service.
+ */
+function getNetworkManager() {
+  if (!_networkManager) {
+    _networkManager = Cc["@mozilla.org/network/manager;1"]
+                    .getService(Ci.nsINetworkManager);
+    ok(_networkManager, "NetworkManager");
+  }
+
+  return _networkManager;
+}
+
+/**
+ * Flush permission settings and call |finish()|.
+ */
+function cleanUp() {
+  waitFor(function() {
+    SpecialPowers.flushPermissions(function() {
+      // Use ok here so that we have at least one test run.
+      ok(true, "permissions flushed");
+
+      finish();
+    });
+  }, function() {
+    return _pendingEmulatorCmdCount === 0;
+  });
+}
+
+/**
+ * Basic test routine helper for mobile connection tests.
+ *
+ * This helper does nothing but clean-ups.
+ *
+ * @param aTestCaseMain
+ *        A function that takes no parameter.
+ */
+function startTestBase(aTestCaseMain) {
+  Promise.resolve()
+    .then(aTestCaseMain)
+    .then(cleanUp, function() {
+      ok(false, 'promise rejects during test.');
+      cleanUp();
+    });
+}
+
+/**
+ * Common test routine helper for mobile connection tests.
+ *
+ * This function ensures global |mobileConnection| variable is available during
+ * the process and performs clean-ups as well.
+ *
+ * @param aTestCaseMain
+ *        A function that takes one parameter -- mobileConnection.
+ * @param aAdditonalPermissions [optional]
+ *        An array of permission strings other than "mobileconnection" to be
+ *        pushed. Default: empty string.
+ * @param aServiceId [optional]
+ *        A numeric DSDS service id. Default: 0.
+ */
+function startTestCommon(aTestCaseMain, aAdditionalPermissions, aServiceId) {
+  startTestBase(function() {
+    return ensureMobileConnection(aAdditionalPermissions, aServiceId)
+      .then(aTestCaseMain);
+  });
+}
--- a/dom/mobileconnection/tests/marionette/manifest.ini
+++ b/dom/mobileconnection/tests/marionette/manifest.ini
@@ -18,8 +18,10 @@ disabled = Bug 808783
 [test_mobile_roaming_preference.js]
 [test_call_barring_get_option.js]
 [test_call_barring_set_error.js]
 [test_call_barring_change_password.js]
 [test_mobile_set_radio.js]
 [test_mobile_last_known_network.js]
 [test_mobile_icc_change.js]
 [test_mobile_connections_array_uninitialized.js]
+[test_mobile_data_ipv6.js]
+disabled = Bug 978071
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/tests/marionette/test_mobile_data_ipv6.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = "head.js";
+
+/**
+ * Test resulting IP address format with given APN settings.
+ *
+ * This test utility function performs following steps:
+ *
+ *   1) set "ril.data.apnSettings" to a given settings object,
+ *   2) enable data connection and wait for a "datachange" event,
+ *   3) check the IP address type of the active network interface,
+ *   4) disable data connection.
+ *
+ * Fulfill params: (none)
+ * Reject params: (none)
+ *
+ * @param aApnSettings
+ *        An APN settings value.
+ * @param aIsIPv6
+ *        A boolean value indicating whether we're expecting an IPv6 address.
+ *
+ * @return A deferred promise.
+ */
+function doTest(aApnSettings, aIsIPv6) {
+  return setDataApnSettings([])
+    .then(() => setDataApnSettings(aApnSettings))
+    .then(() => setDataEnabledAndWait(true))
+    .then(function() {
+      let nm = getNetworkManager();
+      let active = nm.active;
+      ok(active, "Active network interface");
+
+      log("  Interface: " + active.name);
+      log("  Address: " + active.ip);
+      if (aIsIPv6) {
+        ok(active.ip.indexOf(":") > 0, "IPv6 address");
+      } else {
+        ok(active.ip.indexOf(":") < 0, "IPv4 address");
+      }
+    })
+    .then(() => setDataEnabledAndWait(false));
+}
+
+function doTestHome(aApnSettings, aProtocol) {
+  log("Testing \"" + aProtocol + "\"@HOME... ");
+
+  // aApnSettings is a double-array of per PDP context settings.  The first
+  // index is a DSDS service ID, and the second one is the index of pre-defined
+  // PDP context settings of a specified radio interface.  We use 0 for both as
+  // default here.
+  aApnSettings[0][0].protocol = aProtocol;
+  delete aApnSettings[0][0].roaming_protocol;
+
+  return doTest(aApnSettings, aProtocol === "IPV6");
+}
+
+function doTestRoaming(aApnSettings, aRoaminProtocol) {
+  log("Testing \"" + aRoaminProtocol + "\"@ROMAING... ");
+
+  // aApnSettings is a double-array of per PDP context settings.  The first
+  // index is a DSDS service ID, and the second one is the index of pre-defined
+  // PDP context settings of a specified radio interface.  We use 0 for both as
+  // default here.
+  delete aApnSettings[0][0].protocol;
+  aApnSettings[0][0].roaming_protocol = aRoaminProtocol;
+
+  return doTest(aApnSettings, aRoaminProtocol === "IPV6");
+}
+
+startTestCommon(function() {
+  let origApnSettings;
+
+  return setDataRoamingEnabled(true)
+    .then(getDataApnSettings)
+    .then(function(aResult) {
+      // If already set, then save original APN settings.
+      origApnSettings = JSON.parse(JSON.stringify(aResult));
+      return aResult;
+    }, function() {
+      // Return our own default value.
+      return [[{ "carrier": "T-Mobile US",
+                 "apn": "epc.tmobile.com",
+                 "mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc",
+                 "types": ["default", "supl", "mms"] }]];
+    })
+
+    .then(function(aApnSettings) {
+      return Promise.resolve()
+
+        .then(() => doTestHome(aApnSettings, "NoSuchProtocol"))
+        .then(() => doTestHome(aApnSettings, "IP"))
+        .then(() => doTestHome(aApnSettings, "IPV6"))
+
+        .then(() => setEmulatorRoamingAndWait(true))
+
+        .then(() => doTestRoaming(aApnSettings, "NoSuchProtocol"))
+        .then(() => doTestRoaming(aApnSettings, "IP"))
+        .then(() => doTestRoaming(aApnSettings, "IPV6"))
+
+        .then(() => setEmulatorRoamingAndWait(false));
+    })
+
+    .then(() => setDataRoamingEnabled(false))
+    .then(function() {
+      if (origApnSettings) {
+        return setDataApnSettings(origApnSettings);
+      }
+    });
+}, ["settings-read", "settings-write"]);
--- a/dom/mobilemessage/tests/marionette/head.js
+++ b/dom/mobilemessage/tests/marionette/head.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
 
 let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
 
-/* Push required permissions and test if |navigator.mozMobileMessage| exists.
+/**
+ * Push required permissions and test if |navigator.mozMobileMessage| exists.
  * Resolve if it does, reject otherwise.
  *
  * Fulfill params:
  *   manager -- an reference to navigator.mozMobileMessage.
  *
  * Reject params: (none)
  *
  * @return A deferred promise.
@@ -39,17 +40,18 @@ function ensureMobileMessage() {
     } else {
       deferred.reject();
     }
   });
 
   return deferred.promise;
 }
 
-/* Send a SMS message to a single receiver.  Resolve if it succeeds, reject
+/**
+ * Send a SMS message to a single receiver.  Resolve if it succeeds, reject
  * otherwise.
  *
  * Fulfill params:
  *   message -- the sent SmsMessage.
  *
  * Reject params:
  *   error -- a DOMError.
  *
@@ -67,17 +69,18 @@ function sendSmsWithSuccess(aReceiver, a
   };
   request.onerror = function(event) {
     deferred.reject(event.target.error);
   };
 
   return deferred.promise;
 }
 
-/* Send a MMS message with specified parameters.  Resolve if it fails, reject
+/**
+ * Send a MMS message with specified parameters.  Resolve if it fails, reject
  * otherwise.
  *
  * Fulfill params:
  *   message -- the failed MmsMessage
  *
  * Reject params: (none)
  *
  * @param aMmsParameters a MmsParameters instance.
@@ -95,17 +98,18 @@ function sendMmsWithFailure(aMmsParamete
   let request = manager.sendMMS(aMmsParameters);
   request.onsuccess = function(event) {
     deferred.reject();
   };
 
   return deferred.promise;
 }
 
-/* Retrieve messages from database.
+/**
+ * Retrieve messages from database.
  *
  * Fulfill params:
  *   messages -- an array of {Sms,Mms}Message instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @param aFilter an optional MozSmsFilter instance.
@@ -131,31 +135,33 @@ function getMessages(aFilter, aReverse) 
 
     deferred.resolve(messages);
   };
   cursor.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
-/* Retrieve all messages from database.
+/**
+ * Retrieve all messages from database.
  *
  * Fulfill params:
  *   messages -- an array of {Sms,Mms}Message instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @return A deferred promise.
  */
 function getAllMessages() {
   return getMessages(null, false);
 }
 
-/* Retrieve all threads from database.
+/**
+ * Retrieve all threads from database.
  *
  * Fulfill params:
  *   threads -- an array of MozMobileMessageThread instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @return A deferred promise.
@@ -174,17 +180,18 @@ function getAllThreads() {
 
     deferred.resolve(threads);
   };
   cursor.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
-/* Retrieve a single specified thread from database.
+/**
+ * Retrieve a single specified thread from database.
  *
  * Fulfill params:
  *   thread -- a MozMobileMessageThread instance.
  *
  * Reject params:
  *   event -- a DOMEvent if an error occurs in the retrieving process, or
  *            undefined if there's no such thread.
  *
@@ -199,17 +206,18 @@ function getThreadById(aThreadId) {
         if (thread.id === aThreadId) {
           return thread;
         }
       }
       throw undefined;
     });
 }
 
-/* Delete messages specified from database.
+/**
+ * Delete messages specified from database.
  *
  * Fulfill params:
  *   result -- an array of boolean values indicating whether delesion was
  *             actually performed on the message record with corresponding id.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
@@ -229,17 +237,18 @@ function deleteMessagesById(aMessageIds)
   request.onsuccess = function(event) {
     deferred.resolve(event.target.result);
   };
   request.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
-/* Delete messages specified from database.
+/**
+ * Delete messages specified from database.
  *
  * Fulfill params:
  *   result -- an array of boolean values indicating whether delesion was
  *             actually performed on the message record with corresponding id.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
@@ -247,34 +256,36 @@ function deleteMessagesById(aMessageIds)
  *
  * @return A deferred promise.
  */
 function deleteMessages(aMessages) {
   let ids = messagesToIds(aMessages);
   return deleteMessagesById(ids);
 }
 
-/* Delete all messages from database.
+/**
+ * Delete all messages from database.
  *
  * Fulfill params:
  *   ids -- an array of numeric values identifying those deleted
  *          {Sms,Mms}Messages.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
  * @return A deferred promise.
  */
 function deleteAllMessages() {
   return getAllMessages().then(deleteMessages);
 }
 
 let pendingEmulatorCmdCount = 0;
 
-/* Send emulator command with safe guard.
+/**
+ * Send emulator command with safe guard.
  *
  * We should only call |finish()| after all emulator command transactions
  * end, so here comes with the pending counter.  Resolve when the emulator
  * gives positive response, and reject otherwise.
  *
  * Fulfill params:
  *   result -- an array of emulator response lines.
  *
@@ -296,64 +307,68 @@ function runEmulatorCmdSafe(aCommand) {
     } else {
       deferred.reject(aResult);
     }
   });
 
   return deferred.promise;
 }
 
-/* Send simple text SMS to emulator.
+/**
+ * Send simple text SMS to emulator.
  *
  * Fulfill params:
  *   result -- an array of emulator response lines.
  *
  * Reject params:
  *   result -- an array of emulator response lines.
  *
  * @return A deferred promise.
  */
 function sendTextSmsToEmulator(aFrom, aText) {
   let command = "sms send " + aFrom + " " + aText;
   return runEmulatorCmdSafe(command);
 }
 
-/* Send raw SMS TPDU to emulator.
+/**
+ * Send raw SMS TPDU to emulator.
  *
  * Fulfill params:
  *   result -- an array of emulator response lines.
  *
  * Reject params:
  *   result -- an array of emulator response lines.
  *
  * @return A deferred promise.
  */
 function sendRawSmsToEmulator(aPdu) {
   let command = "sms pdu " + aPdu;
   return runEmulatorCmdSafe(command);
 }
 
-/* Name space for MobileMessageDB.jsm.  Only initialized after first call to
+/**
+ * Name space for MobileMessageDB.jsm.  Only initialized after first call to
  * newMobileMessageDB.
  */
 let MMDB;
 
 // Create a new MobileMessageDB instance.
 function newMobileMessageDB() {
   if (!MMDB) {
     MMDB = Cu.import("resource://gre/modules/MobileMessageDB.jsm", {});
     is(typeof MMDB.MobileMessageDB, "function", "MMDB.MobileMessageDB");
   }
 
   let mmdb = new MMDB.MobileMessageDB();
   ok(mmdb, "MobileMessageDB instance");
   return mmdb;
 }
 
-/* Initialize a MobileMessageDB.  Resolve if initialized with success, reject
+/**
+ * Initialize a MobileMessageDB.  Resolve if initialized with success, reject
  * otherwise.
  *
  * Fulfill params: a MobileMessageDB instance.
  * Reject params: a MobileMessageDB instance.
  *
  * @param aMmdb
  *        A MobileMessageDB instance.
  * @param aDbName
@@ -373,80 +388,101 @@ function initMobileMessageDB(aMmdb, aDbN
     } else {
       deferred.resolve(aMmdb);
     }
   });
 
   return deferred.promise;
 }
 
-/* Close a MobileMessageDB.
+/**
+ * Close a MobileMessageDB.
  *
  * @return The passed MobileMessageDB instance.
  */
 function closeMobileMessageDB(aMmdb) {
   aMmdb.close();
   return aMmdb;
 }
 
-/* Create a new array of id attribute of input messages.
+/**
+ * Create a new array of id attribute of input messages.
  *
  * @param aMessages an array of {Sms,Mms}Message instances.
  *
  * @return an array of numeric values.
  */
 function messagesToIds(aMessages) {
   let ids = [];
   for (let message of aMessages) {
     ids.push(message.id);
   }
   return ids;
 }
 
 // A reference to a nsIUUIDGenerator service.
 let uuidGenerator;
 
-/* Generate a new UUID.
+/**
+ * Generate a new UUID.
  *
  * @return A UUID string.
  */
 function newUUID() {
   if (!uuidGenerator) {
     uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
                     .getService(Ci.nsIUUIDGenerator);
     ok(uuidGenerator, "uuidGenerator");
   }
 
   return uuidGenerator.generateUUID().toString();
 }
 
-/* Flush permission settings and call |finish()|.
+/**
+ * Flush permission settings and call |finish()|.
  */
 function cleanUp() {
   waitFor(function() {
     SpecialPowers.flushPermissions(function() {
       // Use ok here so that we have at least one test run.
       ok(true, "permissions flushed");
 
       finish();
     });
   }, function() {
     return pendingEmulatorCmdCount === 0;
   });
 }
 
+/**
+ * Basic test routine helper for mobile message tests.
+ *
+ * This helper does nothing but clean-ups.
+ *
+ * @param aTestCaseMain
+ *        A function that takes no parameter.
+ */
 function startTestBase(aTestCaseMain) {
   Promise.resolve()
          .then(aTestCaseMain)
          .then(cleanUp, function() {
            ok(false, 'promise rejects during test.');
            cleanUp();
          });
 }
 
+/**
+ * Common test routine helper for mobile message tests.
+ *
+ * This function ensures global |manager| variable is available during the
+ * process and performs clean-ups as well.
+ *
+ * @param aTestCaseMain
+ *        A function that takes no parameter.
+ */
 function startTestCommon(aTestCaseMain) {
   startTestBase(function() {
     return ensureMobileMessage()
       .then(deleteAllMessages)
       .then(aTestCaseMain)
       .then(deleteAllMessages);
   });
 }
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -4513,22 +4513,32 @@ RILNetworkInterface.prototype = {
     // Use the default authType if the value in database is invalid.
     // For the case that user might not select the authentication type.
     if (authType == -1) {
       if (DEBUG) {
         this.debug("Invalid authType " + this.apnSetting.authtype);
       }
       authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT);
     }
+    let pdptype = !radioInterface.rilContext.data.roaming
+                ? this.apnSetting.protocol
+                : this.apnSetting.roaming_protocol;
+    if (RIL.RIL_DATACALL_PDP_TYPES.indexOf(pdptype) < 0) {
+      if (DEBUG) {
+        this.debug("Invalid pdptype '" + pdptype + "', using '" +
+                   RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT + "'");
+      }
+      pdptype = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT;
+    }
     radioInterface.setupDataCall(radioTechnology,
                                  this.apnSetting.apn,
                                  this.apnSetting.user,
                                  this.apnSetting.password,
                                  authType,
-                                 "IP");
+                                 pdptype);
     this.connecting = true;
   },
 
   reset: function() {
     let apnRetryTimer;
     this.connecting = false;
     // We will retry the connection in increasing times
     // based on the function: time = A * numer_of_retries^2 + B
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2356,16 +2356,25 @@ this.GECKO_DATACALL_AUTH_PAP_OR_CHAP = "
 this.GECKO_DATACALL_AUTH_DEFAULT = GECKO_DATACALL_AUTH_PAP_OR_CHAP;
 this.RIL_DATACALL_AUTH_TO_GECKO = [
   GECKO_DATACALL_AUTH_NONE,         // DATACALL_AUTH_NONE
   GECKO_DATACALL_AUTH_PAP,          // DATACALL_AUTH_PAP
   GECKO_DATACALL_AUTH_CHAP,         // DATACALL_AUTH_CHAP
   GECKO_DATACALL_AUTH_PAP_OR_CHAP   // DATACALL_AUTH_PAP_OR_CHAP
 ];
 
+this.GECKO_DATACALL_PDP_TYPE_IP = "IP";
+this.GECKO_DATACALL_PDP_TYPE_IPV6 = "IPV6";
+this.GECKO_DATACALL_PDP_TYPE_DEFAULT = GECKO_DATACALL_PDP_TYPE_IP;
+this.RIL_DATACALL_PDP_TYPES = [
+  GECKO_DATACALL_PDP_TYPE_IP,
+  GECKO_DATACALL_PDP_TYPE_IPV6,
+  // TODO: Bug 978711 - Support IPV4V6
+];
+
 this.DATACALL_PROFILE_DEFAULT = 0;
 this.DATACALL_PROFILE_TETHERED = 1;
 this.DATACALL_PROFILE_OEM_BASE = 1000;
 
 this.DATACALL_DEACTIVATE_NO_REASON = 0;
 this.DATACALL_DEACTIVATE_RADIO_SHUTDOWN = 1;
 
 this.DATACALL_ACTIVE_UNKNOWN = -1;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -3957,18 +3957,19 @@ RilObject.prototype = {
         continue;
       }
       // Minor change, just update and notify.
       if (DEBUG) {
         this.context.debug("Data link minor change, just update and notify.");
       }
       currentDataCall.gw = updatedDataCall.gw;
       if (updatedDataCall.dns) {
-        currentDataCall.dns[0] = updatedDataCall.dns[0];
-        currentDataCall.dns[1] = updatedDataCall.dns[1];
+        currentDataCall.dns = updatedDataCall.dns.slice();
+      } else {
+        currentDataCall.dns = [];
       }
       currentDataCall.rilMessageType = "datacallstatechange";
       this.sendChromeMessage(currentDataCall);
     }
 
     for each (let newDataCall in datacalls) {
       if (!newDataCall.ifname) {
         continue;
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -174,16 +174,17 @@ LayerManagerComposite::BeginTransactionW
 
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return;
   }
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget);
+  mTarget = aTarget;
 }
 
 bool
 LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
   if (!mRoot) {
     mInTransaction = false;
@@ -238,16 +239,17 @@ LayerManagerComposite::EndTransaction(Dr
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
     Render();
   }
 
   mCompositor->SetTargetContext(nullptr);
+  mTarget = nullptr;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   Log();
   MOZ_LAYERS_LOG(("]----- EndTransaction"));
 #endif
 }
 
 already_AddRefed<gfxASurface>
@@ -439,17 +441,17 @@ LayerManagerComposite::Render()
 
   if (gfxPrefs::LayersDump()) {
     this->Dump();
   }
 
   /** Our more efficient but less powerful alter ego, if one is available. */
   nsRefPtr<Composer2D> composer2D = mCompositor->GetWidget()->GetComposer2D();
 
-  if (composer2D && composer2D->TryRender(mRoot, mWorldMatrix)) {
+  if (!mTarget && composer2D && composer2D->TryRender(mRoot, mWorldMatrix)) {
     if (mFPS) {
       double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
       if (gfxPrefs::LayersDrawFPS()) {
         printf_stderr("HWComposer: FPS is %g\n", fps);
       }
     }
     mCompositor->EndFrameForExternalComposition(mWorldMatrix);
     return;
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -256,16 +256,21 @@ private:
    */
   void RenderDebugOverlay(const gfx::Rect& aBounds);
 
   void WorldTransformRect(nsIntRect& aRect);
 
   RefPtr<Compositor> mCompositor;
   nsAutoPtr<LayerProperties> mClonedLayerTreeProperties;
 
+  /** 
+   * Context target, nullptr when drawing directly to our swap chain.
+   */
+  RefPtr<gfx::DrawTarget> mTarget;
+
   gfx::Matrix mWorldMatrix;
   nsIntRegion mInvalidRegion;
   nsAutoPtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   bool mDebugOverlayWantsNextFrame;
 };
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -573,29 +573,29 @@ CompositorParent::ScheduleComposition()
     delta = TimeStamp::Now() - mLastCompose;
 
   int32_t rate = CalculateCompositionFrameRate();
 
   // If rate == 0 (ASAP mode), minFrameDelta must be 0 so there's no delay.
   TimeDuration minFrameDelta = TimeDuration::FromMilliseconds(
     rate == 0 ? 0.0 : std::max(0.0, 1000.0 / rate));
 
-#ifdef COMPOSITOR_PERFORMANCE_WARNING
-  mExpectedComposeTime = TimeStamp::Now() + minFrameDelta;
-#endif
 
   mCurrentCompositeTask = NewRunnableMethod(this, &CompositorParent::Composite);
 
   if (!initialComposition && delta < minFrameDelta) {
     TimeDuration delay = minFrameDelta - delta;
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
-    mExpectedComposeTime = TimeStamp::Now() + delay;
+    mExpectedComposeStartTime = TimeStamp::Now() + delay;
 #endif
     ScheduleTask(mCurrentCompositeTask, delay.ToMilliseconds());
   } else {
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+    mExpectedComposeStartTime = TimeStamp::Now();
+#endif
     ScheduleTask(mCurrentCompositeTask, 0);
   }
 }
 
 void
 CompositorParent::Composite()
 {
   CompositeToTarget(nullptr);
@@ -603,16 +603,26 @@ CompositorParent::Composite()
 
 void
 CompositorParent::CompositeToTarget(DrawTarget* aTarget)
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
   PROFILER_LABEL("CompositorParent", "Composite");
   NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(),
                     "Composite can only be called on the compositor thread");
+
+#ifdef COMPOSITOR_PERFORMANCE_WARNING
+  TimeDuration scheduleDelta = TimeStamp::Now() - mExpectedComposeStartTime;
+  if (scheduleDelta > TimeDuration::FromMilliseconds(2) ||
+      scheduleDelta < TimeDuration::FromMilliseconds(-2)) {
+    printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n",
+                  scheduleDelta.ToMilliseconds());
+  }
+#endif
+
   mCurrentCompositeTask = nullptr;
 
   mLastCompose = TimeStamp::Now();
 
   if (!CanComposite()) {
     return;
   }
 
@@ -653,19 +663,25 @@ CompositorParent::CompositeToTarget(Draw
   mLayerManager->SetDebugOverlayWantsNextFrame(false);
   mLayerManager->EndEmptyTransaction();
 
   if (mLayerManager->DebugOverlayWantsNextFrame()) {
     ScheduleComposition();
   }
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
-  if (mExpectedComposeTime + TimeDuration::FromMilliseconds(15) < TimeStamp::Now()) {
-    printf_stderr("Compositor: Composite took %i ms.\n",
-                  15 + (int)(TimeStamp::Now() - mExpectedComposeTime).ToMilliseconds());
+  TimeDuration executionTime = TimeStamp::Now() - mLastCompose;
+  TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
+  int32_t frameRate = CalculateCompositionFrameRate();
+  if (frameRate > 0) {
+    frameBudget = TimeDuration::FromSeconds(1.0 / frameRate);
+  }
+  if (executionTime > frameBudget) {
+    printf_stderr("Compositor: Composite execution took %4.1f ms\n",
+                  executionTime.ToMilliseconds());
   }
 #endif
 
   // 0 -> Full-tilt composite
   if (gfxPrefs::LayersCompositionFrameRate() == 0) {
     // Special full-tilt composite mode for performance testing
     ScheduleComposition();
   }
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -301,17 +301,17 @@ private:
   nsRefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
   nsIWidget* mWidget;
   CancelableTask *mCurrentCompositeTask;
   TimeStamp mLastCompose;
   TimeStamp mTestTime;
   bool mIsTesting;
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
-  TimeStamp mExpectedComposeTime;
+  TimeStamp mExpectedComposeStartTime;
 #endif
 
   bool mPaused;
 
   bool mUseExternalSurfaceSize;
   nsIntSize mEGLSurfaceSize;
 
   mozilla::Monitor mPauseCompositionMonitor;
--- a/testing/marionette/client/marionette/tests/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit-tests.ini
@@ -20,8 +20,9 @@ skip = false
 [include:../../../../../dom/voicemail/test/marionette/manifest.ini]
 [include:../../../../../dom/battery/test/marionette/manifest.ini]
 [include:../../../../../dom/mobilemessage/tests/marionette/manifest.ini]
 [include:../../../../../dom/mobileconnection/tests/marionette/manifest.ini]
 [include:../../../../../dom/system/gonk/tests/marionette/manifest.ini]
 [include:../../../../../dom/icc/tests/marionette/manifest.ini]
 [include:../../../../../dom/system/tests/marionette/manifest.ini]
 [include:../../../../../dom/nfc/tests/marionette/manifest.ini]
+[include:../../../../../dom/events/test/marionette/manifest.ini]