Merge m-c to inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 30 Sep 2015 14:10:21 -0700
changeset 297765 9c3f3e60e52802422bd637a9273e11426bbeaada
parent 297717 9009233203a765d35131df7da70bc7373eb2c9f1 (current diff)
parent 297764 9169f652fe5e69c2d77ac31929934a5bc3342e6e (diff)
child 297766 b03af020cad5c41b4dead1d205b43906164737ba
push id5986
push usermartin.thomson@gmail.com
push dateWed, 30 Sep 2015 21:28:01 +0000
reviewersmerge
milestone44.0a1
Merge m-c to inbound a=merge
browser/base/content/browser.js
browser/base/content/sanitize.js
browser/base/content/test/general/browser_sanitizeDialog.js
browser/base/content/test/newtab/browser_newtab_bug722273.js
browser/base/content/test/newtab/head.js
browser/base/content/urlbarBindings.xml
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1140,8 +1140,13 @@ pref("dom.audiochannel.mutedByDefault", 
 // requests.
 pref("dom.bluetooth.app-origin", "app://bluetooth.gaiamobile.org");
 
 // Default device name for Presentation API
 pref("dom.presentation.device.name", "Firefox OS");
 
 // Enable notification of performance timing
 pref("dom.performance.enable_notify_performance_timing", true);
+
+// Multi-screen
+pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html");
+pref("b2g.multiscreen.system_remote_url", "index_remote.html");
+
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -15,16 +15,17 @@ Cu.import('resource://gre/modules/Paymen
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
 Cu.import('resource://gre/modules/Keyboard.jsm');
 Cu.import('resource://gre/modules/ErrorPage.jsm');
 Cu.import('resource://gre/modules/AlertsHelper.jsm');
 Cu.import('resource://gre/modules/RequestSyncService.jsm');
 Cu.import('resource://gre/modules/SystemUpdateService.jsm');
 #ifdef MOZ_WIDGET_GONK
+Cu.import('resource://gre/modules/MultiscreenHandler.jsm');
 Cu.import('resource://gre/modules/NetworkStatsService.jsm');
 Cu.import('resource://gre/modules/ResourceStatsService.jsm');
 #endif
 Cu.import('resource://gre/modules/KillSwitchMain.jsm');
 
 // Identity
 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
 SignInToWebsiteController.init();
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/shell_remote.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- 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/.  -->
+
+<html xmlns="http://www.w3.org/1999/xhtml"
+      id="shellRemote"
+      windowtype="navigator:remote-browser"
+      sizemode="fullscreen"
+      >
+
+<head>
+  <link rel="stylesheet" href="shell.css" type="text/css">
+  <script type="application/javascript;version=1.8"
+          src="chrome://b2g/content/shell_remote.js"> </script>
+</head>
+  <body id="container">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/shell_remote.js
@@ -0,0 +1,71 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* 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/. */
+
+"use strict";
+
+const {utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function debug(aStr) {
+  // dump(" -*- ShellRemote.js: " + aStr + "\n");
+}
+
+let remoteShell = {
+
+  get homeURL() {
+    let systemAppManifestURL = Services.io.newURI(this.systemAppManifestURL, null, null);
+    let shellRemoteURL = Services.prefs.getCharPref("b2g.multiscreen.system_remote_url");
+    shellRemoteURL = Services.io.newURI(shellRemoteURL, null, systemAppManifestURL);
+    return shellRemoteURL.spec;
+  },
+
+  get systemAppManifestURL() {
+    return Services.prefs.getCharPref("b2g.system_manifest_url");
+  },
+
+  _started: false,
+
+  hasStarted: function () {
+    return this._started;
+  },
+
+  start: function () {
+    this._started = true;
+
+    let homeURL = this.homeURL;
+    if (!homeURL) {
+      debug("ERROR! Remote home URL undefined.");
+      return;
+    }
+    let manifestURL = this.systemAppManifestURL;
+    // <html:iframe id="remote-systemapp"
+    //              mozbrowser="true" allowfullscreen="true"
+    //              src="blank.html"/>
+    let systemAppFrame =
+      document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
+    systemAppFrame.setAttribute("id", "remote-systemapp");
+    systemAppFrame.setAttribute("mozbrowser", "true");
+    systemAppFrame.setAttribute("mozapp", manifestURL);
+    systemAppFrame.setAttribute("allowfullscreen", "true");
+    systemAppFrame.setAttribute("src", "blank.html");
+
+    let container = document.getElementById("container");
+    this.contentBrowser = container.appendChild(systemAppFrame);
+    this.contentBrowser.src = homeURL + window.location.hash;
+  },
+
+  stop: function () {
+  },
+
+};
+
+window.onload = function() {
+  if (remoteShell.hasStarted() == false) {
+    remoteShell.start();
+  }
+};
+
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -8,16 +8,18 @@ chrome.jar:
 % content branding %content/branding/
 % content b2g %content/
 
   content/arrow.svg                     (content/arrow.svg)
 * content/settings.js                   (content/settings.js)
 * content/shell.html                    (content/shell.html)
 * content/shell.js                      (content/shell.js)
   content/shell.css                     (content/shell.css)
+  content/shell_remote.html             (content/shell_remote.html)
+  content/shell_remote.js               (content/shell_remote.js)
   content/blank.html                    (content/blank.html)
   content/blank.css                     (content/blank.css)
 #ifdef MOZ_WIDGET_GONK
   content/devtools/adb.js               (content/devtools/adb.js)
 #endif
 * content/devtools/debugger.js          (content/devtools/debugger.js)
   content/devtools/hud.js               (content/devtools/hud.js)
 #ifdef FXOS_SIMULATOR
new file mode 100644
--- /dev/null
+++ b/b2g/components/MultiscreenHandler.jsm
@@ -0,0 +1,92 @@
+/* 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["MultiscreenHandler"];
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function debug(aStr) {
+  // dump("MultiscreenHandler: " + aStr + "\n");
+}
+
+let window = Services.wm.getMostRecentWindow("navigator:browser");
+
+// Multi-screen support on b2g. The following implementation will open a new
+// top-level window once we receive a display connected event.
+let MultiscreenHandler = {
+
+  topLevelWindows: new Map(),
+
+  init: function init() {
+    Services.obs.addObserver(this, "display-changed", false);
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+  },
+
+  uninit: function uninit() {
+    Services.obs.removeObserver(this, "display-changed");
+    Services.obs.removeObserver(this, "xpcom-shutdown");
+  },
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "display-changed":
+        this.handleDisplayChangeEvent(aSubject);
+        break
+      case "xpcom-shutdown":
+        this.uninit();
+        break
+    }
+  },
+
+  openTopLevelWindow: function openTopLevelWindow(aDisplay) {
+    if (this.topLevelWindows.get(aDisplay.id)) {
+      debug("Top level window for display id: " + aDisplay.id + " has been opened.");
+      return;
+    }
+
+    let flags = Services.prefs.getCharPref("toolkit.defaultChromeFeatures") +
+                ",mozDisplayId=" + aDisplay.id;
+    let remoteShellURL = Services.prefs.getCharPref("b2g.multiscreen.chrome_remote_url") +
+                         "#" + aDisplay.id;
+    let win = Services.ww.openWindow(null, remoteShellURL, "myTopWindow" + aDisplay.id, flags, null);
+
+    this.topLevelWindows.set(aDisplay.id, win);
+  },
+
+  closeTopLevelWindow: function closeTopLevelWindow(aDisplay) {
+    let win = this.topLevelWindows.get(aDisplay.id);
+
+    if (win) {
+      win.close();
+      this.topLevelWindows.delete(aDisplay.id);
+    }
+  },
+
+  handleDisplayChangeEvent: function handleDisplayChangeEvent(aSubject) {
+
+    let display = aSubject.QueryInterface(Ci.nsIDisplayInfo);
+    let name = "multiscreen.enabled";
+    let req = window.navigator.mozSettings.createLock().get(name);
+
+    req.addEventListener("success", () => {
+      let isMultiscreenEnabled = req.result[name];
+      if (display.connected) {
+        if (isMultiscreenEnabled) {
+          this.openTopLevelWindow(display);
+        }
+      } else {
+        this.closeTopLevelWindow(display);
+      }
+    });
+  },
+
+};
+
+MultiscreenHandler.init();
+this.MultiscreenHandler = MultiscreenHandler;
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -62,16 +62,17 @@ EXTRA_JS_MODULES += [
     'ContentRequestHelper.jsm',
     'DebuggerActors.js',
     'ErrorPage.jsm',
     'Frames.jsm',
     'FxAccountsMgmtService.jsm',
     'LogCapture.jsm',
     'LogParser.jsm',
     'LogShake.jsm',
+    'MultiscreenHandler.jsm',
     'OrientationChangeHandler.jsm',
     'SafeMode.jsm',
     'Screenshot.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
     'WebappsUpdater.jsm',
 ]
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- 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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- 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="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <!-- 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-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
--- 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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "14a64f1ebd353bccc3f1c0399e1a01a03327749e", 
+        "git_revision": "be2da974ba076e06e2ff96cd3ac911769d3067a3", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "72d170d61e82726b0fd2a398d21796b11ab2ad66", 
+    "revision": "2194f873784f9366087ee7899640b58c7150dff4", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <!-- 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/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--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="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14a64f1ebd353bccc3f1c0399e1a01a03327749e"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="be2da974ba076e06e2ff96cd3ac911769d3067a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1283,19 +1283,16 @@ var gBrowserInit = {
 
     UpdateUrlbarSearchSplitterState();
 
     if (!(isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") ||
         !focusAndSelectUrlBar()) {
       gBrowser.selectedBrowser.focus();
     }
 
-    // Set up Sanitize Item
-    this._initializeSanitizer();
-
     // Enable/Disable auto-hide tabbar
     gBrowser.tabContainer.updateVisibility();
 
     BookmarkingUI.init();
 
     gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
     var homeButton = document.getElementById("home-button");
@@ -1670,19 +1667,16 @@ var gBrowserInit = {
   },
 
   nonBrowserWindowDelayedStartup: function() {
     this._delayedStartupTimeoutId = null;
 
     // initialise the offline listener
     BrowserOffline.init();
 
-    // Set up Sanitize Item
-    this._initializeSanitizer();
-
     // initialize the private browsing UI
     gPrivateBrowsingUI.init();
 
 #ifdef MOZ_SERVICES_SYNC
     // initialize the sync UI
     gSyncUI.init();
 #endif
 
@@ -1697,62 +1691,16 @@ var gBrowserInit = {
     if (this._delayedStartupTimeoutId) {
       clearTimeout(this._delayedStartupTimeoutId);
       return;
     }
 
     BrowserOffline.uninit();
   },
 #endif
-
-  _initializeSanitizer: function() {
-    const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
-    if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
-      gPrefService.clearUserPref(kDidSanitizeDomain);
-      // We need to persist this preference change, since we want to
-      // check it at next app start even if the browser exits abruptly
-      gPrefService.savePrefFile(null);
-    }
-
-    /**
-     * Migrate Firefox 3.0 privacy.item prefs under one of these conditions:
-     *
-     * a) User has customized any privacy.item prefs
-     * b) privacy.sanitize.sanitizeOnShutdown is set
-     */
-    if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) {
-      let itemBranch = gPrefService.getBranch("privacy.item.");
-      let itemArray = itemBranch.getChildList("");
-
-      // See if any privacy.item prefs are set
-      let doMigrate = itemArray.some(name => itemBranch.prefHasUserValue(name));
-      // Or if sanitizeOnShutdown is set
-      if (!doMigrate)
-        doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown");
-
-      if (doMigrate) {
-        let cpdBranch = gPrefService.getBranch("privacy.cpd.");
-        let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown.");
-        for (let name of itemArray) {
-          try {
-            // don't migrate password or offlineApps clearing in the CRH dialog since
-            // there's no UI for those anymore. They default to false. bug 497656
-            if (name != "passwords" && name != "offlineApps")
-              cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name));
-            clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name));
-          }
-          catch(e) {
-            Cu.reportError("Exception thrown during privacy pref migration: " + e);
-          }
-        }
-      }
-
-      gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true);
-    }
-  },
 }
 
 
 /* Legacy global init functions */
 var BrowserStartup        = gBrowserInit.onLoad.bind(gBrowserInit);
 var BrowserShutdown       = gBrowserInit.onUnload.bind(gBrowserInit);
 #ifdef XP_MACOSX
 var nonBrowserWindowStartup        = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit);
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -1,39 +1,62 @@
-# -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
-# 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/.
+// -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
+/* 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/. */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
                                   "resource://gre/modules/TelemetryStopwatch.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "console",
+                                  "resource://gre/modules/devtools/Console.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+                                  "resource://gre/modules/Timer.jsm");
 
-function Sanitizer() {}
+/**
+ * A number of iterations after which to yield time back
+ * to the system.
+ */
+const YIELD_PERIOD = 10;
+
+function Sanitizer() {
+}
 Sanitizer.prototype = {
   // warning to the caller: this one may raise an exception (e.g. bug #265028)
   clearItem: function (aItemName)
   {
     if (this.items[aItemName].canClear)
       this.items[aItemName].clear();
   },
 
+  promiseCanClearItem: function (aItemName, aArg) {
+    return new Promise(resolve => {
+      return this.canClearItem(aItemName,
+                              (_, canClear) => resolve(canClear),
+                              aArg)
+    });
+  },
+
   canClearItem: function (aItemName, aCallback, aArg)
   {
     let canClear = this.items[aItemName].canClear;
     if (typeof canClear == "function") {
       canClear(aCallback, aArg);
       return false;
     }
 
@@ -52,305 +75,311 @@ Sanitizer.prototype = {
    * Deletes privacy sensitive data in a batch, according to user preferences.
    * Returns a promise which is resolved if no errors occurred.  If an error
    * occurs, a message is reported to the console and all other items are still
    * cleared before the promise is finally rejected.
    *
    * If the consumer specifies the (optional) array parameter, only those
    * items get cleared (irrespective of the preference settings)
    */
-  sanitize: function (aItemsToClear)
-  {
-    var deferred = Promise.defer();
-    var seenError = false;
+  sanitize: Task.async(function*(aItemsToClear = null) {
+    let progress = {};
+    let promise = this._sanitize(aItemsToClear, progress);
+
+    //
+    // Depending on preferences, the sanitizer may perform asynchronous
+    // work before it starts cleaning up the Places database (e.g. closing
+    // windows). We need to make sure that the connection to that database
+    // hasn't been closed by the time we use it.
+    //
+    let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"]
+       .getService(Ci.nsPIPlacesDatabase)
+       .shutdownClient
+       .jsclient;
+
+    shutdownClient.addBlocker("sanitize.js: Sanitize",
+      promise,
+      {
+        fetchState: () => {
+          return { progress };
+        }
+      }
+    );
+    try {
+      yield promise;
+    } finally {
+      Services.obs.notifyObservers(null, "sanitizer-sanitization-complete", "");
+    }
+  }),
+
+  _sanitize: Task.async(function*(aItemsToClear, progress = {}) {
+    let seenError = false;
+    let itemsToClear;
     if (Array.isArray(aItemsToClear)) {
-      var itemsToClear = [...aItemsToClear];
+      // Shallow copy the array, as we are going to modify
+      // it in place later.
+      itemsToClear = [...aItemsToClear];
     } else {
       let branch = Services.prefs.getBranch(this.prefDomain);
-      itemsToClear = Object.keys(this.items).filter(itemName => branch.getBoolPref(itemName));
+      itemsToClear = Object.keys(this.items).filter(itemName => {
+        try {
+          return branch.getBoolPref(itemName);
+        } catch (ex) {
+          return false;
+        }
+      });
+    }
+
+    // Store the list of items to clear, in case we are killed before we
+    // get a chance to complete.
+    Preferences.set(Sanitizer.PREF_SANITIZE_IN_PROGRESS, JSON.stringify(itemsToClear));
+
+    // Store the list of items to clear, for debugging/forensics purposes
+    for (let k of itemsToClear) {
+      progress[k] = "ready";
     }
 
     // Ensure open windows get cleared first, if they're in our list, so that they don't stick
     // around in the recently closed windows list, and so we can cancel the whole thing
     // if the user selects to keep a window open from a beforeunload prompt.
     let openWindowsIndex = itemsToClear.indexOf("openWindows");
     if (openWindowsIndex != -1) {
       itemsToClear.splice(openWindowsIndex, 1);
-      let item = this.items.openWindows;
+      yield this.items.openWindows.clear();
+      progress.openWindows = "cleared";
+    }
 
-      let ok = item.clear(() => {
-        try {
-          let clearedPromise = this.sanitize(itemsToClear);
-          clearedPromise.then(deferred.resolve, deferred.reject);
-        } catch(e) {
-          let error = "Sanitizer threw after closing windows: " + e;
-          Cu.reportError(error);
-          deferred.reject(error);
-        }
-      });
-      // When cancelled, reject immediately
-      if (!ok) {
-        deferred.reject("Sanitizer canceled closing windows");
-      }
-
-      return deferred.promise;
+    // Cache the range of times to clear
+    let range = null;
+    // If we ignore timespan, clear everything,
+    // otherwise, pick a range.
+    if (!this.ignoreTimespan) {
+      range = this.range || Sanitizer.getClearRange();
     }
 
-    let cookiesIndex = itemsToClear.indexOf("cookies");
-    if (cookiesIndex != -1) {
-      itemsToClear.splice(cookiesIndex, 1);
-      let item = this.items.cookies;
-      item.range = this.range;
-      let ok = item.clear(() => {
-        try {
-          if (!itemsToClear.length) {
-            // we're done
-            deferred.resolve();
-            return;
-          }
-          let clearedPromise = this.sanitize(itemsToClear);
-          clearedPromise.then(deferred.resolve, deferred.reject);
-        } catch(e) {
-          let error = "Sanitizer threw after clearing cookies: " + e;
-          Cu.reportError(error);
-          deferred.reject(error);
-        }
-      });
-      // When cancelled, reject immediately
-      if (!ok) {
-        deferred.reject("Sanitizer canceled clearing cookies");
-      }
-
-      return deferred.promise;
-    }
-
-    TelemetryStopwatch.start("FX_SANITIZE_TOTAL");
-
-    // Cache the range of times to clear
-    if (this.ignoreTimespan)
-      var range = null;  // If we ignore timespan, clear everything
-    else
-      range = this.range || Sanitizer.getClearRange();
-
-    let itemCount = Object.keys(itemsToClear).length;
-    let onItemComplete = function() {
-      if (!--itemCount) {
-        TelemetryStopwatch.finish("FX_SANITIZE_TOTAL");
-        seenError ? deferred.reject() : deferred.resolve();
-      }
-    };
     for (let itemName of itemsToClear) {
       let item = this.items[itemName];
+      if (!("clear" in item)) {
+        progress[itemName] = "`clear` not in item";
+        continue;
+      }
       item.range = range;
-      if ("clear" in item) {
-        let clearCallback = (itemName, aCanClear) => {
-          // Some of these clear() may raise exceptions (see bug #265028)
-          // to sanitize as much as possible, we catch and store them,
-          // rather than fail fast.
-          // Callers should check returned errors and give user feedback
-          // about items that could not be sanitized
-          let item = this.items[itemName];
-          try {
-            if (aCanClear)
-              item.clear();
-          } catch(er) {
-            seenError = true;
-            Components.utils.reportError("Error sanitizing " + itemName +
-                                         ": " + er + "\n");
-          }
-          onItemComplete();
-        };
-        this.canClearItem(itemName, clearCallback);
-      } else {
-        onItemComplete();
+      let canClear = yield this.promiseCanClearItem(itemName);
+      if (!canClear) {
+        progress[itemName] = "cannot clear item";
+        continue;
+      }
+      // Some of these clear() may raise exceptions (see bug #265028)
+      // to sanitize as much as possible, we catch and store them,
+      // rather than fail fast.
+      // Callers should check returned errors and give user feedback
+      // about items that could not be sanitized
+      let refObj = {};
+      try {
+        TelemetryStopwatch.start("FX_SANITIZE_TOTAL", refObj);
+        yield item.clear();
+        progress[itemName] = "cleared";
+      } catch(er) {
+        progress[itemName] = "failed";
+        seenError = true;
+        console.error("Error sanitizing " + itemName, er);
+      } finally {
+        TelemetryStopwatch.finish("FX_SANITIZE_TOTAL", refObj);
       }
     }
 
-    return deferred.promise;
-  },
+    // Sanitization is complete.
+    Preferences.reset(Sanitizer.PREF_SANITIZE_IN_PROGRESS);
+    progress = {};
+    if (seenError) {
+      throw new Error("Error sanitizing");
+    }
+  }),
 
   // Time span only makes sense in certain cases.  Consumers who want
   // to only clear some private data can opt in by setting this to false,
   // and can optionally specify a specific range.  If timespan is not ignored,
   // and range is not set, sanitize() will use the value of the timespan
   // pref to determine a range
   ignoreTimespan : true,
   range : null,
 
   items: {
     cache: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_CACHE");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_CACHE", refObj);
 
         var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"].
                     getService(Ci.nsICacheStorageService);
         try {
           // Cache doesn't consult timespan, nor does it have the
           // facility for timespan-based eviction.  Wipe it.
           cache.clear();
         } catch(er) {}
 
         var imageCache = Cc["@mozilla.org/image/tools;1"].
                          getService(Ci.imgITools).getImgCacheForDocument(null);
         try {
           imageCache.clearCache(false); // true=chrome, false=content
         } catch(er) {}
 
-        TelemetryStopwatch.finish("FX_SANITIZE_CACHE");
+        TelemetryStopwatch.finish("FX_SANITIZE_CACHE", refObj);
       },
 
       get canClear()
       {
         return true;
       }
     },
 
     cookies: {
-      clear: function (aCallback)
+      clear: Task.async(function* ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_COOKIES");
-        TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2");
+        let yieldCounter = 0;
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_COOKIES", refObj);
+        TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2", refObj);
 
         var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"]
                                   .getService(Ci.nsICookieManager);
         if (this.range) {
           // Iterate through the cookies and delete any created after our cutoff.
           var cookiesEnum = cookieMgr.enumerator;
           while (cookiesEnum.hasMoreElements()) {
             var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
 
-            if (cookie.creationTime > this.range[0])
+            if (cookie.creationTime > this.range[0]) {
               // This cookie was created after our cutoff, clear it
               cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
+
+              if (++yieldCounter % YIELD_PERIOD == 0) {
+                yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long
+              }
+            }
           }
         }
         else {
           // Remove everything
           cookieMgr.removeAll();
+          yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long
         }
-
-        TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2");
+        TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2", refObj);
 
         // Clear deviceIds. Done asynchronously (returns before complete).
         let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
                                  .getService(Ci.nsIMediaManagerService);
         mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
 
         // Clear plugin data.
-        TelemetryStopwatch.start("FX_SANITIZE_PLUGINS");
-        this.clearPluginCookies().then(
-          function() {
-            TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS");
-            TelemetryStopwatch.finish("FX_SANITIZE_COOKIES");
-            aCallback();
-          });
-        return true;
-      },
+        TelemetryStopwatch.start("FX_SANITIZE_PLUGINS", refObj);
+        yield this.promiseClearPluginCookies();
+        TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS", refObj);
+        TelemetryStopwatch.finish("FX_SANITIZE_COOKIES", refObj);
+      }),
 
-      clearPluginCookies: function() {
+      promiseClearPluginCookies: Task.async(function*() {
         const phInterface = Ci.nsIPluginHost;
         const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
         let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
 
         // Determine age range in seconds. (-1 means clear all.) We don't know
         // that this.range[1] is actually now, so we compute age range based
         // on the lower bound. If this.range results in a negative age, do
         // nothing.
         let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000) : -1;
         if (!this.range || age >= 0) {
           let tags = ph.getPluginTags();
-          function iterate(tag) {
-            let promise = new Promise(resolve => {
-              try {
-                let onClear = function(rv) {
-                  // If the plugin doesn't support clearing by age, clear everything.
-                  if (rv == Components.results. NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
-                    ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, function() {
-                      resolve();
-                    });
-                  } else {
-                    resolve();
-                  }
-                };
-                ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, onClear);
-              } catch (ex) {
-                resolve();
+          for (let tag of tags) {
+            try {
+              let rv = yield new Promise(resolve =>
+                ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, resolve)
+              );
+              // If the plugin doesn't support clearing by age, clear everything.
+              if (rv == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
+                yield new Promise(resolve =>
+                  ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve)
+                );
               }
-            });
-            return promise;
+            } catch (ex) {
+              // Ignore errors from plug-ins
+            }
           }
-          let promises = [];
-          for (let tag of tags) {
-            promises.push(iterate(tag));
-          }
-          return Promise.all(promises);
         }
-      },
+      }),
 
       get canClear()
       {
         return true;
       }
     },
 
     offlineApps: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_OFFLINEAPPS");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_OFFLINEAPPS", refObj);
         Components.utils.import("resource:///modules/offlineAppCache.jsm");
         OfflineAppCacheHelper.clear();
-        TelemetryStopwatch.finish("FX_SANITIZE_OFFLINEAPPS");
+        TelemetryStopwatch.finish("FX_SANITIZE_OFFLINEAPPS", refObj);
       },
 
       get canClear()
       {
         return true;
       }
     },
 
     history: {
-      clear: function ()
+      clear: Task.async(function* ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_HISTORY");
-
-        if (this.range)
-          PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]);
-        else
-          PlacesUtils.history.removeAllPages();
-
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_HISTORY", refObj);
         try {
-          var os = Components.classes["@mozilla.org/observer-service;1"]
-                             .getService(Components.interfaces.nsIObserverService);
-          let clearStartingTime = this.range ? String(this.range[0]) : "";
-          os.notifyObservers(null, "browser:purge-session-history", clearStartingTime);
-        }
-        catch (e) { }
+          if (this.range) {
+            yield PlacesUtils.history.removeVisitsByFilter({
+              beginDate: new Date(this.range[0] / 1000),
+              endDate: new Date(this.range[1] / 1000)
+            });
+          } else {
+            // Remove everything.
+            yield PlacesUtils.history.clear();
+          }
 
-        try {
-          var predictor = Components.classes["@mozilla.org/network/predictor;1"]
-                                    .getService(Components.interfaces.nsINetworkPredictor);
-          predictor.reset();
-        } catch (e) { }
+          try {
+            let clearStartingTime = this.range ? String(this.range[0]) : "";
+            Services.obs.notifyObservers(null, "browser:purge-session-history", clearStartingTime);
+          } catch (e) { }
 
-        TelemetryStopwatch.finish("FX_SANITIZE_HISTORY");
-      },
+          try {
+            let predictor = Components.classes["@mozilla.org/network/predictor;1"]
+                                      .getService(Components.interfaces.nsINetworkPredictor);
+            predictor.reset();
+          } catch (e) {
+            console.error("Error while resetting the predictor", e);
+          }
+        } finally {
+          TelemetryStopwatch.finish("FX_SANITIZE_HISTORY", refObj);
+        }
+      }),
 
       get canClear()
       {
         // bug 347231: Always allow clearing history due to dependencies on
         // the browser:purge-session-history notification. (like error console)
         return true;
       }
     },
 
     formdata: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_FORMDATA");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj);
 
         // Clear undo history of all searchBars
         var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
                                       .getService(Components.interfaces.nsIWindowMediator);
         var windows = windowManager.getEnumerator("navigator:browser");
         while (windows.hasMoreElements()) {
           let currentWindow = windows.getNext();
           let currentDocument = currentWindow.document;
@@ -367,17 +396,17 @@ Sanitizer.prototype = {
         }
 
         let change = { op: "remove" };
         if (this.range) {
           [ change.firstUsedStart, change.firstUsedEnd ] = this.range;
         }
         FormHistory.update(change);
 
-        TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA");
+        TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA", refObj);
       },
 
       canClear : function(aCallback, aArg)
       {
         var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
                                       .getService(Components.interfaces.nsIWindowMediator);
         var windows = windowManager.getEnumerator("navigator:browser");
         while (windows.hasMoreElements()) {
@@ -414,72 +443,75 @@ Sanitizer.prototype = {
         FormHistory.count({}, countDone);
         return false;
       }
     },
 
     downloads: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS", refObj);
         Task.spawn(function () {
           let filterByTime = null;
           if (this.range) {
             // Convert microseconds back to milliseconds for date comparisons.
             let rangeBeginMs = this.range[0] / 1000;
             let rangeEndMs = this.range[1] / 1000;
             filterByTime = download => download.startTime >= rangeBeginMs &&
                                        download.startTime <= rangeEndMs;
           }
 
           // Clear all completed/cancelled downloads
           let list = yield Downloads.getList(Downloads.ALL);
           list.removeFinished(filterByTime);
-          TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS");
+          TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj);
         }.bind(this)).then(null, error => {
-          TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS");
+          TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj);
           Components.utils.reportError(error);
         });
       },
 
       canClear : function(aCallback, aArg)
       {
         aCallback("downloads", true, aArg);
         return false;
       }
     },
 
     sessions: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_SESSIONS");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_SESSIONS", refObj);
 
         // clear all auth tokens
         var sdr = Components.classes["@mozilla.org/security/sdr;1"]
                             .getService(Components.interfaces.nsISecretDecoderRing);
         sdr.logoutAndTeardown();
 
         // clear FTP and plain HTTP auth sessions
         var os = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
         os.notifyObservers(null, "net:clear-active-logins", null);
 
-        TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS");
+        TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS", refObj);
       },
 
       get canClear()
       {
         return true;
       }
     },
 
     siteSettings: {
       clear: function ()
       {
-        TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS", refObj);
 
         // Clear site-specific permissions like "Allow this site to open popups"
         // we ignore the "end" range and hope it is now() - none of the
         // interfaces used here support a true range anyway.
         let startDateMS = this.range == null ? null : this.range[0] / 1000;
         var pm = Components.classes["@mozilla.org/permissionmanager;1"]
                            .getService(Components.interfaces.nsIPermissionManager);
         if (startDateMS == null) {
@@ -519,17 +551,17 @@ Sanitizer.prototype = {
         try {
           var push = Cc["@mozilla.org/push/NotificationService;1"]
                       .getService(Ci.nsIPushNotificationService);
           push.clearAll();
         } catch (e) {
           dump("Web Push may not be available.\n");
         }
 
-        TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS");
+        TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS", refObj);
       },
 
       get canClear()
       {
         return true;
       }
     },
     openWindows: {
@@ -554,122 +586,138 @@ Sanitizer.prototype = {
         }
         return true;
       },
       _resetAllWindowClosures: function(aWindowList) {
         for (let win of aWindowList) {
           win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
         }
       },
-      clear: function(aCallback)
-      {
+      clear: Task.async(function*() {
         // NB: this closes all *browser* windows, not other windows like the library, about window,
         // browser console, etc.
 
-        if (!aCallback) {
-          throw "Sanitizer's openWindows clear() requires a callback.";
-        }
-
         // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
         // dialogs
         let existingWindow = Services.appShell.hiddenDOMWindow;
         let startDate = existingWindow.performance.now();
 
         // First check if all these windows are OK with being closed:
         let windowEnumerator = Services.wm.getEnumerator("navigator:browser");
         let windowList = [];
         while (windowEnumerator.hasMoreElements()) {
           let someWin = windowEnumerator.getNext();
           windowList.push(someWin);
           // If someone says "no" to a beforeunload prompt, we abort here:
           if (!this._canCloseWindow(someWin)) {
             this._resetAllWindowClosures(windowList);
-            return false;
+            throw new Error("Sanitize could not close windows: cancelled by user");
           }
 
           // ...however, beforeunload prompts spin the event loop, and so the code here won't get
           // hit until the prompt has been dismissed. If more than 1 minute has elapsed since we
           // started prompting, stop, because the user might not even remember initiating the
           // 'forget', and the timespans will be all wrong by now anyway:
           if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
             this._resetAllWindowClosures(windowList);
-            return false;
+            throw new Error("Sanitize could not close windows: timeout");
           }
         }
 
         // If/once we get here, we should actually be able to close all windows.
 
-        TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS");
+        let refObj = {};
+        TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj);
 
         // First create a new window. We do this first so that on non-mac, we don't
         // accidentally close the app by closing all the windows.
         let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler);
         let defaultArgs = handler.defaultArgs;
         let features = "chrome,all,dialog=no," + this.privateStateForNewWindow;
         let newWindow = existingWindow.openDialog("chrome://browser/content/", "_blank",
                                                   features, defaultArgs);
 
-        // Window creation and destruction is asynchronous. We need to wait
-        // until all existing windows are fully closed, and the new window is
-        // fully open, before continuing. Otherwise the rest of the sanitizer
-        // could run too early (and miss new cookies being set when a page
-        // closes) and/or run too late (and not have a fully-formed window yet
-        // in existence). See bug 1088137.
-        let newWindowOpened = false;
-        function onWindowOpened(subject, topic, data) {
-          if (subject != newWindow)
-            return;
-
-          Services.obs.removeObserver(onWindowOpened, "browser-delayed-startup-finished");
-          newWindowOpened = true;
-          // If we're the last thing to happen, invoke callback.
-          if (numWindowsClosing == 0) {
-            TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS");
-            aCallback();
+        if (AppConstants.platform == "macosx") {
+          let onFullScreen = function(e) {
+            newWindow.removeEventListener("fullscreen", onFullScreen);
+            let docEl = newWindow.document.documentElement;
+            let sizemode = docEl.getAttribute("sizemode");
+            if (!newWindow.fullScreen && sizemode == "fullscreen") {
+              docEl.setAttribute("sizemode", "normal");
+              e.preventDefault();
+              e.stopPropagation();
+              return false;
+            }
           }
+          newWindow.addEventListener("fullscreen", onFullScreen);
         }
 
-        let numWindowsClosing = windowList.length;
-        function onWindowClosed() {
-          numWindowsClosing--;
-          if (numWindowsClosing == 0) {
-            Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed");
+        let promiseReady = new Promise(resolve => {
+          // Window creation and destruction is asynchronous. We need to wait
+          // until all existing windows are fully closed, and the new window is
+          // fully open, before continuing. Otherwise the rest of the sanitizer
+          // could run too early (and miss new cookies being set when a page
+          // closes) and/or run too late (and not have a fully-formed window yet
+          // in existence). See bug 1088137.
+          let newWindowOpened = false;
+          function onWindowOpened(subject, topic, data) {
+            if (subject != newWindow)
+              return;
+
+            Services.obs.removeObserver(onWindowOpened, "browser-delayed-startup-finished");
+            if (AppConstants.platform == "macosx") {
+              newWindow.removeEventListener("fullscreen", onFullScreen);
+            }
+            newWindowOpened = true;
             // If we're the last thing to happen, invoke callback.
-            if (newWindowOpened) {
-              TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS");
-              aCallback();
+            if (numWindowsClosing == 0) {
+              TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj);
+              resolve();
             }
           }
-        }
 
+          let numWindowsClosing = windowList.length;
+          function onWindowClosed() {
+            numWindowsClosing--;
+            if (numWindowsClosing == 0) {
+              Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed");
+              // If we're the last thing to happen, invoke callback.
+              if (newWindowOpened) {
+                TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj);
+                resolve();
+              }
+            }
+          }
+        });
         Services.obs.addObserver(onWindowOpened, "browser-delayed-startup-finished", false);
         Services.obs.addObserver(onWindowClosed, "xul-window-destroyed", false);
 
         // Start the process of closing windows
         while (windowList.length) {
           windowList.pop().close();
         }
         newWindow.focus();
-        return true;
-      },
+        yield promiseReady;
+      }),
 
       get canClear()
       {
         return true;
       }
     },
   }
 };
 
 
 
 // "Static" members
-Sanitizer.prefDomain          = "privacy.sanitize.";
-Sanitizer.prefShutdown        = "sanitizeOnShutdown";
-Sanitizer.prefDidShutdown     = "didShutdownSanitize";
+Sanitizer.PREF_DOMAIN = "privacy.sanitize.";
+Sanitizer.PREF_SANITIZE_ON_SHUTDOWN = "privacy.sanitize.sanitizeOnShutdown";
+Sanitizer.PREF_SANITIZE_IN_PROGRESS = "privacy.sanitize.sanitizeInProgress";
+Sanitizer.PREF_SANITIZE_DID_SHUTDOWN = "privacy.sanitize.didShutdownSanitize";
 
 // Time span constants corresponding to values of the privacy.sanitize.timeSpan
 // pref.  Used to determine how much history to clear, for various items
 Sanitizer.TIMESPAN_EVERYTHING = 0;
 Sanitizer.TIMESPAN_HOUR       = 1;
 Sanitizer.TIMESPAN_2HOURS     = 2;
 Sanitizer.TIMESPAN_4HOURS     = 3;
 Sanitizer.TIMESPAN_TODAY      = 4;
@@ -718,76 +766,84 @@ Sanitizer.getClearRange = function (ts) 
 };
 
 Sanitizer._prefs = null;
 Sanitizer.__defineGetter__("prefs", function()
 {
   return Sanitizer._prefs ? Sanitizer._prefs
     : Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
                          .getService(Components.interfaces.nsIPrefService)
-                         .getBranch(Sanitizer.prefDomain);
+                         .getBranch(Sanitizer.PREF_DOMAIN);
 });
 
 // Shows sanitization UI
 Sanitizer.showUI = function(aParentWindow)
 {
   var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                      .getService(Components.interfaces.nsIWindowWatcher);
-#ifdef XP_MACOSX
-  ww.openWindow(null, // make this an app-modal window on Mac
-#else
-  ww.openWindow(aParentWindow,
-#endif
+  let win = AppConstants.platform == "macosx" ?
+    null: // make this an app-modal window on Mac
+    aParentWindow;
+  ww.openWindow(win,
                 "chrome://browser/content/sanitize.xul",
                 "Sanitize",
                 "chrome,titlebar,dialog,centerscreen,modal",
                 null);
 };
 
 /**
  * Deletes privacy sensitive data in a batch, optionally showing the
  * sanitize UI, according to user preferences
  */
 Sanitizer.sanitize = function(aParentWindow)
 {
   Sanitizer.showUI(aParentWindow);
 };
 
-Sanitizer.onStartup = function()
-{
-  // we check for unclean exit with pending sanitization
-  Sanitizer._checkAndSanitize();
-};
+Sanitizer.onStartup = Task.async(function*() {
+  // Make sure that we are triggered during shutdown, at the right time.
+  let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"]
+     .getService(Ci.nsPIPlacesDatabase)
+     .shutdownClient
+     .jsclient;
 
-Sanitizer.onShutdown = function()
-{
-  // we check if sanitization is needed and perform it
-  Sanitizer._checkAndSanitize();
-};
-
-// this is called on startup and shutdown, to perform pending sanitizations
-Sanitizer._checkAndSanitize = function()
-{
-  const prefs = Sanitizer.prefs;
-  if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
-      !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
+  shutdownClient.addBlocker("sanitize.js: Sanitize on shutdown",
+    () => Sanitizer.onShutdown());
 
     // One time migration to remove support for the clear saved passwords on exit feature.
     if (!Services.prefs.getBoolPref("privacy.sanitize.migrateClearSavedPwdsOnExit")) {
       let deprecatedPref = "privacy.clearOnShutdown.passwords";
       let doUpdate = Services.prefs.prefHasUserValue(deprecatedPref) &&
                      Services.prefs.getBoolPref(deprecatedPref);
       if (doUpdate) {
         Services.logins.removeAllLogins();
         Services.prefs.setBoolPref("signon.rememberSignons", false);
       }
       Services.prefs.clearUserPref(deprecatedPref);
       Services.prefs.setBoolPref("privacy.sanitize.migrateClearSavedPwdsOnExit", true);
-    }
+  }
 
-    // this is a shutdown or a startup after an unclean exit
-    var s = new Sanitizer();
-    s.prefDomain = "privacy.clearOnShutdown.";
-    s.sanitize().then(function() {
-      prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
-    });
+  // Handle incomplete sanitizations
+  if (Preferences.has(Sanitizer.PREF_SANITIZE_IN_PROGRESS)) {
+    // Firefox crashed during sanitization.
+    let s = new Sanitizer();
+    let json = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS);
+    let itemsToClear = JSON.parse(json);
+    yield s.sanitize(itemsToClear);
   }
-};
+  if (Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN)) {
+    // Firefox crashed before having a chance to sanitize during shutdown.
+    // (note that if Firefox crashed during shutdown sanitization, we
+    // will hit both `if` so we will run a second double-sanitization).
+    yield Sanitizer.onShutdown();
+  }
+});
+
+Sanitizer.onShutdown = Task.async(function*() {
+  if (!Preferences.get(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN)) {
+    return;
+  }
+  // Need to sanitize upon shutdown
+  let s = new Sanitizer();
+  s.prefDomain = "privacy.clearOnShutdown.";
+  yield s.sanitize();
+  Preferences.set(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN, true);
+});
--- a/browser/base/content/sanitizeDialog.js
+++ b/browser/base/content/sanitizeDialog.js
@@ -1,15 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+let {Sanitizer} = Cu.import("resource:///modules/Sanitizer.jsm", {});
 
 var gSanitizePromptDialog = {
 
   get bundleBrowser()
   {
     if (!this._bundleBrowser)
       this._bundleBrowser = document.getElementById("bundleBrowser");
     return this._bundleBrowser;
@@ -38,40 +41,47 @@ var gSanitizePromptDialog = {
   init: function ()
   {
     // This is used by selectByTimespan() to determine if the window has loaded.
     this._inited = true;
 
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
 
+    let tasks = [];
     let sanitizeItemList = document.querySelectorAll("#itemList > [preference]");
     for (let i = 0; i < sanitizeItemList.length; i++) {
       let prefItem = sanitizeItemList[i];
       let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
-      s.canClearItem(name, function canClearCallback(aItem, aCanClear, aPrefItem) {
-        if (!aCanClear) {
-          aPrefItem.preference = null;
-          aPrefItem.checked = false;
-          aPrefItem.disabled = true;
+      let promise = s.promiseCanClearItem(name).then(canClear => {
+        if (canClear) {
+          return;
         }
-      }, prefItem);
+        prefItem.preference = null;
+        prefItem.checked = false;
+        prefItem.disabled = true;
+      });
+      tasks.push(promise);
     }
 
     document.documentElement.getButton("accept").label =
       this.bundleBrowser.getString("sanitizeButtonOK");
 
     if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) {
       this.prepareWarning();
       this.warningBox.hidden = false;
       document.title =
         this.bundleBrowser.getString("sanitizeDialog2.everything.title");
     }
     else
       this.warningBox.hidden = true;
+
+    Promise.all(tasks).then(() => {
+      Services.obs.notifyObservers(null, "sanitize-dialog-setup-complete", "");
+    });
   },
 
   selectByTimespan: function ()
   {
     // This method is the onselect handler for the duration dropdown.  As a
     // result it's called a couple of times before onload calls init().
     if (!this._inited)
       return;
@@ -114,25 +124,25 @@ var gSanitizePromptDialog = {
     // once the async operation completes (either with or without errors)
     // we close the window.
     let docElt = document.documentElement;
     let acceptButton = docElt.getButton("accept");
     acceptButton.disabled = true;
     acceptButton.setAttribute("label",
                               this.bundleBrowser.getString("sanitizeButtonClearing"));
     docElt.getButton("cancel").disabled = true;
+
     try {
       s.sanitize().then(null, Components.utils.reportError)
                   .then(() => window.close())
                   .then(null, Components.utils.reportError);
     } catch (er) {
       Components.utils.reportError("Exception during sanitize: " + er);
       return true; // We *do* want to close immediately on error.
     }
-    return false;
   },
 
   /**
    * If the panel that displays a warning when the duration is "Everything" is
    * not set up, sets it up.  Otherwise does nothing.
    *
    * @param aDontShowItemList Whether only the warning message should be updated.
    *                          True means the item list visibility status should not
@@ -500,17 +510,17 @@ var gSanitizePromptDialog = {
         }
       }
       // Otherwise use the predetermined range.
       else
         s.range = [this.durationStartTimes[durValue], Date.now() * 1000];
     }
 
     try {
-      s.sanitize();
+      s.sanitize(); // We ignore the resulting Promise
     } catch (er) {
       Components.utils.reportError("Exception during sanitize: " + er);
     }
     return true;
   },
 
   /**
    * In order to mark the custom Places tree view and its nsINavHistoryResult
--- a/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js
+++ b/browser/base/content/test/general/browser_sanitize-passwordDisabledHosts.js
@@ -1,23 +1,21 @@
 // Bug 474792 - Clear "Never remember passwords for this site" when
 // clearing site-specific settings in Clear Recent History dialog
 
 var tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 var Sanitizer = tempScope.Sanitizer;
 
-function test() {
-
+add_task(function*() {
   var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
 
   // Add a disabled host
   pwmgr.setLoginSavingEnabled("http://example.com", false);
-  
   // Sanity check
   is(pwmgr.getLoginSavingEnabled("http://example.com"), false,
      "example.com should be disabled for password saving since we haven't cleared that yet.");
 
   // Set up the sanitizer to just clear siteSettings
   let s = new Sanitizer();
   s.ignoreTimespan = false;
   s.prefDomain = "privacy.cpd.";
@@ -26,16 +24,16 @@ function test() {
   itemPrefs.setBoolPref("downloads", false);
   itemPrefs.setBoolPref("cache", false);
   itemPrefs.setBoolPref("cookies", false);
   itemPrefs.setBoolPref("formdata", false);
   itemPrefs.setBoolPref("offlineApps", false);
   itemPrefs.setBoolPref("passwords", false);
   itemPrefs.setBoolPref("sessions", false);
   itemPrefs.setBoolPref("siteSettings", true);
-  
+
   // Clear it
-  s.sanitize();
-  
+  yield s.sanitize();
+
   // Make sure it's gone
   is(pwmgr.getLoginSavingEnabled("http://example.com"), true,
      "example.com should be enabled for password saving again now that we've cleared.");
-}
+});
--- a/browser/base/content/test/general/browser_sanitize-sitepermissions.js
+++ b/browser/base/content/test/general/browser_sanitize-sitepermissions.js
@@ -10,17 +10,17 @@ function countPermissions() {
   let enumerator = Services.perms.enumerator;
   while (enumerator.hasMoreElements()) {
     result++;
     enumerator.getNext();
   }
   return result;
 }
 
-function test() {
+add_task(function* test() {
   // sanitize before we start so we have a good baseline.
   // Set up the sanitizer to just clear siteSettings
   let s = new Sanitizer();
   s.ignoreTimespan = false;
   s.prefDomain = "privacy.cpd.";
   var itemPrefs = gPrefService.getBranch(s.prefDomain);
   itemPrefs.setBoolPref("history", false);
   itemPrefs.setBoolPref("downloads", false);
@@ -40,13 +40,13 @@ function test() {
   // Add a permission entry
   var pm = Services.perms;
   pm.add(makeURI("http://example.com"), "testing", pm.ALLOW_ACTION);
 
   // Sanity check
   ok(pm.enumerator.hasMoreElements(), "Permission manager should have elements, since we just added one");
 
   // Clear it
-  s.sanitize();
+  yield s.sanitize();
 
   // Make sure it's gone
   is(numAtStart, countPermissions(), "Permission manager should have the same count it started with");
-}
+});
--- a/browser/base/content/test/general/browser_sanitizeDialog.js
+++ b/browser/base/content/test/general/browser_sanitizeDialog.js
@@ -13,225 +13,222 @@
  * test checks the UI of the dialog and makes sure it's correctly connected to
  * the sanitize timespan code.
  *
  * Some of this code, especially the history creation parts, was taken from
  * browser/base/content/test/general/browser_sanitize-timespans.js.
  */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-var {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
+let {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Timer",
+                                  "resource://gre/modules/Timer.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+                                  "resource://testing-common/PlacesTestUtils.jsm");
 
-var tempScope = {};
+let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
-var Sanitizer = tempScope.Sanitizer;
+let Sanitizer = tempScope.Sanitizer;
 
 const kMsecPerMin = 60 * 1000;
 const kUsecPerMin = 60 * 1000000;
 
-var formEntries, downloadIDs, olderDownloadIDs;
-
-// Add tests here.  Each is a function that's called by doNextTest().
-var gAllTests = [
+add_task(function* init() {
+  requestLongerTimeout(2);
+  blankSlate();
+  registerCleanupFunction(() => {
+    blankSlate();
+    return PlacesTestUtils.promiseAsyncUpdates();
+  });
+});
 
-  /**
-   * Initializes the dialog to its default state.
-   */
-  function () {
+/**
+ * Initializes the dialog to its default state.
+ */
+add_task(function* default_state() {
+  let wh = new WindowHelper();
+  wh.onload = function () {
+    // Select "Last Hour"
+    this.selectDuration(Sanitizer.TIMESPAN_HOUR);
+    // Hide details
+    if (!this.getItemList().collapsed)
+      this.toggleDetails();
+    this.acceptDialog();
+  };
+  wh.open();
+  return wh.promiseClosed;
+});
+
+/**
+ * Cancels the dialog, makes sure history not cleared.
+ */
+add_task(function* test_cancel() {
+  // Add history (within the past hour)
+  let uris = [];
+  let places = [];
+  let pURI;
+  for (let i = 0; i < 30; i++) {
+    pURI = makeURI("http://" + i + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
+    uris.push(pURI);
+  }
+
+  return new Promise(resolve => {
+    PlacesTestUtils.addVisits(places).then(() => {
     let wh = new WindowHelper();
     wh.onload = function () {
-      // Select "Last Hour"
       this.selectDuration(Sanitizer.TIMESPAN_HOUR);
-      // Hide details
-      if (!this.getItemList().collapsed)
-        this.toggleDetails();
-      this.acceptDialog();
-    };
-    wh.open();
-  },
+      this.checkPrefCheckbox("history", false);
+      this.checkDetails(false);
+
+      // Show details
+      this.toggleDetails();
+      this.checkDetails(true);
 
-  /**
-   * Cancels the dialog, makes sure history not cleared.
-   */
-  function () {
-    // Add history (within the past hour)
-    let uris = [];
-    let places = [];
-    let pURI;
-    for (let i = 0; i < 30; i++) {
-      pURI = makeURI("http://" + i + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
-      uris.push(pURI);
-    }
-
-    PlacesTestUtils.addVisits(places).then(() => {
-      let wh = new WindowHelper();
-      wh.onload = function () {
-        this.selectDuration(Sanitizer.TIMESPAN_HOUR);
-        this.checkPrefCheckbox("history", false);
-        this.checkDetails(false);
-
-        // Show details
-        this.toggleDetails();
-        this.checkDetails(true);
+      // Hide details
+      this.toggleDetails();
+      this.checkDetails(false);
+      this.cancelDialog();
+    };
+    wh.onunload = function* () {
+      yield promiseHistoryClearedState(uris, false);
+      yield blankSlate();
+      yield promiseHistoryClearedState(uris, true);
+    };
+    wh.promiseClosed.then(resolve);
+    wh.open();
+  })});
+});
 
-        // Hide details
-        this.toggleDetails();
-        this.checkDetails(false);
-        this.cancelDialog();
-      };
-      wh.onunload = function () {
-        yield promiseHistoryClearedState(uris, false);
-        yield blankSlate();
-        yield promiseHistoryClearedState(uris, true);
-      };
-      wh.open();
-    });
-  },
-
-  function () {
-    // Add downloads (within the past hour).
-    Task.spawn(function () {
-      downloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        yield addDownloadWithMinutesAgo(downloadIDs, i);
-      }
-      // Add downloads (over an hour ago).
-      olderDownloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
-      }
+/**
+ * Ensures that the combined history-downloads checkbox clears both history
+ * visits and downloads when checked; the dialog respects simple timespan.
+ */
+add_task(function* test_history_downloads_checked() {
+  // Add downloads (within the past hour).
+  let downloadIDs = [];
+  for (let i = 0; i < 5; i++) {
+    yield addDownloadWithMinutesAgo(downloadIDs, i);
+  }
+  // Add downloads (over an hour ago).
+  let olderDownloadIDs = [];
+  for (let i = 0; i < 5; i++) {
+    yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
+  }
 
-      doNextTest();
-    }).then(null, Components.utils.reportError);
-  },
+  // Add history (within the past hour).
+  let uris = [];
+  let places = [];
+  let pURI;
+  for (let i = 0; i < 30; i++) {
+    pURI = makeURI("http://" + i + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
+    uris.push(pURI);
+  }
+  // Add history (over an hour ago).
+  let olderURIs = [];
+  for (let i = 0; i < 5; i++) {
+    pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)});
+    olderURIs.push(pURI);
+  }
+  let promiseSanitized = promiseSanitizationComplete();
 
-  /**
-   * Ensures that the combined history-downloads checkbox clears both history
-   * visits and downloads when checked; the dialog respects simple timespan.
-   */
-  function () {
-    // Add history (within the past hour).
-    let uris = [];
-    let places = [];
-    let pURI;
-    for (let i = 0; i < 30; i++) {
-      pURI = makeURI("http://" + i + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
-      uris.push(pURI);
-    }
-    // Add history (over an hour ago).
-    let olderURIs = [];
-    for (let i = 0; i < 5; i++) {
-      pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)});
-      olderURIs.push(pURI);
-    }
-
+  return new Promise(resolve => {
     PlacesTestUtils.addVisits(places).then(() => {
       let totalHistoryVisits = uris.length + olderURIs.length;
 
       let wh = new WindowHelper();
       wh.onload = function () {
         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
         this.checkPrefCheckbox("history", true);
         this.acceptDialog();
       };
-      wh.onunload = function () {
+      wh.onunload = function* () {
         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR,
                   "timeSpan pref should be hour after accepting dialog with " +
                   "hour selected");
         boolPrefIs("cpd.history", true,
                    "history pref should be true after accepting dialog with " +
                    "history checkbox checked");
         boolPrefIs("cpd.downloads", true,
                    "downloads pref should be true after accepting dialog with " +
                    "history checkbox checked");
 
+        yield promiseSanitized;
+
         // History visits and downloads within one hour should be cleared.
         yield promiseHistoryClearedState(uris, true);
         yield ensureDownloadsClearedState(downloadIDs, true);
 
         // Visits and downloads > 1 hour should still exist.
         yield promiseHistoryClearedState(olderURIs, false);
         yield ensureDownloadsClearedState(olderDownloadIDs, false);
 
         // OK, done, cleanup after ourselves.
         yield blankSlate();
         yield promiseHistoryClearedState(olderURIs, true);
         yield ensureDownloadsClearedState(olderDownloadIDs, true);
       };
+      wh.promiseClosed.then(resolve);
       wh.open();
     });
-  },
-
-  /**
-   * Add form history entries for the next test.
-   */
-  function () {
-    formEntries = [];
+  });
+});
 
-    let iter = function() {
-      for (let i = 0; i < 5; i++) {
-        formEntries.push(addFormEntryWithMinutesAgo(iter, i));
-        yield undefined;
-      }
-      doNextTest();
-    }();
+/**
+ * Ensures that the combined history-downloads checkbox removes neither
+ * history visits nor downloads when not checked.
+ */
+add_task(function* test_history_downloads_unchecked() {
+  // Add form entries
+  let formEntries = [];
 
-    iter.next();
-  },
+  for (let i = 0; i < 5; i++) {
+    formEntries.push((yield promiseAddFormEntryWithMinutesAgo(i)));
+  }
+
 
-  function () {
-    // Add downloads (within the past hour).
-    Task.spawn(function () {
-      downloadIDs = [];
-      for (let i = 0; i < 5; i++) {
-        yield addDownloadWithMinutesAgo(downloadIDs, i);
-      }
-
-      doNextTest();
-    }).then(null, Components.utils.reportError);
-  },
+  // Add downloads (within the past hour).
+  let downloadIDs = [];
+  for (let i = 0; i < 5; i++) {
+    yield addDownloadWithMinutesAgo(downloadIDs, i);
+  }
 
-  /**
-   * Ensures that the combined history-downloads checkbox removes neither
-   * history visits nor downloads when not checked.
-   */
-  function () {
-    // Add history, downloads, form entries (within the past hour).
-    let uris = [];
-    let places = [];
-    let pURI;
-    for (let i = 0; i < 5; i++) {
-      pURI = makeURI("http://" + i + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
-      uris.push(pURI);
-    }
+  // Add history, downloads, form entries (within the past hour).
+  let uris = [];
+  let places = [];
+  let pURI;
+  for (let i = 0; i < 5; i++) {
+    pURI = makeURI("http://" + i + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
+    uris.push(pURI);
+  }
 
+  return new Promise(resolve => {
     PlacesTestUtils.addVisits(places).then(() => {
       let wh = new WindowHelper();
       wh.onload = function () {
         is(this.isWarningPanelVisible(), false,
            "Warning panel should be hidden after previously accepting dialog " +
            "with a predefined timespan");
         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
 
         // Remove only form entries, leave history (including downloads).
         this.checkPrefCheckbox("history", false);
         this.checkPrefCheckbox("formdata", true);
         this.acceptDialog();
       };
-      wh.onunload = function () {
+      wh.onunload = function* () {
         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR,
                   "timeSpan pref should be hour after accepting dialog with " +
                   "hour selected");
         boolPrefIs("cpd.history", false,
                    "history pref should be false after accepting dialog with " +
                    "history checkbox unchecked");
         boolPrefIs("cpd.downloads", false,
                    "downloads pref should be false after accepting dialog with " +
@@ -246,35 +243,41 @@ var gAllTests = [
           is(exists, false, "form entry " + entry + " should no longer exist");
         });
 
         // OK, done, cleanup after ourselves.
         yield blankSlate();
         yield promiseHistoryClearedState(uris, true);
         yield ensureDownloadsClearedState(downloadIDs, true);
       };
+      wh.promiseClosed.then(resolve);
       wh.open();
     });
-  },
+  });
+});
 
-  /**
-   * Ensures that the "Everything" duration option works.
-   */
-  function () {
-    // Add history.
-    let uris = [];
-    let places = [];
-    let pURI;
-    // within past hour, within past two hours, within past four hours and 
-    // outside past four hours
-    [10, 70, 130, 250].forEach(function(aValue) {
-      pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
-      uris.push(pURI);
-    });
+/**
+ * Ensures that the "Everything" duration option works.
+ */
+add_task(function* test_everything() {
+  // Add history.
+  let uris = [];
+  let places = [];
+  let pURI;
+  // within past hour, within past two hours, within past four hours and 
+  // outside past four hours
+  [10, 70, 130, 250].forEach(function(aValue) {
+    pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
+    uris.push(pURI);
+  });
+
+  let promiseSanitized = promiseSanitizationComplete();
+
+  return new Promise(resolve => {
     PlacesTestUtils.addVisits(places).then(() => {
       let wh = new WindowHelper();
       wh.onload = function () {
         is(this.isWarningPanelVisible(), false,
            "Warning panel should be hidden after previously accepting dialog " +
            "with a predefined timespan");
         this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
         this.checkPrefCheckbox("history", true);
@@ -286,84 +289,88 @@ var gAllTests = [
 
         // Show details
         this.toggleDetails();
         this.checkDetails(true);
 
         this.acceptDialog();
       };
       wh.onunload = function () {
+        yield promiseSanitized;
         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING,
                   "timeSpan pref should be everything after accepting dialog " +
                   "with everything selected");
 
         yield promiseHistoryClearedState(uris, true);
       };
+      wh.promiseClosed.then(resolve);
       wh.open();
     });
-  },
+  });
+});
 
-  /**
-   * Ensures that the "Everything" warning is visible on dialog open after
-   * the previous test.
-   */
-  function () {
-    // Add history.
-    let uris = [];
-    let places = [];
-    let pURI;
-    // within past hour, within past two hours, within past four hours and 
-    // outside past four hours
-    [10, 70, 130, 250].forEach(function(aValue) {
-      pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
-      places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
-      uris.push(pURI);
-    });
+/**
+ * Ensures that the "Everything" warning is visible on dialog open after
+ * the previous test.
+ */
+add_task(function* test_everything_warning() {
+  // Add history.
+  let uris = [];
+  let places = [];
+  let pURI;
+  // within past hour, within past two hours, within past four hours and 
+  // outside past four hours
+  [10, 70, 130, 250].forEach(function(aValue) {
+    pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
+    places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
+    uris.push(pURI);
+  });
+
+  let promiseSanitized = promiseSanitizationComplete();
+
+  return new Promise(resolve => {
     PlacesTestUtils.addVisits(places).then(() => {
       let wh = new WindowHelper();
       wh.onload = function () {
         is(this.isWarningPanelVisible(), true,
            "Warning panel should be visible after previously accepting dialog " +
            "with clearing everything");
         this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
         this.checkPrefCheckbox("history", true);
         this.acceptDialog();
       };
       wh.onunload = function () {
         intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING,
                   "timeSpan pref should be everything after accepting dialog " +
                   "with everything selected");
 
+        yield promiseSanitized;
+
         yield promiseHistoryClearedState(uris, true);
       };
+      wh.promiseClosed.then(resolve);
       wh.open();
     });
-  },
-
-  /**
-   * Add form history entry for the next test.
-   */
-  function () {
-    let iter = function() {
-      formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
-      yield undefined;
-      doNextTest();
-    }();
+  });
+});
 
-    iter.next();
-  },
+/**
+ * The next three tests checks that when a certain history item cannot be
+ * cleared then the checkbox should be both disabled and unchecked.
+ * In addition, we ensure that this behavior does not modify the preferences.
+ */
+add_task(function* test_cannot_clear_history() {
+  // Add form entries
+  let formEntries = [ (yield promiseAddFormEntryWithMinutesAgo(10)) ];
 
-  /**
-   * The next three tests checks that when a certain history item cannot be
-   * cleared then the checkbox should be both disabled and unchecked.
-   * In addition, we ensure that this behavior does not modify the preferences.
-   */
-  function () {
-    // Add history.
-    let pURI = makeURI("http://" + 10 + "-minutes-ago.com/");
+  let promiseSanitized = promiseSanitizationComplete();
+
+  // Add history.
+  let pURI = makeURI("http://" + 10 + "-minutes-ago.com/");
+  return new Promise(resolve => {
     PlacesTestUtils.addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}).then(() => {
       let uris = [ pURI ];
 
       let wh = new WindowHelper();
       wh.onload = function() {
         // Check that the relevant checkboxes are enabled
         var cb = this.win.document.querySelectorAll(
                    "#itemList > [preference='privacy.cpd.formdata']");
@@ -374,312 +381,320 @@ var gAllTests = [
                    "#itemList > [preference='privacy.cpd.history']");
         ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " +
            "clear history should be enabled.");
 
         this.checkAllCheckboxes();
         this.acceptDialog();
       };
       wh.onunload = function () {
+        yield promiseSanitized;
+
         yield promiseHistoryClearedState(uris, true);
 
         let exists = yield formNameExists(formEntries[0]);
         is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
       };
+      wh.promiseClosed.then(resolve);
       wh.open();
     });
-  },
-  function () {
-    let wh = new WindowHelper();
-    wh.onload = function() {
-      boolPrefIs("cpd.history", true,
-                 "history pref should be true after accepting dialog with " +
-                 "history checkbox checked");
-      boolPrefIs("cpd.formdata", true,
-                 "formdata pref should be true after accepting dialog with " +
-                 "formdata checkbox checked");
+  });
+});
+
+add_task(function* test_no_formdata_history_to_clear() {
+  let promiseSanitized = promiseSanitizationComplete();
+  let wh = new WindowHelper();
+  wh.onload = function() {
+    boolPrefIs("cpd.history", true,
+               "history pref should be true after accepting dialog with " +
+               "history checkbox checked");
+    boolPrefIs("cpd.formdata", true,
+               "formdata pref should be true after accepting dialog with " +
+               "formdata checkbox checked");
+
+    // Even though the formdata pref is true, because there is no history
+    // left to clear, the checkbox will be disabled.
+    var cb = this.win.document.querySelectorAll(
+               "#itemList > [preference='privacy.cpd.formdata']");
+    ok(cb.length == 1 && cb[0].disabled && !cb[0].checked,
+       "There is no formdata history, checkbox should be disabled and be " +
+       "cleared to reduce user confusion (bug 497664).");
+
+    var cb = this.win.document.querySelectorAll(
+               "#itemList > [preference='privacy.cpd.history']");
+    ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
+       "There is no history, but history checkbox should always be enabled " +
+       "and will be checked from previous preference.");
+
+    this.acceptDialog();
+  }
+  wh.open();
+  yield wh.promiseClosed;
+  yield promiseSanitized;
+});
+
+add_task(function* test_form_entries() {
+  let formEntry = (yield promiseAddFormEntryWithMinutesAgo(10));
+
+  let promiseSanitized = promiseSanitizationComplete();
+
+  let wh = new WindowHelper();
+  wh.onload = function() {
+    boolPrefIs("cpd.formdata", true,
+               "formdata pref should persist previous value after accepting " +
+               "dialog where you could not clear formdata.");
+
+    var cb = this.win.document.querySelectorAll(
+               "#itemList > [preference='privacy.cpd.formdata']");
+
+    info("There exists formEntries so the checkbox should be in sync with the pref.");
+    is(cb.length, 1, "There is only one checkbox for form data");
+    ok(!cb[0].disabled, "The checkbox is enabled");
+    ok(cb[0].checked, "The checkbox is checked");
+
+    this.acceptDialog();
+  };
+  wh.onunload = function () {
+    yield promiseSanitized;
+    let exists = yield formNameExists(formEntry);
+    is(exists, false, "form entry " + formEntry + " should no longer exist");
+  };
+  wh.open();
+  return wh.promiseClosed;
+});
 
 
-      // Even though the formdata pref is true, because there is no history
-      // left to clear, the checkbox will be disabled.
-      var cb = this.win.document.querySelectorAll(
-                 "#itemList > [preference='privacy.cpd.formdata']");
-
-      // Wait until the checkbox is disabled. This is done asynchronously
-      // from Sanitizer.init() as FormHistory.count() is a purely async API.
-      promiseWaitForCondition(() => cb[0].disabled).then(() => {
-        ok(cb.length == 1 && cb[0].disabled && !cb[0].checked,
-           "There is no formdata history, checkbox should be disabled and be " +
-           "cleared to reduce user confusion (bug 497664).");
-
-        cb = this.win.document.querySelectorAll(
-                   "#itemList > [preference='privacy.cpd.history']");
-        ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
-           "There is no history, but history checkbox should always be enabled " +
-           "and will be checked from previous preference.");
-
-        this.acceptDialog();
-      });
-    }
-    wh.open();
-  },
-
-  /**
-   * Add form history entry for the next test.
-   */
-  function () {
-    let iter = function() {
-      formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
-      yield undefined;
-      doNextTest();
-    }();
-
-    iter.next();
-  },
-
-  function () {
-    let wh = new WindowHelper();
-    wh.onload = function() {
-      boolPrefIs("cpd.formdata", true,
-                 "formdata pref should persist previous value after accepting " +
-                 "dialog where you could not clear formdata.");
-
-      var cb = this.win.document.querySelectorAll(
-                 "#itemList > [preference='privacy.cpd.formdata']");
-      ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
-         "There exists formEntries so the checkbox should be in sync with " +
-         "the pref.");
-
-      this.acceptDialog();
-    };
-    wh.onunload = function () {
-      let exists = yield formNameExists(formEntries[0]);
-      is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
-    };
-    wh.open();
-  },
-
-
-  /**
-   * These next six tests together ensure that toggling details persists
-   * across dialog openings.
-   */
-  function () {
+/**
+ * Ensure that toggling details persists
+ * across dialog openings.
+ */
+add_task(function* test_toggling_details_persists() {
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Check all items and select "Everything"
       this.checkAllCheckboxes();
       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
 
       // Hide details
       this.toggleDetails();
       this.checkDetails(false);
       this.acceptDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should remain closed because all items are checked.
       this.checkDetails(false);
 
       // Uncheck history.
       this.checkPrefCheckbox("history", false);
       this.acceptDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should be open because not all items are checked.
       this.checkDetails(true);
 
       // Modify the Site Preferences item state (bug 527820)
       this.checkAllCheckboxes();
       this.checkPrefCheckbox("siteSettings", false);
       this.acceptDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should be open because not all items are checked.
       this.checkDetails(true);
 
       // Hide details
       this.toggleDetails();
       this.checkDetails(false);
       this.cancelDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should be open because not all items are checked.
       this.checkDetails(true);
 
       // Select another duration
       this.selectDuration(Sanitizer.TIMESPAN_HOUR);
       // Hide details
       this.toggleDetails();
       this.checkDetails(false);
       this.acceptDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should not be open because "Last Hour" is selected
       this.checkDetails(false);
 
       this.cancelDialog();
     };
     wh.open();
-  },
-  function () {
+    yield wh.promiseClosed;
+  }
+  {
     let wh = new WindowHelper();
     wh.onload = function () {
       // Details should have remained closed
       this.checkDetails(false);
 
       // Show details
       this.toggleDetails();
       this.checkDetails(true);
       this.cancelDialog();
     };
     wh.open();
-  },
-  function () {
-    // Test for offline cache deletion
+    yield wh.promiseClosed;
+  }
+});
+
+// Test for offline cache deletion
+add_task(function* test_offline_cache() {
+  // Prepare stuff, we will work with www.example.com
+  var URL = "http://www.example.com";
+
+  var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Ci.nsIIOService);
+  var URI = ios.newURI(URL, null, null);
 
-    // Prepare stuff, we will work with www.example.com
-    var URL = "http://www.example.com";
+  var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+           .getService(Ci.nsIScriptSecurityManager);
+  var principal = sm.getNoAppCodebasePrincipal(URI);
+
+  // Give www.example.com privileges to store offline data
+  var pm = Cc["@mozilla.org/permissionmanager;1"]
+           .getService(Ci.nsIPermissionManager);
+  pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
+  pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
+
+  // Store something to the offline cache
+  var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"]
+                     .getService(Ci.nsIApplicationCacheService);
+  var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default);
+  var appcache = appcacheserv.createApplicationCache(appcachegroupid);
+
+  var cacheserv = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                  .getService(Ci.nsICacheStorageService);
+  var storage = cacheserv.appCacheStorage(LoadContextInfo.default, appcache);
 
-    var ios = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService);
-    var URI = ios.newURI(URL, null, null);
+  // Open the dialog
+  let wh = new WindowHelper();
+  wh.onload = function () {
+    this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
+    // Show details
+    this.toggleDetails();
+    // Clear only offlineApps
+    this.uncheckAllCheckboxes();
+    this.checkPrefCheckbox("offlineApps", true);
+    this.acceptDialog();
+  };
+  wh.onunload = function () {
+    // Check if the cache has been deleted
+    var size = -1;
+    var visitor = {
+      onCacheStorageInfo: function (aEntryCount, aConsumption, aCapacity, aDiskDirectory)
+      {
+        size = aConsumption;
+      }
+    };
+    storage.asyncVisitStorage(visitor, false);
+    // Offline cache visit happens synchronously, since it's forwarded to the old code
+    is(size, 0, "offline application cache entries evicted");
+  };
 
-    var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
-             .getService(Ci.nsIScriptSecurityManager);
-    var principal = sm.createCodebasePrincipal(URI, {});
+  var cacheListener = {
+    onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+    onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+      is(status, Cr.NS_OK);
+      var stream = entry.openOutputStream(0);
+      var content = "content";
+      stream.write(content, content.length);
+      stream.close();
+      entry.close();
+      wh.open();
+    }
+  };
+
+  storage.asyncOpenURI(makeURI(URL), "", Ci.nsICacheStorage.OPEN_TRUNCATE, cacheListener);
+  return wh.promiseClosed;
+});
+
+// Test for offline apps permission deletion
+add_task(function* test_offline_apps_permissions() {
+  // Prepare stuff, we will work with www.example.com
+  var URL = "http://www.example.com";
 
-    // Give www.example.com privileges to store offline data
+  var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Ci.nsIIOService);
+  var URI = ios.newURI(URL, null, null);
+
+  var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+           .getService(Ci.nsIScriptSecurityManager);
+  var principal = sm.createCodebasePrincipal(URI, {});
+
+  let promiseSanitized = promiseSanitizationComplete();
+
+  // Open the dialog
+  let wh = new WindowHelper();
+  wh.onload = function () {
+    this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
+    // Show details
+    this.toggleDetails();
+    // Clear only offlineApps
+    this.uncheckAllCheckboxes();
+    this.checkPrefCheckbox("siteSettings", true);
+    this.acceptDialog();
+  };
+  wh.onunload = function () {
+    yield promiseSanitized;
+
+    // Check all has been deleted (privileges, data, cache)
     var pm = Cc["@mozilla.org/permissionmanager;1"]
              .getService(Ci.nsIPermissionManager);
-    pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
-    pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
-
-    // Store something to the offline cache
-    var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"]
-                       .getService(Ci.nsIApplicationCacheService);
-    var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default);
-    var appcache = appcacheserv.createApplicationCache(appcachegroupid);
-
-    var cacheserv = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
-                    .getService(Ci.nsICacheStorageService);
-    var storage = cacheserv.appCacheStorage(LoadContextInfo.default, appcache);
-
-    // Open the dialog
-    let wh = new WindowHelper();
-    wh.onload = function () {
-      this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
-      // Show details
-      this.toggleDetails();
-      // Clear only offlineApps
-      this.uncheckAllCheckboxes();
-      this.checkPrefCheckbox("offlineApps", true);
-      this.acceptDialog();
-    };
-    wh.onunload = function () {
-      // Check if the cache has been deleted
-      var size = -1;
-      var visitor = {
-        onCacheStorageInfo: function (aEntryCount, aConsumption, aCapacity, aDiskDirectory)
-        {
-          size = aConsumption;
-        }
-      };
-      storage.asyncVisitStorage(visitor, false);
-      // Offline cache visit happens synchronously, since it's forwarded to the old code
-      is(size, 0, "offline application cache entries evicted");
-    };
+    is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed");
+  };
+  wh.open();
+  return wh.promiseClosed;
+});
 
-    var cacheListener = {
-      onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
-      onCacheEntryAvailable: function (entry, isnew, appcache, status) {
-        is(status, Cr.NS_OK);
-        var stream = entry.openOutputStream(0);
-        var content = "content";
-        stream.write(content, content.length);
-        stream.close();
-        entry.close();
-        wh.open();
-      }
-    };
-
-    storage.asyncOpenURI(makeURI(URL), "", Ci.nsICacheStorage.OPEN_TRUNCATE, cacheListener);
-  },
-  function () {
-    // Test for offline apps permission deletion
-
-    // Prepare stuff, we will work with www.example.com
-    var URL = "http://www.example.com";
-
-    var ios = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService);
-    var URI = ios.newURI(URL, null, null);
-
-    var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
-             .getService(Ci.nsIScriptSecurityManager);
-    var principal = sm.createCodebasePrincipal(URI, {});
-
-    // Open the dialog
-    let wh = new WindowHelper();
-    wh.onload = function () {
-      this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
-      // Show details
-      this.toggleDetails();
-      // Clear only offlineApps
-      this.uncheckAllCheckboxes();
-      this.checkPrefCheckbox("siteSettings", true);
-      this.acceptDialog();
-    };
-    wh.onunload = function () {
-      // Check all has been deleted (privileges, data, cache)
-      var pm = Cc["@mozilla.org/permissionmanager;1"]
-               .getService(Ci.nsIPermissionManager);
-      is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed");
-    };
-    wh.open();
-  }
-];
-
-// Index in gAllTests of the test currently being run.  Incremented for each
-// test run.  See doNextTest().
-var gCurrTest = 0;
-
-var now_mSec = Date.now();
-var now_uSec = now_mSec * 1000;
+let now_mSec = Date.now();
+let now_uSec = now_mSec * 1000;
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * This wraps the dialog and provides some convenience methods for interacting
  * with it.
  *
  * @param aWin
  *        The dialog's nsIDOMWindow
  */
 function WindowHelper(aWin) {
   this.win = aWin;
+  this.promiseClosed = new Promise(resolve => {this._resolveClosed = resolve});
 }
 
 WindowHelper.prototype = {
   /**
    * "Presses" the dialog's OK button.
    */
   acceptDialog: function () {
     is(this.win.document.documentElement.getButton("accept").disabled, false,
@@ -814,69 +829,55 @@ WindowHelper.prototype = {
       if (aTopic != "domwindowopened")
         return;
 
       Services.ww.unregisterNotification(windowObserver);
 
       var loaded = false;
       let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
 
+      let promiseDialogReady = promiseSanitizationDialogReady();
+
       win.addEventListener("load", function onload(event) {
         win.removeEventListener("load", onload, false);
 
         if (win.name !== "SanitizeDialog")
           return;
 
         wh.win = win;
         loaded = true;
 
-        executeSoon(function () {
-          // Some exceptions that reach here don't reach the test harness, but
-          // ok()/is() do...
-          try {
-            wh.onload();
-          }
-          catch (exc) {
-            win.close();
-            ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
-            finish();
-          }
+        Task.spawn(function*() {
+          yield promiseDialogReady;
+          yield new Promise(resolve => setTimeout(resolve, 0));
+          yield wh.onload();
         });
       }, false);
 
       win.addEventListener("unload", function onunload(event) {
         if (win.name !== "SanitizeDialog") {
           win.removeEventListener("unload", onunload, false);
           return;
         }
 
         // Why is unload fired before load?
         if (!loaded)
           return;
 
         win.removeEventListener("unload", onunload, false);
         wh.win = win;
 
-        executeSoon(function () {
-          // Some exceptions that reach here don't reach the test harness, but
-          // ok()/is() do...
-          try {
-            if (wh.onunload) {
-              Task.spawn(wh.onunload).then(function() {
-                waitForAsyncUpdates(doNextTest);
-              }).then(null, Components.utils.reportError);
-            } else {
-              waitForAsyncUpdates(doNextTest);
-            }
+        // Some exceptions that reach here don't reach the test harness, but
+        // ok()/is() do...
+        Task.spawn(function*() {
+          if (wh.onunload) {
+            yield wh.onunload();
           }
-          catch (exc) {
-            win.close();
-            ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
-            finish();
-          }
+          yield PlacesTestUtils.promiseAsyncUpdates();
+          wh._resolveClosed();
         });
       }, false);
     }
     Services.ww.registerNotification(windowObserver);
     Services.ww.openWindow(null,
                            "chrome://browser/content/sanitize.xul",
                            "SanitizeDialog",
                            "chrome,titlebar,dialog,centerscreen,modal",
@@ -904,16 +905,24 @@ WindowHelper.prototype = {
   /**
    * Toggles the details progressive disclosure button.
    */
   toggleDetails: function () {
     this.getDetailsButton().click();
   }
 };
 
+function promiseSanitizationDialogReady() {
+  return promiseTopicObserved("sanitize-dialog-setup-complete");
+}
+
+function promiseSanitizationComplete() {
+  return promiseTopicObserved("sanitizer-sanitization-complete");
+}
+
 /**
  * Adds a download to history.
  *
  * @param aMinutesAgo
  *        The download will be downloaded this many minutes ago
  */
 function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
   let publicList = yield Downloads.getList(Downloads.PUBLIC);
@@ -935,29 +944,33 @@ function addDownloadWithMinutesAgo(aExpe
 }
 
 /**
  * Adds a form entry to history.
  *
  * @param aMinutesAgo
  *        The entry will be added this many minutes ago
  */
-function addFormEntryWithMinutesAgo(then, aMinutesAgo) {
+function promiseAddFormEntryWithMinutesAgo(aMinutesAgo) {
   let name = aMinutesAgo + "-minutes-ago";
 
   // Artifically age the entry to the proper vintage.
   let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin);
 
-  FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp },
+  return new Promise((resolve, reject) =>
+    FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp },
                      { handleError: function (error) {
                          do_throw("Error occurred updating form history: " + error);
+                         reject();
                        },
-                       handleCompletion: function (reason) { then.next(); }
-                     });
-  return name;
+                       handleCompletion: function (reason) {
+                         resolve(name);
+                       }
+                     })
+   )
 }
 
 /**
  * Checks if a form entry exists.
  */
 function formNameExists(name)
 {
   let deferred = Promise.defer();
@@ -1035,32 +1048,16 @@ function downloadExists(aPath)
   return Task.spawn(function() {
     let publicList = yield Downloads.getList(Downloads.PUBLIC);
     let listArray = yield publicList.getAll();
     throw new Task.Result(listArray.some(i => i.target.path == aPath));
   });
 }
 
 /**
- * Runs the next test in the gAllTests array.  If all tests have been run,
- * finishes the entire suite.
- */
-function doNextTest() {
-  if (gAllTests.length <= gCurrTest) {
-    blankSlate();
-    waitForAsyncUpdates(finish);
-  }
-  else {
-    let ct = gCurrTest;
-    gCurrTest++;
-    gAllTests[ct]();
-  }
-}
-
-/**
  * Ensures that the specified downloads are either cleared or not.
  *
  * @param aDownloadIDs
  *        Array of download database IDs
  * @param aShouldBeCleared
  *        True if each download should be cleared, false otherwise
  */
 function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
@@ -1089,18 +1086,8 @@ function intPrefIs(aPrefName, aExpectedV
  * Creates a visit time.
  *
  * @param aMinutesAgo
  *        The visit will be visited this many minutes ago
  */
 function visitTimeForMinutesAgo(aMinutesAgo) {
   return now_uSec - aMinutesAgo * kUsecPerMin;
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
-function test() {
-  requestLongerTimeout(2);
-  waitForExplicitFinish();
-  blankSlate();
-  // Kick off all the tests in the gAllTests array.
-  waitForAsyncUpdates(doNextTest);
-}
--- a/browser/base/content/test/newtab/browser_newtab_bug722273.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js
@@ -6,63 +6,72 @@ const URL = "http://fake-site.com/";
 
 var tmp = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("chrome://browser/content/sanitize.js", tmp);
 
 var {Sanitizer} = tmp;
 
-function runTests() {
-  sanitizeHistory();
-  yield addFakeVisits();
-  yield addNewTabPageTab();
-
+add_task(function*() {
+  yield promiseSanitizeHistory();
+  yield promiseAddFakeVisits();
+  yield addNewTabPageTabPromise();
   is(getCell(0).site.url, URL, "first site is our fake site");
 
-  whenPagesUpdated();
-  yield sanitizeHistory();
+  whenPagesUpdated(() => {});
+  yield promiseSanitizeHistory();
 
-  ok(!getCell(0).site, "the fake site is gone");
-}
+  // Now wait until the grid is updated
+  while (true) {
+    if (!getCell(0).site) {
+      break;
+    }
+    info("the fake site is still present");
+    yield new Promise(resolve => setTimeout(resolve, 1000));
+  }
+  ok(!getCell(0).site, "fake site is gone");
+});
 
-function addFakeVisits() {
+function promiseAddFakeVisits() {
   let visits = [];
   for (let i = 59; i > 0; i--) {
     visits.push({
       visitDate: NOW - i * 60 * 1000000,
       transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
     });
   }
   let place = {
     uri: makeURI(URL),
     title: "fake site",
     visits: visits
   };
-  PlacesUtils.asyncHistory.updatePlaces(place, {
-    handleError: () => ok(false, "couldn't add visit"),
-    handleResult: function () {},
-    handleCompletion: function () {
-      NewTabUtils.links.populateCache(function () {
-        NewTabUtils.allPages.update();
-        TestRunner.next();
-      }, true);
-    }
+  return new Promise((resolve, reject) => {
+    PlacesUtils.asyncHistory.updatePlaces(place, {
+      handleError: () => reject(new Error("Couldn't add visit")),
+      handleResult: function () {},
+      handleCompletion: function () {
+        NewTabUtils.links.populateCache(function () {
+          NewTabUtils.allPages.update();
+          resolve();
+        }, true);
+      }
+    });
   });
 }
 
-function sanitizeHistory() {
+function promiseSanitizeHistory() {
   let s = new Sanitizer();
   s.prefDomain = "privacy.cpd.";
 
   let prefs = gPrefService.getBranch(s.prefDomain);
   prefs.setBoolPref("history", true);
   prefs.setBoolPref("downloads", false);
   prefs.setBoolPref("cache", false);
   prefs.setBoolPref("cookies", false);
   prefs.setBoolPref("formdata", false);
   prefs.setBoolPref("offlineApps", false);
   prefs.setBoolPref("passwords", false);
   prefs.setBoolPref("sessions", false);
   prefs.setBoolPref("siteSettings", false);
 
-  s.sanitize();
+  return s.sanitize();
 }
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -111,28 +111,68 @@ function watchLinksChangeOnce() {
   };
   observer.onDownloadFail = observer.onManyLinksChanged;
   DirectoryLinksProvider.addObserver(observer);
   return deferred.promise;
 };
 
 /**
  * Provide the default test function to start our test runner.
+ *
+ * We need different code paths for tests that are still wired for
+ * `TestRunner` and tests that have been ported to `add_task` as
+ * we cannot have both in the same file.
  */
-function test() {
-  waitForExplicitFinish();
-  // start TestRunner.run() after directory links is downloaded and written to disk
-  watchLinksChangeOnce().then(() => {
-    // Wait for hidden page to update with the desired links
-    whenPagesUpdated(() => TestRunner.run(), true);
+function isTestPortedToAddTask() {
+  return gTestPath.endsWith("browser_newtab_bug722273.js");
+}
+if (!isTestPortedToAddTask()) {
+  this.test = function() {
+    waitForExplicitFinish();
+    // start TestRunner.run() after directory links is downloaded and written to disk
+    watchLinksChangeOnce().then(() => {
+      // Wait for hidden page to update with the desired links
+      whenPagesUpdated(() => TestRunner.run(), true);
+    });
+
+    // Save the original directory source (which is set globally for tests)
+    gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE);
+    Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource);
+  }
+} else {
+  add_task(function* setup() {
+    registerCleanupFunction(function() {
+      return new Promise(resolve => {
+        function cleanupAndFinish() {
+          PlacesTestUtils.clearHistory().then(() => {
+            whenPagesUpdated(resolve);
+            NewTabUtils.restore();
+          });
+        }
+
+        let callbacks = NewTabUtils.links._populateCallbacks;
+        let numCallbacks = callbacks.length;
+
+        if (numCallbacks)
+          callbacks.splice(0, numCallbacks, cleanupAndFinish);
+        else
+          cleanupAndFinish();
+      });
+    });
+
+    let promiseReady = Task.spawn(function*() {
+      yield watchLinksChangeOnce();
+      yield new Promise(resolve => whenPagesUpdated(resolve, true));
+    });
+
+    // Save the original directory source (which is set globally for tests)
+    gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE);
+    Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource);
+    yield promiseReady;
   });
-
-  // Save the original directory source (which is set globally for tests)
-  gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE);
-  Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource);
 }
 
 /**
  * The test runner that controls the execution flow of our tests.
  */
 var TestRunner = {
   /**
    * Starts the test runner.
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -229,20 +229,24 @@ file, You can obtain one at http://mozil
           let textNode = this.editor.rootElement.firstChild;
           let value = textNode.textContent;
           if (!value)
             return;
 
           // Get the URL from the fixup service:
           let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
                       Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
-          let uriInfo = Services.uriFixup.getFixupURIInfo(value, flags);
+          let uriInfo;
+          try {
+            uriInfo = Services.uriFixup.getFixupURIInfo(value, flags);
+          } catch (ex) {}
           // Ignore if we couldn't make a URI out of this, the URI resulted in a search,
           // or the URI has a non-http(s)/ftp protocol.
-          if (!uriInfo.fixedURI ||
+          if (!uriInfo ||
+              !uriInfo.fixedURI ||
               uriInfo.keywordProviderName ||
               ["http", "https", "ftp"].indexOf(uriInfo.fixedURI.scheme) == -1) {
             return;
           }
 
           // If we trimmed off the http scheme, ensure we stick it back on before
           // trying to figure out what domain we're accessing, so we don't get
           // confused by user:pass@host http URLs. We later use
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -133,17 +133,17 @@ browser.jar:
         content/browser/sync/utils.js                 (content/sync/utils.js)
 *       content/browser/sync/customize.xul            (content/sync/customize.xul)
         content/browser/sync/customize.js             (content/sync/customize.js)
         content/browser/sync/customize.css            (content/sync/customize.css)
 #endif
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
-*       content/browser/sanitize.js                   (content/sanitize.js)
+        content/browser/sanitize.js                   (content/sanitize.js)
 *       content/browser/sanitize.xul                  (content/sanitize.xul)
 *       content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
         content/browser/contentSearchUI.js            (content/contentSearchUI.js)
         content/browser/contentSearchUI.css           (content/contentSearchUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
         content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/components/loop/standalone/content/css/webapp.css
@@ -249,18 +249,17 @@ html[dir="rtl"] .rooms-footer .footer-lo
   background-image: url("../img/gum-opera.svg");
 }
 
 /* Room Info area layout */
 
 .room-inner-info-area {
   color: #fff;
   margin: auto;
-  /* 290px is the width of the widest info child, i.e., a tile */
-  width: 290px;
+  padding: 0 5px;
 }
 
 .room-inner-info-area > button {
   border-radius: 3px;
   font-size: 1.2em;
   padding: .2em 1.2em;
   cursor: pointer;
 }
@@ -295,8 +294,19 @@ html[dir="rtl"] .rooms-footer .footer-lo
 .room-inner-info-area > .room-waiting-tile {
   border: 0;
   border-radius: 5px;
   /* These sizes are the size of the tile image and title */
   height: 204px;
   /* Override the default iframe 300px width with the inherited width */
   width: 100%;
 }
+
+/* Terms of Service */
+
+.room-inner-info-area > .terms-service {
+  margin-top: 20px;
+}
+
+.room-inner-info-area > .terms-service,
+.room-inner-info-area > .terms-service > a {
+  color: #999;
+}
\ No newline at end of file
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -113,18 +113,17 @@ loop.standaloneRoomViews = (function(moz
               React.createElement("p", {className: "roomName"},  this.state.roomName), 
               React.createElement("p", {className: "loop-logo"}), 
               
                 this.state.failureReason ?
                   this._renderFailureText() :
                   this._renderJoinButton()
               
             ), 
-            React.createElement(ToSView, {
-              dispatcher: this.props.dispatcher}), 
+            React.createElement(ToSView, {dispatcher: this.props.dispatcher}), 
             React.createElement("p", {className: "mozilla-logo"})
           )
         )
       );
     }
   });
 
   /**
@@ -304,17 +303,18 @@ loop.standaloneRoomViews = (function(moz
       switch(this.props.roomState) {
         case ROOM_STATES.ENDED:
         case ROOM_STATES.READY: {
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("button", {className: "btn btn-join btn-info", 
                       onClick: this.props.joinRoom}, 
                 mozL10n.get("rooms_room_join_label")
-              )
+              ), 
+              React.createElement(ToSView, {dispatcher: this.props.dispatcher})
             )
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
                                 {clientShortname: mozL10n.get("clientShortname2")});
           var utils = loop.shared.utils;
           var isChrome = utils.isChrome(navigator.userAgent);
@@ -417,19 +417,17 @@ loop.standaloneRoomViews = (function(moz
   var StandaloneRoomFooter = React.createClass({displayName: "StandaloneRoomFooter",
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
     },
 
     render: function() {
       return (
         React.createElement("footer", {className: "rooms-footer"}, 
-          React.createElement("div", {className: "footer-logo"}), 
-          React.createElement(ToSView, {
-            dispatcher: this.props.dispatcher})
+          React.createElement("div", {className: "footer-logo"})
         )
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
     mixins: [
       Backbone.Events,
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -113,18 +113,17 @@ loop.standaloneRoomViews = (function(moz
               <p className="roomName">{ this.state.roomName }</p>
               <p className="loop-logo" />
               {
                 this.state.failureReason ?
                   this._renderFailureText() :
                   this._renderJoinButton()
               }
             </div>
-            <ToSView
-              dispatcher={this.props.dispatcher} />
+            <ToSView dispatcher={this.props.dispatcher} />
             <p className="mozilla-logo" />
           </div>
         </div>
       );
     }
   });
 
   /**
@@ -305,16 +304,17 @@ loop.standaloneRoomViews = (function(moz
         case ROOM_STATES.ENDED:
         case ROOM_STATES.READY: {
           return (
             <div className="room-inner-info-area">
               <button className="btn btn-join btn-info"
                       onClick={this.props.joinRoom}>
                 {mozL10n.get("rooms_room_join_label")}
               </button>
+              <ToSView dispatcher={this.props.dispatcher} />
             </div>
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
                                 {clientShortname: mozL10n.get("clientShortname2")});
           var utils = loop.shared.utils;
           var isChrome = utils.isChrome(navigator.userAgent);
@@ -418,18 +418,16 @@ loop.standaloneRoomViews = (function(moz
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
     },
 
     render: function() {
       return (
         <footer className="rooms-footer">
           <div className="footer-logo" />
-          <ToSView
-            dispatcher={this.props.dispatcher} />
         </footer>
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({
     mixins: [
       Backbone.Events,
--- a/browser/components/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/components/loop/standalone/content/l10n/en-US/loop.properties
@@ -25,17 +25,17 @@ unsupported_platform_heading=Sorry!
 unsupported_platform_message={{platform}} does not currently support {{clientShortname}}
 unsupported_platform_ios=iOS
 unsupported_platform_windows_phone=Windows Phone
 unsupported_platform_blackberry=Blackberry
 unsupported_platform_learn_more_link=Learn more about why your platform doesn't support {{clientShortname}}
 promote_firefox_hello_heading=Download {{brandShortname}} to make free audio and video calls!
 get_firefox_button=Get {{brandShortname}}
 initiate_call_cancel_button=Cancel
-legal_text_and_links=By using {{clientShortname}} you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
+legal_text_and_links=By using {{clientShortname}} you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}.
 terms_of_use_link_text=Terms of use
 privacy_notice_link_text=Privacy notice
 self_view_hidden_message=Self-view hidden but still being sent; resize window \
   to show
 
 ## LOCALIZATION NOTE(brandShortname): This should not be localized and
 ## should remain "Firefox" for all locales.
 brandShortname=Firefox
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -138,22 +138,16 @@ CtypesKernelHelpers.prototype = {
                                systemTime.wSecond,
                                systemTime.wMilliseconds) / 1000);
   }
 };
 
 function CtypesVaultHelpers() {
   this._structs = {};
   this._functions = {};
-  // the size of the vault handle in 32 bits version is 32 and 64 in 64 bits version
-  if (wintypes.VOIDP.size == 4) {
-    this._vaultHandleType = wintypes.DWORD;
-  } else {
-    this._vaultHandleType = wintypes.DWORDLONG;
-  }
 
   this._structs.GUID = new ctypes.StructType("GUID", [
     {id: wintypes.DWORD.array(4)},
   ]);
 
   this._structs.VAULT_ITEM_ELEMENT = new ctypes.StructType("VAULT_ITEM_ELEMENT", [
     // not documented
     {schemaElementId: wintypes.DWORD},
@@ -200,41 +194,41 @@ function CtypesVaultHelpers() {
       this._vaultcliLib.declare("VaultOpenVault",
                                 ctypes.winapi_abi,
                                 wintypes.DWORD,
                                 // GUID
                                 this._structs.GUID.ptr,
                                 // Flags
                                 wintypes.DWORD,
                                 // Vault Handle
-                                this._vaultHandleType.ptr);
+                                wintypes.VOIDP.ptr);
     this._functions.VaultEnumerateItems =
       this._vaultcliLib.declare("VaultEnumerateItems",
                                 ctypes.winapi_abi,
                                 wintypes.DWORD,
                                 // Vault Handle
-                                this._vaultHandleType,
+                                wintypes.VOIDP,
                                 // Flags
                                 wintypes.DWORD,
                                 // Items Count
                                 wintypes.PDWORD,
                                 // Items
                                 ctypes.voidptr_t);
     this._functions.VaultCloseVault =
       this._vaultcliLib.declare("VaultCloseVault",
                                 ctypes.winapi_abi,
                                 wintypes.DWORD,
                                 // Vault Handle
-                                this._vaultHandleType);
+                                wintypes.VOIDP);
     this._functions.VaultGetItem =
       this._vaultcliLib.declare("VaultGetItem",
                                 ctypes.winapi_abi,
                                 wintypes.DWORD,
                                 // Vault Handle
-                                this._vaultHandleType,
+                                wintypes.VOIDP,
                                 // Schema Id
                                 this._structs.GUID.ptr,
                                 // Resource
                                 this._structs.VAULT_ITEM_ELEMENT.ptr,
                                 // Identity
                                 this._structs.VAULT_ITEM_ELEMENT.ptr,
                                 // Package Sid
                                 this._structs.VAULT_ITEM_ELEMENT.ptr,
@@ -823,17 +817,17 @@ WindowsVaultFormPasswords.prototype = {
     try {
 
       // web credentials vault id
       let vaultGuid = new ctypesVaultHelpers._structs.GUID(WEB_CREDENTIALS_VAULT_ID);
       // number of available vaults
       let vaultCount = new wintypes.DWORD;
       error = new wintypes.DWORD;
       // web credentials vault
-      vault = new ctypesVaultHelpers._vaultHandleType;
+      vault = new wintypes.VOIDP;
       // open the current vault using the vaultGuid
       error = ctypesVaultHelpers._functions.VaultOpenVault(vaultGuid.address(), 0, vault.address());
       if (error != RESULT_SUCCESS) {
         throw new Error("Unable to open Vault: " + error);
       }
       successfulVaultOpen = true;
 
       let item = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -511,16 +511,19 @@ BrowserGlue.prototype = {
         this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput));
         break;
       case "tablet-mode-change":
         if (data == "tablet-mode") {
           Services.telemetry.getHistogramById("FX_TABLET_MODE_USED_DURING_SESSION")
                             .add(1);
         }
         break;
+      case "test-initialize-sanitizer":
+        this._sanitizer.onStartup();
+        break;
     }
   },
 
   _handleURLBarTelemetry(input) {
     if (!input ||
         input.id != "urlbar" ||
         input.inPrivateContext ||
         input.popup.selectedIndex < 0) {
@@ -1795,17 +1798,16 @@ BrowserGlue.prototype = {
   },
 
   /**
    * Places shut-down tasks
    * - finalize components depending on Places.
    * - export bookmarks as HTML, if so configured.
    */
   _onPlacesShutdown: function BG__onPlacesShutdown() {
-    this._sanitizer.onShutdown();
     PageThumbs.uninit();
 
     if (this._bookmarksBackupIdleTime) {
       this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
       delete this._bookmarksBackupIdleTime;
     }
   },
 
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -44,16 +44,18 @@ function run_test() {
 
 add_task(function* test_execute() {
   do_print("Initialize browserglue before Places");
 
   // Avoid default bookmarks import.
   let glue = Cc["@mozilla.org/browser/browserglue;1"].
              getService(Ci.nsIObserver);
   glue.observe(null, "initial-migration-will-import-default-bookmarks", null);
+  glue.observe(null, "test-initialize-sanitizer", null);
+
 
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.offlineApps", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.history", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.downloads", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.formData", true);
@@ -66,45 +68,37 @@ add_task(function* test_execute() {
   for (let aUrl of URIS) {
     yield PlacesTestUtils.addVisits({
       uri: uri(aUrl), visitDate: timeInMicroseconds++,
       transition: PlacesUtils.history.TRANSITION_TYPED
     });
   }
   do_print("Add cache.");
   yield storeCache(FTP_URL, "testData");
-});
 
-add_task(function* run_test_continue() {
   do_print("Simulate and wait shutdown.");
   yield shutdownPlaces();
 
-  let stmt = DBConn().createStatement(
+  let stmt = DBConn(true).createStatement(
     "SELECT id FROM moz_places WHERE url = :page_url "
   );
 
   try {
     URIS.forEach(function(aUrl) {
       stmt.params.page_url = aUrl;
       do_check_false(stmt.executeStep());
       stmt.reset();
     });
   } finally {
     stmt.finalize();
   }
 
   do_print("Check cache");
   // Check cache.
-  let promiseCacheChecked = checkCache(FTP_URL);
-
-  do_print("Shutdown the download manager");
-  // Shutdown the download manager.
-  Services.obs.notifyObservers(null, "quit-application", null);
-
-  yield promiseCacheChecked;
+  yield checkCache(FTP_URL);
 });
 
 function storeCache(aURL, aContent) {
   let cache = Services.cache2;
   let storage = cache.diskCacheStorage(LoadContextInfo.default, false);
 
   return new Promise(resolve => {
     let storeCacheListener = {
new file mode 100644
--- /dev/null
+++ b/browser/modules/Sanitizer.jsm
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+//
+// A shared module for sanitize.js
+//
+// Until bug 1167238 lands, this serves only as a way to ensure that
+// sanitize is loaded from its own compartment, rather than from that
+// of the sanitize dialog.
+//
+
+this.EXPORTED_SYMBOLS = ["Sanitizer"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let scope = {};
+Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
+ .loadSubScript("chrome://browser/content/sanitize.js", scope);
+
+this.Sanitizer = scope.Sanitizer;
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -33,16 +33,17 @@ EXTRA_JS_MODULES += [
     'NewTabURL.jsm',
     'offlineAppCache.jsm',
     'PanelFrame.jsm',
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
+    'Sanitizer.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'TransientPrefs.jsm',
     'WebappManager.jsm',
     'webrtcUI.jsm',
 ]
 
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -13,18 +13,18 @@
     "dump": true,
     "EventEmitter": true,
     "exports": true,
     "loader": true,
     "module": true,
     "require": true,
     "Services": true,
     "Task": true,
+    "XPCNativeWrapper": true,
     "XPCOMUtils": true,
-    "XPCNativeWrapper": true,
   },
   "rules": {
     // These are the rules that have been configured so far to match the
     // devtools coding style.
 
     // Disallow using variables outside the blocks they are defined (especially
     // since only let and const are used, see "no-var").
     "block-scoped-var": 2,
--- a/devtools/client/shared/test/browser_outputparser.js
+++ b/devtools/client/shared/test/browser_outputparser.js
@@ -16,16 +16,17 @@ add_task(function*() {
 
 function* performTest() {
   let [host, , doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
   let parser = new OutputParser(doc);
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
+  testParseURL(doc, parser);
 
   host.destroy();
 }
 
 // Class name used in color swatch.
 var COLOR_TEST_CLASS = "test-class";
 
 // Create a new CSS color-parsing test.  |name| is the name of the CSS
@@ -93,17 +94,17 @@ function testParseCssProperty(doc, parse
     makeColorTest("hellothere", "'red'", ["'red'"]),
 
     makeColorTest("filter",
                   "blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)",
                   ["<span data-filters=\"blur(1px) drop-shadow(0 0 0 blue) ",
                    "url(red.svg#blue)\"><span>",
                    "blur(1px) drop-shadow(0 0 0 ",
                    {name: "blue", value: "#00F"},
-                   ") url(\"red.svg#blue\")</span></span>"]),
+                   ") url(red.svg#blue)</span></span>"]),
 
     makeColorTest("color", "currentColor", ["currentColor"]),
   ];
 
   let target = doc.querySelector("div");
   ok(target, "captain, we have the div");
 
   for (let test of tests) {
@@ -131,8 +132,79 @@ function testParseCssVar(doc, parser) {
   ok(target, "captain, we have the div");
   target.appendChild(frag);
 
   is(target.innerHTML, "var(--some-kind-of-green)",
      "CSS property correctly parsed");
 
   target.innerHTML = "";
 }
+
+function testParseURL(doc, parser) {
+  info("Test that URL parsing preserves quoting style");
+
+  const tests = [
+    {
+      desc: "simple test without quotes",
+      leader: "url(",
+      trailer: ")",
+    },
+    {
+      desc: "simple test with single quotes",
+      leader: "url('",
+      trailer: "')",
+    },
+    {
+      desc: "simple test with double quotes",
+      leader: "url(\"",
+      trailer: "\")",
+    },
+    {
+      desc: "test with single quotes and whitespace",
+      leader: "url( \t'",
+      trailer: "'\r\n\f)",
+    },
+    {
+      desc: "simple test with uppercase",
+      leader: "URL(",
+      trailer: ")",
+    },
+    {
+      desc: "bad url, missing paren",
+      leader: "url(",
+      trailer: "",
+      expectedTrailer: ")"
+    },
+    {
+      desc: "bad url, double quote, missing paren",
+      leader: "url(\"",
+      trailer: "\"",
+      expectedTrailer: "\")",
+    },
+    {
+      desc: "bad url, single quote, missing paren and quote",
+      leader: "url('",
+      trailer: "",
+      expectedTrailer: "')"
+    }
+  ];
+
+  for (let test of tests) {
+    let url = test.leader + "something.jpg" + test.trailer;
+    let frag = parser.parseCssProperty("background", url, {
+      urlClass: "test-urlclass"
+    });
+
+    let target = doc.querySelector("div");
+    target.appendChild(frag);
+
+    let expectedTrailer = test.expectedTrailer || test.trailer;
+
+    let expected = test.leader +
+        "<a href=\"something.jpg\" class=\"test-urlclass\" " +
+        "target=\"_blank\">something.jpg</a>" +
+        expectedTrailer;
+
+    is(target.innerHTML, expected, test.desc);
+
+    target.innerHTML = "";
+  }
+}
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -89,16 +89,17 @@ loader.lazyGetter(this, "ppmm", () => {
  * actually produce any actors until they are reached in the course of
  * iteration: alliterative lazy live lists.
  */
 function RootActor(aConnection, aParameters) {
   this.conn = aConnection;
   this._parameters = aParameters;
   this._onTabListChanged = this.onTabListChanged.bind(this);
   this._onAddonListChanged = this.onAddonListChanged.bind(this);
+  this._onWorkerListChanged = this.onWorkerListChanged.bind(this);
   this._extraActors = {};
 
   this._globalActorPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._globalActorPool);
 
   this._chromeActor = null;
 }
 
@@ -343,16 +344,47 @@ RootActor.prototype = {
     });
   },
 
   onAddonListChanged: function () {
     this.conn.send({ from: this.actorID, type: "addonListChanged" });
     this._parameters.addonList.onListChanged = null;
   },
 
+  onListWorkers: function () {
+    let workerList = this._parameters.workerList;
+    if (!workerList) {
+      return { from: this.actorID, error: "noWorkers",
+               message: "This root actor has no workers." };
+    }
+
+    return workerList.getList().then(actors => {
+      let pool = new ActorPool(this.conn);
+      for (let actor of actors) {
+        pool.addActor(actor);
+      }
+
+      this.conn.removeActorPool(this._workerActorPool);
+      this._workerActorPool = pool;
+      this.conn.addActorPool(this._workerActorPool);
+
+      workerList.onListChanged = this._onWorkerListChanged;
+
+      return {
+        "from": this.actorID,
+        "workers": actors.map(actor => actor.form())
+      };
+    });
+  },
+
+  onWorkerListChanged: function () {
+    this.conn.send({ from: this.actorID, type: "workerListChanged" });
+    this._parameters.workerList.onListChanged = null;
+  },
+
   onListProcesses: function () {
     let processes = [];
     for (let i = 0; i < ppmm.childCount; i++) {
       processes.push({
         id: i, // XXX: may not be a perfect id, but process message manager doesn't expose anything...
         parent: i == 0, // XXX Weak, but appear to be stable
         tabCount: undefined, // TODO: exposes process message manager on frameloaders in order to compute this
       });
@@ -429,15 +461,16 @@ RootActor.prototype = {
     }
    }
 };
 
 RootActor.prototype.requestTypes = {
   "listTabs": RootActor.prototype.onListTabs,
   "getTab": RootActor.prototype.onGetTab,
   "listAddons": RootActor.prototype.onListAddons,
+  "listWorkers": RootActor.prototype.onListWorkers,
   "listProcesses": RootActor.prototype.onListProcesses,
   "getProcess": RootActor.prototype.onGetProcess,
   "echo": RootActor.prototype.onEcho,
   "protocolDescription": RootActor.prototype.onProtocolDescription
 };
 
 exports.RootActor = RootActor;
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -588,17 +588,17 @@ var StyleSheetActor = protocol.ActorClas
   /**
    * Fetch the text for this stylesheet from the cache or network. Return
    * cached text if it's already been fetched.
    *
    * @return {Promise}
    *         Promise that resolves with a string text of the stylesheet.
    */
   _getText: function() {
-    if (this.text) {
+    if (typeof this.text === "string") {
       return promise.resolve(this.text);
     }
 
     if (!this.href) {
       // this is an inline <style> sheet
       let content = this.ownerNode.textContent;
       this.text = content;
       return promise.resolve(content);
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -10,24 +10,24 @@ var { Ci, Cu } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
 var { ActorPool, createExtraActors, appendExtraActors } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dbg_assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
-var { WorkerActorList } = require("devtools/server/actors/worker");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
+loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker", true);
 loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 // Assumptions on events module:
 // events needs to be dispatched synchronously,
 // by calling the listeners in the order or registration.
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
@@ -124,16 +124,17 @@ exports.sendShutdownEvent = sendShutdown
  *        The conection to the client.
  */
 function createRootActor(aConnection)
 {
   return new RootActor(aConnection,
                        {
                          tabList: new BrowserTabList(aConnection),
                          addonList: new BrowserAddonList(aConnection),
+                         workerList: new WorkerActorList({}),
                          globalActorFactories: DebuggerServer.globalActorFactories,
                          onShutdown: sendShutdownEvent
                        });
 }
 
 /**
  * A live list of BrowserTabActors representing the current browser tabs,
  * to be provided to the root actor to answer 'listTabs' requests.
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -37,17 +37,18 @@ function WorkerActor(dbg) {
 }
 
 WorkerActor.prototype = {
   actorPrefix: "worker",
 
   form: function () {
     return {
       actor: this.actorID,
-      url: this._dbg.url
+      url: this._dbg.url,
+      type: this._dbg.type
     };
   },
 
   onAttach: function () {
     if (this._dbg.isClosed) {
       return { error: "closed" };
     }
 
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -76,16 +76,17 @@ skip-if = e10s # Bug 1183605 - devtools/
 [browser_perf-samples-01.js]
 [browser_perf-samples-02.js]
 #[browser_perf-front-profiler-01.js] bug 1077464
 #[browser_perf-front-profiler-05.js] bug 1077464
 #[browser_perf-front-profiler-06.js]
 [browser_storage_dynamic_windows.js]
 [browser_storage_listings.js]
 [browser_storage_updates.js]
+[browser_stylesheets_getTextEmpty.js]
 [browser_stylesheets_nested-iframes.js]
 [browser_timeline.js]
 skip-if = buildapp == 'mulet'
 [browser_timeline_actors.js]
 skip-if = buildapp == 'mulet'
 [browser_timeline_iframes.js]
 skip-if = buildapp == 'mulet'
 [browser_directorscript_actors_exports.js]
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
@@ -0,0 +1,42 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that StyleSheetActor.getText handles empty text correctly.
+
+const {StyleSheetsFront} = require("devtools/server/actors/stylesheets");
+
+const CONTENT = "<style>body { background-color: #f0c; }</style>";
+const TEST_URI = "data:text/html;charset=utf-8," + encodeURIComponent(CONTENT);
+
+add_task(function*() {
+  yield addTab(TEST_URI);
+
+  info("Initialising the debugger server and client.");
+  initDebuggerServer();
+  let client = new DebuggerClient(DebuggerServer.connectPipe());
+  let form = yield connectDebuggerClient(client);
+
+  info("Attaching to the active tab.");
+  yield new Promise(resolve => {
+    client.attachTab(form.actor, resolve);
+  });
+
+  let front = StyleSheetsFront(client, form);
+  ok(front, "The StyleSheetsFront was created.");
+
+  let sheets = yield front.getStyleSheets();
+  ok(sheets, "getStyleSheets() succeeded");
+  is(sheets.length, 1,
+     "getStyleSheets() returned the correct number of sheets");
+
+  let sheet = sheets[0];
+  yield sheet.update("", false);
+  let longStr = yield sheet.getText();
+  let source = yield longStr.string();
+  is(source, "", "text is empty");
+
+  yield closeDebuggerClient(client);
+});
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -154,16 +154,17 @@ const UnsolicitedNotifications = {
   "networkEvent": "networkEvent",
   "networkEventUpdate": "networkEventUpdate",
   "newGlobal": "newGlobal",
   "newScript": "newScript",
   "tabDetached": "tabDetached",
   "tabListChanged": "tabListChanged",
   "reflowActivity": "reflowActivity",
   "addonListChanged": "addonListChanged",
+  "workerListChanged": "workerListChanged",
   "tabNavigated": "tabNavigated",
   "frameUpdate": "frameUpdate",
   "pageError": "pageError",
   "documentLoad": "documentLoad",
   "enteredFrame": "enteredFrame",
   "exitedFrame": "exitedFrame",
   "appOpen": "appOpen",
   "appClose": "appClose",
@@ -1473,23 +1474,32 @@ RootClient.prototype = {
    *
    * @param function aOnResponse
    *        Called with the response packet.
    */
   listAddons: DebuggerClient.requester({ type: "listAddons" },
                                        { telemetry: "LISTADDONS" }),
 
   /**
+   * List the registered workers.
+   *
+   * @param function aOnResponse
+   *        Called with the response packet.
+   */
+  listWorkers: DebuggerClient.requester({ type: "listWorkers" },
+                                        { telemetry: "LISTWORKERS" }),
+
+  /**
    * List the running processes.
    *
    * @param function aOnResponse
    *        Called with the response packet.
    */
   listProcesses: DebuggerClient.requester({ type: "listProcesses" },
-                                       { telemetry: "LISTPROCESSES" }),
+                                          { telemetry: "LISTPROCESSES" }),
 
   /**
    * Fetch the TabActor for the currently selected tab, or for a specific
    * tab given as first parameter.
    *
    * @param [optional] object aFilter
    *        A dictionary object with following optional attributes:
    *         - outerWindowID: used to match tabs in parent process
--- a/devtools/shared/output-parser.js
+++ b/devtools/shared/output-parser.js
@@ -393,45 +393,72 @@ OutputParser.prototype = {
 
     let swatch = event.target;
     let color = this.colorSwatches.get(swatch);
     let val = color.nextColorUnit();
 
     swatch.nextElementSibling.textContent = val;
   },
 
-   /**
-    * Append a URL to the output.
-    *
-    * @param  {String} match
-    *         Complete match that may include "url(xxx)"
-    * @param  {String} url
-    *         Actual URL
-    * @param  {Object} [options]
-    *         Options object. For valid options and default values see
-    *         _mergeOptions().
-    */
-  _appendURL: function(match, url, options={}) {
+  /**
+   * A helper function that sanitizes a possibly-unterminated URL.
+   */
+  _sanitizeURL: function(url) {
+    // Re-lex the URL and add any needed termination characters.
+    let urlTokenizer = DOMUtils.getCSSLexer(url);
+    // Just read until EOF; there will only be a single token.
+    while (urlTokenizer.nextToken()) {
+      // Nothing.
+    }
+
+    return urlTokenizer.performEOFFixup(url, true);
+  },
+
+  /**
+   * Append a URL to the output.
+   *
+   * @param  {String} match
+   *         Complete match that may include "url(xxx)"
+   * @param  {String} url
+   *         Actual URL
+   * @param  {Object} [options]
+   *         Options object. For valid options and default values see
+   *         _mergeOptions().
+   */
+  _appendURL: function(match, url, options) {
     if (options.urlClass) {
-      this._appendTextNode("url(\"");
+      // Sanitize the URL.  Note that if we modify the URL, we just
+      // leave the termination characters.  This isn't strictly
+      // "as-authored", but it makes a bit more sense.
+      match = this._sanitizeURL(match);
+      // This regexp matches a URL token.  It puts the "url(", any
+      // leading whitespace, and any opening quote into |leader|; the
+      // URL text itself into |body|, and any trailing quote, trailing
+      // whitespace, and the ")" into |trailer|.  We considered adding
+      // functionality for this to CSSLexer, in some way, but this
+      // seemed simpler on the whole.
+      let [, leader, , body, trailer] =
+        /^(url\([ \t\r\n\f]*(["']?))(.*?)(\2[ \t\r\n\f]*\))$/i.exec(match);
+
+      this._appendTextNode(leader);
 
       let href = url;
       if (options.baseURI) {
         href = options.baseURI.resolve(url);
       }
 
       this._appendNode("a", {
         target: "_blank",
         class: options.urlClass,
         href: href
-      }, url);
+      }, body);
 
-      this._appendTextNode("\")");
+      this._appendTextNode(trailer);
     } else {
-      this._appendTextNode("url(\"" + url + "\")");
+      this._appendTextNode(match);
     }
   },
 
   /**
    * Create a node.
    *
    * @param  {String} tagName
    *         Tag type e.g. "div"
--- a/devtools/shared/shared/tests/unit/.eslintrc
+++ b/devtools/shared/shared/tests/unit/.eslintrc
@@ -1,4 +1,4 @@
 {
   // Extend from the common devtools xpcshell eslintrc config.
-  "extends": "../../../../../devtools/client/.eslintrc.xpcshell"
+  "extends": "../../../../../devtools/.eslintrc.xpcshell"
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11586,17 +11586,22 @@ nsDocShell::AddState(JS::Handle<JS::Valu
   //
   // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
   // nullptr for aRequest param to FireOnLocationChange(...). Such an update
   // notification is allowed only when we know docshell is not loading a new
   // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
   // FireOnLocationChange(...) breaks security UI.
   if (!equalURIs) {
     document->SetDocumentURI(newURI);
-    SetCurrentURI(newURI, nullptr, true, LOCATION_CHANGE_SAME_DOCUMENT);
+    // We can't trust SetCurrentURI to do always fire locationchange events
+    // when we expect it to, so we hack around that by doing it ourselves...
+    SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
+    if (mLoadType != LOAD_ERROR_PAGE) {
+      FireDummyOnLocationChange();
+    }
 
     AddURIVisit(newURI, oldURI, oldURI, 0);
 
     // AddURIVisit doesn't set the title for the new URI in global history,
     // so do that here.
     if (mUseGlobalHistory && !mInPrivateBrowsing) {
       nsCOMPtr<IHistory> history = services::GetHistoryService();
       if (history) {
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -28,27 +28,29 @@ support-files =
   file_bug234628-9.html
   file_bug420605.html
   file_bug503832.html
   file_bug655270.html
   file_bug670318.html
   file_bug852909.pdf
   file_bug852909.png
   file_bug1046022.html
+  file_bug1206879.html
   file_multiple_pushState.html
   print_postdata.sjs
   test-form_sjis.html
   timelineMarkers-04.html
   browser_timelineMarkers-frame-02.js
   browser_timelineMarkers-frame-03.js
   browser_timelineMarkers-frame-04.js
   browser_timelineMarkers-frame-05.js
   head.js
   frame-head.js
 
+[browser_bug1206879.js]
 [browser_bug134911.js]
 skip-if = e10s # Bug ?????? - BrowserSetForcedCharacterSet() in browser.js references docShell
 [browser_bug234628-1.js]
 skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-10.js]
 skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-11.js]
 skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_bug1206879.js
@@ -0,0 +1,31 @@
+add_task(function*() {
+  let url = getRootDirectory(gTestPath) + "file_bug1206879.html";
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+
+  let numLocationChanges = 0;
+
+  let listener = {
+    onLocationChange: function(browser, wp, request, uri, flags) {
+      if (browser != tab.linkedBrowser) {
+        return;
+      }
+      info("onLocationChange: " + uri.spec);
+      numLocationChanges++;
+      this.resolve();
+    }
+  };
+  let locationPromise = new Promise((resolve, reject) => {
+    listener.resolve = resolve;
+    gBrowser.addTabsProgressListener(listener);
+  });
+  yield ContentTask.spawn(tab.linkedBrowser, {}, function() {
+    content.frames[0].history.pushState(null, null, "foo");
+  });
+
+  yield locationPromise;
+
+  gBrowser.removeTab(tab);
+  gBrowser.removeTabsProgressListener(listener);
+  is(numLocationChanges, 1,
+     "pushState with a different URI should cause a LocationChange event.");
+});
--- a/docshell/test/browser/browser_bug554155.js
+++ b/docshell/test/browser/browser_bug554155.js
@@ -4,17 +4,18 @@ function test() {
   let tab = gBrowser.addTab("http://example.com");
 
   tab.linkedBrowser.addEventListener("load", function() {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
     let numLocationChanges = 0;
 
     let listener = {
-      onLocationChange: function() {
+      onLocationChange: function(browser, webProgress, request, uri, flags) {
+        info("location change: " + (uri && uri.spec));
         numLocationChanges++;
       }
     };
 
     gBrowser.addTabsProgressListener(listener);
 
     // pushState to a new URL (http://example.com/foo").  This should trigger
     // exactly one LocationChange event.
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/file_bug1206879.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test page for bug 1206879</title>
+  </head>
+  <body>
+    <iframe src="http://example.com/"></iframe>
+  </body>
+</html>
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -356,17 +356,23 @@ BluetoothA2dpManager::Connect(const nsAS
   mController = aController;
 
   if (!sBtA2dpInterface) {
     BT_LOGR("sBluetoothA2dpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
-  sBtA2dpInterface->Connect(aDeviceAddress, new ConnectResultHandler());
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(aDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  sBtA2dpInterface->Connect(deviceAddress, new ConnectResultHandler());
 }
 
 void
 BluetoothA2dpManager::OnDisconnectError()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(mController);
 
@@ -417,17 +423,23 @@ BluetoothA2dpManager::Disconnect(Bluetoo
     BT_LOGR("sBluetoothA2dpInterface is null");
     if (aController) {
       aController->NotifyCompletion(
         NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     }
     return;
   }
 
-  sBtA2dpInterface->Disconnect(mDeviceAddress, new DisconnectResultHandler());
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  sBtA2dpInterface->Disconnect(deviceAddress, new DisconnectResultHandler());
 }
 
 void
 BluetoothA2dpManager::OnConnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   /**
@@ -563,18 +575,24 @@ BluetoothA2dpManager::HandleSinkProperty
 /*
  * Reset connection state to DISCONNECTED to handle backend error. The state
  * change triggers UI status bar update as ordinary bluetooth turn-off sequence.
  */
 void
 BluetoothA2dpManager::HandleBackendError()
 {
   if (mSinkState != SinkState::SINK_DISCONNECTED) {
+    BluetoothAddress deviceAddress;
+    nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+    if (NS_FAILED(rv)) {
+      return;
+    }
+
     ConnectionStateNotification(A2DP_CONNECTION_STATE_DISCONNECTED,
-      mDeviceAddress);
+      deviceAddress);
   }
 }
 
 void
 BluetoothA2dpManager::NotifyConnectionStatusChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -618,33 +636,36 @@ BluetoothA2dpManager::IsConnected()
 }
 
 /*
  * Notifications
  */
 
 void
 BluetoothA2dpManager::ConnectionStateNotification(
-  BluetoothA2dpConnectionState aState, const nsAString& aBdAddr)
+  BluetoothA2dpConnectionState aState, const BluetoothAddress& aBdAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString a2dpState;
   AvStatusToSinkString(aState, a2dpState);
 
   InfallibleTArray<BluetoothNamedValue> props;
   AppendNamedValue(props, "State", a2dpState);
 
+  nsAutoString addressStr;
+  AddressToString(aBdAddr, addressStr);
+
   HandleSinkPropertyChanged(BluetoothSignal(NS_LITERAL_STRING("AudioSink"),
-                                            nsString(aBdAddr), props));
+                                            addressStr, props));
 }
 
 void
 BluetoothA2dpManager::AudioStateNotification(BluetoothA2dpAudioState aState,
-                                             const nsAString& aBdAddr)
+                                             const BluetoothAddress& aBdAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString a2dpState;
 
   if (aState == A2DP_AUDIO_STATE_STARTED) {
     a2dpState = NS_LITERAL_STRING("playing");
   } else if (aState == A2DP_AUDIO_STATE_STOPPED) {
@@ -653,13 +674,16 @@ BluetoothA2dpManager::AudioStateNotifica
   } else if (aState == A2DP_AUDIO_STATE_REMOTE_SUSPEND) {
     // for avdtp state suspend stream from remote side
     a2dpState = NS_LITERAL_STRING("connected");
   }
 
   InfallibleTArray<BluetoothNamedValue> props;
   AppendNamedValue(props, "State", a2dpState);
 
+  nsAutoString addressStr;
+  AddressToString(aBdAddr, addressStr);
+
   HandleSinkPropertyChanged(BluetoothSignal(NS_LITERAL_STRING("AudioSink"),
-                                            nsString(aBdAddr), props));
+                                            addressStr, props));
 }
 
 NS_IMPL_ISUPPORTS(BluetoothA2dpManager, nsIObserver)
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
@@ -55,19 +55,19 @@ private:
   class OnErrorProfileResultHandlerRunnable;
 
   BluetoothA2dpManager();
 
   void HandleShutdown();
   void NotifyConnectionStatusChanged();
 
   void ConnectionStateNotification(BluetoothA2dpConnectionState aState,
-                                   const nsAString& aBdAddr) override;
+                                   const BluetoothAddress& aBdAddr) override;
   void AudioStateNotification(BluetoothA2dpAudioState aState,
-                              const nsAString& aBdAddr) override;
+                              const BluetoothAddress& aBdAddr) override;
 
   nsString mDeviceAddress;
   nsRefPtr<BluetoothProfileController> mController;
 
   // A2DP data member
   bool mA2dpConnected;
   SinkState mSinkState;
 };
--- a/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
@@ -792,29 +792,29 @@ BluetoothAvrcpManager::RegisterNotificat
 
 /* This method is used to get CT features from the Feature Bit Mask. If
  * Advanced Control Player bit is set, the CT supports volume sync (absolute
  * volume feature). If Browsing bit is set, AVRCP 1.4 Browse feature will be
  * supported.
  */
 void
 BluetoothAvrcpManager::RemoteFeatureNotification(
-    const nsAString& aBdAddr, unsigned long aFeatures)
+  const BluetoothAddress& aBdAddr, unsigned long aFeatures)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // TODO: Support AVRCP 1.4 absolute volume/browse
 }
 
 /* This method is used to get notifications about volume changes on the
  * remote car kit (if it supports AVRCP 1.4), not notification from phone.
  */
 void
 BluetoothAvrcpManager::VolumeChangeNotification(uint8_t aVolume,
-                                               uint8_t aCType)
+                                                uint8_t aCType)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // TODO: Support AVRCP 1.4 absolute volume/browse
 }
 
 void
 BluetoothAvrcpManager::PassthroughCmdNotification(int aId, int aKeyState)
--- a/dom/bluetooth/bluedroid/BluetoothAvrcpManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothAvrcpManager.h
@@ -96,17 +96,17 @@ private:
   void GetElementAttrNotification(
     uint8_t aNumAttrs,
     const BluetoothAvrcpMediaAttribute* aAttrs) override;
 
   void RegisterNotificationNotification(
     BluetoothAvrcpEvent aEvent, uint32_t aParam) override;
 
   void RemoteFeatureNotification(
-    const nsAString& aBdAddr, unsigned long aFeatures) override;
+    const BluetoothAddress& aBdAddr, unsigned long aFeatures) override;
 
   void VolumeChangeNotification(uint8_t aVolume, uint8_t aCType) override;
 
   void PassthroughCmdNotification(int aId, int aKeyState) override;
 
   nsString mDeviceAddress;
   nsRefPtr<BluetoothProfileController> mController;
 
--- a/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.cpp
@@ -47,47 +47,45 @@ BluetoothDaemonA2dpModule::HandleSvc(con
   (this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
 }
 
 // Commands
 //
 
 nsresult
 BluetoothDaemonA2dpModule::ConnectCmd(
-  const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(new DaemonSocketPDU(SERVICE_ID,
-                                                           OPCODE_CONNECT,
-                                                           6)); // Address
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+                                                     OPCODE_CONNECT,
+                                                     6)); // Address
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonA2dpModule::DisconnectCmd(
-  const nsAString& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothA2dpResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(new DaemonSocketPDU(SERVICE_ID,
-                                                           OPCODE_DISCONNECT,
-                                                           6)); // Address
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+                                                     OPCODE_DISCONNECT,
+                                                     6)); // Address
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
@@ -177,29 +175,29 @@ class BluetoothDaemonA2dpModule::Connect
   : private PDUInitOp
 {
 public:
   ConnectionStateInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothA2dpConnectionState& aArg1, nsString& aArg2) const
+  operator () (BluetoothA2dpConnectionState& aArg1,
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
 
@@ -218,29 +216,28 @@ class BluetoothDaemonA2dpModule::AudioSt
 {
 public:
   AudioStateInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (BluetoothA2dpAudioState& aArg1,
-               nsString& aArg2) const
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
 
@@ -258,23 +255,22 @@ class BluetoothDaemonA2dpModule::AudioCo
   : private PDUInitOp
 {
 public:
   AudioConfigInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1, uint32_t aArg2, uint8_t aArg3) const
+  operator () (BluetoothAddress& aArg1, uint32_t aArg2, uint8_t aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read address */
-    nsresult rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read sample rate */
     rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
@@ -441,29 +437,29 @@ BluetoothDaemonA2dpInterface::Cleanup(
     DispatchError(aRes, rv);
   }
 }
 
 /* Connect / Disconnect */
 
 void
 BluetoothDaemonA2dpInterface::Connect(
-  const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothA2dpResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonA2dpInterface::Disconnect(
-  const nsAString& aBdAddr, BluetoothA2dpResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothA2dpResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
--- a/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonA2dpInterface.h
@@ -44,19 +44,19 @@ public:
 
   void SetNotificationHandler(
     BluetoothA2dpNotificationHandler* aNotificationHandler);
 
   //
   // Commands
   //
 
-  nsresult ConnectCmd(const nsAString& aBdAddr,
+  nsresult ConnectCmd(const BluetoothAddress& aBdAddr,
                       BluetoothA2dpResultHandler* aRes);
-  nsresult DisconnectCmd(const nsAString& aBdAddr,
+  nsresult DisconnectCmd(const BluetoothAddress& aBdAddr,
                          BluetoothA2dpResultHandler* aRes);
 
 protected:
   void HandleSvc(const DaemonSocketPDUHeader& aHeader,
                  DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes);
 
   //
   // Responses
@@ -88,28 +88,31 @@ protected:
 
   //
   // Notifications
   //
 
   class NotificationHandlerWrapper;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothA2dpConnectionState, nsString,
-    BluetoothA2dpConnectionState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothA2dpConnectionState, BluetoothAddress,
+    BluetoothA2dpConnectionState, const BluetoothAddress&>
     ConnectionStateNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothA2dpAudioState, nsString,
-    BluetoothA2dpAudioState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothA2dpAudioState, BluetoothAddress,
+    BluetoothA2dpAudioState, const BluetoothAddress&>
     AudioStateNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
-    NotificationHandlerWrapper, void, nsString, uint32_t, uint8_t,
-    const nsAString&, uint32_t, uint8_t>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, uint32_t, uint8_t,
+    const BluetoothAddress&, uint32_t, uint8_t>
     AudioConfigNotification;
 
   class ConnectionStateInitOp;
   class AudioStateInitOp;
   class AudioConfigInitOp;
 
   void ConnectionStateNtf(const DaemonSocketPDUHeader& aHeader,
                           DaemonSocketPDU& aPDU);
@@ -139,19 +142,19 @@ public:
 
   void Init(
     BluetoothA2dpNotificationHandler* aNotificationHandler,
     BluetoothA2dpResultHandler* aRes) override;
   void Cleanup(BluetoothA2dpResultHandler* aRes) override;
 
   /* Connect / Disconnect */
 
-  void Connect(const nsAString& aBdAddr,
+  void Connect(const BluetoothAddress& aBdAddr,
                BluetoothA2dpResultHandler* aRes) override;
-  void Disconnect(const nsAString& aBdAddr,
+  void Disconnect(const BluetoothAddress& aBdAddr,
                   BluetoothA2dpResultHandler* aRes) override;
 
 private:
   void DispatchError(BluetoothA2dpResultHandler* aRes,
                      BluetoothStatus aStatus);
   void DispatchError(BluetoothA2dpResultHandler* aRes, nsresult aRv);
 
   BluetoothDaemonA2dpModule* mModule;
--- a/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.cpp
@@ -478,23 +478,22 @@ class BluetoothDaemonAvrcpModule::Remote
   : private PDUInitOp
 {
 public:
   RemoteFeatureInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1, unsigned long& aArg2) const
+  operator () (BluetoothAddress& aArg1, unsigned long& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read address */
-    nsresult rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read feature */
     rv = UnpackPDU(
       pdu,
       UnpackConversion<BluetoothAvrcpRemoteFeatureBits, unsigned long>(aArg2));
--- a/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonAvrcpInterface.h
@@ -184,18 +184,19 @@ protected:
 
   //
   // Notifications
   //
 
   class NotificationHandlerWrapper;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, nsString, unsigned long,
-    const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, unsigned long,
+    const BluetoothAddress&>
     RemoteFeatureNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable0<
     NotificationHandlerWrapper, void>
     GetPlayStatusNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable0<
     NotificationHandlerWrapper, void>
--- a/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.cpp
@@ -146,125 +146,119 @@ BluetoothDaemonCoreModule::SetAdapterPro
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
 BluetoothDaemonCoreModule::GetRemoteDevicePropertiesCmd(
-  const nsAString& aRemoteAddr, BluetoothResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REMOTE_DEVICE_PROPERTIES,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
 BluetoothDaemonCoreModule::GetRemoteDevicePropertyCmd(
-  const nsAString& aRemoteAddr,
+  const BluetoothAddress& aRemoteAddr,
   const nsAString& aName,
   BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REMOTE_DEVICE_PROPERTY,
                         0));
 
   nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr),
+    aRemoteAddr,
     PackConversion<nsAString, BluetoothPropertyType>(aName), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
 BluetoothDaemonCoreModule::SetRemoteDevicePropertyCmd(
-  const nsAString& aRemoteAddr,
+  const BluetoothAddress& aRemoteAddr,
   const BluetoothNamedValue& aProperty,
   BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_REMOTE_DEVICE_PROPERTY,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr),
-    aProperty, *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, aProperty, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
 BluetoothDaemonCoreModule::GetRemoteServiceRecordCmd(
-  const nsAString& aRemoteAddr, const BluetoothUuid& aUuid,
+  const BluetoothAddress& aRemoteAddr, const BluetoothUuid& aUuid,
   BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REMOTE_SERVICE_RECORD,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr),
-    aUuid, *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, aUuid, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
 BluetoothDaemonCoreModule::GetRemoteServicesCmd(
-  const nsAString& aRemoteAddr, BluetoothResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REMOTE_SERVICES, 0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
@@ -301,131 +295,126 @@ BluetoothDaemonCoreModule::CancelDiscove
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonCoreModule::CreateBondCmd(const nsAString& aBdAddr,
+BluetoothDaemonCoreModule::CreateBondCmd(const BluetoothAddress& aBdAddr,
                                          BluetoothTransport aTransport,
                                          BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CREATE_BOND,
                         0));
 
 #if ANDROID_VERSION >= 21
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr), aTransport, *pdu);
+  nsresult rv = PackPDU(aBdAddr, aTransport, *pdu);
 #else
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr), *pdu);
+  nsresult rv = PackPDU(aBdAddr, *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonCoreModule::RemoveBondCmd(const nsAString& aBdAddr,
+BluetoothDaemonCoreModule::RemoveBondCmd(const BluetoothAddress& aBdAddr,
                                          BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_REMOVE_BOND,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr), *pdu);
+  nsresult rv = PackPDU(aBdAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonCoreModule::CancelBondCmd(const nsAString& aBdAddr,
+BluetoothDaemonCoreModule::CancelBondCmd(const BluetoothAddress& aBdAddr,
                                          BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CANCEL_BOND,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr), *pdu);
+  nsresult rv = PackPDU(aBdAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonCoreModule::PinReplyCmd(const nsAString& aBdAddr, bool aAccept,
+BluetoothDaemonCoreModule::PinReplyCmd(const BluetoothAddress& aBdAddr,
+                                       bool aAccept,
                                        const nsAString& aPinCode,
                                        BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_PIN_REPLY,
                         0));
 
   nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+    aBdAddr,
     aAccept,
     PackConversion<nsAString, BluetoothPinCode>(aPinCode), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonCoreModule::SspReplyCmd(const nsAString& aBdAddr,
+BluetoothDaemonCoreModule::SspReplyCmd(const BluetoothAddress& aBdAddr,
                                        BluetoothSspVariant aVariant,
                                        bool aAccept, uint32_t aPasskey,
                                        BluetoothResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_SSP_REPLY,
                         0));
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr),
-    aVariant, aAccept, aPasskey, *pdu);
+  nsresult rv = PackPDU(aBdAddr, aVariant, aAccept, aPasskey, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
@@ -861,30 +850,29 @@ class BluetoothDaemonCoreModule::RemoteD
   : private PDUInitOp
 {
 public:
   RemoteDevicePropertiesInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothStatus& aArg1, nsString& aArg2, int& aArg3,
+  operator () (BluetoothStatus& aArg1, BluetoothAddress& aArg2, int& aArg3,
                nsAutoArrayPtr<BluetoothProperty>& aArg4) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read status */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read number of properties */
     uint8_t numProperties;
     rv = UnpackPDU(pdu, numProperties);
     if (NS_FAILED(rv)) {
@@ -968,23 +956,22 @@ class BluetoothDaemonCoreModule::PinRequ
   : private PDUInitOp
 {
 public:
   PinRequestInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1, nsString& aArg2, uint32_t& aArg3) const
+  operator () (BluetoothAddress& aArg1, nsString& aArg2, uint32_t& aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read remote address */
-    nsresult rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read remote name */
     rv = UnpackPDU(
       pdu, UnpackConversion<BluetoothRemoteName, nsAString>(aArg2));
     if (NS_FAILED(rv)) {
@@ -1015,24 +1002,23 @@ class BluetoothDaemonCoreModule::SspRequ
   : private PDUInitOp
 {
 public:
   SspRequestInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1, nsString& aArg2, uint32_t& aArg3,
+  operator () (BluetoothAddress& aArg1, nsString& aArg2, uint32_t& aArg3,
                BluetoothSspVariant& aArg4, uint32_t& aArg5) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read remote address */
-    nsresult rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read remote name */
     rv = UnpackPDU(
       pdu, UnpackConversion<BluetoothRemoteName, nsAString>(aArg2));
     if (NS_FAILED(rv)) {
@@ -1075,30 +1061,29 @@ class BluetoothDaemonCoreModule::BondSta
   : private PDUInitOp
 {
 public:
   BondStateChangedInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothStatus& aArg1, nsString& aArg2,
+  operator () (BluetoothStatus& aArg1, BluetoothAddress& aArg2,
                BluetoothBondState& aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read status */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read remote address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read bond state */
     rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
@@ -1122,29 +1107,29 @@ class BluetoothDaemonCoreModule::AclStat
   : private PDUInitOp
 {
 public:
   AclStateChangedInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothStatus& aArg1, nsString& aArg2, bool& aArg3) const
+  operator () (BluetoothStatus& aArg1, BluetoothAddress& aArg2,
+               bool& aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read status */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read remote address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read ACL state */
     rv = UnpackPDU(
       pdu, UnpackConversion<BluetoothAclState, bool>(aArg3));
     if (NS_FAILED(rv)) {
--- a/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonCoreInterface.h
@@ -80,53 +80,54 @@ public:
   nsresult GetAdapterPropertiesCmd(BluetoothResultHandler* aRes);
 
   nsresult GetAdapterPropertyCmd(const nsAString& aName,
                                  BluetoothResultHandler* aRes);
 
   nsresult SetAdapterPropertyCmd(const BluetoothNamedValue& aProperty,
                                  BluetoothResultHandler* aRes);
 
-  nsresult GetRemoteDevicePropertiesCmd(const nsAString& aRemoteAddr,
+  nsresult GetRemoteDevicePropertiesCmd(const BluetoothAddress& aRemoteAddr,
                                         BluetoothResultHandler* aRes);
 
-  nsresult GetRemoteDevicePropertyCmd(const nsAString& aRemoteAddr,
+  nsresult GetRemoteDevicePropertyCmd(const BluetoothAddress& aRemoteAddr,
                                       const nsAString& aName,
                                       BluetoothResultHandler* aRes);
 
-  nsresult SetRemoteDevicePropertyCmd(const nsAString& aRemoteAddr,
+  nsresult SetRemoteDevicePropertyCmd(const BluetoothAddress& aRemoteAddr,
                                       const BluetoothNamedValue& aProperty,
                                       BluetoothResultHandler* aRes);
 
-  nsresult GetRemoteServiceRecordCmd(const nsAString& aRemoteAddr,
+  nsresult GetRemoteServiceRecordCmd(const BluetoothAddress& aRemoteAddr,
                                      const BluetoothUuid& aUuid,
                                      BluetoothResultHandler* aRes);
 
-  nsresult GetRemoteServicesCmd(const nsAString& aRemoteAddr,
+  nsresult GetRemoteServicesCmd(const BluetoothAddress& aRemoteAddr,
                                 BluetoothResultHandler* aRes);
 
   nsresult StartDiscoveryCmd(BluetoothResultHandler* aRes);
 
   nsresult CancelDiscoveryCmd(BluetoothResultHandler* aRes);
 
-  nsresult CreateBondCmd(const nsAString& aBdAddr,
+  nsresult CreateBondCmd(const BluetoothAddress& aBdAddr,
                          BluetoothTransport aTransport,
                          BluetoothResultHandler* aRes);
 
-  nsresult RemoveBondCmd(const nsAString& aBdAddr,
+  nsresult RemoveBondCmd(const BluetoothAddress& aBdAddr,
                          BluetoothResultHandler* aRes);
 
-  nsresult CancelBondCmd(const nsAString& aBdAddr,
+  nsresult CancelBondCmd(const BluetoothAddress& aBdAddr,
                          BluetoothResultHandler* aRes);
 
-  nsresult PinReplyCmd(const nsAString& aBdAddr, bool aAccept,
+  nsresult PinReplyCmd(const BluetoothAddress& aBdAddr, bool aAccept,
                        const nsAString& aPinCode,
                        BluetoothResultHandler* aRes);
 
-  nsresult SspReplyCmd(const nsAString& aBdAddr, BluetoothSspVariant aVariant,
+  nsresult SspReplyCmd(const BluetoothAddress& aBdAddr,
+                       BluetoothSspVariant aVariant,
                        bool aAccept, uint32_t aPasskey,
                        BluetoothResultHandler* aRes);
 
   nsresult DutModeConfigureCmd(bool aEnable, BluetoothResultHandler* aRes);
 
   nsresult DutModeSendCmd(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen,
                           BluetoothResultHandler* aRes);
 
@@ -245,48 +246,48 @@ private:
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void, BluetoothStatus, int,
     nsAutoArrayPtr<BluetoothProperty>, BluetoothStatus, int,
     const BluetoothProperty*>
     AdapterPropertiesNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
-    NotificationHandlerWrapper, void, BluetoothStatus, nsString, int,
-    nsAutoArrayPtr<BluetoothProperty>, BluetoothStatus, const nsAString&,
-    int, const BluetoothProperty*>
+    NotificationHandlerWrapper, void, BluetoothStatus, BluetoothAddress, int,
+    nsAutoArrayPtr<BluetoothProperty>, BluetoothStatus,
+    const BluetoothAddress&, int, const BluetoothProperty*>
     RemoteDevicePropertiesNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
     NotificationHandlerWrapper, void, int, nsAutoArrayPtr<BluetoothProperty>,
     int, const BluetoothProperty*>
     DeviceFoundNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
     NotificationHandlerWrapper, void, bool>
     DiscoveryStateChangedNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
-    NotificationHandlerWrapper, void, nsString, nsString, uint32_t,
-    const nsAString&, const nsAString&>
+    NotificationHandlerWrapper, void, BluetoothAddress, nsString, uint32_t,
+    const BluetoothAddress&, const nsAString&>
     PinRequestNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable5<
-    NotificationHandlerWrapper, void, nsString, nsString, uint32_t,
-    BluetoothSspVariant, uint32_t, const nsAString&, const nsAString&>
+    NotificationHandlerWrapper, void, BluetoothAddress, nsString, uint32_t,
+    BluetoothSspVariant, uint32_t, const BluetoothAddress&, const nsAString&>
     SspRequestNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
-    NotificationHandlerWrapper, void, BluetoothStatus, nsString,
-    BluetoothBondState, BluetoothStatus, const nsAString&>
+    NotificationHandlerWrapper, void, BluetoothStatus, BluetoothAddress,
+    BluetoothBondState, BluetoothStatus, const BluetoothAddress&>
     BondStateChangedNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
-    NotificationHandlerWrapper, void, BluetoothStatus, nsString, bool,
-    BluetoothStatus, const nsAString&>
+    NotificationHandlerWrapper, void, BluetoothStatus, BluetoothAddress, bool,
+    BluetoothStatus, const BluetoothAddress&>
     AclStateChangedNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void, uint16_t, nsAutoArrayPtr<uint8_t>,
     uint8_t, uint16_t, const uint8_t*>
     DutModeRecvNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
--- a/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.cpp
@@ -116,60 +116,60 @@ BluetoothDaemonGattModule::ClientScanCmd
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientConnectCmd(
-  int aClientIf, const nsAString& aBdAddr, bool aIsDirect,
+  int aClientIf, const BluetoothAddress& aBdAddr, bool aIsDirect,
   BluetoothTransport aTransport, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_CONNECT,
                         4 + // Client Interface
                         6 + // Remote Address
                         1 + // Is Direct
                         4)); // Transport
 
   nsresult rv = PackPDU(
     PackConversion<int, int32_t>(aClientIf),
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+    aBdAddr,
     PackConversion<bool, uint8_t>(aIsDirect),
     PackConversion<BluetoothTransport, int32_t>(aTransport), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientDisconnectCmd(
-  int aClientIf, const nsAString& aBdAddr, int aConnId,
+  int aClientIf, const BluetoothAddress& aBdAddr, int aConnId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_DISCONNECT,
                         4 + // Client Interface
                         6 + // Remote Address
                         4)); // Connection ID
 
   nsresult rv;
   rv = PackPDU(PackConversion<int, int32_t>(aClientIf),
-               PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+               aBdAddr,
                PackConversion<int, int32_t>(aConnId), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -199,27 +199,28 @@ BluetoothDaemonGattModule::ClientListenC
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientRefreshCmd(
-  int aClientIf, const nsAString& aBdAddr, BluetoothGattResultHandler* aRes)
+  int aClientIf, const BluetoothAddress& aBdAddr,
+  BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_REFRESH,
                         4 + // Client Interface
                         6)); // Remote Address
 
   nsresult rv = PackPDU(PackConversion<int, int32_t>(aClientIf),
-                        PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+                        aBdAddr,
                         *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -479,110 +480,106 @@ BluetoothDaemonGattModule::ClientExecute
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientRegisterNotificationCmd(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   const BluetoothGattServiceId& aServiceId, const BluetoothGattId& aCharId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_REGISTER_NOTIFICATION,
                         4 + // Client Interface
                         6 + // Remote Address
                         18 + // Service ID
                         17)); // Characteristic ID
 
   nsresult rv = PackPDU(PackConversion<int, int32_t>(aClientIf),
-                        PackConversion<nsAString, BluetoothAddress>(aBdAddr),
-                        aServiceId, aCharId, *pdu);
+                        aBdAddr, aServiceId, aCharId, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientDeregisterNotificationCmd(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   const BluetoothGattServiceId& aServiceId, const BluetoothGattId& aCharId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_DEREGISTER_NOTIFICATION,
                         4 + // Client Interface
                         6 + // Remote Address
                         18 + // Service ID
                         17)); // Characteristic ID
 
   nsresult rv = PackPDU(PackConversion<int, int32_t>(aClientIf),
-                        PackConversion<nsAString, BluetoothAddress>(aBdAddr),
-                        aServiceId, aCharId, *pdu);
+                        aBdAddr, aServiceId, aCharId, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientReadRemoteRssiCmd(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_READ_REMOTE_RSSI,
                         4 + // Client Interface
                         6)); // Remote Address
 
   nsresult rv = PackPDU(PackConversion<int, int32_t>(aClientIf),
-                        PackConversion<nsAString, BluetoothAddress>(aBdAddr),
-                        *pdu);
+                        aBdAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ClientGetDeviceTypeCmd(
-  const nsAString& aBdAddr, BluetoothGattResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLIENT_GET_DEVICE_TYPE,
                         6)); // Remote Address
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr), *pdu);
+  nsresult rv = PackPDU(aBdAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
@@ -640,17 +637,17 @@ BluetoothDaemonGattModule::ClientTestCom
                         2 + // U1
                         2 + // U2
                         2 + // U3
                         2 + // U4
                         2)); // U5
 
   nsresult rv = PackPDU(
     PackConversion<int, int32_t>(aCommand),
-    PackConversion<nsAString, BluetoothAddress>(aTestParam.mBdAddr),
+    aTestParam.mBdAddr,
     aTestParam.mU1, aTestParam.mU2, aTestParam.mU3, aTestParam.mU4,
     aTestParam.mU5, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
@@ -700,59 +697,59 @@ BluetoothDaemonGattModule::ServerUnregis
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ServerConnectPeripheralCmd(
-  int aServerIf, const nsAString& aBdAddr, bool aIsDirect,
+  int aServerIf, const BluetoothAddress& aBdAddr, bool aIsDirect,
   BluetoothTransport aTransport, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_SERVER_CONNECT_PERIPHERAL,
                         4 + // Server Interface
                         6 + // Remote Address
                         1 + // Is Direct
                         4)); // Transport
 
   nsresult rv = PackPDU(
     PackConversion<int, int32_t>(aServerIf),
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+    aBdAddr,
     PackConversion<bool, uint8_t>(aIsDirect),
     PackConversion<BluetoothTransport, int32_t>(aTransport), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonGattModule::ServerDisconnectPeripheralCmd(
-  int aServerIf, const nsAString& aBdAddr, int aConnId,
+  int aServerIf, const BluetoothAddress& aBdAddr, int aConnId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_SERVER_DISCONNECT_PERIPHERAL,
                         4 + // Server Interface
                         6 + // Remote Address
                         4)); // Connection Id
 
   nsresult rv = PackPDU(PackConversion<int, int32_t>(aServerIf),
-                        PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+                        aBdAddr,
                         PackConversion<int, int32_t>(aConnId), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -1524,25 +1521,24 @@ class BluetoothDaemonGattModule::ClientS
   : private PDUInitOp
 {
 public:
   ClientScanResultInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1,
+  operator () (BluetoothAddress& aArg1,
                int& aArg2,
                BluetoothGattAdvData& aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read address */
-    nsresult rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read RSSI */
     rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -1580,17 +1576,17 @@ public:
   ClientConnectDisconnectInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
                BluetoothGattStatus& aArg2,
                int& aArg3,
-               nsString& aArg4) const
+               BluetoothAddress& aArg4) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read connection ID */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -1600,18 +1596,17 @@ public:
       return rv;
     }
     /* Read client interface */
     rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg4));
+    rv = UnpackPDU(pdu, aArg4);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
@@ -1748,29 +1743,29 @@ class BluetoothDaemonGattModule::ClientR
 {
 public:
   ClientReadRemoteRssiInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
-               nsString& aArg2,
+               BluetoothAddress& aArg2,
                int& aArg3,
                BluetoothGattStatus& aArg4) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read client interface */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read RSSI */
     rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -1820,17 +1815,17 @@ public:
   ServerConnectionInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
                int& aArg2,
                bool& aArg3,
-               nsString& aArg4) const
+               BluetoothAddress& aArg4) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read connection ID */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -1840,18 +1835,17 @@ public:
       return rv;
     }
     /* Read connected */
     rv = UnpackPDU(pdu, UnpackConversion<int32_t, bool>(aArg3));
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg4));
+    rv = UnpackPDU(pdu, aArg4);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
@@ -1935,17 +1929,17 @@ class BluetoothDaemonGattModule::ServerR
 public:
   ServerRequestReadInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
                int& aArg2,
-               nsString& aArg3,
+               BluetoothAddress& aArg3,
                BluetoothAttributeHandle& aArg4,
                int& aArg5,
                bool& aArg6) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read connection ID */
     nsresult rv = UnpackPDU(pdu, aArg1);
@@ -1953,18 +1947,17 @@ public:
       return rv;
     }
     /* Read trans ID */
     rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg3));
+    rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read attribute handle */
     rv = UnpackPDU(pdu, aArg4);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -2000,17 +1993,17 @@ class BluetoothDaemonGattModule::ServerR
 public:
   ServerRequestWriteInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
                int& aArg2,
-               nsString& aArg3,
+               BluetoothAddress& aArg3,
                BluetoothAttributeHandle& aArg4,
                int& aArg5,
                int& aArg6,
                nsAutoArrayPtr<uint8_t>& aArg7,
                bool& aArg8,
                bool& aArg9) const
   {
     DaemonSocketPDU& pdu = GetPDU();
@@ -2021,18 +2014,17 @@ public:
       return rv;
     }
     /* Read trans ID */
     rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg3));
+    rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read attribute handle */
     rv = UnpackPDU(pdu, aArg4);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -2084,34 +2076,33 @@ class BluetoothDaemonGattModule::ServerR
 public:
   ServerRequestExecuteWriteInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (int& aArg1,
                int& aArg2,
-               nsString& aArg3,
+               BluetoothAddress& aArg3,
                bool& aArg4) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read connection ID */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read trans ID */
     rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg3));
+    rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
     /* Read execute write */
     rv = UnpackPDU(pdu, aArg4);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -2347,31 +2338,31 @@ BluetoothDaemonGattInterface::Scan(
     DispatchError(aRes, rv);
   }
 }
 
 /* Connect / Disconnect */
 
 void
 BluetoothDaemonGattInterface::Connect(
-  int aClientIf, const nsAString& aBdAddr, bool aIsDirect,
+  int aClientIf, const BluetoothAddress& aBdAddr, bool aIsDirect,
   BluetoothTransport aTransport, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientConnectCmd(
     aClientIf, aBdAddr, aIsDirect, aTransport, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonGattInterface::Disconnect(
-  int aClientIf, const nsAString& aBdAddr, int aConnId,
+  int aClientIf, const BluetoothAddress& aBdAddr, int aConnId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientDisconnectCmd(
     aClientIf, aBdAddr, aConnId, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
@@ -2389,17 +2380,18 @@ BluetoothDaemonGattInterface::Listen(
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Clear the attribute cache for a given device*/
 void
 BluetoothDaemonGattInterface::Refresh(
-  int aClientIf, const nsAString& aBdAddr, BluetoothGattResultHandler* aRes)
+  int aClientIf, const BluetoothAddress& aBdAddr,
+  BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientRefreshCmd(aClientIf, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
@@ -2544,61 +2536,61 @@ BluetoothDaemonGattInterface::ExecuteWri
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Register / Deregister Characteristic Notifications or Indications */
 void
 BluetoothDaemonGattInterface::RegisterNotification(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   const BluetoothGattServiceId& aServiceId, const BluetoothGattId& aCharId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientRegisterNotificationCmd(
     aClientIf, aBdAddr, aServiceId, aCharId, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonGattInterface::DeregisterNotification(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   const BluetoothGattServiceId& aServiceId, const BluetoothGattId& aCharId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientDeregisterNotificationCmd(
     aClientIf, aBdAddr, aServiceId, aCharId, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonGattInterface::ReadRemoteRssi(
-  int aClientIf, const nsAString& aBdAddr,
+  int aClientIf, const BluetoothAddress& aBdAddr,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientReadRemoteRssiCmd(
     aClientIf, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonGattInterface::GetDeviceType(
-  const nsAString& aBdAddr, BluetoothGattResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClientGetDeviceTypeCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
@@ -2663,32 +2655,32 @@ BluetoothDaemonGattInterface::Unregister
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Connect / Disconnect */
 void
 BluetoothDaemonGattInterface::ConnectPeripheral(
-  int aServerIf, const nsAString& aBdAddr, bool aIsDirect, /* auto connect */
+  int aServerIf, const BluetoothAddress& aBdAddr, bool aIsDirect, /* auto connect */
   BluetoothTransport aTransport, BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ServerConnectPeripheralCmd(
     aServerIf, aBdAddr, aIsDirect, aTransport, aRes);
 
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonGattInterface::DisconnectPeripheral(
-  int aServerIf, const nsAString& aBdAddr, int aConnId,
+  int aServerIf, const BluetoothAddress& aBdAddr, int aConnId,
   BluetoothGattResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ServerDisconnectPeripheralCmd(
     aServerIf, aBdAddr, aConnId, aRes);
 
   if (NS_FAILED(rv)) {
--- a/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonGattInterface.h
@@ -91,34 +91,34 @@ public:
                                BluetoothGattResultHandler* aRes);
 
   /* Start / Stop LE Scan */
   nsresult ClientScanCmd(int aClientIf, bool aStart,
                          BluetoothGattResultHandler* aRes);
 
   /* Connect / Disconnect */
   nsresult ClientConnectCmd(int aClientIf,
-                            const nsAString& aBdAddr,
+                            const BluetoothAddress& aBdAddr,
                             bool aIsDirect, /* auto connect */
                             BluetoothTransport aTransport,
                             BluetoothGattResultHandler* aRes);
 
   nsresult ClientDisconnectCmd(int aClientIf,
-                               const nsAString& aBdAddr,
+                               const BluetoothAddress& aBdAddr,
                                int aConnId,
                                BluetoothGattResultHandler* aRes);
 
   /* Start / Stop advertisements to listen for incoming connections */
   nsresult ClientListenCmd(int aClientIf,
                            bool aIsStart,
                            BluetoothGattResultHandler* aRes);
 
   /* Clear the attribute cache for a given device*/
   nsresult ClientRefreshCmd(int aClientIf,
-                            const nsAString& aBdAddr,
+                            const BluetoothAddress& aBdAddr,
                             BluetoothGattResultHandler* aRes);
 
   /* Enumerate Attributes */
   nsresult ClientSearchServiceCmd(int aConnId,
                                   bool aFiltered,
                                   const BluetoothUuid& aUuid,
                                   BluetoothGattResultHandler* aRes);
 
@@ -180,33 +180,33 @@ public:
   /* Execute / Abort Prepared Write*/
   nsresult ClientExecuteWriteCmd(int aConnId,
                                  int aIsExecute,
                                  BluetoothGattResultHandler* aRes);
 
   /* Register / Deregister Characteristic Notifications or Indications */
   nsresult ClientRegisterNotificationCmd(
     int aClientIf,
-    const nsAString& aBdAddr,
+    const BluetoothAddress& aBdAddr,
     const BluetoothGattServiceId& aServiceId,
     const BluetoothGattId& aCharId,
     BluetoothGattResultHandler* aRes);
 
   nsresult ClientDeregisterNotificationCmd(
     int aClientIf,
-    const nsAString& aBdAddr,
+    const BluetoothAddress& aBdAddr,
     const BluetoothGattServiceId& aServiceId,
     const BluetoothGattId& aCharId,
     BluetoothGattResultHandler* aRes);
 
   nsresult ClientReadRemoteRssiCmd(int aClientIf,
-                                   const nsAString& aBdAddr,
+                                   const BluetoothAddress& aBdAddr,
                                    BluetoothGattResultHandler* aRes);
 
-  nsresult ClientGetDeviceTypeCmd(const nsAString& aBdAddr,
+  nsresult ClientGetDeviceTypeCmd(const BluetoothAddress& aBdAddr,
                                   BluetoothGattResultHandler* aRes);
 
   /* Set advertising data or scan response data */
   nsresult ClientSetAdvDataCmd(int aServerIf,
                                bool aIsScanRsp,
                                bool aIsNameIncluded,
                                bool aIsTxPowerIncluded,
                                int aMinInterval,
@@ -228,24 +228,24 @@ public:
   nsresult ServerRegisterCmd(const BluetoothUuid& aUuid,
                              BluetoothGattResultHandler* aRes);
 
   nsresult ServerUnregisterCmd(int aServerIf,
                                BluetoothGattResultHandler* aRes);
 
   /* Connect / Disconnect */
   nsresult ServerConnectPeripheralCmd(int aServerIf,
-                                      const nsAString& aBdAddr,
+                                      const BluetoothAddress& aBdAddr,
                                       bool aIsDirect,
                                       BluetoothTransport aTransport,
                                       BluetoothGattResultHandler* aRes);
 
   nsresult ServerDisconnectPeripheralCmd(
     int aServerIf,
-    const nsAString& aBdAddr,
+    const BluetoothAddress& aBdAddr,
     int aConnId,
     BluetoothGattResultHandler* aRes);
 
   /* Add a services / a characteristic / a descriptor */
   nsresult ServerAddServiceCmd(int aServerIf,
                                const BluetoothGattServiceId& aServiceId,
                                uint16_t aNumHandles,
                                BluetoothGattResultHandler* aRes);
@@ -484,30 +484,30 @@ protected:
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int, BluetoothUuid,
     BluetoothGattStatus, int, const BluetoothUuid&>
     ClientRegisterNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void,
-    nsString, int, BluetoothGattAdvData,
-    const nsAString&, int, const BluetoothGattAdvData&>
+    BluetoothAddress, int, BluetoothGattAdvData,
+    const BluetoothAddress&, int, const BluetoothGattAdvData&>
     ClientScanResultNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
-    int, BluetoothGattStatus, int, nsString,
-    int, BluetoothGattStatus, int, const nsAString&>
+    int, BluetoothGattStatus, int, BluetoothAddress,
+    int, BluetoothGattStatus, int, const BluetoothAddress&>
     ClientConnectNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
-    int, BluetoothGattStatus, int, nsString,
-    int, BluetoothGattStatus, int, const nsAString&>
+    int, BluetoothGattStatus, int, BluetoothAddress,
+    int, BluetoothGattStatus, int, const BluetoothAddress&>
     ClientDisconnectNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
     NotificationHandlerWrapper, void,
     int, BluetoothGattStatus>
     ClientSearchCompleteNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
@@ -579,35 +579,35 @@ protected:
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
     NotificationHandlerWrapper, void,
     int, BluetoothGattStatus>
     ClientExecuteWriteNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
-    int, nsString, int, BluetoothGattStatus,
-    int, const nsAString&, int, BluetoothGattStatus>
+    int, BluetoothAddress, int, BluetoothGattStatus,
+    int, const BluetoothAddress&, int, BluetoothGattStatus>
     ClientReadRemoteRssiNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int>
     ClientListenNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int, BluetoothUuid,
     BluetoothGattStatus, int, const BluetoothUuid&>
     ServerRegisterNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
-    int, int, bool, nsString,
-    int, int, bool, const nsAString&>
+    int, int, bool, BluetoothAddress,
+    int, int, bool, const BluetoothAddress&>
     ServerConnectionNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int, BluetoothGattServiceId, BluetoothAttributeHandle,
     BluetoothGattStatus, int, const BluetoothGattServiceId&,
     const BluetoothAttributeHandle&>
     ServerServiceAddedNotification;
@@ -651,32 +651,34 @@ protected:
   typedef mozilla::ipc::DaemonNotificationRunnable3<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int, BluetoothAttributeHandle,
     BluetoothGattStatus, int, const BluetoothAttributeHandle&>
     ServerServiceDeletedNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable6<
     NotificationHandlerWrapper, void,
-    int, int, nsString, BluetoothAttributeHandle, int, bool,
-    int, int, const nsAString&, const BluetoothAttributeHandle&, int, bool>
+    int, int, BluetoothAddress, BluetoothAttributeHandle,
+    int, bool,
+    int, int, const BluetoothAddress&, const BluetoothAttributeHandle&,
+    int, bool>
     ServerRequestReadNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable9<
     NotificationHandlerWrapper, void,
-    int, int, nsString, BluetoothAttributeHandle, int, int,
-    nsAutoArrayPtr<uint8_t>, bool, bool,
-    int, int, const nsAString&, const BluetoothAttributeHandle&, int, int,
-    const uint8_t*, bool, bool>
+    int, int, BluetoothAddress, BluetoothAttributeHandle,
+    int, int, nsAutoArrayPtr<uint8_t>, bool, bool,
+    int, int, const BluetoothAddress&, const BluetoothAttributeHandle&,
+    int, int, const uint8_t*, bool, bool>
     ServerRequestWriteNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable4<
     NotificationHandlerWrapper, void,
-    int, int, nsString, bool,
-    int, int, const nsAString&, bool>
+    int, int, BluetoothAddress, bool,
+    int, int, const BluetoothAddress&, bool>
     ServerRequestExecuteWriteNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
     NotificationHandlerWrapper, void,
     BluetoothGattStatus, int>
     ServerResponseConfirmationNotification;
 
   class ClientScanResultInitOp;
@@ -811,33 +813,33 @@ public:
                         BluetoothGattResultHandler* aRes) override;
 
   /* Start / Stop LE Scan */
   void Scan(int aClientIf, bool aStart,
             BluetoothGattResultHandler* aRes) override;
 
   /* Connect / Disconnect */
   void Connect(int aClientIf,
-               const nsAString& aBdAddr,
+               const BluetoothAddress& aBdAddr,
                bool aIsDirect, /* auto connect */
                BluetoothTransport aTransport,
                BluetoothGattResultHandler* aRes) override;
   void Disconnect(int aClientIf,
-                  const nsAString& aBdAddr,
+                  const BluetoothAddress& aBdAddr,
                   int aConnId,
                   BluetoothGattResultHandler* aRes) override;
 
   /* Start / Stop advertisements to listen for incoming connections */
   void Listen(int aClientIf,
               bool aIsStart,
               BluetoothGattResultHandler* aRes) override;
 
   /* Clear the attribute cache for a given device*/
   void Refresh(int aClientIf,
-               const nsAString& aBdAddr,
+               const BluetoothAddress& aBdAddr,
                BluetoothGattResultHandler* aRes) override;
 
   /* Enumerate Attributes */
   void SearchService(int aConnId,
                      bool aSearchAll,
                      const BluetoothUuid& aUuid,
                      BluetoothGattResultHandler* aRes) override;
   void GetIncludedService(int aConnId,
@@ -888,31 +890,31 @@ public:
   /* Execute / Abort Prepared Write*/
   void ExecuteWrite(int aConnId,
                     int aIsExecute,
                     BluetoothGattResultHandler* aRes) override;
 
 
   /* Register / Deregister Characteristic Notifications or Indications */
   void RegisterNotification(int aClientIf,
-                            const nsAString& aBdAddr,
+                            const BluetoothAddress& aBdAddr,
                             const BluetoothGattServiceId& aServiceId,
                             const BluetoothGattId& aCharId,
                             BluetoothGattResultHandler* aRes) override;
   void DeregisterNotification(int aClientIf,
-                              const nsAString& aBdAddr,
+                              const BluetoothAddress& aBdAddr,
                               const BluetoothGattServiceId& aServiceId,
                               const BluetoothGattId& aCharId,
                               BluetoothGattResultHandler* aRes) override;
 
   void ReadRemoteRssi(int aClientIf,
-                      const nsAString& aBdAddr,
+                      const BluetoothAddress& aBdAddr,
                       BluetoothGattResultHandler* aRes) override;
 
-  void GetDeviceType(const nsAString& aBdAddr,
+  void GetDeviceType(const BluetoothAddress& aBdAddr,
                      BluetoothGattResultHandler* aRes) override;
 
   /* Set advertising data or scan response data */
   void SetAdvData(int aServerIf,
                   bool aIsScanRsp,
                   bool aIsNameIncluded,
                   bool aIsTxPowerIncluded,
                   int aMinInterval,
@@ -930,22 +932,22 @@ public:
   /* Register / Unregister */
   void RegisterServer(const BluetoothUuid& aUuid,
                       BluetoothGattResultHandler* aRes) override;
   void UnregisterServer(int aServerIf,
                         BluetoothGattResultHandler* aRes) override;
 
   /* Connect / Disconnect */
   void ConnectPeripheral(int aServerIf,
-                         const nsAString& aBdAddr,
+                         const BluetoothAddress& aBdAddr,
                          bool aIsDirect, /* auto connect */
                          BluetoothTransport aTransport,
                          BluetoothGattResultHandler* aRes) override;
   void DisconnectPeripheral(int aServerIf,
-                            const nsAString& aBdAddr,
+                            const BluetoothAddress& aBdAddr,
                             int aConnId,
                             BluetoothGattResultHandler* aRes) override;
 
   /* Add a services / a characteristic / a descriptor */
   void AddService(int aServerIf,
                   const BluetoothGattServiceId& aServiceId,
                   uint16_t aNumHandles,
                   BluetoothGattResultHandler* aRes) override;
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.cpp
@@ -14,18 +14,18 @@ using namespace mozilla::ipc;
 //
 // Handsfree module
 //
 
 BluetoothHandsfreeNotificationHandler*
   BluetoothDaemonHandsfreeModule::sNotificationHandler;
 
 #if ANDROID_VERSION < 21
-nsString BluetoothDaemonHandsfreeModule::sConnectedDeviceAddress(
-  NS_ConvertUTF8toUTF16(BLUETOOTH_ADDRESS_NONE));
+BluetoothAddress BluetoothDaemonHandsfreeModule::sConnectedDeviceAddress(
+  BluetoothAddress::ANY);
 #endif
 
 void
 BluetoothDaemonHandsfreeModule::SetNotificationHandler(
   BluetoothHandsfreeNotificationHandler* aNotificationHandler)
 {
   sNotificationHandler = aNotificationHandler;
 }
@@ -50,175 +50,168 @@ BluetoothDaemonHandsfreeModule::HandleSv
   (this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes);
 }
 
 // Commands
 //
 
 nsresult
 BluetoothDaemonHandsfreeModule::ConnectCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CONNECT,
                            6)); // Address
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::DisconnectCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_DISCONNECT,
                            6)); // Address
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::ConnectAudioCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CONNECT_AUDIO,
                            6)); // Address
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::DisconnectAudioCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_DISCONNECT_AUDIO,
                            6)); // Address
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::StartVoiceRecognitionCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_START_VOICE_RECOGNITION,
                            6)); // Address (BlueZ 5.25)
 
   nsresult rv;
 #if ANDROID_VERSION >= 21
-  rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
 #endif
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::StopVoiceRecognitionCmd(
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_STOP_VOICE_RECOGNITION,
                            6)); // Address (BlueZ 5.25)
 
   nsresult rv;
 #if ANDROID_VERSION >= 21
-  rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  rv = PackPDU(aRemoteAddr, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
 #endif
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::VolumeControlCmd(
   BluetoothHandsfreeVolumeType aType, int aVolume,
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_VOLUME_CONTROL,
                            1 + // Volume type
                            1 + // Volume
                            6)); // Address (BlueZ 5.25)
 
 #if ANDROID_VERSION >= 21
   nsresult rv = PackPDU(
-    aType, PackConversion<int, uint8_t>(aVolume),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+    aType, PackConversion<int, uint8_t>(aVolume), aRemoteAddr, *pdu);
 #else
   nsresult rv = PackPDU(aType, PackConversion<int, uint8_t>(aVolume), *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
@@ -254,30 +247,29 @@ BluetoothDaemonHandsfreeModule::DeviceSt
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::CopsResponseCmd(
-  const char* aCops, const nsAString& aRemoteAddr,
+  const char* aCops, const BluetoothAddress& aRemoteAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_COPS_RESPONSE,
                            0 + // Dynamically allocated
                            6)); // Address (BlueZ 5.25)
 
 #if ANDROID_VERSION >= 21
-  nsresult rv = PackPDU(
-    PackCString0(nsDependentCString(aCops)),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(PackCString0(nsDependentCString(aCops)), aRemoteAddr,
+                        *pdu);
 #else
   nsresult rv = PackPDU(PackCString0(nsDependentCString(aCops)), *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
@@ -287,17 +279,17 @@ BluetoothDaemonHandsfreeModule::CopsResp
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::CindResponseCmd(
   int aSvc, int aNumActive, int aNumHeld,
   BluetoothHandsfreeCallState aCallSetupState,
   int aSignal, int aRoam, int aBattChg,
-  const nsAString& aRemoteAddr,
+  const BluetoothAddress& aRemoteAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CIND_RESPONSE,
                            1 + // Service
                            1 + // # Active
@@ -312,17 +304,17 @@ BluetoothDaemonHandsfreeModule::CindResp
   nsresult rv = PackPDU(
     PackConversion<int, uint8_t>(aSvc),
     PackConversion<int, uint8_t>(aNumActive),
     PackConversion<int, uint8_t>(aNumHeld),
     aCallSetupState,
     PackConversion<int, uint8_t>(aSignal),
     PackConversion<int, uint8_t>(aRoam),
     PackConversion<int, uint8_t>(aBattChg),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+    aRemoteAddr, *pdu);
 #else
   nsresult rv = PackPDU(PackConversion<int, uint8_t>(aSvc),
                         PackConversion<int, uint8_t>(aNumActive),
                         PackConversion<int, uint8_t>(aNumHeld),
                         aCallSetupState,
                         PackConversion<int, uint8_t>(aSignal),
                         PackConversion<int, uint8_t>(aRoam),
                         PackConversion<int, uint8_t>(aBattChg), *pdu);
@@ -335,30 +327,29 @@ BluetoothDaemonHandsfreeModule::CindResp
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::FormattedAtResponseCmd(
-  const char* aRsp, const nsAString& aRemoteAddr,
+  const char* aRsp, const BluetoothAddress& aRemoteAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_FORMATTED_AT_RESPONSE,
                            0 + // Dynamically allocated
                            6)); // Address (BlueZ 5.25)
 
 #if ANDROID_VERSION >= 21
-  nsresult rv = PackPDU(
-    PackCString0(nsDependentCString(aRsp)),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(PackCString0(nsDependentCString(aRsp)), aRemoteAddr,
+                                     *pdu);
 #else
   nsresult rv = PackPDU(PackCString0(nsDependentCString(aRsp)), *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
@@ -366,30 +357,30 @@ BluetoothDaemonHandsfreeModule::Formatte
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::AtResponseCmd(
   BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_AT_RESPONSE,
                            1 + // AT Response code
                            1 + // Error code
                            6)); // Address (BlueZ 5.25)
 
 #if ANDROID_VERSION >= 21
-  nsresult rv = PackPDU(
-    aResponseCode, PackConversion<int, uint8_t>(aErrorCode),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(aResponseCode,
+                        PackConversion<int, uint8_t>(aErrorCode),
+                        aRemoteAddr, *pdu);
 #else
   nsresult rv = PackPDU(aResponseCode,
                         PackConversion<int, uint8_t>(aErrorCode), *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
@@ -401,17 +392,17 @@ BluetoothDaemonHandsfreeModule::AtRespon
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::ClccResponseCmd(
   int aIndex,
   BluetoothHandsfreeCallDirection aDir, BluetoothHandsfreeCallState aState,
   BluetoothHandsfreeCallMode aMode, BluetoothHandsfreeCallMptyType aMpty,
   const nsAString& aNumber, BluetoothHandsfreeCallAddressType aType,
-  const nsAString& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ConvertUTF16toUTF8 number(aNumber);
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CLCC_RESPONSE,
                            1 + // Call index
@@ -419,21 +410,19 @@ BluetoothDaemonHandsfreeModule::ClccResp
                            1 + // Call state
                            1 + // Call mode
                            1 + // Call MPTY
                            1 + // Address type
                            number.Length() + 1 + // Number string + \0
                            6)); // Address (BlueZ 5.25)
 
 #if ANDROID_VERSION >= 21
-  nsresult rv = PackPDU(
-    PackConversion<int, uint8_t>(aIndex),
-    aDir, aState, aMode, aMpty, aType,
-    PackCString0(number),
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), *pdu);
+  nsresult rv = PackPDU(PackConversion<int, uint8_t>(aIndex),
+                        aDir, aState, aMode, aMpty, aType,
+                        PackCString0(number), aRemoteAddr, *pdu);
 #else
   nsresult rv = PackPDU(PackConversion<int, uint8_t>(aIndex),
                         aDir, aState, aMode, aMpty, aType,
                         PackCString0(number), *pdu);
 #endif
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -475,29 +464,28 @@ BluetoothDaemonHandsfreeModule::PhoneSta
     return rv;
   }
   unused << pdu.forget();
   return NS_OK;
 }
 
 nsresult
 BluetoothDaemonHandsfreeModule::ConfigureWbsCmd(
-  const nsAString& aRemoteAddr,
+  const BluetoothAddress& aRemoteAddr,
   BluetoothHandsfreeWbsConfig aConfig,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CONFIGURE_WBS,
                            6 + // Address
                            1)); // Config
 
-  nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aRemoteAddr), aConfig, *pdu);
+  nsresult rv = PackPDU(aRemoteAddr, aConfig, *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
@@ -747,38 +735,37 @@ class BluetoothDaemonHandsfreeModule::Co
 {
 public:
   ConnectionStateInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (BluetoothHandsfreeConnectionState& aArg1,
-               nsString& aArg2) const
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
 #if ANDROID_VERSION < 21
     if (aArg1 == HFP_CONNECTION_STATE_CONNECTED) {
       sConnectedDeviceAddress = aArg2;
     } else if (aArg1 == HFP_CONNECTION_STATE_DISCONNECTED) {
-      sConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+      sConnectedDeviceAddress = BluetoothAddress::ANY;
     }
 #endif
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
 
 void
@@ -795,29 +782,29 @@ class BluetoothDaemonHandsfreeModule::Au
   : private PDUInitOp
 {
 public:
   AudioStateInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothHandsfreeAudioState& aArg1, nsString& aArg2) const
+  operator () (BluetoothHandsfreeAudioState& aArg1,
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
 
@@ -836,30 +823,29 @@ class BluetoothDaemonHandsfreeModule::Vo
 {
 public:
   VoiceRecognitionInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (BluetoothHandsfreeVoiceRecognitionState& aArg1,
-               nsString& aArg2) const
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -880,22 +866,21 @@ class BluetoothDaemonHandsfreeModule::An
   : private PDUInitOp
 {
 public:
   AnswerCallInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -916,22 +901,21 @@ class BluetoothDaemonHandsfreeModule::Ha
   : private PDUInitOp
 {
 public:
   HangupCallInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -953,17 +937,17 @@ class BluetoothDaemonHandsfreeModule::Vo
 {
 public:
   VolumeInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
   operator () (BluetoothHandsfreeVolumeType& aArg1, int& aArg2,
-               nsString& aArg3) const
+               BluetoothAddress& aArg3) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read volume type */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -971,18 +955,17 @@ public:
     /* Read volume */
     rv = UnpackPDU(pdu, UnpackConversion<uint8_t, int>(aArg2));
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg3));
+    rv = UnpackPDU(pdu, aArg3);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg3 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1003,28 +986,27 @@ class BluetoothDaemonHandsfreeModule::Di
   : private PDUInitOp
 {
 public:
   DialCallInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1, nsString& aArg2) const
+  operator () (nsString& aArg1, BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     nsresult rv;
     /* Read address
-     * It's a little weird to parse aArg2(aBdAddr) before parsing
-     * aArg1(aNumber), but this order is defined in BlueZ 5.25 anyway.
+     * It's a little weird to parse aArg2 (aBdAddr) before parsing
+     * aArg1 (aNumber), but this order is defined in BlueZ 5.25 anyway.
      */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU( pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
 
     /* Read number */
@@ -1051,30 +1033,29 @@ class BluetoothDaemonHandsfreeModule::Dt
   : private PDUInitOp
 {
 public:
   DtmfInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (char& aArg1, nsString& aArg2) const
+  operator () (char& aArg1, BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read tone */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1095,30 +1076,30 @@ class BluetoothDaemonHandsfreeModule::NR
   : private PDUInitOp
 {
 public:
   NRECInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothHandsfreeNRECState& aArg1, nsString& aArg2) const
+  operator () (BluetoothHandsfreeNRECState& aArg1,
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1139,30 +1120,30 @@ class BluetoothDaemonHandsfreeModule::Ca
   : private PDUInitOp
 {
 public:
   CallHoldInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothHandsfreeCallHoldType& aArg1, nsString& aArg2) const
+  operator () (BluetoothHandsfreeCallHoldType& aArg1,
+               BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read type */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1183,22 +1164,21 @@ class BluetoothDaemonHandsfreeModule::Cn
   : private PDUInitOp
 {
 public:
   CnumInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1219,22 +1199,21 @@ class BluetoothDaemonHandsfreeModule::Ci
   : private PDUInitOp
 {
 public:
   CindInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1255,22 +1234,21 @@ class BluetoothDaemonHandsfreeModule::Co
   : private PDUInitOp
 {
 public:
   CopsInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1291,22 +1269,21 @@ class BluetoothDaemonHandsfreeModule::Cl
   : private PDUInitOp
 {
 public:
   ClccInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1327,28 +1304,27 @@ class BluetoothDaemonHandsfreeModule::Un
   : private PDUInitOp
 {
 public:
   UnknownAtInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsCString& aArg1, nsString& aArg2) const
+  operator () (nsCString& aArg1, BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     nsresult rv;
     /* Read address
      * It's a little weird to parse aArg2(aBdAddr) before parsing
      * aArg1(aAtString), but this order is defined in BlueZ 5.25 anyway.
      */
 #if ANDROID_VERSION >= 21
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg2 = sConnectedDeviceAddress;
 #endif
 
     /* Read string */
@@ -1375,22 +1351,21 @@ class BluetoothDaemonHandsfreeModule::Ke
   : private PDUInitOp
 {
 public:
   KeyPressedInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (nsString& aArg1) const
+  operator () (BluetoothAddress& aArg1) const
   {
     /* Read address */
 #if ANDROID_VERSION >= 21
-    nsresult rv = UnpackPDU(
-      GetPDU(), UnpackConversion<BluetoothAddress, nsAString>(aArg1));
+    nsresult rv = UnpackPDU(GetPDU(), aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 #else
     aArg1 = sConnectedDeviceAddress;
 #endif
     WarnAboutTrailingData();
     return NS_OK;
@@ -1411,29 +1386,28 @@ class BluetoothDaemonHandsfreeModule::Wb
   : private PDUInitOp
 {
 public:
   WbsInitOp(DaemonSocketPDU& aPDU)
     : PDUInitOp(aPDU)
   { }
 
   nsresult
-  operator () (BluetoothHandsfreeWbsConfig& aArg1, nsString& aArg2) const
+  operator () (BluetoothHandsfreeWbsConfig& aArg1, BluetoothAddress& aArg2) const
   {
     DaemonSocketPDU& pdu = GetPDU();
 
     /* Read state */
     nsresult rv = UnpackPDU(pdu, aArg1);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     /* Read address */
-    rv = UnpackPDU(
-      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
+    rv = UnpackPDU(pdu, aArg2);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     WarnAboutTrailingData();
     return NS_OK;
   }
 };
@@ -1607,93 +1581,94 @@ BluetoothDaemonHandsfreeInterface::Clean
     DispatchError(aRes, rv);
   }
 }
 
 /* Connect / Disconnect */
 
 void
 BluetoothDaemonHandsfreeInterface::Connect(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::Disconnect(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::ConnectAudio(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ConnectAudioCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::DisconnectAudio(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->DisconnectAudioCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Voice Recognition */
 
 void
 BluetoothDaemonHandsfreeInterface::StartVoiceRecognition(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->StartVoiceRecognitionCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::StopVoiceRecognition(
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->StopVoiceRecognitionCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Volume */
 
 void
 BluetoothDaemonHandsfreeInterface::VolumeControl(
-  BluetoothHandsfreeVolumeType aType, int aVolume, const nsAString& aBdAddr,
+  BluetoothHandsfreeVolumeType aType, int aVolume,
+  const BluetoothAddress& aBdAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->VolumeControlCmd(aType, aVolume, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
@@ -1716,61 +1691,61 @@ BluetoothDaemonHandsfreeInterface::Devic
     DispatchError(aRes, rv);
   }
 }
 
 /* Responses */
 
 void
 BluetoothDaemonHandsfreeInterface::CopsResponse(
-  const char* aCops, const nsAString& aBdAddr,
+  const char* aCops, const BluetoothAddress& aBdAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->CopsResponseCmd(aCops, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::CindResponse(
   int aSvc, int aNumActive, int aNumHeld,
   BluetoothHandsfreeCallState aCallSetupState,
   int aSignal, int aRoam, int aBattChg,
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld,
                                          aCallSetupState, aSignal,
                                          aRoam, aBattChg, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::FormattedAtResponse(
-  const char* aRsp, const nsAString& aBdAddr,
+  const char* aRsp, const BluetoothAddress& aBdAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->FormattedAtResponseCmd(aRsp, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonHandsfreeInterface::AtResponse(
   BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
-  const nsAString& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->AtResponseCmd(aResponseCode, aErrorCode,
                                        aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
@@ -1779,17 +1754,17 @@ BluetoothDaemonHandsfreeInterface::AtRes
 void
 BluetoothDaemonHandsfreeInterface::ClccResponse(
   int aIndex, BluetoothHandsfreeCallDirection aDir,
   BluetoothHandsfreeCallState aState,
   BluetoothHandsfreeCallMode aMode,
   BluetoothHandsfreeCallMptyType aMpty,
   const nsAString& aNumber,
   BluetoothHandsfreeCallAddressType aType,
-  const nsAString& aBdAddr,
+  const BluetoothAddress& aBdAddr,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty,
                                          aNumber, aType, aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
@@ -1815,17 +1790,17 @@ BluetoothDaemonHandsfreeInterface::Phone
     DispatchError(aRes, rv);
   }
 }
 
 /* Wide Band Speech */
 
 void
 BluetoothDaemonHandsfreeInterface::ConfigureWbs(
-  const nsAString& aBdAddr, BluetoothHandsfreeWbsConfig aConfig,
+  const BluetoothAddress& aBdAddr, BluetoothHandsfreeWbsConfig aConfig,
   BluetoothHandsfreeResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
   nsresult rv = mModule->ConfigureWbsCmd(aBdAddr, aConfig, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHandsfreeInterface.h
@@ -55,80 +55,81 @@ public:
 
   void SetNotificationHandler(
     BluetoothHandsfreeNotificationHandler* aNotificationHandler);
 
   //
   // Commands
   //
 
-  nsresult ConnectCmd(const nsAString& aBdAddr,
+  nsresult ConnectCmd(const BluetoothAddress& aBdAddr,
                       BluetoothHandsfreeResultHandler* aRes);
-  nsresult DisconnectCmd(const nsAString& aBdAddr,
+  nsresult DisconnectCmd(const BluetoothAddress& aBdAddr,
                          BluetoothHandsfreeResultHandler* aRes);
-  nsresult ConnectAudioCmd(const nsAString& aBdAddr,
+  nsresult ConnectAudioCmd(const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeResultHandler* aRes);
-  nsresult DisconnectAudioCmd(const nsAString& aBdAddr,
+  nsresult DisconnectAudioCmd(const BluetoothAddress& aBdAddr,
                               BluetoothHandsfreeResultHandler* aRes);
 
   /* Voice Recognition */
 
-  nsresult StartVoiceRecognitionCmd(const nsAString& aBdAddr,
+  nsresult StartVoiceRecognitionCmd(const BluetoothAddress& aBdAddr,
                                     BluetoothHandsfreeResultHandler* aRes);
-  nsresult StopVoiceRecognitionCmd(const nsAString& aBdAddr,
+  nsresult StopVoiceRecognitionCmd(const BluetoothAddress& aBdAddr,
                                    BluetoothHandsfreeResultHandler* aRes);
 
   /* Volume */
 
   nsresult VolumeControlCmd(BluetoothHandsfreeVolumeType aType, int aVolume,
-                            const nsAString& aBdAddr,
+                            const BluetoothAddress& aBdAddr,
                             BluetoothHandsfreeResultHandler* aRes);
 
   /* Device status */
 
   nsresult DeviceStatusNotificationCmd(
     BluetoothHandsfreeNetworkState aNtkState,
     BluetoothHandsfreeServiceType aSvcType,
     int aSignal, int aBattChg,
     BluetoothHandsfreeResultHandler* aRes);
 
   /* Responses */
 
-  nsresult CopsResponseCmd(const char* aCops, const nsAString& aBdAddr,
+  nsresult CopsResponseCmd(const char* aCops, const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeResultHandler* aRes);
   nsresult CindResponseCmd(int aSvc, int aNumActive, int aNumHeld,
                            BluetoothHandsfreeCallState aCallSetupState,
                            int aSignal, int aRoam, int aBattChg,
-                           const nsAString& aBdAddr,
+                           const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeResultHandler* aRes);
-  nsresult FormattedAtResponseCmd(const char* aRsp, const nsAString& aBdAddr,
+  nsresult FormattedAtResponseCmd(const char* aRsp,
+                                  const BluetoothAddress& aBdAddr,
                                   BluetoothHandsfreeResultHandler* aRes);
   nsresult AtResponseCmd(BluetoothHandsfreeAtResponse aResponseCode,
-                         int aErrorCode, const nsAString& aBdAddr,
+                         int aErrorCode, const BluetoothAddress& aBdAddr,
                          BluetoothHandsfreeResultHandler* aRes);
   nsresult ClccResponseCmd(int aIndex, BluetoothHandsfreeCallDirection aDir,
                            BluetoothHandsfreeCallState aState,
                            BluetoothHandsfreeCallMode aMode,
                            BluetoothHandsfreeCallMptyType aMpty,
                            const nsAString& aNumber,
                            BluetoothHandsfreeCallAddressType aType,
-                           const nsAString& aBdAddr,
+                           const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeResultHandler* aRes);
 
   /* Phone State */
 
   nsresult PhoneStateChangeCmd(int aNumActive, int aNumHeld,
                                BluetoothHandsfreeCallState aCallSetupState,
                                const nsAString& aNumber,
                                BluetoothHandsfreeCallAddressType aType,
                                BluetoothHandsfreeResultHandler* aRes);
 
   /* Wide Band Speech */
 
-  nsresult ConfigureWbsCmd(const nsAString& aBdAddr,
+  nsresult ConfigureWbsCmd(const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeWbsConfig aConfig,
                            BluetoothHandsfreeResultHandler* aRes);
 
 protected:
   void HandleSvc(const DaemonSocketPDUHeader& aHeader,
                  DaemonSocketPDU& aPDU,
                  DaemonSocketResultHandler* aRes);
 
@@ -214,90 +215,107 @@ protected:
 
   //
   // Notifications
   //
 
   class NotificationHandlerWrapper;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeConnectionState,
-    nsString, BluetoothHandsfreeConnectionState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeConnectionState, BluetoothAddress,
+    BluetoothHandsfreeConnectionState, const BluetoothAddress&>
     ConnectionStateNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeAudioState,
-    nsString, BluetoothHandsfreeAudioState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeAudioState, BluetoothAddress,
+    BluetoothHandsfreeAudioState, const BluetoothAddress&>
     AudioStateNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeVoiceRecognitionState,
-    nsString, BluetoothHandsfreeVoiceRecognitionState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeVoiceRecognitionState, BluetoothAddress,
+    BluetoothHandsfreeVoiceRecognitionState, const BluetoothAddress&>
     VoiceRecognitionNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     AnswerCallNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     HangupCallNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable3<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeVolumeType,
-    int, nsString, BluetoothHandsfreeVolumeType, int, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeVolumeType, int, BluetoothAddress,
+    BluetoothHandsfreeVolumeType, int, const BluetoothAddress&>
     VolumeNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, nsString, nsString, const nsAString&,
-    const nsAString&>
+    NotificationHandlerWrapper, void,
+    nsString, BluetoothAddress,
+    const nsAString&, const BluetoothAddress&>
     DialCallNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, char, nsString, char, const nsAString&>
+    NotificationHandlerWrapper, void,
+    char, BluetoothAddress, char, const BluetoothAddress&>
     DtmfNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeNRECState, nsString,
-    BluetoothHandsfreeNRECState, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeNRECState, BluetoothAddress,
+    BluetoothHandsfreeNRECState, const BluetoothAddress&>
     NRECNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeCallHoldType,
-    nsString, BluetoothHandsfreeCallHoldType, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeCallHoldType, BluetoothAddress,
+    BluetoothHandsfreeCallHoldType, const BluetoothAddress&>
     CallHoldNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     CnumNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     CindNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     CopsNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     ClccNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, nsCString, nsString,
-    const nsACString&, const nsAString&>
+    NotificationHandlerWrapper, void,
+    nsCString, BluetoothAddress,
+    const nsACString&, const BluetoothAddress&>
     UnknownAtNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable1<
-    NotificationHandlerWrapper, void, nsString, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothAddress, const BluetoothAddress&>
     KeyPressedNotification;
 
   typedef mozilla::ipc::DaemonNotificationRunnable2<
-    NotificationHandlerWrapper, void, BluetoothHandsfreeWbsConfig, nsString,
-    BluetoothHandsfreeWbsConfig, const nsAString&>
+    NotificationHandlerWrapper, void,
+    BluetoothHandsfreeWbsConfig, BluetoothAddress,
+    BluetoothHandsfreeWbsConfig, const BluetoothAddress&>
     WbsNotification;
 
   class ConnectionStateInitOp;
   class AudioStateInitOp;
   class VoiceRecognitionInitOp;
   class AnswerCallInitOp;
   class HangupCallInitOp;
   class VolumeInitOp;
@@ -367,21 +385,22 @@ protected:
 
   void HandleNtf(const DaemonSocketPDUHeader& aHeader,
                  DaemonSocketPDU& aPDU,
                  DaemonSocketResultHandler* aRes);
 
   static BluetoothHandsfreeNotificationHandler* sNotificationHandler;
 #if ANDROID_VERSION < 21
   /* |sConnectedDeviceAddress| stores Bluetooth device address of the
-   * connected device. Before BlueZ 5.25, we maintain this address by ourselves
-   * through ConnectionStateNtf(); after BlueZ 5.25, every callback carries
-   * this address directly so we don't have to keep it.
+   * connected device. Before BlueZ 5.25, we maintain this address by
+   * ourselves through ConnectionStateNtf(); after BlueZ 5.25, every
+   * callback carries this address directly so we don't have to keep
+   * it.
    */
-  static nsString sConnectedDeviceAddress;
+  static BluetoothAddress sConnectedDeviceAddress;
 #endif
 };
 
 class BluetoothDaemonHandsfreeInterface final
   : public BluetoothHandsfreeInterface
 {
   class CleanupResultHandler;
   class InitResultHandler;
@@ -398,78 +417,78 @@ public:
 
   void Init(
     BluetoothHandsfreeNotificationHandler* aNotificationHandler,
     int aMaxNumClients, BluetoothHandsfreeResultHandler* aRes) override;
   void Cleanup(BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Connect / Disconnect */
 
-  void Connect(const nsAString& aBdAddr,
+  void Connect(const BluetoothAddress& aBdAddr,
                BluetoothHandsfreeResultHandler* aRes) override;
-  void Disconnect(const nsAString& aBdAddr,
+  void Disconnect(const BluetoothAddress& aBdAddr,
                   BluetoothHandsfreeResultHandler* aRes) override;
-  void ConnectAudio(const nsAString& aBdAddr,
+  void ConnectAudio(const BluetoothAddress& aBdAddr,
                     BluetoothHandsfreeResultHandler* aRes) override;
-  void DisconnectAudio(const nsAString& aBdAddr,
+  void DisconnectAudio(const BluetoothAddress& aBdAddr,
                        BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Voice Recognition */
 
-  void StartVoiceRecognition(const nsAString& aBdAddr,
+  void StartVoiceRecognition(const BluetoothAddress& aBdAddr,
                              BluetoothHandsfreeResultHandler* aRes) override;
-  void StopVoiceRecognition(const nsAString& aBdAddr,
+  void StopVoiceRecognition(const BluetoothAddress& aBdAddr,
                             BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Volume */
 
   void VolumeControl(BluetoothHandsfreeVolumeType aType, int aVolume,
-                     const nsAString& aBdAddr,
+                     const BluetoothAddress& aBdAddr,
                      BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Device status */
 
   void DeviceStatusNotification(BluetoothHandsfreeNetworkState aNtkState,
                                 BluetoothHandsfreeServiceType aSvcType,
                                 int aSignal, int aBattChg,
                                 BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Responses */
 
-  void CopsResponse(const char* aCops, const nsAString& aBdAddr,
+  void CopsResponse(const char* aCops, const BluetoothAddress& aBdAddr,
                     BluetoothHandsfreeResultHandler* aRes);
   void CindResponse(int aSvc, int aNumActive, int aNumHeld,
                     BluetoothHandsfreeCallState aCallSetupState,
                     int aSignal, int aRoam, int aBattChg,
-                    const nsAString& aBdAddr,
+                    const BluetoothAddress& aBdAddr,
                     BluetoothHandsfreeResultHandler* aRes) override;
-  void FormattedAtResponse(const char* aRsp, const nsAString& aBdAddr,
+  void FormattedAtResponse(const char* aRsp, const BluetoothAddress& aBdAddr,
                            BluetoothHandsfreeResultHandler* aRes) override;
   void AtResponse(BluetoothHandsfreeAtResponse aResponseCode, int aErrorCode,
-                  const nsAString& aBdAddr,
+                  const BluetoothAddress& aBdAddr,
                   BluetoothHandsfreeResultHandler* aRes) override;
   void ClccResponse(int aIndex, BluetoothHandsfreeCallDirection aDir,
                     BluetoothHandsfreeCallState aState,
                     BluetoothHandsfreeCallMode aMode,
                     BluetoothHandsfreeCallMptyType aMpty,
                     const nsAString& aNumber,
                     BluetoothHandsfreeCallAddressType aType,
-                    const nsAString& aBdAddr,
+                    const BluetoothAddress& aBdAddr,
                     BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Phone State */
 
   void PhoneStateChange(int aNumActive, int aNumHeld,
                         BluetoothHandsfreeCallState aCallSetupState,
                         const nsAString& aNumber,
                         BluetoothHandsfreeCallAddressType aType,
                         BluetoothHandsfreeResultHandler* aRes) override;
 
   /* Wide Band Speech */
-  void ConfigureWbs(const nsAString& aBdAddr,
+  void ConfigureWbs(const BluetoothAddress& aBdAddr,
                     BluetoothHandsfreeWbsConfig aConfig,
                     BluetoothHandsfreeResultHandler* aRes) override;
 
 private:
   void DispatchError(BluetoothHandsfreeResultHandler* aRes,
                      BluetoothStatus aStatus);
   void DispatchError(BluetoothHandsfreeResultHandler* aRes, nsresult aRv);
 
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp
@@ -509,30 +509,16 @@ Convert(int32_t aIn, BluetoothGattStatus
     aOut = GATT_STATUS_UNKNOWN_ERROR;
   } else {
     aOut = sGattStatus[aIn];
   }
   return NS_OK;
 }
 
 nsresult
-Convert(const nsAString& aIn, BluetoothAddress& aOut)
-{
-  NS_ConvertUTF16toUTF8 bdAddressUTF8(aIn);
-  const char* str = bdAddressUTF8.get();
-
-  for (size_t i = 0; i < MOZ_ARRAY_LENGTH(aOut.mAddr); ++i, ++str) {
-    aOut.mAddr[i] =
-      static_cast<uint8_t>(strtoul(str, const_cast<char**>(&str), 16));
-  }
-
-  return NS_OK;
-}
-
-nsresult
 Convert(const nsAString& aIn, BluetoothPinCode& aOut)
 {
   if (MOZ_HAL_IPC_CONVERT_WARN_IF(
         aIn.Length() > MOZ_ARRAY_LENGTH(aOut.mPinCode), nsAString,
         BluetoothPinCode)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
@@ -616,41 +602,16 @@ Convert(BluetoothAclState aIn, bool& aOu
         aIn >= MOZ_ARRAY_LENGTH(sBool), BluetoothAclState, bool)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sBool[aIn];
   return NS_OK;
 }
 
 nsresult
-Convert(const BluetoothAddress& aIn, nsAString& aOut)
-{
-  char str[BLUETOOTH_ADDRESS_LENGTH + 1];
-
-  int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
-                     static_cast<int>(aIn.mAddr[0]),
-                     static_cast<int>(aIn.mAddr[1]),
-                     static_cast<int>(aIn.mAddr[2]),
-                     static_cast<int>(aIn.mAddr[3]),
-                     static_cast<int>(aIn.mAddr[4]),
-                     static_cast<int>(aIn.mAddr[5]));
-  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
-        res < 0, BluetoothAddress, nsAString)) {
-    return NS_ERROR_ILLEGAL_VALUE;
-  } else if (MOZ_HAL_IPC_CONVERT_WARN_IF(
-        (size_t)res >= sizeof(str), BluetoothAddress, nsAString)) {
-    return NS_ERROR_OUT_OF_MEMORY; /* string buffer too small */
-  }
-
-  aOut = NS_ConvertUTF8toUTF16(str);
-
-  return NS_OK;
-}
-
-nsresult
 Convert(const BluetoothAttributeHandle& aIn, int32_t& aOut)
 {
   aOut = static_cast<int32_t>(aIn.mHandle);
   return NS_OK;
 }
 
 nsresult
 Convert(BluetoothAvrcpEvent aIn, uint8_t& aOut)
@@ -1624,18 +1585,17 @@ UnpackPDU(DaemonSocketPDU& aPDU, Bluetoo
         }
         // We construct an nsCString here because the string
         // returned from the PDU is not 0-terminated.
         aOut.mString = NS_ConvertUTF8toUTF16(
           nsCString(reinterpret_cast<const char*>(data), len));
       }
       break;
     case PROPERTY_BDADDR:
-      rv = UnpackPDU<BluetoothAddress>(
-        aPDU, UnpackConversion<BluetoothAddress, nsAString>(aOut.mString));
+      rv = UnpackPDU(aPDU, aOut.mBdAddress);
       break;
     case PROPERTY_UUIDS: {
         size_t numUuids = len / MAX_UUID_SIZE;
         aOut.mUuidArray.SetLength(numUuids);
         rv = UnpackPDU(aPDU, aOut.mUuidArray);
       }
       break;
     case PROPERTY_CLASS_OF_DEVICE:
@@ -1650,27 +1610,18 @@ UnpackPDU(DaemonSocketPDU& aPDU, Bluetoo
       rv = UnpackPDU(aPDU, aOut.mServiceRecord);
       break;
     case PROPERTY_ADAPTER_SCAN_MODE:
       rv = UnpackPDU(aPDU, aOut.mScanMode);
       break;
     case PROPERTY_ADAPTER_BONDED_DEVICES: {
         /* unpack addresses */
         size_t numAddresses = len / BLUETOOTH_ADDRESS_BYTES;
-        nsAutoArrayPtr<BluetoothAddress> addresses;
-        UnpackArray<BluetoothAddress> addressArray(addresses, numAddresses);
-        rv = UnpackPDU(aPDU, addressArray);
-        if (NS_FAILED(rv)) {
-          return rv;
-        }
-        /* convert addresses to strings */
-        aOut.mStringArray.SetLength(numAddresses);
-        ConvertArray<BluetoothAddress> convertArray(addressArray.mData,
-                                                    addressArray.mLength);
-        rv = Convert(convertArray, aOut.mStringArray);
+        aOut.mBdAddressArray.SetLength(numAddresses);
+        rv = UnpackPDU(aPDU, aOut.mBdAddressArray);
       }
       break;
     case PROPERTY_REMOTE_RSSI: {
         int8_t rssi;
         rv = UnpackPDU(aPDU, rssi);
         aOut.mInt32 = rssi;
       }
       break;
@@ -1831,24 +1782,18 @@ UnpackPDU(DaemonSocketPDU& aPDU, Bluetoo
   }
   /* unpack status */
   return UnpackPDU(aPDU, aOut.mStatus);
 }
 
 nsresult
 UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattNotifyParam& aOut)
 {
-
-  /* unpack address and convert to nsString */
-  BluetoothAddress address;
-  nsresult rv = UnpackPDU(aPDU, address);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  rv = Convert(address, aOut.mBdAddr);
+  /* unpack address */
+  nsresult rv = UnpackPDU(aPDU, aOut.mBdAddr);
   if (NS_FAILED(rv)) {
     return rv;
   }
   /* unpack service id */
   rv = UnpackPDU(aPDU, aOut.mServiceId);
   if (NS_FAILED(rv)) {
     return rv;
   }
--- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h
@@ -27,20 +27,16 @@ using namespace mozilla::ipc::DaemonSock
 // Helper structures
 //
 
 enum BluetoothAclState {
   ACL_STATE_CONNECTED,
   ACL_STATE_DISCONNECTED
 };
 
-struct BluetoothAddress {
-  uint8_t mAddr[6];
-};
-
 struct BluetoothAvrcpAttributeTextPairs {
   BluetoothAvrcpAttributeTextPairs(const uint8_t* aAttr,
                                    const char** aText,
                                    size_t aLength)
     : mAttr(aAttr)
     , mText(aText)
     , mLength(aLength)
   { }
@@ -200,34 +196,28 @@ Convert(uint8_t aIn, BluetoothStatus& aO
 
 nsresult
 Convert(int32_t aIn, BluetoothAttributeHandle& aOut);
 
 nsresult
 Convert(int32_t aIn, BluetoothGattStatus& aOut);
 
 nsresult
-Convert(const nsAString& aIn, BluetoothAddress& aOut);
-
-nsresult
 Convert(const nsAString& aIn, BluetoothPinCode& aOut);
 
 nsresult
 Convert(const nsAString& aIn, BluetoothPropertyType& aOut);
 
 nsresult
 Convert(const nsAString& aIn, BluetoothServiceName& aOut);
 
 nsresult
 Convert(BluetoothAclState aIn, bool& aOut);
 
 nsresult
-Convert(const BluetoothAddress& aIn, nsAString& aOut);
-
-nsresult
 Convert(const BluetoothAttributeHandle& aIn, int32_t& aOut);
 
 nsresult
 Convert(BluetoothAvrcpEvent aIn, uint8_t& aOut);
 
 nsresult
 Convert(BluetoothAvrcpNotification aIn, uint8_t& aOut);
 
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp
@@ -662,66 +662,66 @@ BluetoothDaemonInterface::SetAdapterProp
     DispatchError(aRes, rv);
   }
 }
 
 /* Remote Device Properties */
 
 void
 BluetoothDaemonInterface::GetRemoteDeviceProperties(
-  const nsAString& aRemoteAddr, BluetoothResultHandler* aRes)
+  const BluetoothAddress& aRemoteAddr, BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->GetRemoteDevicePropertiesCmd(aRemoteAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonInterface::GetRemoteDeviceProperty(
-  const nsAString& aRemoteAddr, const nsAString& aName,
+  const BluetoothAddress& aRemoteAddr, const nsAString& aName,
   BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->GetRemoteDevicePropertyCmd(aRemoteAddr, aName, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
 BluetoothDaemonInterface::SetRemoteDeviceProperty(
-  const nsAString& aRemoteAddr, const BluetoothNamedValue& aProperty,
+  const BluetoothAddress& aRemoteAddr, const BluetoothNamedValue& aProperty,
   BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->SetRemoteDevicePropertyCmd(aRemoteAddr, aProperty, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Remote Services */
 
 void
-BluetoothDaemonInterface::GetRemoteServiceRecord(const nsAString& aRemoteAddr,
-                                                 const BluetoothUuid& aUuid,
-                                                 BluetoothResultHandler* aRes)
+BluetoothDaemonInterface::GetRemoteServiceRecord(
+  const BluetoothAddress& aRemoteAddr, const BluetoothUuid& aUuid,
+  BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->GetRemoteServiceRecordCmd(aRemoteAddr, aUuid, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
-BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr,
-                                            BluetoothResultHandler* aRes)
+BluetoothDaemonInterface::GetRemoteServices(
+  const BluetoothAddress& aRemoteAddr, BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->GetRemoteServicesCmd(aRemoteAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
@@ -745,74 +745,75 @@ BluetoothDaemonInterface::CancelDiscover
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Bonds */
 
 void
-BluetoothDaemonInterface::CreateBond(const nsAString& aBdAddr,
+BluetoothDaemonInterface::CreateBond(const BluetoothAddress& aBdAddr,
                                      BluetoothTransport aTransport,
                                      BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->CreateBondCmd(aBdAddr, aTransport, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
-BluetoothDaemonInterface::RemoveBond(const nsAString& aBdAddr,
+BluetoothDaemonInterface::RemoveBond(const BluetoothAddress& aBdAddr,
                                      BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->RemoveBondCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
-BluetoothDaemonInterface::CancelBond(const nsAString& aBdAddr,
+BluetoothDaemonInterface::CancelBond(const BluetoothAddress& aBdAddr,
                                      BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->CancelBondCmd(aBdAddr, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 /* Connection */
 
 void
-BluetoothDaemonInterface::GetConnectionState(const nsAString& aBdAddr,
+BluetoothDaemonInterface::GetConnectionState(const BluetoothAddress& aBdAddr,
                                              BluetoothResultHandler* aRes)
 {
   // NO-OP: no corresponding interface of current BlueZ
 }
 
 /* Authentication */
 
 void
-BluetoothDaemonInterface::PinReply(const nsAString& aBdAddr, bool aAccept,
+BluetoothDaemonInterface::PinReply(const BluetoothAddress& aBdAddr,
+                                   bool aAccept,
                                    const nsAString& aPinCode,
                                    BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->PinReplyCmd(aBdAddr, aAccept, aPinCode, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
   }
 }
 
 void
-BluetoothDaemonInterface::SspReply(const nsAString& aBdAddr,
+BluetoothDaemonInterface::SspReply(const BluetoothAddress& aBdAddr,
                                    BluetoothSspVariant aVariant,
                                    bool aAccept, uint32_t aPasskey,
                                    BluetoothResultHandler* aRes)
 {
   nsresult rv = static_cast<BluetoothDaemonCoreModule*>
     (mProtocol)->SspReplyCmd(aBdAddr, aVariant, aAccept, aPasskey, aRes);
   if (NS_FAILED(rv)) {
     DispatchError(aRes, rv);
--- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h
@@ -57,59 +57,61 @@ public:
   void GetAdapterProperties(BluetoothResultHandler* aRes) override;
   void GetAdapterProperty(const nsAString& aName,
                           BluetoothResultHandler* aRes) override;
   void SetAdapterProperty(const BluetoothNamedValue& aProperty,
                           BluetoothResultHandler* aRes) override;
 
   /* Remote Device Properties */
 
-  void GetRemoteDeviceProperties(const nsAString& aRemoteAddr,
+  void GetRemoteDeviceProperties(const BluetoothAddress& aRemoteAddr,
                                  BluetoothResultHandler* aRes) override;
-  void GetRemoteDeviceProperty(const nsAString& aRemoteAddr,
+  void GetRemoteDeviceProperty(const BluetoothAddress& aRemoteAddr,
                                const nsAString& aName,
                                BluetoothResultHandler* aRes) override;
-  void SetRemoteDeviceProperty(const nsAString& aRemoteAddr,
+  void SetRemoteDeviceProperty(const BluetoothAddress& aRemoteAddr,
                                const BluetoothNamedValue& aProperty,
                                BluetoothResultHandler* aRes) override;
 
   /* Remote Services */
 
-  void GetRemoteServiceRecord(const nsAString& aRemoteAddr,
+  void GetRemoteServiceRecord(const BluetoothAddress& aRemoteAddr,
                               const BluetoothUuid& aUuid,
                               BluetoothResultHandler* aRes) override;
-  void GetRemoteServices(const nsAString& aRemoteAddr,
+  void GetRemoteServices(const BluetoothAddress& aRemoteAddr,
                          BluetoothResultHandler* aRes) override;
 
   /* Discovery */
 
   void StartDiscovery(BluetoothResultHandler* aRes) override;
   void CancelDiscovery(BluetoothResultHandler* aRes) override;
 
   /* Bonds */
 
-  void CreateBond(const nsAString& aBdAddr, BluetoothTransport aTransport,
+  void CreateBond(const BluetoothAddress& aBdAddr,
+                  BluetoothTransport aTransport,
                   BluetoothResultHandler* aRes) override;
-  void RemoveBond(const nsAString& aBdAddr,
+  void RemoveBond(const BluetoothAddress& aBdAddr,
                   BluetoothResultHandler* aRes) override;
-  void CancelBond(const nsAString& aBdAddr,
+  void CancelBond(const BluetoothAddress& aBdAddr,
                   BluetoothResultHandler* aRes) override;
 
   /* Connection */
 
-  void GetConnectionState(const nsAString& aBdAddr,
+  void GetConnectionState(const BluetoothAddress& aBdAddr,
                           BluetoothResultHandler* aRes) override;
 
   /* Authentication */
 
-  void PinReply(const nsAString& aBdAddr, bool aAccept,
+  void PinReply(const BluetoothAddress& aBdAddr, bool aAccept,
                 const nsAString& aPinCode,
                 BluetoothResultHandler* aRes) override;
 
-  void SspReply(const nsAString& aBdAddr, BluetoothSspVariant aVariant,
+  void SspReply(const BluetoothAddress& aBdAddr,
+                BluetoothSspVariant aVariant,
                 bool aAccept, uint32_t aPasskey,
                 BluetoothResultHandler* aRes) override;
 
   /* DUT Mode */
 
   void DutModeConfigure(bool aEnable, BluetoothResultHandler* aRes);
   void DutModeSend(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen,
                    BluetoothResultHandler* aRes) override;
--- a/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.cpp
@@ -49,31 +49,31 @@ BluetoothDaemonSocketModule::ListenCmd(B
   if (NS_FAILED(rv)) {
     return rv;
   }
   unused << pdu.forget();
   return rv;
 }
 
 nsresult
-BluetoothDaemonSocketModule::ConnectCmd(const nsAString& aBdAddr,
+BluetoothDaemonSocketModule::ConnectCmd(const BluetoothAddress& aBdAddr,
                                         BluetoothSocketType aType,
                                         const BluetoothUuid& aServiceUuid,
                                         int aChannel, bool aEncrypt,
                                         bool aAuth,
                                         BluetoothSocketResultHandler* aRes)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoPtr<DaemonSocketPDU> pdu(
     new DaemonSocketPDU(SERVICE_ID, OPCODE_CONNECT,
                         0));
 
   nsresult rv = PackPDU(
-    PackConversion<nsAString, BluetoothAddress>(aBdAddr),
+    aBdAddr,
     aType,
     aServiceUuid,
     PackConversion<int, int32_t>(aChannel),
     SocketFlags(aEncrypt, aAuth), *pdu);
   if (NS_FAILED(rv)) {
     return rv;
   }
   rv = Send(pdu, aRes);
@@ -116,20 +116,21 @@ class BluetoothDaemonSocketModule::Accep
 public:
   AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
   : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) override
   {
     if (aStatus == STATUS_SUCCESS) {
-      IntStringIntResultRunnable::Dispatch(
+      AcceptResultRunnable::Dispatch(
         GetResultHandler(), &BluetoothSocketResultHandler::Accept,
-        ConstantInitOp3<int, nsString, int>(GetClientFd(), GetBdAddress(),
-                                            GetConnectionStatus()));
+        ConstantInitOp3<int, BluetoothAddress, int>(GetClientFd(),
+                                                    GetBdAddress(),
+                                                    GetConnectionStatus()));
     } else {
       ErrorRunnable::Dispatch(GetResultHandler(),
                               &BluetoothSocketResultHandler::OnError,
                               ConstantInitOp1<BluetoothStatus>(aStatus));
     }
 
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<AcceptWatcher>(this));
@@ -230,17 +231,17 @@ public:
   }
 };
 
 void
 BluetoothDaemonSocketModule::ListenRsp(const DaemonSocketPDUHeader& aHeader,
                                        DaemonSocketPDU& aPDU,
                                        BluetoothSocketResultHandler* aRes)
 {
-  IntResultRunnable::Dispatch(
+  ListenResultRunnable::Dispatch(
     aRes, &BluetoothSocketResultHandler::Listen, ListenInitOp(aPDU));
 }
 
 /* |ConnectWatcher| specializes SocketMessageWatcher for
  * connect operations by reading the socket messages from
  * Bluedroid and forwarding the connected socket to the
  * resource handler.
  */
@@ -250,20 +251,21 @@ class BluetoothDaemonSocketModule::Conne
 public:
   ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
   : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) override
   {
     if (aStatus == STATUS_SUCCESS) {
-      IntStringIntResultRunnable::Dispatch(
+      ConnectResultRunnable::Dispatch(
         GetResultHandler(), &BluetoothSocketResultHandler::Connect,
-        ConstantInitOp3<int, nsString, int>(GetFd(), GetBdAddress(),
-                                            GetConnectionStatus()));
+        ConstantInitOp3<int, BluetoothAddress, int>(GetFd(),
+                                                    GetBdAddress(),
+                                                    GetConnectionStatus()));
     } else {
       ErrorRunnable::Dispatch(GetResultHandler(),
                               &BluetoothSocketResultHandler::OnError,
                               ConstantInitOp1<BluetoothStatus>(aStatus));
     }
 
     MessageLoopForIO::current()->PostTask(
       FROM_HERE, new DeleteTask<ConnectWatcher>(this));
@@ -315,17 +317,17 @@ BluetoothDaemonSocketInterface::Listen(B
   nsresult rv = mModule->ListenCmd(aType, aServiceName, aServiceUuid,
                                    aChannel, aEncrypt, aAuth, aRes);
   if (NS_FAILED(rv))  {
     DispatchError(aRes, rv);
   }
 }
 
 void
-BluetoothDaemonSocketInterface::Connect(const nsAString& aBdAddr,
+BluetoothDaemonSocketInterface::Connect(const BluetoothAddress& aBdAddr,
                                         BluetoothSocketType aType,
                                         const BluetoothUuid& aServiceUuid,
                                         int aChannel, bool aEncrypt,
                                         bool aAuth,
                                         BluetoothSocketResultHandler* aRes)
 {
   MOZ_ASSERT(mModule);
 
--- a/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothDaemonSocketInterface.h
@@ -39,17 +39,17 @@ public:
   //
 
   nsresult ListenCmd(BluetoothSocketType aType,
                      const nsAString& aServiceName,
                      const BluetoothUuid& aServiceUuid,
                      int aChannel, bool aEncrypt, bool aAuth,
                      BluetoothSocketResultHandler* aRes);
 
-  nsresult ConnectCmd(const nsAString& aBdAddr,
+  nsresult ConnectCmd(const BluetoothAddress& aBdAddr,
                       BluetoothSocketType aType,
                       const BluetoothUuid& aServiceUuid,
                       int aChannel, bool aEncrypt, bool aAuth,
                       BluetoothSocketResultHandler* aRes);
 
   nsresult AcceptCmd(int aFd, BluetoothSocketResultHandler* aRes);
 
   nsresult CloseCmd(BluetoothSocketResultHandler* aRes);
@@ -64,32 +64,37 @@ private:
   class ConnectWatcher;
   class ListenInitOp;
 
   uint8_t SocketFlags(bool aEncrypt, bool aAuth);
 
   // Responses
   //
 
-  typedef mozilla::ipc::DaemonResultRunnable0<
-    BluetoothSocketResultHandler, void>
-    ResultRunnable;
+  typedef mozilla::ipc::DaemonResultRunnable3<
+    BluetoothSocketResultHandler, void, int, BluetoothAddress, int, int,
+    const BluetoothAddress&, int>
+    AcceptResultRunnable;
 
-  typedef mozilla::ipc::DaemonResultRunnable1<
-    BluetoothSocketResultHandler, void, int, int>
-    IntResultRunnable;
+  typedef mozilla::ipc::DaemonResultRunnable3<
+    BluetoothSocketResultHandler, void, int, BluetoothAddress, int, int,
+    const BluetoothAddress&, int>
+    ConnectResultRunnable;
 
   typedef mozilla::ipc::DaemonResultRunnable1<
     BluetoothSocketResultHandler, void, BluetoothStatus, BluetoothStatus>
     ErrorRunnable;
 
-  typedef mozilla::ipc::DaemonResultRunnable3<
-    BluetoothSocketResultHandler, void, int, nsString, int, int,
-    const nsAString_internal&, int>
-    IntStringIntResultRunnable;
+  typedef mozilla::ipc::DaemonResultRunnable1<
+    BluetoothSocketResultHandler, void, int, int>
+    ListenResultRunnable;
+
+  typedef mozilla::ipc::DaemonResultRunnable0<
+    BluetoothSocketResultHandler, void>
+    ResultRunnable;
 
   void ErrorRsp(const DaemonSocketPDUHeader& aHeader,
                 DaemonSocketPDU& aPDU,
                 BluetoothSocketResultHandler* aRes);
 
   void ListenRsp(const DaemonSocketPDUHeader& aHeader,
                  DaemonSocketPDU& aPDU,
                  BluetoothSocketResultHandler* aRes);
@@ -107,17 +112,17 @@ public:
   ~BluetoothDaemonSocketInterface();
 
   void Listen(BluetoothSocketType aType,
               const nsAString& aServiceName,
               const BluetoothUuid& aServiceUuid,
               int aChannel, bool aEncrypt, bool aAuth,
               BluetoothSocketResultHandler* aRes) override;
 
-  void Connect(const nsAString& aBdAddr,
+  void Connect(const BluetoothAddress& aBdAddr,
                BluetoothSocketType aType,
                const BluetoothUuid& aServiceUuid,
                int aChannel, bool aEncrypt, bool aAuth,
                BluetoothSocketResultHandler* aRes) override;
 
   void Accept(int aFd, BluetoothSocketResultHandler* aRes) override;
 
   void Close(BluetoothSocketResultHandler* aRes) override;
--- a/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothGattManager.cpp
@@ -791,31 +791,48 @@ BluetoothGattManager::Connect(const nsAS
                               const nsAString& aDeviceAddr,
                               BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_INTF_IS_READY_VOID(aRunnable);
 
+  BluetoothAddress deviceAddr;
+  nsresult rv = StringToAddress(aDeviceAddr, deviceAddr);
+  if (NS_FAILED(rv)) {
+    BluetoothService* bs = BluetoothService::Get();
+    NS_ENSURE_TRUE_VOID(bs);
+
+    // Notify BluetoothGatt for client disconnected
+    bs->DistributeSignal(
+      NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
+      aAppUuid,
+      BluetoothValue(false)); // Disconnected
+
+    // Reject the connect request
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
   if (index == sClients->NoIndex) {
     index = sClients->Length();
     sClients->AppendElement(new BluetoothGattClient(aAppUuid, aDeviceAddr));
   }
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mConnectRunnable = aRunnable;
 
   if (client->mClientIf > 0) {
     sBluetoothGattInterface->Connect(client->mClientIf,
-                                           aDeviceAddr,
-                                           true, // direct connect
-                                           TRANSPORT_AUTO,
-                                           new ConnectResultHandler(client));
+                                     deviceAddr,
+                                     true, // direct connect
+                                     TRANSPORT_AUTO,
+                                     new ConnectResultHandler(client));
   } else {
     BluetoothUuid uuid;
     StringToUuid(aAppUuid, uuid);
 
     // connect will be proceeded after client registered
     sBluetoothGattInterface->RegisterClient(
       uuid, new RegisterClientResultHandler(client));
   }
@@ -861,28 +878,45 @@ BluetoothGattManager::Disconnect(const n
                                  const nsAString& aDeviceAddr,
                                  BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_INTF_IS_READY_VOID(aRunnable);
 
+  BluetoothAddress deviceAddr;
+  nsresult rv = StringToAddress(aDeviceAddr, deviceAddr);
+  if (NS_FAILED(rv)) {
+    BluetoothService* bs = BluetoothService::Get();
+    NS_ENSURE_TRUE_VOID(bs);
+
+    // Notify BluetoothGatt that client remains connected
+    bs->DistributeSignal(
+      NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
+      aAppUuid,
+      BluetoothValue(true)); // Connected
+
+    // Reject the Disconnect request
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
   if (NS_WARN_IF(index == sClients->NoIndex)) {
     DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
     return;
   }
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mDisconnectRunnable = aRunnable;
 
   sBluetoothGattInterface->Disconnect(
     client->mClientIf,
-    aDeviceAddr,
+    deviceAddr,
     client->mConnId,
     new DisconnectResultHandler(client));
 }
 
 class BluetoothGattManager::DiscoverResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
@@ -976,28 +1010,35 @@ BluetoothGattManager::ReadRemoteRssi(int
                                      const nsAString& aDeviceAddr,
                                      BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_INTF_IS_READY_VOID(aRunnable);
 
+  BluetoothAddress deviceAddr;
+  nsresult rv = StringToAddress(aDeviceAddr, deviceAddr);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    InterfaceIdComparator());
   if (NS_WARN_IF(index == sClients->NoIndex)) {
     DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
     return;
   }
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mReadRemoteRssiRunnable = aRunnable;
 
   sBluetoothGattInterface->ReadRemoteRssi(
-    aClientIf, aDeviceAddr,
+    aClientIf, deviceAddr,
     new ReadRemoteRssiResultHandler(client));
 }
 
 class BluetoothGattManager::RegisterNotificationsResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
   RegisterNotificationsResultHandler(BluetoothGattClient* aClient)
@@ -1060,20 +1101,27 @@ BluetoothGattManager::RegisterNotificati
   // Reject the request if there is an ongoing request or client is already
   // disconnected
   if (client->mRegisterNotificationsRunnable || client->mConnId <= 0) {
     DispatchReplyError(aRunnable,
                        NS_LITERAL_STRING("RegisterNotifications failed"));
     return;
   }
 
+  BluetoothAddress deviceAddr;
+  nsresult rv = StringToAddress(client->mDeviceAddr, deviceAddr);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   client->mRegisterNotificationsRunnable = aRunnable;
 
   sBluetoothGattInterface->RegisterNotification(
-    client->mClientIf, client->mDeviceAddr, aServId, aCharId,
+    client->mClientIf, deviceAddr, aServId, aCharId,
     new RegisterNotificationsResultHandler(client));
 }
 
 class BluetoothGattManager::DeregisterNotificationsResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
   DeregisterNotificationsResultHandler(BluetoothGattClient* aClient)
@@ -1135,20 +1183,27 @@ BluetoothGattManager::DeregisterNotifica
 
   // Reject the request if there is an ongoing request
   if (client->mDeregisterNotificationsRunnable) {
     DispatchReplyError(aRunnable,
                        NS_LITERAL_STRING("DeregisterNotifications failed"));
     return;
   }
 
+  BluetoothAddress deviceAddr;
+  nsresult rv = StringToAddress(client->mDeviceAddr, deviceAddr);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   client->mDeregisterNotificationsRunnable = aRunnable;
 
   sBluetoothGattInterface->DeregisterNotification(
-    client->mClientIf, client->mDeviceAddr, aServId, aCharId,
+    client->mClientIf, deviceAddr, aServId, aCharId,
     new DeregisterNotificationsResultHandler(client));
 }
 
 class BluetoothGattManager::ReadCharacteristicValueResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
   ReadCharacteristicValueResultHandler(BluetoothGattClient* aClient)
@@ -1517,52 +1572,62 @@ private:
   nsRefPtr<BluetoothGattServer> mServer;
 };
 
 class BluetoothGattManager::ConnectPeripheralResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
   ConnectPeripheralResultHandler(BluetoothGattServer* aServer,
-                                 const nsAString& aDeviceAddr)
-  : mServer(aServer)
-  , mDeviceAddr(aDeviceAddr)
+                                 const BluetoothAddress& aDeviceAddr)
+    : mServer(aServer)
+    , mDeviceAddr(aDeviceAddr)
   {
     MOZ_ASSERT(mServer);
-    MOZ_ASSERT(!mDeviceAddr.IsEmpty());
+    MOZ_ASSERT(mDeviceAddr != BluetoothAddress::ANY);
   }
 
   void OnError(BluetoothStatus aStatus) override
   {
     BT_WARNING("BluetoothGattServerInterface::ConnectPeripheral failed: %d",
                (int)aStatus);
     MOZ_ASSERT(mServer->mConnectPeripheralRunnable);
 
+    nsAutoString addressStr;
+    AddressToString(mDeviceAddr, addressStr);
+
     DispatchReplyError(mServer->mConnectPeripheralRunnable,
                        NS_LITERAL_STRING("ConnectPeripheral failed"));
     mServer->mConnectPeripheralRunnable = nullptr;
-    mServer->mConnectionMap.Remove(mDeviceAddr);
+    mServer->mConnectionMap.Remove(addressStr);
   }
 
 private:
   nsRefPtr<BluetoothGattServer> mServer;
-  nsString mDeviceAddr;
+  BluetoothAddress mDeviceAddr;
 };
 
 void
 BluetoothGattManager::ConnectPeripheral(
   const nsAString& aAppUuid,
   const nsAString& aAddress,
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_INTF_IS_READY_VOID(aRunnable);
 
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   size_t index = sServers->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
   if (index == sServers->NoIndex) {
     index = sServers->Length();
     sServers->AppendElement(new BluetoothGattServer(aAppUuid));
   }
   nsRefPtr<BluetoothGattServer> server = (*sServers)[index];
 
   /**
@@ -1591,20 +1656,20 @@ BluetoothGattManager::ConnectPeripheral(
   }
 
   server->mConnectionMap.Put(aAddress, 0);
   server->mConnectPeripheralRunnable = aRunnable;
 
   if (server->mServerIf > 0) {
     sBluetoothGattInterface->ConnectPeripheral(
       server->mServerIf,
-      aAddress,
+      address,
       true, // direct connect
       TRANSPORT_AUTO,
-      new ConnectPeripheralResultHandler(server, aAddress));
+      new ConnectPeripheralResultHandler(server, address));
   } else if (!server->mIsRegistering) { /* avoid triggering another registration
                                          * procedure if there is an on-going one
                                          * already */
     BluetoothUuid uuid;
     StringToUuid(aAppUuid, uuid);
 
     // connect will be proceeded after server registered
     sBluetoothGattInterface->RegisterServer(
@@ -1644,16 +1709,23 @@ BluetoothGattManager::DisconnectPeripher
   const nsAString& aAddress,
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_INTF_IS_READY_VOID(aRunnable);
 
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
   size_t index = sServers->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
   if (NS_WARN_IF(index == sServers->NoIndex)) {
     DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
     return;
   }
   nsRefPtr<BluetoothGattServer> server = (*sServers)[index];
 
   if (NS_WARN_IF(server->mServerIf <= 0)) {
@@ -1675,17 +1747,17 @@ BluetoothGattManager::DisconnectPeripher
     DispatchReplySuccess(aRunnable);
     return;
   }
 
   server->mDisconnectPeripheralRunnable = aRunnable;
 
   sBluetoothGattInterface->DisconnectPeripheral(
     server->mServerIf,
-    aAddress,
+    address,
     connId,
     new DisconnectPeripheralResultHandler(server));
 }
 
 class BluetoothGattManager::UnregisterServerResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
@@ -2446,31 +2518,47 @@ BluetoothGattManager::RegisterClientNoti
 
   if (client->mStartLeScanRunnable) {
     // Client just registered, proceed remaining startLeScan request.
     sBluetoothGattInterface->Scan(
       aClientIf, true /* start */,
       new StartLeScanResultHandler(client));
   } else if (client->mConnectRunnable) {
     // Client just registered, proceed remaining connect request.
+    BluetoothAddress address;
+    nsresult rv = StringToAddress(client->mDeviceAddr, address);
+    if (NS_FAILED(rv)) {
+      // Notify BluetoothGatt for client disconnected
+      bs->DistributeSignal(
+        NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
+        client->mAppUuid,
+        BluetoothValue(false)); // Disconnected
+
+      // Reject the connect request
+      DispatchReplyError(client->mConnectRunnable,
+                         NS_LITERAL_STRING("Connect failed"));
+      client->mConnectRunnable = nullptr;
+      return;
+    }
+
     sBluetoothGattInterface->Connect(
-      aClientIf, client->mDeviceAddr, true /* direct connect */,
+      aClientIf, address, true /* direct connect */,
       TRANSPORT_AUTO,
       new ConnectResultHandler(client));
   }
 }
 
 class BluetoothGattManager::ScanDeviceTypeResultHandler final
   : public BluetoothGattResultHandler
 {
 public:
-  ScanDeviceTypeResultHandler(const nsAString& aBdAddr, int aRssi,
+  ScanDeviceTypeResultHandler(const BluetoothAddress& aBdAddr, int aRssi,
                               const BluetoothGattAdvData& aAdvData)
-  : mBdAddr(aBdAddr)
-  , mRssi(static_cast<int32_t>(aRssi))
+    : mBdAddr(aBdAddr)
+    , mRssi(static_cast<int32_t>(aRssi))
   {
     mAdvData.AppendElements(aAdvData.mAdvData, sizeof(aAdvData.mAdvData));
   }
 
   void GetDeviceType(BluetoothTypeOfDevice type)
   {
     DistributeSignalDeviceFound(type);
   }
@@ -2482,37 +2570,40 @@ public:
 
 private:
   void DistributeSignalDeviceFound(BluetoothTypeOfDevice type)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     InfallibleTArray<BluetoothNamedValue> properties;
 
-    AppendNamedValue(properties, "Address", mBdAddr);
+    nsAutoString addressStr;
+    AddressToString(mBdAddr, addressStr);
+
+    AppendNamedValue(properties, "Address", addressStr);
     AppendNamedValue(properties, "Rssi", mRssi);
     AppendNamedValue(properties, "GattAdv", mAdvData);
     AppendNamedValue(properties, "Type", static_cast<uint32_t>(type));
 
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE_VOID(bs);
 
     bs->DistributeSignal(NS_LITERAL_STRING("LeDeviceFound"),
                          NS_LITERAL_STRING(KEY_ADAPTER),
                          BluetoothValue(properties));
   }
 
-  nsString mBdAddr;
+  BluetoothAddress mBdAddr;
   int32_t mRssi;
   nsTArray<uint8_t> mAdvData;
 };
 
 void
 BluetoothGattManager::ScanResultNotification(
-  const nsAString& aBdAddr, int aRssi,
+  const BluetoothAddress& aBdAddr, int aRssi,
   const BluetoothGattAdvData& aAdvData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE_VOID(sBluetoothGattInterface);
 
   // Distribute "LeDeviceFound" signal after we know the corresponding
   // BluetoothTypeOfDevice of the device
@@ -2520,17 +2611,17 @@ BluetoothGattManager::ScanResultNotifica
     aBdAddr,
     new ScanDeviceTypeResultHandler(aBdAddr, aRssi, aAdvData));
 }
 
 void
 BluetoothGattManager::ConnectNotification(int aConnId,
                                           BluetoothGattStatus aStatus,
                                           int aClientIf,
-                                          const nsAString& aDeviceAddr)
+                                          const BluetoothAddress& aDeviceAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    InterfaceIdComparator());
@@ -2569,20 +2660,21 @@ BluetoothGattManager::ConnectNotificatio
   // Resolve the connect request
   if (client->mConnectRunnable) {
     DispatchReplySuccess(client->mConnectRunnable);
     client->mConnectRunnable = nullptr;
   }
 }
 
 void
-BluetoothGattManager::DisconnectNotification(int aConnId,
-                                             BluetoothGattStatus aStatus,
-                                             int aClientIf,
-                                             const nsAString& aDeviceAddr)
+BluetoothGattManager::DisconnectNotification(
+  int aConnId,
+  BluetoothGattStatus aStatus,
+  int aClientIf,
+  const BluetoothAddress& aDeviceAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    InterfaceIdComparator());
@@ -3094,32 +3186,35 @@ BluetoothGattManager::WriteDescriptorNot
 }
 
 void
 BluetoothGattManager::ExecuteWriteNotification(int aConnId,
                                                BluetoothGattStatus aStatus)
 { }
 
 void
-BluetoothGattManager::ReadRemoteRssiNotification(int aClientIf,
-                                                 const nsAString& aBdAddr,
-                                                 int aRssi,
-                                                 BluetoothGattStatus aStatus)
+BluetoothGattManager::ReadRemoteRssiNotification(
+  int aClientIf,
+  const BluetoothAddress& aBdAddr,
+  int aRssi,
+  BluetoothGattStatus aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    InterfaceIdComparator());
   NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   if (aStatus != GATT_STATUS_SUCCESS) { // operation failed
-    BT_LOGD("ReadRemoteRssi failed: clientIf = %d, bdAddr = %s, rssi = %d, " \
-            "status = %d", aClientIf, NS_ConvertUTF16toUTF8(aBdAddr).get(),
+    nsAutoString addressStr;
+    AddressToString(aBdAddr, addressStr);
+    BT_LOGD("ReadRemoteRssi failed: clientIf = %d, bdAddr = %s, rssi = %d, "
+            "status = %d", aClientIf, NS_ConvertUTF16toUTF8(addressStr).get(),
             aRssi, (int)aStatus);
 
     // Reject the read remote rssi request
     if (client->mReadRemoteRssiRunnable) {
       DispatchReplyError(client->mReadRemoteRssiRunnable,
                          NS_LITERAL_STRING("ReadRemoteRssi failed"));
       client->mReadRemoteRssiRunnable = nullptr;
     }
@@ -3194,17 +3289,25 @@ BluetoothGattManager::RegisterServerNoti
 
   // Notify BluetoothGattServer to update the serverIf
   bs->DistributeSignal(
     NS_LITERAL_STRING("ServerRegistered"),
     uuid, BluetoothValue(uint32_t(aServerIf)));
 
   if (server->mConnectPeripheralRunnable) {
     // Only one entry exists in the map during first connect peripheral request
-    nsString deviceAddr(server->mConnectionMap.Iter().Key());
+    BluetoothAddress deviceAddr;
+    nsresult rv = StringToAddress(server->mConnectionMap.Iter().Key(), deviceAddr);
+    if (NS_FAILED(rv)) {
+      DispatchReplyError(server->mConnectPeripheralRunnable,
+                         NS_LITERAL_STRING("ConnectPeripheral failed"));
+      server->mConnectPeripheralRunnable = nullptr;
+      server->mConnectionMap.Remove(server->mConnectionMap.Iter().Key());
+      return;
+    }
 
     sBluetoothGattInterface->ConnectPeripheral(
       aServerIf, deviceAddr, true /* direct connect */, TRANSPORT_AUTO,
       new ConnectPeripheralResultHandler(server, deviceAddr));
   }
 
   if (server->mAddServiceState.mRunnable) {
     sBluetoothGattInterface->AddService(
@@ -3214,40 +3317,43 @@ BluetoothGattManager::RegisterServerNoti
       new ServerAddServiceResultHandler(server));
   }
 }
 
 void
 BluetoothGattManager::ConnectionNotification(int aConnId,
                                              int aServerIf,
                                              bool aConnected,
-                                             const nsAString& aBdAddr)
+                                             const BluetoothAddress& aBdAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sServers->IndexOf(aServerIf, 0 /* Start */,
                                    InterfaceIdComparator());
   NS_ENSURE_TRUE_VOID(index != sServers->NoIndex);
 
   nsRefPtr<BluetoothGattServer> server = (*sServers)[index];
 
+  nsAutoString addressStr;
+  AddressToString(aBdAddr, addressStr);
+
   // Update the connection map based on the connection status
   if (aConnected) {
-    server->mConnectionMap.Put(aBdAddr, aConnId);
+    server->mConnectionMap.Put(addressStr, aConnId);
   } else {
-    server->mConnectionMap.Remove(aBdAddr);
+    server->mConnectionMap.Remove(addressStr);
   }
 
   // Notify BluetoothGattServer that connection status changed
   InfallibleTArray<BluetoothNamedValue> props;
   AppendNamedValue(props, "Connected", aConnected);
-  AppendNamedValue(props, "Address", nsString(aBdAddr));
+  AppendNamedValue(props, "Address", addressStr);
   bs->DistributeSignal(
     NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
     server->mAppUuid,
     BluetoothValue(props));
 
   // Resolve or reject connect/disconnect peripheral requests
   if (server->mConnectPeripheralRunnable) {
     if (aConnected) {
@@ -3514,17 +3620,17 @@ BluetoothGattManager::ServiceDeletedNoti
     server->mRemoveServiceRunnable = nullptr;
   }
 }
 
 void
 BluetoothGattManager::RequestReadNotification(
   int aConnId,
   int aTransId,
-  const nsAString& aBdAddr,
+  const BluetoothAddress& aBdAddr,
   const BluetoothAttributeHandle& aAttributeHandle,
   int aOffset,
   bool aIsLong)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE_VOID(aConnId);
   BluetoothService* bs = BluetoothService::Get();
@@ -3544,35 +3650,38 @@ BluetoothGattManager::RequestReadNotific
       aConnId,
       aTransId,
       GATT_STATUS_REQUEST_NOT_SUPPORTED,
       response,
       new ServerSendResponseResultHandler(server));
     return;
   }
 
+  nsAutoString addressStr;
+  AddressToString(aBdAddr, addressStr);
+
   // Distribute a signal to gattServer
   InfallibleTArray<BluetoothNamedValue> properties;
 
   AppendNamedValue(properties, "TransId", aTransId);
   AppendNamedValue(properties, "AttrHandle", aAttributeHandle);
-  AppendNamedValue(properties, "Address", nsString(aBdAddr));
+  AppendNamedValue(properties, "Address", addressStr);
   AppendNamedValue(properties, "NeedResponse", true);
   AppendNamedValue(properties, "Value", new nsTArray<uint8_t>());
 
   bs->DistributeSignal(NS_LITERAL_STRING("ReadRequested"),
                        server->mAppUuid,
                        properties);
 }
 
 void
 BluetoothGattManager::RequestWriteNotification(
   int aConnId,
   int aTransId,
-  const nsAString& aBdAddr,
+  const BluetoothAddress& aBdAddr,
   const BluetoothAttributeHandle& aAttributeHandle,
   int aOffset,
   int aLength,
   const uint8_t* aValue,
   bool aNeedResponse,
   bool aIsPrepareWrite)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3597,22 +3706,25 @@ BluetoothGattManager::RequestWriteNotifi
         aTransId,
         GATT_STATUS_REQUEST_NOT_SUPPORTED,
         response,
         new ServerSendResponseResultHandler(server));
     }
     return;
   }
 
+  nsAutoString addressStr;
+  AddressToString(aBdAddr, addressStr);
+
   // Distribute a signal to gattServer
   InfallibleTArray<BluetoothNamedValue> properties;
 
   AppendNamedValue(properties, "TransId", aTransId);
   AppendNamedValue(properties, "AttrHandle", aAttributeHandle);
-  AppendNamedValue(properties, "Address", nsString(aBdAddr));
+  AppendNamedValue(properties, "Address", addressStr);
   AppendNamedValue(properties, "NeedResponse", aNeedResponse);
 
   nsTArray<uint8_t> value;
   value.AppendElements(aValue, aLength);
   AppendNamedValue(properties, "Value", value);
 
   bs->DistributeSignal(NS_LITERAL_STRING("WrtieRequested"),
                        server->mAppUuid,
--- a/dom/bluetooth/bluedroid/BluetoothGattManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothGattManager.h
@@ -202,28 +202,28 @@ private:
 
   void HandleShutdown();
 
   void RegisterClientNotification(BluetoothGattStatus aStatus,
                                   int aClientIf,
                                   const BluetoothUuid& aAppUuid) override;
 
   void ScanResultNotification(
-    const nsAString& aBdAddr, int aRssi,
+    const BluetoothAddress& aBdAddr, int aRssi,
     const BluetoothGattAdvData& aAdvData) override;
 
   void ConnectNotification(int aConnId,
                            BluetoothGattStatus aStatus,
                            int aClientIf,
-                           const nsAString& aBdAddr) override;
+                           const BluetoothAddress& aBdAddr) override;
 
   void DisconnectNotification(int aConnId,
                               BluetoothGattStatus aStatus,
                               int aClientIf,
-                              const nsAString& aBdAddr) override;
+                              const BluetoothAddress& aBdAddr) override;
 
   void SearchCompleteNotification(int aConnId,
                                   BluetoothGattStatus aStatus) override;
 
   void SearchResultNotification(int aConnId,
                                 const BluetoothGattServiceId& aServiceId)
                                 override;
 
@@ -271,34 +271,34 @@ private:
                                    BluetoothGattStatus aStatus,
                                    const BluetoothGattWriteParam& aWriteParam)
                                    override;
 
   void ExecuteWriteNotification(int aConnId,
                                 BluetoothGattStatus aStatus) override;
 
   void ReadRemoteRssiNotification(int aClientIf,
-                                  const nsAString& aBdAddr,
+                                  const BluetoothAddress& aBdAddr,
                                   int aRssi,
                                   BluetoothGattStatus aStatus) override;
 
   void ListenNotification(BluetoothGattStatus aStatus,
                           int aServerIf) override;
 
   void ProceedDiscoverProcess(BluetoothGattClient* aClient,
                               const BluetoothGattServiceId& aServiceId);
 
   void RegisterServerNotification(BluetoothGattStatus aStatus,
                                   int aServerIf,
                                   const BluetoothUuid& aAppUuid) override;
 
   void ConnectionNotification(int aConnId,
                               int aServerIf,
                               bool aConnected,
-                              const nsAString& aBdAddr) override;
+                              const BluetoothAddress& aBdAddr) override;
 
   void
   ServiceAddedNotification(
     BluetoothGattStatus aStatus,
     int aServerIf,
     const BluetoothGattServiceId& aServiceId,
     const BluetoothAttributeHandle& aServiceHandle) override;
 
@@ -341,25 +341,25 @@ private:
   ServiceDeletedNotification(
     BluetoothGattStatus aStatus,
     int aServerIf,
     const BluetoothAttributeHandle& aServiceHandle) override;
 
   void
   RequestReadNotification(int aConnId,
                           int aTransId,
-                          const nsAString& aBdAddr,
+                          const BluetoothAddress& aBdAddr,
                           const BluetoothAttributeHandle& aAttributeHandle,
                           int aOffset,
                           bool aIsLong) override;
 
   void
   RequestWriteNotification(int aConnId,
                            int aTransId,
-                           const nsAString& aBdAddr,
+                           const BluetoothAddress& aBdAddr,
                            const BluetoothAttributeHandle& aAttributeHandle,
                            int aOffset,
                            int aLength,
                            const uint8_t* aValue,
                            bool aNeedResponse,
                            bool aIsPrepareWrite) override;
 
   static bool mInShutdown;
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -795,25 +795,36 @@ BluetoothServiceBluedroid::GetAdaptersIn
    *     |
    *     |__ BluetoothNamedValue =
    *     |     {"Adapter", BluetoothValue = BluetoothNamedValue[]}
    *     ...
    */
   InfallibleTArray<BluetoothNamedValue> adaptersProperties;
   uint32_t numAdapters = 1; // Bluedroid supports single adapter only
 
+  nsAutoString bdAddressStr;
+  AddressToString(mBdAddress, bdAddressStr);
+
+  nsTArray<nsString> bondedAddresses;
+
+  for (uint32_t i = 0; i < mBondedAddresses.Length(); ++i) {
+    nsAutoString bondedAddressStr;
+    AddressToString(mBondedAddresses[i], bondedAddressStr);
+    bondedAddresses.AppendElement(bondedAddressStr);
+  }
+
   for (uint32_t i = 0; i < numAdapters; i++) {
     InfallibleTArray<BluetoothNamedValue> properties;
 
     AppendNamedValue(properties, "State", mEnabled);
-    AppendNamedValue(properties, "Address", mBdAddress);
+    AppendNamedValue(properties, "Address", bdAddressStr);
     AppendNamedValue(properties, "Name", mBdName);
     AppendNamedValue(properties, "Discoverable", mDiscoverable);
     AppendNamedValue(properties, "Discovering", mDiscovering);
-    AppendNamedValue(properties, "PairedDevices", mBondedAddresses);
+    AppendNamedValue(properties, "PairedDevices", bondedAddresses);
 
     AppendNamedValue(adaptersProperties, "Adapter",
                      BluetoothValue(properties));
   }
 
   DispatchReplySuccess(aRunnable, adaptersProperties);
   return NS_OK;
 }
@@ -833,42 +844,45 @@ public:
 
 class BluetoothServiceBluedroid::GetRemoteDevicePropertiesResultHandler
   final
   : public BluetoothResultHandler
 {
 public:
   GetRemoteDevicePropertiesResultHandler(
     nsTArray<GetDeviceRequest>& aRequests,
-    const nsAString& aDeviceAddress)
+    const BluetoothAddress& aDeviceAddress)
     : mRequests(aRequests)
     , mDeviceAddress(aDeviceAddress)
   { }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(!mRequests.IsEmpty());
 
+    nsAutoString addressString;
+    AddressToString(mDeviceAddress, addressString);
+
     BT_WARNING("GetRemoteDeviceProperties(%s) failed: %d",
-               NS_ConvertUTF16toUTF8(mDeviceAddress).get(), aStatus);
+               NS_ConvertUTF16toUTF8(addressString).get(), aStatus);
 
     /* Dispatch result after the final pending operation */
     if (--mRequests[0].mDeviceCount == 0) {
       if (mRequests[0].mRunnable) {
         DispatchReplySuccess(mRequests[0].mRunnable,
                              mRequests[0].mDevicesPack);
       }
       mRequests.RemoveElementAt(0);
     }
   }
 
 private:
   nsTArray<GetDeviceRequest>& mRequests;
-  nsString mDeviceAddress;
+  BluetoothAddress mDeviceAddress;
 };
 
 nsresult
 BluetoothServiceBluedroid::GetConnectedDevicePropertiesInternal(
   uint16_t aServiceUuid, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -883,18 +897,25 @@ BluetoothServiceBluedroid::GetConnectedD
 
   // Reply success if no device of this profile is connected
   if (!profile->IsConnected()) {
     DispatchReplySuccess(aRunnable, InfallibleTArray<BluetoothNamedValue>());
     return NS_OK;
   }
 
   // Get address of the connected device
-  nsString address;
-  profile->GetAddress(address);
+  nsString addressString;
+  profile->GetAddress(addressString);
+
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(addressString, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return rv;
+  }
 
   // Append request of the connected device
   GetDeviceRequest request(1, aRunnable);
   mGetDeviceRequests.AppendElement(request);
 
   sBtInterface->GetRemoteDeviceProperties(address,
     new GetRemoteDevicePropertiesResultHandler(mGetDeviceRequests, address));
 
@@ -914,20 +935,27 @@ BluetoothServiceBluedroid::GetPairedDevi
     return NS_OK;
   }
 
   // Append request of all paired devices
   GetDeviceRequest request(aDeviceAddress.Length(), aRunnable);
   mGetDeviceRequests.AppendElement(request);
 
   for (uint8_t i = 0; i < aDeviceAddress.Length(); i++) {
+
+    BluetoothAddress address;
+    nsresult rv = StringToAddress(aDeviceAddress[i], address);
+    if (NS_FAILED(rv)) {
+      DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+      return rv;
+    }
+
     // Retrieve all properties of devices
-    sBtInterface->GetRemoteDeviceProperties(aDeviceAddress[i],
-      new GetRemoteDevicePropertiesResultHandler(mGetDeviceRequests,
-                                                 aDeviceAddress[i]));
+    sBtInterface->GetRemoteDeviceProperties(address,
+      new GetRemoteDevicePropertiesResultHandler(mGetDeviceRequests, address));
   }
 
   return NS_OK;
 }
 
 class BluetoothServiceBluedroid::DispatchReplyErrorResultHandler final
   : public BluetoothResultHandler
 {
@@ -977,18 +1005,25 @@ BluetoothServiceBluedroid::FetchUuidsInt
   /*
    * get_remote_services request will not be performed by bluedroid
    * if it is currently discovering nearby remote devices.
    */
   if (mDiscovering) {
     StopDiscoveryInternal(aRunnable);
   }
 
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return rv;
+  }
+
   mFetchUuidsRunnables.AppendElement(aRunnable);
-  sBtInterface->GetRemoteServices(aDeviceAddress,
+  sBtInterface->GetRemoteServices(address,
     new DispatchReplyErrorResultHandler(mFetchUuidsRunnables, aRunnable));
 
   return NS_OK;
 }
 
 void
 BluetoothServiceBluedroid::StopDiscoveryInternal(
   BluetoothReplyRunnable* aRunnable)
@@ -1016,64 +1051,62 @@ BluetoothServiceBluedroid::SetProperty(B
     new DispatchReplyErrorResultHandler(mSetAdapterPropertyRunnables,
                                         aRunnable));
 
   return NS_OK;
 }
 
 struct BluetoothServiceBluedroid::GetRemoteServiceRecordRequest final
 {
-  GetRemoteServiceRecordRequest(const nsAString& aDeviceAddress,
+  GetRemoteServiceRecordRequest(const BluetoothAddress& aDeviceAddress,
                                 const BluetoothUuid& aUuid,
                                 BluetoothProfileManagerBase* aManager)
     : mDeviceAddress(aDeviceAddress)
     , mUuid(aUuid)
     , mManager(aManager)
   {
-    MOZ_ASSERT(!mDeviceAddress.IsEmpty());
     MOZ_ASSERT(mManager);
   }
 
-  nsString mDeviceAddress;
+  BluetoothAddress mDeviceAddress;
   BluetoothUuid mUuid;
   BluetoothProfileManagerBase* mManager;
 };
 
 class BluetoothServiceBluedroid::GetRemoteServiceRecordResultHandler final
   : public BluetoothResultHandler
 {
 public:
   GetRemoteServiceRecordResultHandler(
     nsTArray<GetRemoteServiceRecordRequest>& aGetRemoteServiceRecordArray,
-    const nsAString& aDeviceAddress,
+    const BluetoothAddress& aDeviceAddress,
     const BluetoothUuid& aUuid)
     : mGetRemoteServiceRecordArray(aGetRemoteServiceRecordArray)
     , mDeviceAddress(aDeviceAddress)
     , mUuid(aUuid)
-  {
-    MOZ_ASSERT(!mDeviceAddress.IsEmpty());
-  }
+  { }
 
   void OnError(BluetoothStatus aStatus) override
   {
     // Find call in array
 
     ssize_t i = FindRequest();
 
     if (i == -1) {
       BT_WARNING("No GetRemoteService request found");
       return;
     }
 
     // Signal error to profile manager
 
-    nsAutoString uuidStr;
+    nsAutoString addressStr, uuidStr;
+    AddressToString(mDeviceAddress, addressStr);
     UuidToString(mUuid, uuidStr);
     mGetRemoteServiceRecordArray[i].mManager->OnGetServiceChannel(
-      mDeviceAddress, uuidStr, -1);
+      addressStr, uuidStr, -1);
     mGetRemoteServiceRecordArray.RemoveElementAt(i);
   }
 
   void CancelDiscovery() override
   {
     // Disabled discovery mode, now perform SDP operation.
     sBtInterface->GetRemoteServiceRecord(mDeviceAddress, mUuid, this);
   }
@@ -1087,70 +1120,75 @@ private:
         return i;
       }
     }
 
     return -1;
   }
 
   nsTArray<GetRemoteServiceRecordRequest>& mGetRemoteServiceRecordArray;
-  nsString mDeviceAddress;
+  BluetoothAddress mDeviceAddress;
   BluetoothUuid mUuid;
 };
 
 nsresult
 BluetoothServiceBluedroid::GetServiceChannel(
   const nsAString& aDeviceAddress,
   const nsAString& aServiceUuid,
   BluetoothProfileManagerBase* aManager)
 {
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   BluetoothUuid uuid;
   StringToUuid(aServiceUuid, uuid);
+
   mGetRemoteServiceRecordArray.AppendElement(
-    GetRemoteServiceRecordRequest(aDeviceAddress, uuid, aManager));
+    GetRemoteServiceRecordRequest(address, uuid, aManager));
 
   nsRefPtr<BluetoothResultHandler> res =
     new GetRemoteServiceRecordResultHandler(mGetRemoteServiceRecordArray,
-                                            aDeviceAddress, uuid);
+                                            address, uuid);
 
   /* Stop discovery of remote devices here, because SDP operations
    * won't be performed while the adapter is in discovery mode.
    */
   if (mDiscovering) {
     sBtInterface->CancelDiscovery(res);
   } else {
-    sBtInterface->GetRemoteServiceRecord(
-      aDeviceAddress, uuid, res);
+    sBtInterface->GetRemoteServiceRecord(address, uuid, res);
   }
 
   return NS_OK;
 }
 
 struct BluetoothServiceBluedroid::GetRemoteServicesRequest final
 {
-  GetRemoteServicesRequest(const nsAString& aDeviceAddress,
+  GetRemoteServicesRequest(const BluetoothAddress& aDeviceAddress,
                            BluetoothProfileManagerBase* aManager)
     : mDeviceAddress(aDeviceAddress)
     , mManager(aManager)
   {
-    MOZ_ASSERT(!mDeviceAddress.IsEmpty());
     MOZ_ASSERT(mManager);
   }
 
-  const nsString mDeviceAddress;
+  BluetoothAddress mDeviceAddress;
   BluetoothProfileManagerBase* mManager;
 };
 
 class BluetoothServiceBluedroid::GetRemoteServicesResultHandler final
   : public BluetoothResultHandler
 {
 public:
   GetRemoteServicesResultHandler(
     nsTArray<GetRemoteServicesRequest>& aGetRemoteServicesArray,
-    const nsAString& aDeviceAddress,
+    const BluetoothAddress& aDeviceAddress,
     BluetoothProfileManagerBase* aManager)
     : mGetRemoteServicesArray(aGetRemoteServicesArray)
     , mDeviceAddress(aDeviceAddress)
     , mManager(aManager)
   { }
 
   void OnError(BluetoothStatus aStatus) override
   {
@@ -1162,17 +1200,19 @@ public:
       BT_WARNING("No GetRemoteServices request found");
       return;
     }
 
     // Cleanup array
     mGetRemoteServicesArray.RemoveElementAt(i);
 
     // There's no error-signaling mechanism; just call manager
-    mManager->OnUpdateSdpRecords(mDeviceAddress);
+    nsAutoString addressStr;
+    AddressToString(mDeviceAddress, addressStr);
+    mManager->OnUpdateSdpRecords(addressStr);
   }
 
   void CancelDiscovery() override
   {
     // Disabled discovery mode, now perform SDP operation.
     sBtInterface->GetRemoteServices(mDeviceAddress, this);
   }
 
@@ -1185,70 +1225,90 @@ private:
         return i;
       }
     }
 
     return -1;
   }
 
   nsTArray<GetRemoteServicesRequest>& mGetRemoteServicesArray;
-  const nsString mDeviceAddress;
+  BluetoothAddress mDeviceAddress;
   BluetoothProfileManagerBase* mManager;
 };
 
 bool
 BluetoothServiceBluedroid::UpdateSdpRecords(
   const nsAString& aDeviceAddress,
   BluetoothProfileManagerBase* aManager)
 {
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
   mGetRemoteServicesArray.AppendElement(
-    GetRemoteServicesRequest(aDeviceAddress, aManager));
+    GetRemoteServicesRequest(address, aManager));
 
   nsRefPtr<BluetoothResultHandler> res =
     new GetRemoteServicesResultHandler(mGetRemoteServicesArray,
-                                       aDeviceAddress, aManager);
+                                       address, aManager);
 
   /* Stop discovery of remote devices here, because SDP operations
    * won't be performed while the adapter is in discovery mode.
    */
   if (mDiscovering) {
     sBtInterface->CancelDiscovery(res);
   } else {
-    sBtInterface->GetRemoteServices(aDeviceAddress, res);
+    sBtInterface->GetRemoteServices(address, res);
   }
 
   return true;
 }
 
 nsresult
 BluetoothServiceBluedroid::CreatePairedDeviceInternal(
   const nsAString& aDeviceAddress, int aTimeout,
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return rv;
+  }
+
   mCreateBondRunnables.AppendElement(aRunnable);
-  sBtInterface->CreateBond(aDeviceAddress, TRANSPORT_AUTO,
+  sBtInterface->CreateBond(address, TRANSPORT_AUTO,
     new DispatchReplyErrorResultHandler(mCreateBondRunnables, aRunnable));
 
   return NS_OK;
 }
 
 nsresult
 BluetoothServiceBluedroid::RemoveDeviceInternal(
   const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return rv;
+  }
+
   mRemoveBondRunnables.AppendElement(aRunnable);
-  sBtInterface->RemoveBond(aDeviceAddress,
+  sBtInterface->RemoveBond(address,
     new DispatchReplyErrorResultHandler(mRemoveBondRunnables, aRunnable));
 
   return NS_OK;
 }
 
 class BluetoothServiceBluedroid::PinReplyResultHandler final
   : public BluetoothResultHandler
 {
@@ -1275,17 +1335,24 @@ void
 BluetoothServiceBluedroid::PinReplyInternal(
   const nsAString& aDeviceAddress, bool aAccept,
   const nsAString& aPinCode, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
-  sBtInterface->PinReply(aDeviceAddress, aAccept, aPinCode,
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
+  sBtInterface->PinReply(address, aAccept, aPinCode,
                          new PinReplyResultHandler(aRunnable));
 }
 
 void
 BluetoothServiceBluedroid::SetPinCodeInternal(
   const nsAString& aDeviceAddress, const nsAString& aPinCode,
   BluetoothReplyRunnable* aRunnable)
 {
@@ -1326,17 +1393,24 @@ void
 BluetoothServiceBluedroid::SspReplyInternal(
   const nsAString& aDeviceAddress, BluetoothSspVariant aVariant,
   bool aAccept, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
-  sBtInterface->SspReply(aDeviceAddress, aVariant, aAccept, 0 /* passkey */,
+  BluetoothAddress address;
+  nsresult rv = StringToAddress(aDeviceAddress, address);
+  if (NS_FAILED(rv)) {
+    DispatchReplyError(aRunnable, STATUS_PARM_INVALID);
+    return;
+  }
+
+  sBtInterface->SspReply(address, aVariant, aAccept, 0 /* passkey */,
                          new SspReplyResultHandler(aRunnable));
 }
 
 void
 BluetoothServiceBluedroid::SetPairingConfirmationInternal(
   const nsAString& aDeviceAddress, bool aConfirm,
   BluetoothReplyRunnable* aRunnable)
 {
@@ -1806,22 +1880,25 @@ BluetoothServiceBluedroid::AdapterStateC
       BluetoothGattManager::DeinitGattInterface
     };
 
     // Return error if BluetoothService is unavailable
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE_VOID(bs);
 
     // Cleanup static adapter properties and notify adapter.
-    mBdAddress.Truncate();
+    mBdAddress.Clear();
     mBdName.Truncate();
 
+    nsAutoString bdAddressStr;
+    AddressToString(mBdAddress, bdAddressStr);
+
     InfallibleTArray<BluetoothNamedValue> props;
     AppendNamedValue(props, "Name", mBdName);
-    AppendNamedValue(props, "Address", mBdAddress);
+    AppendNamedValue(props, "Address", bdAddressStr);
     if (mDiscoverable) {
       mDiscoverable = false;
       AppendNamedValue(props, "Discoverable", false);
     }
     if (mDiscovering) {
       mDiscovering = false;
       AppendNamedValue(props, "Discovering", false);
     }
@@ -1910,18 +1987,21 @@ BluetoothServiceBluedroid::AdapterProper
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
   for (int i = 0; i < aNumProperties; i++) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDADDR) {
-      mBdAddress = p.mString;
-      AppendNamedValue(propertiesArray, "Address", mBdAddress);
+      mBdAddress = p.mBdAddress;
+      nsAutoString addressStr;
+      AddressToString(mBdAddress, addressStr);
+
+      AppendNamedValue(propertiesArray, "Address", addressStr);
 
     } else if (p.mType == PROPERTY_BDNAME) {
       mBdName = p.mString;
       AppendNamedValue(propertiesArray, "Name", mBdName);
 
     } else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
 
       // If BT is not enabled, Bluetooth scan mode should be non-discoverable
@@ -1931,23 +2011,31 @@ BluetoothServiceBluedroid::AdapterProper
         mDiscoverable = (p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
         AppendNamedValue(propertiesArray, "Discoverable", mDiscoverable);
       }
     } else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
       // We have to cache addresses of bonded devices. Unlike BlueZ,
       // Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
       // event after bond completed.
       BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d",
-              p.mStringArray.Length());
+              p.mBdAddressArray.Length());
 
       // Whenever reloading paired devices, force refresh
       mBondedAddresses.Clear();
-      mBondedAddresses.AppendElements(p.mStringArray);
-
-      AppendNamedValue(propertiesArray, "PairedDevices", mBondedAddresses);
+      mBondedAddresses.AppendElements(p.mBdAddressArray);
+
+      nsTArray<nsString> bondedAddresses;
+
+      for (unsigned int j = 0; j < p.mBdAddressArray.Length(); ++j) {
+        nsAutoString addressStr;
+        AddressToString(p.mBdAddressArray[j], addressStr);
+        bondedAddresses.AppendElement(addressStr);
+      }
+
+      AppendNamedValue(propertiesArray, "PairedDevices", bondedAddresses);
     } else if (p.mType == PROPERTY_UNKNOWN) {
       /* Bug 1065999: working around unknown properties */
     } else {
       BT_LOGD("Unhandled adapter property type: %d", p.mType);
       continue;
     }
   }
 
@@ -1970,36 +2058,38 @@ BluetoothServiceBluedroid::AdapterProper
  *   (1) automatically by Bluedroid when BT is turning on, or
  *   (2) as result of remote device properties update during discovery, or
  *   (3) as result of CreateBond, or
  *   (4) as result of GetRemoteDeviceProperties, or
  *   (5) as result of GetRemoteServices.
  */
 void
 BluetoothServiceBluedroid::RemoteDevicePropertiesNotification(
-  BluetoothStatus aStatus, const nsAString& aBdAddr,
+  BluetoothStatus aStatus, const BluetoothAddress& aBdAddr,
   int aNumProperties, const BluetoothProperty* aProperties)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
-  nsString bdAddr(aBdAddr);
-  AppendNamedValue(propertiesArray, "Address", bdAddr);
+  nsAutoString bdAddrStr;
+  AddressToString(aBdAddr, bdAddrStr);
+
+  AppendNamedValue(propertiesArray, "Address", bdAddrStr);
 
   for (int i = 0; i < aNumProperties; ++i) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDNAME) {
       AppendNamedValue(propertiesArray, "Name", p.mString);
 
       // Update <address, name> mapping
-      mDeviceNameMap.Remove(bdAddr);
-      mDeviceNameMap.Put(bdAddr, p.mString);
+      mDeviceNameMap.Remove(aBdAddr);
+      mDeviceNameMap.Put(aBdAddr, p.mString);
     } else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
       uint32_t cod = p.mUint32;
       AppendNamedValue(propertiesArray, "Cod", cod);
 
     } else if (p.mType == PROPERTY_UUIDS) {
 
       size_t index;
 
@@ -2007,17 +2097,17 @@ BluetoothServiceBluedroid::RemoteDeviceP
 
       for (index = 0; index < mGetRemoteServicesArray.Length(); ++index) {
         if (mGetRemoteServicesArray[index].mDeviceAddress == aBdAddr) {
           break;
         }
       }
 
       if (index < mGetRemoteServicesArray.Length()) {
-        mGetRemoteServicesArray[index].mManager->OnUpdateSdpRecords(aBdAddr);
+        mGetRemoteServicesArray[index].mManager->OnUpdateSdpRecords(bdAddrStr);
         mGetRemoteServicesArray.RemoveElementAt(index);
         continue; // continue with outer loop
       }
 
       // Handler for |FetchUuidsInternal|
 
       nsTArray<nsString> uuids;
 
@@ -2046,17 +2136,17 @@ BluetoothServiceBluedroid::RemoteDeviceP
         if ((mGetRemoteServiceRecordArray[i].mDeviceAddress == aBdAddr) &&
             (mGetRemoteServiceRecordArray[i].mUuid == p.mServiceRecord.mUuid)) {
 
           // Signal channel to profile manager
           nsAutoString uuidStr;
           UuidToString(mGetRemoteServiceRecordArray[i].mUuid, uuidStr);
 
           mGetRemoteServiceRecordArray[i].mManager->OnGetServiceChannel(
-            aBdAddr, uuidStr, p.mServiceRecord.mChannel);
+            bdAddrStr, uuidStr, p.mServiceRecord.mChannel);
 
           mGetRemoteServiceRecordArray.RemoveElementAt(i);
           break;
         }
       }
       unused << NS_WARN_IF(i == mGetRemoteServiceRecordArray.Length());
     } else if (p.mType == PROPERTY_UNKNOWN) {
       /* Bug 1065999: working around unknown properties */
@@ -2073,17 +2163,17 @@ BluetoothServiceBluedroid::RemoteDeviceP
   //
   // |DispatchReplySuccess| creates its own internal runnable, which is
   // always run after we completed the current method. This means that we
   // can exchange |DispatchReplySuccess| with other operations without
   // changing the order of (1,2) and (3).
 
   // Update to registered BluetoothDevice objects
   BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
-                         bdAddr, propertiesArray);
+                         bdAddrStr, propertiesArray);
 
   // FetchUuids task
   if (!mFetchUuidsRunnables.IsEmpty()) {
     // propertiesArray contains Address and Uuids only
     DispatchReplySuccess(mFetchUuidsRunnables[0],
                          propertiesArray[1].value()); /* Uuids */
     mFetchUuidsRunnables.RemoveElementAt(0);
     DistributeSignal(signal);
@@ -2094,17 +2184,17 @@ BluetoothServiceBluedroid::RemoteDeviceP
   if (mGetDeviceRequests.IsEmpty()) {
     // Callback is called after Bluetooth is turned on
     DistributeSignal(signal);
     return;
   }
 
   // Use address as the index
   mGetDeviceRequests[0].mDevicesPack.AppendElement(
-    BluetoothNamedValue(bdAddr, propertiesArray));
+    BluetoothNamedValue(bdAddrStr, propertiesArray));
 
   if (--mGetDeviceRequests[0].mDeviceCount == 0) {
     if (mGetDeviceRequests[0].mRunnable) {
       DispatchReplySuccess(mGetDeviceRequests[0].mRunnable,
                            mGetDeviceRequests[0].mDevicesPack);
     }
     mGetDeviceRequests.RemoveElementAt(0);
   }
@@ -2115,24 +2205,28 @@ BluetoothServiceBluedroid::RemoteDeviceP
 void
 BluetoothServiceBluedroid::DeviceFoundNotification(
   int aNumProperties, const BluetoothProperty* aProperties)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
-  nsString bdAddr, bdName;
+  BluetoothAddress bdAddr;
+  nsString bdName;
+
   for (int i = 0; i < aNumProperties; i++) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDADDR) {
-      AppendNamedValue(propertiesArray, "Address", p.mString);
-      bdAddr = p.mString;
+      nsAutoString addressStr;
+      AddressToString(p.mBdAddress, addressStr);
+      AppendNamedValue(propertiesArray, "Address", addressStr);
+      bdAddr = p.mBdAddress;
     } else if (p.mType == PROPERTY_BDNAME) {
       AppendNamedValue(propertiesArray, "Name", p.mString);
       bdName = p.mString;
     } else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
       AppendNamedValue(propertiesArray, "Cod", p.mUint32);
 
     } else if (p.mType == PROPERTY_UUIDS) {
       nsTArray<nsString> uuids;
@@ -2187,62 +2281,66 @@ BluetoothServiceBluedroid::DiscoveryStat
   if (!mChangeDiscoveryRunnables.IsEmpty()) {
     DispatchReplySuccess(mChangeDiscoveryRunnables[0]);
     mChangeDiscoveryRunnables.RemoveElementAt(0);
   }
 }
 
 void
 BluetoothServiceBluedroid::PinRequestNotification(
-  const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod)
+  const BluetoothAddress& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
   // If |aBdName| is empty, get device name from |mDeviceNameMap|;
   // Otherwise update <address, name> mapping with |aBdName|
-  nsString bdAddr(aRemoteBdAddr);
+  nsAutoString bdAddr;
+  AddressToString(aRemoteBdAddr, bdAddr);
+
   nsString bdName(aBdName);
   if (bdName.IsEmpty()) {
-    mDeviceNameMap.Get(bdAddr, &bdName);
+    mDeviceNameMap.Get(aRemoteBdAddr, &bdName);
   } else {
-    mDeviceNameMap.Remove(bdAddr);
-    mDeviceNameMap.Put(bdAddr, bdName);
+    mDeviceNameMap.Remove(aRemoteBdAddr);
+    mDeviceNameMap.Put(aRemoteBdAddr, bdName);
   }
 
   AppendNamedValue(propertiesArray, "address", bdAddr);
   AppendNamedValue(propertiesArray, "name", bdName);
   AppendNamedValue(propertiesArray, "passkey", EmptyString());
   AppendNamedValue(propertiesArray, "type",
                    NS_LITERAL_STRING(PAIRING_REQ_TYPE_ENTERPINCODE));
 
   DistributeSignal(NS_LITERAL_STRING("PairingRequest"),
                    NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
                    BluetoothValue(propertiesArray));
 }
 
 void
 BluetoothServiceBluedroid::SspRequestNotification(
-  const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod,
-  BluetoothSspVariant aPairingVariant, uint32_t aPassKey)
+  const BluetoothAddress& aRemoteBdAddr, const nsAString& aBdName,
+  uint32_t aCod, BluetoothSspVariant aPairingVariant, uint32_t aPassKey)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
   // If |aBdName| is empty, get device name from |mDeviceNameMap|;
   // Otherwise update <address, name> mapping with |aBdName|
-  nsString bdAddr(aRemoteBdAddr);
+  nsAutoString bdAddr;
+  AddressToString(aRemoteBdAddr, bdAddr);
+
   nsString bdName(aBdName);
   if (bdName.IsEmpty()) {
-    mDeviceNameMap.Get(bdAddr, &bdName);
+    mDeviceNameMap.Get(aRemoteBdAddr, &bdName);
   } else {
-    mDeviceNameMap.Remove(bdAddr);
-    mDeviceNameMap.Put(bdAddr, bdName);
+    mDeviceNameMap.Remove(aRemoteBdAddr);
+    mDeviceNameMap.Put(aRemoteBdAddr, bdName);
   }
 
   /**
    * Assign pairing request type and passkey based on the pairing variant.
    *
    * passkey value based on pairing request type:
    * 1) aPasskey: PAIRING_REQ_TYPE_CONFIRMATION and
    *              PAIRING_REQ_TYPE_DISPLAYPASSKEY
@@ -2274,17 +2372,17 @@ BluetoothServiceBluedroid::SspRequestNot
 
   DistributeSignal(NS_LITERAL_STRING("PairingRequest"),
                    NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
                    BluetoothValue(propertiesArray));
 }
 
 void
 BluetoothServiceBluedroid::BondStateChangedNotification(
-  BluetoothStatus aStatus, const nsAString& aRemoteBdAddr,
+  BluetoothStatus aStatus, const BluetoothAddress& aRemoteBdAddr,
   BluetoothBondState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aState == BOND_STATE_BONDING) {
     // No need to handle bonding state
     return;
   }
@@ -2310,27 +2408,29 @@ BluetoothServiceBluedroid::BondStateChan
       DispatchReplyError(mRemoveBondRunnables[0], aStatus);
       mRemoveBondRunnables.RemoveElementAt(0);
     }
 
     return;
   }
 
   // Query pairing device name from hash table
-  nsString remoteBdAddr(aRemoteBdAddr);
+  nsAutoString remoteBdAddr;
+  AddressToString(aRemoteBdAddr, remoteBdAddr);
+
   nsString remotebdName;
-  mDeviceNameMap.Get(remoteBdAddr, &remotebdName);
+  mDeviceNameMap.Get(aRemoteBdAddr, &remotebdName);
 
   // Update bonded address array and append pairing device name
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
   if (!bonded) {
-    mBondedAddresses.RemoveElement(remoteBdAddr);
+    mBondedAddresses.RemoveElement(aRemoteBdAddr);
   } else {
-    if (!mBondedAddresses.Contains(remoteBdAddr)) {
-      mBondedAddresses.AppendElement(remoteBdAddr);
+    if (!mBondedAddresses.Contains(aRemoteBdAddr)) {
+      mBondedAddresses.AppendElement(aRemoteBdAddr);
     }
 
     // We don't assert |!remotebdName.IsEmpty()| since empty string is also
     // valid, according to Bluetooth Core Spec. v3.0 - Sec. 6.22:
     // "a valid Bluetooth name is a UTF-8 encoding string which is up to 248
     // bytes in length."
     AppendNamedValue(propertiesArray, "Name", remotebdName);
   }
@@ -2355,17 +2455,17 @@ BluetoothServiceBluedroid::BondStateChan
   } else if (!bonded && !mRemoveBondRunnables.IsEmpty()) {
     DispatchReplySuccess(mRemoveBondRunnables[0]);
     mRemoveBondRunnables.RemoveElementAt(0);
   }
 }
 
 void
 BluetoothServiceBluedroid::AclStateChangedNotification(
-  BluetoothStatus aStatus, const nsAString& aRemoteBdAddr, bool aState)
+  BluetoothStatus aStatus, const BluetoothAddress& aRemoteBdAddr, bool aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // FIXME: This will be implemented in the later patchset
 }
 
 void
 BluetoothServiceBluedroid::DutModeRecvNotification(uint16_t aOpcode,
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.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_bluetooth_bluedroid_BluetoothServiceBluedroid_h
 #define mozilla_dom_bluetooth_bluedroid_BluetoothServiceBluedroid_h
 
 #include "BluetoothCommon.h"
+#include "BluetoothHashKeys.h"
 #include "BluetoothInterface.h"
 #include "BluetoothService.h"
 #include "nsDataHashtable.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothServiceBluedroid : public BluetoothService
                                 , public BluetoothNotificationHandler
@@ -383,39 +384,39 @@ public:
   //
 
   virtual void AdapterStateChangedNotification(bool aState) override;
   virtual void AdapterPropertiesNotification(
     BluetoothStatus aStatus, int aNumProperties,
     const BluetoothProperty* aProperties) override;
 
   virtual void RemoteDevicePropertiesNotification(
-    BluetoothStatus aStatus, const nsAString& aBdAddr,
+    BluetoothStatus aStatus, const BluetoothAddress& aBdAddr,
     int aNumProperties, const BluetoothProperty* aProperties) override;
 
   virtual void DeviceFoundNotification(
     int aNumProperties, const BluetoothProperty* aProperties) override;
 
   virtual void DiscoveryStateChangedNotification(bool aState) override;
 
-  virtual void PinRequestNotification(const nsAString& aRemoteBdAddr,
+  virtual void PinRequestNotification(const BluetoothAddress& aRemoteBdAddr,
                                       const nsAString& aBdName,
                                       uint32_t aCod) override;
-  virtual void SspRequestNotification(const nsAString& aRemoteBdAddr,
+  virtual void SspRequestNotification(const BluetoothAddress& aRemoteBdAddr,
                                       const nsAString& aBdName,
                                       uint32_t aCod,
                                       BluetoothSspVariant aPairingVariant,
                                       uint32_t aPasskey) override;
 
   virtual void BondStateChangedNotification(
-    BluetoothStatus aStatus, const nsAString& aRemoteBdAddr,
+    BluetoothStatus aStatus, const BluetoothAddress& aRemoteBdAddr,
     BluetoothBondState aState) override;
-  virtual void AclStateChangedNotification(BluetoothStatus aStatus,
-                                           const nsAString& aRemoteBdAddr,
-                                           bool aState) override;
+  virtual void AclStateChangedNotification(
+    BluetoothStatus aStatus, const BluetoothAddress& aRemoteBdAddr,
+    bool aState) override;
 
   virtual void DutModeRecvNotification(uint16_t aOpcode,
                                        const uint8_t* aBuf,
                                        uint8_t aLen) override;
   virtual void LeTestModeNotification(BluetoothStatus aStatus,
                                       uint16_t aNumPackets) override;
 
   virtual void EnergyInfoNotification(
@@ -434,22 +435,22 @@ protected:
 
   static void ConnectDisconnect(bool aConnect,
                                 const nsAString& aDeviceAddress,
                                 BluetoothReplyRunnable* aRunnable,
                                 uint16_t aServiceUuid, uint32_t aCod = 0);
   static void NextBluetoothProfileController();
 
   // Adapter properties
-  nsString mBdAddress;
+  BluetoothAddress mBdAddress;
   nsString mBdName;
   bool mEnabled;
   bool mDiscoverable;
   bool mDiscovering;
-  nsTArray<nsString> mBondedAddresses;
+  nsTArray<BluetoothAddress> mBondedAddresses;
 
   // Backend error recovery
   bool mIsRestart;
   bool mIsFirstTimeToggleOffBt;
 
   // Runnable arrays
   nsTArray<nsRefPtr<BluetoothReplyRunnable>> mChangeAdapterStateRunnables;
   nsTArray<nsRefPtr<BluetoothReplyRunnable>> mSetAdapterPropertyRunnables;
@@ -460,17 +461,17 @@ protected:
 
   // Array of get device requests. Each request remembers
   // 1) remaining device count to receive properties,
   // 2) received remote device properties, and
   // 3) runnable to reply success/error
   nsTArray<GetDeviceRequest> mGetDeviceRequests;
 
   // <address, name> mapping table for remote devices
-  nsDataHashtable<nsStringHashKey, nsString> mDeviceNameMap;
+  nsDataHashtable<BluetoothAddressHashKey, nsString> mDeviceNameMap;
 
   // Arrays for SDP operations
   nsTArray<GetRemoteServiceRecordRequest> mGetRemoteServiceRecordArray;
   nsTArray<GetRemoteServicesRequest> mGetRemoteServicesArray;
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -381,34 +381,37 @@ class AcceptResultHandler final : public
 {
 public:
   AcceptResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
     MOZ_ASSERT(mImpl);
   }
 
-  void Accept(int aFd, const nsAString& aBdAddress,
+  void Accept(int aFd, const BluetoothAddress& aBdAddress,
               int aConnectionStatus) override
   {
     MOZ_ASSERT(mImpl->IsConsumerThread());
 
     mozilla::ScopedClose fd(aFd); // Close received socket fd on error
 
     if (mImpl->IsShutdownOnConsumerThread()) {
       BT_LOGD("mConsumer is null, aborting receive!");
       return;
     }
 
     if (aConnectionStatus != 0) {
       mImpl->mConsumer->NotifyError();
       return;
     }
 
-    mImpl->mConsumer->SetAddress(aBdAddress);
+    nsAutoString addressStr;
+    AddressToString(aBdAddress, addressStr);
+
+    mImpl->mConsumer->SetAddress(addressStr);
     mImpl->GetIOLoop()->PostTask(FROM_HERE,
                                  new AcceptTask(mImpl, fd.forget()));
   }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(mImpl->IsConsumerThread());
     BT_LOGR("BluetoothSocketInterface::Accept failed: %d", (int)aStatus);
@@ -602,32 +605,35 @@ class ConnectSocketResultHandler final :
 {
 public:
   ConnectSocketResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
     MOZ_ASSERT(mImpl);
   }
 
-  void Connect(int aFd, const nsAString& aBdAddress,
+  void Connect(int aFd, const BluetoothAddress& aBdAddress,
                int aConnectionStatus) override
   {
     MOZ_ASSERT(mImpl->IsConsumerThread());
 
     if (mImpl->IsShutdownOnConsumerThread()) {
       BT_LOGD("mConsumer is null, aborting send!");
       return;
     }
 
     if (aConnectionStatus != 0) {
       mImpl->mConsumer->NotifyError();
       return;
     }
 
-    mImpl->mConsumer->SetAddress(aBdAddress);
+    nsAutoString addressStr;
+    AddressToString(aBdAddress, addressStr);
+
+    mImpl->mConsumer->SetAddress(addressStr);
     mImpl->GetIOLoop()->PostTask(FROM_HERE,
                                  new SocketConnectTask(mImpl, aFd));
   }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(mImpl->IsConsumerThread());
     BT_WARNING("Connect failed: %d", (int)aStatus);
@@ -658,18 +664,24 @@ BluetoothSocket::Connect(const nsAString
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
   mImpl = new DroidSocketImpl(aConsumerLoop, aIOLoop, this);
 
   BluetoothSocketResultHandler* res = new ConnectSocketResultHandler(mImpl);
   SetCurrentResultHandler(res);
 
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(aDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   sBluetoothSocketInterface->Connect(
-    aDeviceAddress, aType,
+    deviceAddress, aType,
     aServiceUuid, aChannel,
     aEncrypt, aAuth, res);
 
   return NS_OK;
 }
 
 nsresult
 BluetoothSocket::Connect(const nsAString& aDeviceAddress,
--- a/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.cpp
@@ -130,20 +130,20 @@ SocketMessageWatcher::GetChannel1() cons
 }
 
 int32_t
 SocketMessageWatcher::GetSize() const
 {
   return ReadInt16(OFF_SIZE);
 }
 
-nsString
+BluetoothAddress
 SocketMessageWatcher::GetBdAddress() const
 {
-  nsString bdAddress;
+  BluetoothAddress bdAddress;
   ReadBdAddress(OFF_BDADDRESS, bdAddress);
   return bdAddress;
 }
 
 int32_t
 SocketMessageWatcher::GetChannel2() const
 {
   return ReadInt32(OFF_CHANNEL2);
@@ -254,34 +254,24 @@ SocketMessageWatcher::ReadInt32(unsigned
   return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
          (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
          (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
           static_cast<int32_t>(mBuf[aOffset]);
 }
 
 void
 SocketMessageWatcher::ReadBdAddress(unsigned long aOffset,
-                                    nsAString& aBdAddress) const
+                                    BluetoothAddress& aBdAddress) const
 {
-  char str[BLUETOOTH_ADDRESS_LENGTH + 1];
-
-  int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
-                     static_cast<int>(mBuf[aOffset + 0]),
-                     static_cast<int>(mBuf[aOffset + 1]),
-                     static_cast<int>(mBuf[aOffset + 2]),
-                     static_cast<int>(mBuf[aOffset + 3]),
-                     static_cast<int>(mBuf[aOffset + 4]),
-                     static_cast<int>(mBuf[aOffset + 5]));
-  if (res < 0) {
-    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
-  } else if ((size_t)res >= sizeof(str)) { /* string buffer too small */
-    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
-  } else {
-    aBdAddress = NS_ConvertUTF8toUTF16(str);
-  }
+  aBdAddress = BluetoothAddress(mBuf[aOffset + 0],
+                                mBuf[aOffset + 1],
+                                mBuf[aOffset + 2],
+                                mBuf[aOffset + 3],
+                                mBuf[aOffset + 4],
+                                mBuf[aOffset + 5]);
 }
 
 //
 // SocketMessageWatcherTask
 //
 
 SocketMessageWatcherTask::SocketMessageWatcherTask(
   SocketMessageWatcher* aWatcher)
--- a/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.h
@@ -47,36 +47,36 @@ public:
   void OnFileCanReadWithoutBlocking(int aFd) override;
   void OnFileCanWriteWithoutBlocking(int aFd) override;
 
   void Watch();
   void StopWatching();
 
   bool IsComplete() const;
 
-  int      GetFd() const;
-  int32_t  GetChannel1() const;
-  int32_t  GetSize() const;
-  nsString GetBdAddress() const;
-  int32_t  GetChannel2() const;
-  int32_t  GetConnectionStatus() const;
-  int      GetClientFd() const;
+  int              GetFd() const;
+  int32_t          GetChannel1() const;
+  int32_t          GetSize() const;
+  BluetoothAddress GetBdAddress() const;
+  int32_t          GetChannel2() const;
+  int32_t          GetConnectionStatus() const;
+  int              GetClientFd() const;
 
   BluetoothSocketResultHandler* GetResultHandler() const;
 
 protected:
   SocketMessageWatcher(int aFd, BluetoothSocketResultHandler* aRes);
 
 private:
   BluetoothStatus RecvMsg1();
   BluetoothStatus RecvMsg2();
 
   int16_t ReadInt16(unsigned long aOffset) const;
   int32_t ReadInt32(unsigned long aOffset) const;
-  void    ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const;
+  void    ReadBdAddress(unsigned long aOffset, BluetoothAddress& aBdAddress) const;
 
   MessageLoopForIO::FileDescriptorWatcher mWatcher;
   int mFd;
   int mClientFd;
   unsigned char mLen;
   uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
   nsRefPtr<BluetoothSocketResultHandler> mRes;
 };
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -649,19 +649,25 @@ BluetoothHfpManager::HandleVolumeChanged
   // Adjust volume by headset and we don't have to send volume back to headset
   if (mReceiveVgsFlag) {
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
   if (IsConnected()) {
+    BluetoothAddress deviceAddress;
+    nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+    if (NS_FAILED(rv)) {
+      return;
+    }
+
     NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
     sBluetoothHfpInterface->VolumeControl(
-      HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs, mDeviceAddress,
+      HFP_VOLUME_TYPE_SPEAKER, mCurrentVgs, deviceAddress,
       new VolumeControlResultHandler());
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIMobileConnectionService> mcService =
@@ -786,20 +792,26 @@ BluetoothHfpManager::SendCLCC(Call& aCal
                                                HFP_CALL_STATE_ACTIVE;
   }
 
   if (callState == HFP_CALL_STATE_INCOMING &&
       FindFirstCall(nsITelephonyService::CALL_STATE_CONNECTED)) {
     callState = HFP_CALL_STATE_WAITING;
   }
 
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   sBluetoothHfpInterface->ClccResponse(
     aIndex, aCall.mDirection, callState, HFP_CALL_MODE_VOICE,
     HFP_CALL_MPTY_TYPE_SINGLE, aCall.mNumber,
-    aCall.mType, mDeviceAddress, new ClccResponseResultHandler());
+    aCall.mType, deviceAddress, new ClccResponseResultHandler());
 }
 
 class BluetoothHfpManager::FormattedAtResponseResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
   {
@@ -808,18 +820,24 @@ public:
   }
 };
 
 void
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   sBluetoothHfpInterface->FormattedAtResponse(
-    aMessage, mDeviceAddress, new FormattedAtResponseResultHandler());
+    aMessage, deviceAddress, new FormattedAtResponseResultHandler());
 }
 
 class BluetoothHfpManager::AtResponseResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
   {
@@ -828,18 +846,24 @@ public:
   }
 };
 
 void
 BluetoothHfpManager::SendResponse(BluetoothHandsfreeAtResponse aResponseCode)
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   sBluetoothHfpInterface->AtResponse(
-    aResponseCode, 0, mDeviceAddress, new AtResponseResultHandler());
+    aResponseCode, 0, deviceAddress, new AtResponseResultHandler());
 }
 
 class BluetoothHfpManager::PhoneStateChangeResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
   {
@@ -1130,23 +1154,29 @@ BluetoothHfpManager::ToggleCalls()
  * error. The state change triggers UI status bar update as ordinary bluetooth
  * turn-off sequence.
  */
 void
 BluetoothHfpManager::HandleBackendError()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   if (mConnectionState != HFP_CONNECTION_STATE_DISCONNECTED) {
     ConnectionStateNotification(HFP_CONNECTION_STATE_DISCONNECTED,
-      mDeviceAddress);
+      deviceAddress);
   }
 
   if (mAudioState != HFP_AUDIO_STATE_DISCONNECTED) {
-    AudioStateNotification(HFP_AUDIO_STATE_DISCONNECTED, mDeviceAddress);
+    AudioStateNotification(HFP_AUDIO_STATE_DISCONNECTED, deviceAddress);
   }
 }
 
 class BluetoothHfpManager::ConnectAudioResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
@@ -1160,17 +1190,23 @@ bool
 BluetoothHfpManager::ConnectSco()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE(!sInShutdown, false);
   NS_ENSURE_TRUE(IsConnected() && !IsScoConnected(), false);
   NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
 
-  sBluetoothHfpInterface->ConnectAudio(mDeviceAddress,
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  sBluetoothHfpInterface->ConnectAudio(deviceAddress,
                                        new ConnectAudioResultHandler());
 
   return true;
 }
 
 class BluetoothHfpManager::DisconnectAudioResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
@@ -1183,17 +1219,23 @@ public:
 };
 
 bool
 BluetoothHfpManager::DisconnectSco()
 {
   NS_ENSURE_TRUE(IsScoConnected(), false);
   NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
 
-  sBluetoothHfpInterface->DisconnectAudio(mDeviceAddress,
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  sBluetoothHfpInterface->DisconnectAudio(deviceAddress,
                                           new DisconnectAudioResultHandler());
 
   return true;
 }
 
 bool
 BluetoothHfpManager::IsScoConnected()
 {
@@ -1260,17 +1302,23 @@ BluetoothHfpManager::Connect(const nsASt
     BT_LOGR("sBluetoothHfpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
   mDeviceAddress = aDeviceAddress;
   mController = aController;
 
-  sBluetoothHfpInterface->Connect(mDeviceAddress,
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  sBluetoothHfpInterface->Connect(deviceAddress,
                                   new ConnectResultHandler(this));
 }
 
 void
 BluetoothHfpManager::OnDisconnectError()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(mController);
@@ -1311,17 +1359,23 @@ BluetoothHfpManager::Disconnect(Bluetoot
     if (aController) {
       aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     }
     return;
   }
 
   mController = aController;
 
-  sBluetoothHfpInterface->Disconnect(mDeviceAddress,
+  BluetoothAddress deviceAddress;
+  nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  sBluetoothHfpInterface->Disconnect(deviceAddress,
                                      new DisconnectResultHandler(this));
 }
 
 void
 BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -1373,77 +1427,83 @@ BluetoothHfpManager::GetAddress(nsAStrin
 }
 
 //
 // Bluetooth notifications
 //
 
 void
 BluetoothHfpManager::ConnectionStateNotification(
-  BluetoothHandsfreeConnectionState aState, const nsAString& aBdAddress)
+  BluetoothHandsfreeConnectionState aState, const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BT_LOGR("state %d", aState);
 
   mPrevConnectionState = mConnectionState;
   mConnectionState = aState;
 
   if (aState == HFP_CONNECTION_STATE_SLC_CONNECTED) {
-    mDeviceAddress = aBdAddress;
+    AddressToString(aBdAddress, mDeviceAddress);
     NotifyConnectionStateChanged(
       NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
 
   } else if (aState == HFP_CONNECTION_STATE_DISCONNECTED) {
     DisconnectSco();
     NotifyConnectionStateChanged(
       NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID));
 
   } else if (aState == HFP_CONNECTION_STATE_CONNECTED) {
+    BluetoothAddress deviceAddress;
+    nsresult rv = StringToAddress(mDeviceAddress, deviceAddress);
+    if (NS_FAILED(rv)) {
+      return;
+    }
+
     // Once RFCOMM is connected, enable NREC before each new SLC connection
-    NRECNotification(HFP_NREC_STARTED, mDeviceAddress);
+    NRECNotification(HFP_NREC_STARTED, deviceAddress);
   }
 }
 
 void
 BluetoothHfpManager::AudioStateNotification(
-  BluetoothHandsfreeAudioState aState, const nsAString& aBdAddress)
+  BluetoothHandsfreeAudioState aState, const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   BT_LOGR("state %d", aState);
 
   mAudioState = aState;
 
   if (aState == HFP_AUDIO_STATE_CONNECTED ||
       aState == HFP_AUDIO_STATE_DISCONNECTED) {
     NotifyConnectionStateChanged(
       NS_LITERAL_STRING(BLUETOOTH_SCO_STATUS_CHANGED_ID));
   }
 }
 
 void
-BluetoothHfpManager::AnswerCallNotification(const nsAString& aBdAddress)
+BluetoothHfpManager::AnswerCallNotification(const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NotifyDialer(NS_LITERAL_STRING("ATA"));
 }
 
 void
-BluetoothHfpManager::HangupCallNotification(const nsAString& aBdAddress)
+BluetoothHfpManager::HangupCallNotification(const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NotifyDialer(NS_LITERAL_STRING("CHUP"));
 }
 
 void
 BluetoothHfpManager::VolumeNotification(
-  BluetoothHandsfreeVolumeType aType, int aVolume, const nsAString& aBdAddress)
+  BluetoothHandsfreeVolumeType aType, int aVolume, const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE_VOID(aVolume >= 0 && aVolume <= 15);
 
   if (aType == HFP_VOLUME_TYPE_MICROPHONE) {
     mCurrentVgm = aVolume;
   } else if (aType == HFP_VOLUME_TYPE_SPEAKER) {
@@ -1460,17 +1520,17 @@ BluetoothHfpManager::VolumeNotification(
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     NS_ENSURE_TRUE_VOID(os);
 
     os->NotifyObservers(nullptr, "bluetooth-volume-change", data.get());
   }
 }
 
 void
-BluetoothHfpManager::DtmfNotification(char aDtmf, const nsAString& aBdAddress)
+BluetoothHfpManager::DtmfNotification(char aDtmf, const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE_VOID(IsValidDtmf(aDtmf));
 
   nsAutoCString message("VTS=");
   message += aDtmf;
   NotifyDialer(NS_ConvertUTF8toUTF16(message));
@@ -1479,17 +1539,17 @@ BluetoothHfpManager::DtmfNotification(ch
 /**
  * NREC status will be set when:
  * 1. Get an AT command from HF device.
  *    (Bluetooth HFP spec v1.6 merely defines for the "Disable" part.)
  * 2. Once RFCOMM is connected, enable NREC before each new SLC connection.
  */
 void
 BluetoothHfpManager::NRECNotification(BluetoothHandsfreeNRECState aNrec,
-                                      const nsAString& aBdAddr)
+                                      const BluetoothAddress& aBdAddr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Notify Gecko observers
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
 
   mNrecEnabled = static_cast<bool>(aNrec);
@@ -1500,17 +1560,17 @@ BluetoothHfpManager::NRECNotification(Bl
                                      mDeviceAddress.get()))) {
     BT_WARNING("Failed to notify bluetooth-hfp-nrec-status-changed observsers!");
   }
 
 }
 
 void
 BluetoothHfpManager::CallHoldNotification(BluetoothHandsfreeCallHoldType aChld,
-                                          const nsAString& aBdAddress)
+                                          const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!IsSupportedChld((int)aChld)) {
     // We currently don't support Enhanced Call Control.
     // AT+CHLD=1x and AT+CHLD=2x will be ignored
     SendResponse(HFP_AT_RESPONSE_ERROR);
     return;
@@ -1519,17 +1579,17 @@ BluetoothHfpManager::CallHoldNotificatio
   SendResponse(HFP_AT_RESPONSE_OK);
 
   nsAutoCString message("CHLD=");
   message.AppendInt((int)aChld);
   NotifyDialer(NS_ConvertUTF8toUTF16(message));
 }
 
 void BluetoothHfpManager::DialCallNotification(const nsAString& aNumber,
-                                               const nsAString& aBdAddress)
+                                               const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCString message = NS_ConvertUTF16toUTF8(aNumber);
 
   // There are three cases based on aNumber,
   // 1) Empty value:    Redial, BLDN
   // 2) >xxx:           Memory dial, ATD>xxx
@@ -1560,17 +1620,17 @@ void BluetoothHfpManager::DialCallNotifi
 
     nsAutoCString newMsg("ATD");
     newMsg += StringHead(message, message.Length() - 1);
     NotifyDialer(NS_ConvertUTF8toUTF16(newMsg));
   }
 }
 
 void
-BluetoothHfpManager::CnumNotification(const nsAString& aBdAddress)
+BluetoothHfpManager::CnumNotification(const BluetoothAddress& aBdAddress)
 {
   static const uint8_t sAddressType[] {
     [HFP_CALL_ADDRESS_TYPE_UNKNOWN] = 0x81,
     [HFP_CALL_ADDRESS_TYPE_INTERNATIONAL] = 0x91 // for completeness
   };
 
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -1594,17 +1654,17 @@ public:
   void OnError(BluetoothStatus aStatus) override
   {
     BT_WARNING("BluetoothHandsfreeInterface::CindResponse failed: %d",
                (int)aStatus);
   }
 };
 
 void
-BluetoothHfpManager::CindNotification(const nsAString& aBdAddress)
+BluetoothHfpManager::CindNotification(const BluetoothAddress& aBdAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
   int numActive = GetNumberOfCalls(nsITelephonyService::CALL_STATE_CONNECTED);
   int numHeld = GetNumberOfCalls(nsITelephonyService::CALL_STATE_HELD);
   BluetoothHandsfreeCallState callState =
@@ -1624,29 +1684,29 @@ public:
   void OnError(BluetoothStatus aStatus) override
   {
     BT_WARNING("BluetoothHandsfreeInterface::CopsResponse failed: %d",
                (int)aStatus);
   }
 };
 
 void
-BluetoothHfpManager::CopsNotification(const nsAString& aBdAddr