Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 01 Jul 2015 16:40:01 -0400
changeset 251100 14d26a6ef7a4bf09439399cea30dbc43c303c964
parent 251099 43873ca11c57bfbdb7b18d7d341df0da68612a36 (current diff)
parent 250996 c36f684394962d30ab721f790538805ffc9192ac (diff)
child 251101 3c6b62ee308670396c88fe900aa8558ec093caae
push id61766
push usercbook@mozilla.com
push dateThu, 02 Jul 2015 13:47:40 +0000
treeherdermozilla-inbound@8ee689f2899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -250,18 +250,18 @@ nsAccUtils::TableFor(Accessible* aRow)
     if (table) {
       roles::Role tableRole = table->Role();
       if (tableRole == roles::GROUPING) { // if there's a rowgroup.
         table = table->Parent();
         if (table)
           tableRole = table->Role();
       }
 
-      return tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ?
-        table : nullptr;
+      return (tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ||
+              tableRole == roles::MATHML_TABLE) ? table : nullptr;
     }
   }
 
   return nullptr;
 }
 
 HyperTextAccessible*
 nsAccUtils::GetTextContainer(nsINode* aNode)
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -818,16 +818,18 @@ ConvertToNSArray(nsTArray<Accessible*>& 
       return @"AXMathRow";
 
     case roles::MATHML_SUB:
     case roles::MATHML_SUP:
     case roles::MATHML_SUB_SUP:
       return @"AXMathSubscriptSuperscript";
 
     case roles::MATHML_ROW:
+    case roles::MATHML_STYLE:
+    case roles::MATHML_ERROR:
       return @"AXMathRow";
 
     case roles::MATHML_UNDER:
     case roles::MATHML_OVER:
     case roles::MATHML_UNDER_OVER:
       return @"AXMathUnderOver";
 
     case roles::MATHML_SQUARE_ROOT:
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -1,16 +1,18 @@
 /* -*- 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";
 
+window.performance.mark('gecko-settings-loadstart');
+
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 // The load order is important here SettingsRequestManager _must_ be loaded
 // prior to using SettingsListener otherwise there is a race in acquiring the
 // lock and fulfilling it. If we ever move SettingsListener or this file down in
--- a/b2g/chrome/content/shell.html
+++ b/b2g/chrome/content/shell.html
@@ -11,17 +11,20 @@
 #endif
       >
 
 <head>
   <link rel="stylesheet" href="shell.css" type="text/css">
 #ifdef FXOS_SIMULATOR
   <link rel="stylesheet" href="desktop.css" type="text/css">
 #endif
-
+  <script type="text/javascript">
+  <!-- Add raptor performance marker -->
+  window.performance.mark('gecko-shell-html-load');
+  </script>
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/settings.js"> </script>
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/shell.js"> </script>
 
 #ifndef MOZ_WIDGET_GONK
   <!-- various task that has to happen only on desktop -->
   <script type="application/javascript;version=1.8"
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1,14 +1,16 @@
 /* -*- 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/. */
 
+window.performance.mark('gecko-shell-loadstart');
+
 Cu.import('resource://gre/modules/ContactService.jsm');
 Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
 Cu.import('resource://gre/modules/AlarmService.jsm');
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/NotificationDB.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
@@ -70,16 +72,18 @@ XPCOMUtils.defineLazyServiceGetter(Servi
                                   'nsICaptivePortalDetector');
 #endif
 
 #ifdef MOZ_SAFE_BROWSING
 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
               "resource://gre/modules/SafeBrowsing.jsm");
 #endif
 
+window.performance.measure('gecko-shell-jsm-loaded', 'gecko-shell-loadstart');
+
 function getContentWindow() {
   return shell.contentBrowser.contentWindow;
 }
 
 function debug(str) {
   dump(' -*- Shell.js: ' + str + '\n');
 }
 
@@ -223,30 +227,32 @@ var shell = {
   },
 
   _started: false,
   hasStarted: function shell_hasStarted() {
     return this._started;
   },
 
   bootstrap: function() {
+    window.performance.mark('gecko-shell-bootstrap');
     let startManifestURL =
       Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
         .getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
     if (startManifestURL) {
       Cu.import('resource://gre/modules/Bootstraper.jsm');
       Bootstraper.ensureSystemAppInstall(startManifestURL)
                  .then(this.start.bind(this))
                  .catch(Bootstraper.bailout);
     } else {
       this.start();
     }
   },
 
   start: function shell_start() {
+    window.performance.mark('gecko-shell-start');
     this._started = true;
 
     // This forces the initialization of the cookie service before we hit the
     // network.
     // See bug 810209
     let cookies = Cc["@mozilla.org/cookieService;1"];
 
     try {
@@ -367,16 +373,18 @@ var shell = {
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
 
+    window.performance.mark('gecko-shell-system-frame-set');
+
     ppmm.addMessageListener("content-handler", this);
     ppmm.addMessageListener("dial-handler", this);
     ppmm.addMessageListener("sms-handler", this);
     ppmm.addMessageListener("mail-handler", this);
     ppmm.addMessageListener("file-picker", this);
 #ifdef MOZ_SAFE_BROWSING
     setTimeout(function() {
       SafeBrowsing.init();
@@ -642,16 +650,17 @@ var shell = {
       a.onerror = function() {
         let sender = message.target.QueryInterface(Ci.nsIMessageSender);
         sender.sendAsyncMessage(activity.response, { success: false });
       }
     }
   },
 
   notifyContentStart: function shell_notifyContentStart() {
+    window.performance.mark('gecko-shell-notify-content-start');
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
 
     let content = this.contentBrowser.contentWindow;
 
     this.reportCrash(true);
 
     SystemAppProxy.registerFrame(shell.contentBrowser);
--- 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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- 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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <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="4efd19d199ae52656604f794c5a77518400220fd">
     <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="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- 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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- 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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "26b853b7cf94ea9e9ac6f20c55db462bd213a959", 
+        "git_revision": "858764a56982eb558259ccc689bbee855f090085", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "f7729852bdfb3f7051829e28463868a151c8ae62", 
+    "revision": "d43e6298ab2fc2d3033dac41d38138f9117ab9d3", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/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="4efd19d199ae52656604f794c5a77518400220fd">
     <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="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- 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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="26b853b7cf94ea9e9ac6f20c55db462bd213a959"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="858764a56982eb558259ccc689bbee855f090085"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -18,52 +18,52 @@ include $(topsrcdir)/toolkit/mozapps/ins
 DIST_FILES =
 
 # Log file from the 'make upload' step. We need this to parse out the URLs of
 # the uploaded files.
 AUTOMATION_UPLOAD_OUTPUT = $(DIST)/automation-upload.txt
 
 # Helper variables to convert from MOZ_AUTOMATION_* variables to the
 # corresponding the make target
-tier_BUILD_SYMBOLS = buildsymbols
-tier_L10N_CHECK = l10n-check
-tier_PRETTY_L10N_CHECK = pretty-l10n-check
-tier_INSTALLER = installer
-tier_PRETTY_INSTALLER = pretty-installer
-tier_PACKAGE = package
-tier_PRETTY_PACKAGE = pretty-package
-tier_PACKAGE_TESTS = package-tests
-tier_PRETTY_PACKAGE_TESTS = pretty-package-tests
-tier_UPDATE_PACKAGING = update-packaging
-tier_PRETTY_UPDATE_PACKAGING = pretty-update-packaging
-tier_UPLOAD_SYMBOLS = uploadsymbols
-tier_UPLOAD = upload
-tier_SDK = sdk
+tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols
+tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check
+tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check
+tier_MOZ_AUTOMATION_INSTALLER = installer
+tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer
+tier_MOZ_AUTOMATION_PACKAGE = package
+tier_MOZ_AUTOMATION_PRETTY_PACKAGE = pretty-package
+tier_MOZ_AUTOMATION_PACKAGE_TESTS = package-tests
+tier_MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS = pretty-package-tests
+tier_MOZ_AUTOMATION_UPDATE_PACKAGING = update-packaging
+tier_MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING = pretty-update-packaging
+tier_MOZ_AUTOMATION_UPLOAD_SYMBOLS = uploadsymbols
+tier_MOZ_AUTOMATION_UPLOAD = upload
+tier_MOZ_AUTOMATION_SDK = sdk
 
 # Automation build steps. Everything in MOZ_AUTOMATION_TIERS also gets used in
 # TIERS for mach display. As such, the MOZ_AUTOMATION_TIERS are roughly sorted
 # here in the order that they will be executed (since mach doesn't know of the
 # dependencies between them).
 moz_automation_symbols = \
-  PACKAGE_TESTS \
-  PRETTY_PACKAGE_TESTS \
-  BUILD_SYMBOLS \
-  UPLOAD_SYMBOLS \
-  PACKAGE \
-  PRETTY_PACKAGE \
-  INSTALLER \
-  PRETTY_INSTALLER \
-  UPDATE_PACKAGING \
-  PRETTY_UPDATE_PACKAGING \
-  L10N_CHECK \
-  PRETTY_L10N_CHECK \
-  UPLOAD \
-  SDK \
+  MOZ_AUTOMATION_PACKAGE_TESTS \
+  MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS \
+  MOZ_AUTOMATION_BUILD_SYMBOLS \
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS \
+  MOZ_AUTOMATION_PACKAGE \
+  MOZ_AUTOMATION_PRETTY_PACKAGE \
+  MOZ_AUTOMATION_INSTALLER \
+  MOZ_AUTOMATION_PRETTY_INSTALLER \
+  MOZ_AUTOMATION_UPDATE_PACKAGING \
+  MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING \
+  MOZ_AUTOMATION_L10N_CHECK \
+  MOZ_AUTOMATION_PRETTY_L10N_CHECK \
+  MOZ_AUTOMATION_UPLOAD \
+  MOZ_AUTOMATION_SDK \
   $(NULL)
-MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$(MOZ_AUTOMATION_$(sym))),$(tier_$(sym))))
+MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$($(sym))),$(tier_$(sym))))
 
 # Dependencies between automation build steps
 automation/uploadsymbols: automation/buildsymbols
 
 automation/update-packaging: automation/package
 automation/update-packaging: automation/installer
 automation/pretty-update-packaging: automation/pretty-package
 automation/pretty-update-packaging: automation/pretty-installer
@@ -114,15 +114,19 @@ AUTOMATION_EXTRA_CMDLINE-package-tests =
 AUTOMATION_EXTRA_CMDLINE-pretty-package-tests = -j1
 
 # The commands only run if the corresponding MOZ_AUTOMATION_* variable is
 # enabled. This means, for example, if we enable MOZ_AUTOMATION_UPLOAD, then
 # 'buildsymbols' will only run if MOZ_AUTOMATION_BUILD_SYMBOLS is also set.
 # However, the target automation/buildsymbols will still be executed in this
 # case because it is a prerequisite of automation/upload.
 define automation_commands
-$(call BUILDSTATUS,TIER_START $1)
 @$(MAKE) $1 $(AUTOMATION_EXTRA_CMDLINE-$1)
 $(call BUILDSTATUS,TIER_FINISH $1)
 endef
 
-automation/%:
+# The tier start message is in a separate target so make doesn't buffer it
+# until the step completes with output syncing enabled.
+automation-start/%:
+	$(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call BUILDSTATUS,TIER_START $*))
+
+automation/%: automation-start/%
 	$(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call automation_commands,$*))
--- a/dom/base/test/test_XHRDocURI.html
+++ b/dom/base/test/test_XHRDocURI.html
@@ -494,17 +494,24 @@ function runTest() {
   yield undefined;
 
   history.pushState({}, "pushStateTest", window.location.href + "/pushStateTest");
   ok(document.documentURI.indexOf("pushStateTest") > -1);
 
   var chromeDoc = SpecialPowers.wrap(document);
   ok(chromeDoc.documentURI.indexOf("pushStateTest") > -1);
 
+  SimpleTest.executeSoon(function() { gen.next(); });
+
+  yield undefined;
+
   history.back();
+  SimpleTest.executeSoon(function() { gen.next(); });
+
+  yield undefined;
 
   SimpleTest.finish();
   SpecialPowers.removePermission("systemXHR", document);
   yield undefined;
 }
 
 </script>
 </pre>
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -3,29 +3,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentEventHandler.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/Selection.h"
 #include "nsCaret.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsCopySupport.h"
 #include "nsFocusManager.h"
 #include "nsFontMetrics.h"
 #include "nsFrameSelection.h"
 #include "nsIContentIterator.h"
 #include "nsIPresShell.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
-#include "nsISelectionPrivate.h"
-#include "nsIDOMRange.h"
 #include "nsIFrame.h"
 #include "nsIObjectFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsRange.h"
 #include "nsTextFragment.h"
 #include "nsTextFrame.h"
 #include "nsView.h"
@@ -70,26 +69,29 @@ ContentEventHandler::InitCommon()
 {
   if (mSelection) {
     return NS_OK;
   }
 
   nsresult rv = InitBasic();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsCOMPtr<nsISelection> sel;
   nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
-                                     getter_AddRefs(mSelection));
-
-  nsCOMPtr<nsIDOMRange> firstRange;
-  rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
-  // This shell doesn't support selection.
-  if (NS_FAILED(rv)) {
+                                     getter_AddRefs(sel));
+  mSelection = static_cast<Selection*>(sel.get());
+  if (NS_WARN_IF(!mSelection)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  mFirstSelectedRange = static_cast<nsRange*>(firstRange.get());
+
+  // This shell doesn't support selection.
+  if (NS_WARN_IF(!mSelection->RangeCount())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  mFirstSelectedRange = mSelection->GetRangeAt(0);
 
   nsINode* startNode = mFirstSelectedRange->GetStartParent();
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
   nsINode* endNode = mFirstSelectedRange->GetEndParent();
   NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
 
   // See bug 537041 comment 5, the range could have removed node.
   NS_ENSURE_TRUE(startNode->GetCurrentDoc() == mPresShell->GetDocument(),
@@ -109,25 +111,26 @@ ContentEventHandler::Init(WidgetQueryCon
 
   nsresult rv = InitCommon();
   NS_ENSURE_SUCCESS(rv, rv);
 
   aEvent->mSucceeded = false;
 
   aEvent->mReply.mContentsRoot = mRootContent.get();
 
-  bool isCollapsed;
-  rv = mSelection->GetIsCollapsed(&isCollapsed);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
-  aEvent->mReply.mHasSelection = !isCollapsed;
+  aEvent->mReply.mHasSelection = !mSelection->IsCollapsed();
 
   nsRect r;
   nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r);
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
+  if (!frame) {
+    frame = mRootContent->GetPrimaryFrame();
+    if (NS_WARN_IF(!frame)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
   aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
 
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::Init(WidgetSelectionEvent* aEvent)
 {
@@ -745,17 +748,17 @@ ContentEventHandler::SetRangeFromFlatTex
     MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
     rv = aRange->SetStart(mRootContent, int32_t(mRootContent->GetChildCount()));
     NS_ENSURE_SUCCESS(rv, rv);
     if (aNewOffset) {
       *aNewOffset = offset;
     }
   }
   rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount()));
-  NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
+  NS_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetEnd failed");
   return rv;
 }
 
 /* static */ LineBreakType
 ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
 {
   return GetLineBreakType(aEvent->mUseNativeLineBreak);
 }
@@ -801,31 +804,27 @@ ContentEventHandler::OnQuerySelectedText
   NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
                "The reply string must be empty");
 
   LineBreakType lineBreakType = GetLineBreakType(aEvent);
   rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange,
                                 &aEvent->mReply.mOffset, lineBreakType);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
-  rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
-  NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
-  rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
-  NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
+  nsCOMPtr<nsINode> anchorNode = mSelection->GetAnchorNode();
+  nsCOMPtr<nsINode> focusNode = mSelection->GetFocusNode();
+  if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
+    return NS_ERROR_FAILURE;
+  }
 
-  int32_t anchorOffset, focusOffset;
-  rv = mSelection->GetAnchorOffset(&anchorOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = mSelection->GetFocusOffset(&focusOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
-  nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
-  NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
+  int32_t anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
+  int32_t focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
+  if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
+    return NS_ERROR_FAILURE;
+  }
 
   int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
                                                   focusNode, focusOffset);
   aEvent->mReply.mReversed = compare > 0;
 
   if (compare) {
     rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
                                  lineBreakType);
@@ -1027,42 +1026,39 @@ ContentEventHandler::OnQueryCaretRect(Wi
 {
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   LineBreakType lineBreakType = GetLineBreakType(aEvent);
 
+  nsRect caretRect;
+
   // When the selection is collapsed and the queried offset is current caret
   // position, we should return the "real" caret rect.
-  bool selectionIsCollapsed;
-  rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRect caretRect;
-  nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
-
-  if (selectionIsCollapsed) {
-    uint32_t offset;
-    rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
-                                  lineBreakType);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (offset == aEvent->mInput.mOffset) {
-      if (!caretFrame) {
-        return NS_ERROR_FAILURE;
+  if (mSelection->IsCollapsed()) {
+    nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
+    if (caretFrame) {
+      uint32_t offset;
+      rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
+                                    lineBreakType);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (offset == aEvent->mInput.mOffset) {
+        rv = ConvertToRootViewRelativeOffset(caretFrame, caretRect);
+        NS_ENSURE_SUCCESS(rv, rv);
+        nscoord appUnitsPerDevPixel =
+          caretFrame->PresContext()->AppUnitsPerDevPixel();
+        aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
+          caretRect.ToOutsidePixels(appUnitsPerDevPixel));
+        aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
+        aEvent->mReply.mOffset = aEvent->mInput.mOffset;
+        aEvent->mSucceeded = true;
+        return NS_OK;
       }
-      rv = ConvertToRootViewRelativeOffset(caretFrame, caretRect);
-      NS_ENSURE_SUCCESS(rv, rv);
-      aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
-        caretRect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel()));
-      aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
-      aEvent->mReply.mOffset = aEvent->mInput.mOffset;
-      aEvent->mSucceeded = true;
-      return NS_OK;
     }
   }
 
   // Otherwise, we should set the guessed caret rect.
   nsRefPtr<nsRange> range = new nsRange(mRootContent);
   rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0,
                                   lineBreakType, true,
                                   &aEvent->mReply.mOffset);
@@ -1522,19 +1518,21 @@ static void AdjustRangeForSelection(nsIC
 nsresult
 ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
 {
   aEvent->mSucceeded = false;
 
   // Get selection to manipulate
   // XXX why do we need to get them from ISM? This method should work fine
   //     without ISM.
+  nsCOMPtr<nsISelection> sel;
   nsresult rv =
-    IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
+    IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(sel),
                                               getter_AddRefs(mRootContent));
+  mSelection = static_cast<Selection*>(sel.get());
   if (rv != NS_ERROR_NOT_AVAILABLE) {
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     rv = Init(aEvent);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Get range from offset and length
@@ -1545,45 +1543,44 @@ ContentEventHandler::OnSelectionEvent(Wi
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsINode* startNode = range->GetStartParent();
   nsINode* endNode = range->GetEndParent();
   int32_t startNodeOffset = range->StartOffset();
   int32_t endNodeOffset = range->EndOffset();
   AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
   AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
+  if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
+      NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
+    return NS_ERROR_UNEXPECTED;
+  }
 
-  nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
-  nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
-  NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
-  selPrivate->StartBatchChanges();
+  mSelection->StartBatchChanges();
 
   // Clear selection first before setting
   rv = mSelection->RemoveAllRanges();
   // Need to call EndBatchChanges at the end even if call failed
   if (NS_SUCCEEDED(rv)) {
     if (aEvent->mReversed) {
-      rv = mSelection->Collapse(endDomNode, endNodeOffset);
+      rv = mSelection->Collapse(endNode, endNodeOffset);
     } else {
-      rv = mSelection->Collapse(startDomNode, startNodeOffset);
+      rv = mSelection->Collapse(startNode, startNodeOffset);
     }
     if (NS_SUCCEEDED(rv) &&
-        (startDomNode != endDomNode || startNodeOffset != endNodeOffset)) {
+        (startNode != endNode || startNodeOffset != endNodeOffset)) {
       if (aEvent->mReversed) {
-        rv = mSelection->Extend(startDomNode, startNodeOffset);
+        rv = mSelection->Extend(startNode, startNodeOffset);
       } else {
-        rv = mSelection->Extend(endDomNode, endNodeOffset);
+        rv = mSelection->Extend(endNode, endNodeOffset);
       }
     }
   }
-  selPrivate->EndBatchChanges();
+  mSelection->EndBatchChanges();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  selPrivate->ScrollIntoViewInternal(
+  mSelection->ScrollIntoViewInternal(
     nsISelectionController::SELECTION_FOCUS_REGION,
     false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -3,18 +3,18 @@
 /* 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_ContentEventHandler_h_
 #define mozilla_ContentEventHandler_h_
 
 #include "mozilla/EventForwards.h"
+#include "mozilla/dom/Selection.h"
 #include "nsCOMPtr.h"
-#include "nsISelection.h"
 #include "nsRange.h"
 
 class nsPresContext;
 
 struct nsRect;
 
 namespace mozilla {
 
@@ -30,16 +30,18 @@ enum LineBreakType
  *   The platforms request some content informations, e.g., the selected text,
  *   the offset of the selected text and the text for specified range.
  *   This class answers to NS_QUERY_* events from actual contents.
  */
 
 class MOZ_STACK_CLASS ContentEventHandler
 {
 public:
+  typedef dom::Selection Selection;
+
   explicit ContentEventHandler(nsPresContext* aPresContext);
 
   // NS_QUERY_SELECTED_TEXT event handler
   nsresult OnQuerySelectedText(WidgetQueryContentEvent* aEvent);
   // NS_QUERY_TEXT_CONTENT event handler
   nsresult OnQueryTextContent(WidgetQueryContentEvent* aEvent);
   // NS_QUERY_CARET_RECT event handler
   nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent);
@@ -57,17 +59,17 @@ public:
   nsresult OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent);
 
   // NS_SELECTION_* event
   nsresult OnSelectionEvent(WidgetSelectionEvent* aEvent);
 
 protected:
   nsPresContext* mPresContext;
   nsCOMPtr<nsIPresShell> mPresShell;
-  nsCOMPtr<nsISelection> mSelection;
+  nsRefPtr<Selection> mSelection;
   nsRefPtr<nsRange> mFirstSelectedRange;
   nsCOMPtr<nsIContent> mRootContent;
 
   nsresult Init(WidgetQueryContentEvent* aEvent);
   nsresult Init(WidgetSelectionEvent* aEvent);
 
   nsresult InitBasic();
   nsresult InitCommon();
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3380,28 +3380,28 @@ EventStateManager::RemoteQueryContentEve
   // Will not be handled locally, remote the event
   GetCrossProcessTarget()->HandleQueryContentEvent(*queryEvent);
   return true;
 }
 
 TabParent*
 EventStateManager::GetCrossProcessTarget()
 {
-  return TabParent::GetIMETabParent();
+  return IMEStateManager::GetActiveTabParent();
 }
 
 bool
 EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent)
 {
   // Check to see if there is a focused, editable content in chrome,
   // in that case, do not forward IME events to content
   nsIContent *focusedContent = GetFocusedContent();
   if (focusedContent && focusedContent->IsEditable())
     return false;
-  return TabParent::GetIMETabParent() != nullptr;
+  return IMEStateManager::GetActiveTabParent() != nullptr;
 }
 
 void
 EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext)
 {
   IMEStateManager::OnDestroyPresContext(aPresContext);
   if (mHoverContent) {
     // Bug 70855: Presentation is going away, possibly for a reframe.
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/unused.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/TabParent.h"
 
 #include "HTMLInputElement.h"
 #include "IMEContentObserver.h"
 
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
@@ -175,29 +176,27 @@ GetNotifyIMEMessageName(IMEMessage aMess
       return "REQUEST_TO_COMMIT_COMPOSITION";
     case REQUEST_TO_CANCEL_COMPOSITION:
       return "REQUEST_TO_CANCEL_COMPOSITION";
     default:
       return "unacceptable IME notification message";
   }
 }
 
-nsIContent* IMEStateManager::sContent = nullptr;
+StaticRefPtr<nsIContent> IMEStateManager::sContent;
 nsPresContext* IMEStateManager::sPresContext = nullptr;
 StaticRefPtr<nsIWidget> IMEStateManager::sFocusedIMEWidget;
+StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
+StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
+TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
 bool IMEStateManager::sIsGettingNewIMEState = false;
 bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
 bool IMEStateManager::sRemoteHasFocus = false;
 
-// sActiveIMEContentObserver points to the currently active IMEContentObserver.
-// sActiveIMEContentObserver is null if there is no focused editor.
-IMEContentObserver* IMEStateManager::sActiveIMEContentObserver = nullptr;
-TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
-
 // static
 void
 IMEStateManager::Init()
 {
   if (!sISMLog) {
     sISMLog = PR_NewLogModule("IMEStateManager");
   }
 
@@ -217,16 +216,53 @@ IMEStateManager::Shutdown()
      sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0));
 
   MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
   delete sTextCompositions;
   sTextCompositions = nullptr;
 }
 
 // static
+void
+IMEStateManager::OnTabParentDestroying(TabParent* aTabParent)
+{
+  if (sActiveTabParent != aTabParent) {
+    return;
+  }
+  MOZ_LOG(sISMLog, LogLevel::Info,
+    ("ISM: IMEStateManager::OnTabParentDestroying(aTabParent=0x%p), "
+     "The active TabParent is being destroyed", aTabParent));
+
+  // The active remote process might have crashed.
+  sActiveTabParent = nullptr;
+
+  // TODO: Need to cancel composition without TextComposition and make
+  //       disable IME.
+}
+
+// static
+void
+IMEStateManager::StopIMEStateManagement()
+{
+  MOZ_LOG(sISMLog, LogLevel::Info,
+    ("ISM: IMEStateManager::StopIMEStateManagement()"));
+
+  // NOTE: Don't set input context from here since this has already lost
+  //       the rights to change input context.
+
+  if (sTextCompositions && sPresContext) {
+    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext);
+  }
+  sPresContext = nullptr;
+  sContent = nullptr;
+  sActiveTabParent = nullptr;
+  DestroyIMEContentObserver();
+}
+
+// static
 nsresult
 IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
 
   // First, if there is a composition in the aPresContext, clean up it.
   if (sTextCompositions) {
     TextCompositionArray::index_type i =
@@ -250,29 +286,30 @@ IMEStateManager::OnDestroyPresContext(ns
 
   if (aPresContext != sPresContext) {
     return NS_OK;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnDestroyPresContext(aPresContext=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
-     aPresContext, sPresContext, sContent, sTextCompositions));
+     aPresContext, sPresContext, sContent.get(), sTextCompositions));
 
   DestroyIMEContentObserver();
 
   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
-  NS_IF_RELEASE(sContent);
+  sContent = nullptr;
   sPresContext = nullptr;
+  sActiveTabParent = nullptr;
   return NS_OK;
 }
 
 // static
 nsresult
 IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
                                  nsIContent* aContent)
 {
@@ -305,31 +342,32 @@ IMEStateManager::OnRemoveContent(nsPresC
   if (!sPresContext || !sContent ||
       !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
     return NS_OK;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnRemoveContent(aPresContext=0x%p, "
      "aContent=0x%p), sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
-     aPresContext, aContent, sPresContext, sContent, sTextCompositions));
+     aPresContext, aContent, sPresContext, sContent.get(), sTextCompositions));
 
   DestroyIMEContentObserver();
 
   // Current IME transaction should commit
   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
 
-  NS_IF_RELEASE(sContent);
+  sContent = nullptr;
   sPresContext = nullptr;
+  sActiveTabParent = nullptr;
 
   return NS_OK;
 }
 
 // static
 nsresult
 IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
                                nsIContent* aContent,
@@ -345,26 +383,33 @@ IMEStateManager::OnChangeFocus(nsPresCon
 }
 
 // static
 nsresult
 IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
                                        nsIContent* aContent,
                                        InputContextAction aAction)
 {
+  nsRefPtr<TabParent> newTabParent = TabParent::GetFrom(aContent);
+
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnChangeFocusInternal(aPresContext=0x%p, "
-     "aContent=0x%p, aAction={ mCause=%s, mFocusChange=%s }), "
-     "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p",
-     aPresContext, aContent, GetActionCauseName(aAction.mCause),
+     "aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), "
+     "sPresContext=0x%p, sContent=0x%p, sActiveTabParent=0x%p, "
+     "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s",
+     aPresContext, aContent, newTabParent.get(),
+     GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange),
-     sPresContext, sContent, sActiveIMEContentObserver));
+     sPresContext, sContent.get(), sActiveTabParent.get(),
+     sActiveIMEContentObserver.get(),
+     GetBoolName(sInstalledMenuKeyboardListener)));
 
   bool focusActuallyChanging =
-    (sContent != aContent || sPresContext != aPresContext);
+    (sContent != aContent || sPresContext != aPresContext ||
+     sActiveTabParent != newTabParent);
 
   nsCOMPtr<nsIWidget> oldWidget =
     sPresContext ? sPresContext->GetRootWidget() : nullptr;
   if (oldWidget && focusActuallyChanging) {
     // If we're deactivating, we shouldn't commit composition forcibly because
     // the user may want to continue the composition.
     if (aPresContext) {
       NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
@@ -379,81 +424,117 @@ IMEStateManager::OnChangeFocusInternal(n
 
   if (!aPresContext) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
        "no nsPresContext is being activated"));
     return NS_OK;
   }
 
+  nsIContentParent* currentContentParent =
+    sActiveTabParent ? sActiveTabParent->Manager() : nullptr;
+  nsIContentParent* newContentParent =
+    newTabParent ? newTabParent->Manager() : nullptr;
+  if (sActiveTabParent && currentContentParent != newContentParent) {
+    MOZ_LOG(sISMLog, LogLevel::Debug,
+      ("ISM:   IMEStateManager::OnChangeFocusInternal(), notifying previous "
+       "focused child process of parent process or another child process "
+       "getting focus"));
+    unused << sActiveTabParent->SendStopIMEStateManagement();
+  }
+
   nsCOMPtr<nsIWidget> widget =
     (sPresContext == aPresContext) ? oldWidget.get() :
                                      aPresContext->GetRootWidget();
   if (NS_WARN_IF(!widget)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("ISM:   IMEStateManager::OnChangeFocusInternal(), FAILED due to "
        "no widget to manage its IME state"));
     return NS_OK;
   }
 
-  IMEState newState = GetNewIMEState(aPresContext, aContent);
+  // If a child process has focus, we should disable IME state until the child
+  // process actually gets focus because if user types keys before that they
+  // are handled by IME.
+  IMEState newState =
+    newTabParent ? IMEState(IMEState::DISABLED) :
+                   GetNewIMEState(aPresContext, aContent);
+  bool setIMEState = true;
 
-  // In e10s, remote content may have IME focus.  The main process (i.e. this process)
-  // would attempt to set state to DISABLED if, for example, the user clicks
-  // some other remote content.  The content process would later re-ENABLE IME, meaning
-  // that all state-changes were unnecessary.
-  // Here we filter the common case where the main process knows that the remote
-  // process controls IME focus.  The DISABLED->re-ENABLED progression can
-  // still happen since remote content may be concurrently communicating its claim
-  // on focus to the main process... but this cannot cause bugs like missed keypresses.
-  // (It just means a lot of needless IPC.)
-  if ((newState.mEnabled == IMEState::DISABLED) && TabParent::GetIMETabParent()) {
-    MOZ_LOG(sISMLog, LogLevel::Debug,
-      ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
-       "Parent process cancels to set DISABLED state because the content process "
-       "has IME focus and has already sets IME state"));  
-    MOZ_ASSERT(XRE_IsParentProcess(),
-      "TabParent::GetIMETabParent() should never return non-null value "
-      "in the content process");
-    return NS_OK;
+  if (newTabParent) {
+    if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS ||
+        aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
+      // XXX When menu keyboard listener is being uninstalled, IME state needs
+      //     to be restored by the child process asynchronously.  Therefore,
+      //     some key events which are fired immediately after closing menu
+      //     may not be handled by IME.
+      unused << newTabParent->
+        SendMenuKeyboardListenerInstalled(sInstalledMenuKeyboardListener);
+      setIMEState = sInstalledMenuKeyboardListener;
+    } else if (focusActuallyChanging) {
+      InputContext context = widget->GetInputContext();
+      if (context.mIMEState.mEnabled == IMEState::DISABLED) {
+        setIMEState = false;
+        MOZ_LOG(sISMLog, LogLevel::Debug,
+          ("ISM:   IMEStateManager::OnChangeFocusInternal(), doesn't set IME "
+           "state because focused element (or document) is in a child process "
+           "and the IME state is already disabled"));
+      } else {
+        MOZ_LOG(sISMLog, LogLevel::Debug,
+          ("ISM:   IMEStateManager::OnChangeFocusInternal(), will disable IME "
+           "until new focused element (or document) in the child process "
+           "will get focus actually"));
+      }
+    } else {
+      // When focus is NOT changed actually, we shouldn't set IME state since
+      // that means that the window is being activated and the child process
+      // may have composition.  Then, we shouldn't commit the composition with
+      // making IME state disabled.
+      setIMEState = false; 
+      MOZ_LOG(sISMLog, LogLevel::Debug,
+        ("ISM:   IMEStateManager::OnChangeFocusInternal(), doesn't set IME "
+         "state because focused element (or document) is already in the child "
+         "process"));
+    }
   }
 
-  if (!focusActuallyChanging) {
-    // actual focus isn't changing, but if IME enabled state is changing,
-    // we should do it.
-    InputContext context = widget->GetInputContext();
-    if (context.mIMEState.mEnabled == newState.mEnabled) {
-      MOZ_LOG(sISMLog, LogLevel::Debug,
-        ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
-         "neither focus nor IME state is changing"));
-      return NS_OK;
-    }
-    aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
+  if (setIMEState) {
+    if (!focusActuallyChanging) {
+      // actual focus isn't changing, but if IME enabled state is changing,
+      // we should do it.
+      InputContext context = widget->GetInputContext();
+      if (context.mIMEState.mEnabled == newState.mEnabled) {
+        MOZ_LOG(sISMLog, LogLevel::Debug,
+          ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
+           "neither focus nor IME state is changing"));
+        return NS_OK;
+      }
+      aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
 
-    // Even if focus isn't changing actually, we should commit current
-    // composition here since the IME state is changing.
-    if (sPresContext && oldWidget && !focusActuallyChanging) {
-      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+      // Even if focus isn't changing actually, we should commit current
+      // composition here since the IME state is changing.
+      if (sPresContext && oldWidget && !focusActuallyChanging) {
+        NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+      }
+    } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
+      // If aContent isn't null or aContent is null but editable, somebody gets
+      // focus.
+      bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
+      aAction.mFocusChange =
+        gotFocus ? InputContextAction::GOT_FOCUS :
+                   InputContextAction::LOST_FOCUS;
     }
-  } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
-    // If aContent isn't null or aContent is null but editable, somebody gets
-    // focus.
-    bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
-    aAction.mFocusChange =
-      gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS;
+
+    // Update IME state for new focus widget
+    SetIMEState(newState, aContent, widget, aAction);
   }
 
-  // Update IME state for new focus widget
-  SetIMEState(newState, aContent, widget, aAction);
-
+  sActiveTabParent = newTabParent;
   sPresContext = aPresContext;
-  if (sContent != aContent) {
-    NS_IF_RELEASE(sContent);
-    NS_IF_ADDREF(sContent = aContent);
-  }
+  sContent = aContent;
 
   // Don't call CreateIMEContentObserver() here, it should be called from
   // focus event handler of editor.
 
   return NS_OK;
 }
 
 // static
@@ -477,17 +558,17 @@ IMEStateManager::OnInstalledMenuKeyboard
 bool
 IMEStateManager::OnMouseButtonEventInEditor(nsPresContext* aPresContext,
                                             nsIContent* aContent,
                                             nsIDOMMouseEvent* aMouseEvent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnMouseButtonEventInEditor(aPresContext=0x%p, "
      "aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
-     aPresContext, aContent, aMouseEvent, sPresContext, sContent));
+     aPresContext, aContent, aMouseEvent, sPresContext, sContent.get()));
 
   if (sPresContext != aPresContext || sContent != aContent) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::OnMouseButtonEventInEditor(), "
        "the mouse event isn't fired on the editor managed by ISM"));
     return false;
   }
 
@@ -534,17 +615,17 @@ IMEStateManager::OnMouseButtonEventInEdi
 void
 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
                                  nsIDOMMouseEvent* aMouseEvent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnClickInEditor(aPresContext=0x%p, aContent=0x%p, "
      "aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
-     aPresContext, aContent, aMouseEvent, sPresContext, sContent));
+     aPresContext, aContent, aMouseEvent, sPresContext, sContent.get()));
 
   if (sPresContext != aPresContext || sContent != aContent) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::OnClickInEditor(), "
        "the mouse event isn't fired on the editor managed by ISM"));
     return;
   }
 
@@ -592,18 +673,18 @@ void
 IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
                                  nsIEditor* aEditor)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, "
      "aEditor=0x%p), sPresContext=0x%p, sContent=0x%p, "
      "sActiveIMEContentObserver=0x%p",
-     aPresContext, aContent, aEditor, sPresContext, sContent,
-     sActiveIMEContentObserver));
+     aPresContext, aContent, aEditor, sPresContext, sContent.get(),
+     sActiveIMEContentObserver.get()));
 
   if (sPresContext != aPresContext || sContent != aContent) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::OnFocusInEditor(), "
        "an editor not managed by ISM gets focus"));
     return;
   }
 
@@ -664,17 +745,17 @@ IMEStateManager::UpdateIMEState(const IM
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::UpdateIMEState(aNewIMEState={ mEnabled=%s, "
      "mOpen=%s }, aContent=0x%p, aEditor=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p, "
      "sIsGettingNewIMEState=%s",
      GetIMEStateEnabledName(aNewIMEState.mEnabled),
      GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, aEditor,
-     sPresContext, sContent, sActiveIMEContentObserver,
+     sPresContext, sContent.get(), sActiveIMEContentObserver.get(),
      GetBoolName(sIsGettingNewIMEState)));
 
   if (sIsGettingNewIMEState) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::UpdateIMEState(), "
        "does nothing because of called while getting new IME state"));
     return;
   }
@@ -839,26 +920,77 @@ MayBeIMEUnawareWebApp(nsINode* aNode)
     aNode = aNode->GetParentNode();
   }
 
   return haveKeyEventsListener;
 }
 
 // static
 void
+IMEStateManager::SetInputContextForChildProcess(
+                   TabParent* aTabParent,
+                   const InputContext& aInputContext,
+                   const InputContextAction& aAction)
+{
+  MOZ_LOG(sISMLog, LogLevel::Info,
+    ("ISM: IMEStateManager::SetInputContextForChildProcess(aTabParent=0x%p, "
+     "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
+     "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
+     "aAction={ mCause=%s, mAction=%s }, aTabParent=0x%p), sPresContext=0x%p, "
+     "sActiveTabParent=0x%p",
+     aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
+     GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
+     NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
+     NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
+     NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
+     GetActionCauseName(aAction.mCause),
+     GetActionFocusChangeName(aAction.mFocusChange),
+     sPresContext, sActiveTabParent.get()));
+
+  if (NS_WARN_IF(aTabParent != sActiveTabParent)) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("ISM:    IMEStateManager::SetInputContextForChildProcess(), FAILED, "
+       "because non-focused tab parent tries to set input context"));
+    return;
+  }
+
+  if (NS_WARN_IF(!sPresContext)) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("ISM:    IMEStateManager::SetInputContextForChildProcess(), FAILED, "
+       "due to no focused presContext"));
+    return;
+  }
+
+  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
+  if (NS_WARN_IF(!widget)) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("ISM:    IMEStateManager::SetInputContextForChildProcess(), FAILED, "
+       "due to no widget in the focused presContext"));
+    return;
+  }
+
+  MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
+
+  SetInputContext(widget, aInputContext, aAction);
+}
+
+// static
+void
 IMEStateManager::SetIMEState(const IMEState& aState,
                              nsIContent* aContent,
                              nsIWidget* aWidget,
                              InputContextAction aAction)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::SetIMEState(aState={ mEnabled=%s, mOpen=%s }, "
-     "aContent=0x%p, aWidget=0x%p, aAction={ mCause=%s, mFocusChange=%s })",
+     "aContent=0x%p (TabParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
+     "mFocusChange=%s })",
      GetIMEStateEnabledName(aState.mEnabled),
-     GetIMEStateSetOpenName(aState.mOpen), aContent, aWidget,
+     GetIMEStateSetOpenName(aState.mOpen), aContent,
+     TabParent::GetFrom(aContent), aWidget,
      GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange)));
 
   NS_ENSURE_TRUE_VOID(aWidget);
 
   InputContext oldContext = aWidget->GetInputContext();
 
   InputContext context;
@@ -931,37 +1063,51 @@ IMEStateManager::SetIMEState(const IMESt
 
   // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
   //     of the process type.
   if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
       XRE_GetProcessType() != GeckoProcessType_Content) {
     aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
   }
 
+  SetInputContext(aWidget, context, aAction);
+}
 
-  MOZ_LOG(sISMLog, LogLevel::Debug,
-    ("ISM:   IMEStateManager::SetIMEState(), "
-     "calling nsIWidget::SetInputContext(context={ mIMEState={ mEnabled=%s, "
-     "mOpen=%s }, mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", "
-     "mActionHint=\"%s\" }, aAction={ mCause=%s, mAction=%s })",
-     GetIMEStateEnabledName(context.mIMEState.mEnabled),
-     GetIMEStateSetOpenName(context.mIMEState.mOpen),
-     NS_ConvertUTF16toUTF8(context.mHTMLInputType).get(),
-     NS_ConvertUTF16toUTF8(context.mHTMLInputInputmode).get(),
-     NS_ConvertUTF16toUTF8(context.mActionHint).get(),
+// static
+void
+IMEStateManager::SetInputContext(nsIWidget* aWidget,
+                                 const InputContext& aInputContext,
+                                 const InputContextAction& aAction)
+{
+  MOZ_LOG(sISMLog, LogLevel::Info,
+    ("ISM: IMEStateManager::SetInputContext(aWidget=0x%p, aInputContext={ "
+     "mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", "
+     "mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
+     "aAction={ mCause=%s, mAction=%s }), sActiveTabParent=0x%p",
+     aWidget,
+     GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
+     GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
+     NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
+     NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
+     NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
      GetActionCauseName(aAction.mCause),
-     GetActionFocusChangeName(aAction.mFocusChange)));
+     GetActionFocusChangeName(aAction.mFocusChange),
+     sActiveTabParent.get()));
+
+  MOZ_RELEASE_ASSERT(aWidget);
 
-  aWidget->SetInputContext(context, aAction);
-  if (oldContext.mIMEState.mEnabled == context.mIMEState.mEnabled) {
+  InputContext oldContext = aWidget->GetInputContext();
+
+  aWidget->SetInputContext(aInputContext, aAction);
+  if (oldContext.mIMEState.mEnabled == aInputContext.mIMEState.mEnabled) {
     return;
   }
 
   nsContentUtils::AddScriptRunner(
-    new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
+    new IMEEnabledStateChangedEvent(aInputContext.mIMEState.mEnabled));
 }
 
 // static
 void
 IMEStateManager::EnsureTextCompositionArray()
 {
   if (sTextCompositions) {
     return;
@@ -1329,41 +1475,41 @@ IMEStateManager::IsIMEObserverNeeded(con
 
 // static
 void
 IMEStateManager::DestroyIMEContentObserver()
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::DestroyIMEContentObserver(), "
      "sActiveIMEContentObserver=0x%p",
-     sActiveIMEContentObserver));
+     sActiveIMEContentObserver.get()));
 
   if (!sActiveIMEContentObserver) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("ISM:   IMEStateManager::DestroyIMEContentObserver() does nothing"));
     return;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Debug,
     ("ISM:   IMEStateManager::DestroyIMEContentObserver(), destroying "
      "the active IMEContentObserver..."));
-  nsRefPtr<IMEContentObserver> tsm;
-  tsm.swap(sActiveIMEContentObserver);
+  nsRefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
+  sActiveIMEContentObserver = nullptr;
   tsm->Destroy();
 }
 
 // static
 void
 IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("ISM: IMEStateManager::CreateIMEContentObserver(aEditor=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p, "
      "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
-     aEditor, sPresContext, sContent, sActiveIMEContentObserver,
+     aEditor, sPresContext, sContent.get(), sActiveIMEContentObserver.get(),
      GetBoolName(sActiveIMEContentObserver ?
        sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
 
   if (NS_WARN_IF(sActiveIMEContentObserver)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("ISM:   IMEStateManager::CreateIMEContentObserver(), FAILED due to "
        "there is already an active IMEContentObserver"));
     MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
@@ -1385,17 +1531,16 @@ IMEStateManager::CreateIMEContentObserve
        "IMEContentObserver because of non-editable IME state"));
     return;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Debug,
     ("ISM:   IMEStateManager::CreateIMEContentObserver() is creating an "
      "IMEContentObserver instance..."));
   sActiveIMEContentObserver = new IMEContentObserver();
-  NS_ADDREF(sActiveIMEContentObserver);
 
   // IMEContentObserver::Init() might create another IMEContentObserver
   // instance.  So, sActiveIMEContentObserver would be replaced with new one.
   // We should hold the current instance here.
   nsRefPtr<IMEContentObserver> kungFuDeathGrip(sActiveIMEContentObserver);
   sActiveIMEContentObserver->Init(widget, sPresContext, sContent, aEditor);
 }
 
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -4,16 +4,17 @@
  * 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_IMEStateManager_h_
 #define mozilla_IMEStateManager_h_
 
 #include "mozilla/EventForwards.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/TabParent.h"
 #include "nsIWidget.h"
 
 class nsIContent;
 class nsIDOMMouseEvent;
 class nsIEditor;
 class nsINode;
 class nsPresContext;
 class nsISelection;
@@ -28,26 +29,60 @@ class TextComposition;
 /**
  * IMEStateManager manages InputContext (e.g., active editor type, IME enabled
  * state and IME open state) of nsIWidget instances, manages IMEContentObserver
  * and provides useful API for IME.
  */
 
 class IMEStateManager
 {
+  typedef dom::TabParent TabParent;
   typedef widget::IMEMessage IMEMessage;
   typedef widget::IMENotification IMENotification;
   typedef widget::IMEState IMEState;
   typedef widget::InputContext InputContext;
   typedef widget::InputContextAction InputContextAction;
 
 public:
   static void Init();
   static void Shutdown();
 
+  /**
+   * GetActiveTabParent() returns a pointer to a TabParent instance which is
+   * managed by the focused content (sContent).  If the focused content isn't
+   * managing another process, this returns nullptr.
+   */
+  static TabParent* GetActiveTabParent()
+  {
+    // If menu has pseudo focus, we should ignore active child process.
+    if (sInstalledMenuKeyboardListener) {
+      return nullptr;
+    }
+    return sActiveTabParent.get();
+  }
+
+  /**
+   * OnTabParentDestroying() is called when aTabParent is being destroyed.
+   */
+  static void OnTabParentDestroying(TabParent* aTabParent);
+
+  /**
+   * SetIMEContextForChildProcess() is called when aTabParent receives
+   * SetInputContext() from the remote process.
+   */
+  static void SetInputContextForChildProcess(TabParent* aTabParent,
+                                             const InputContext& aInputContext,
+                                             const InputContextAction& aAction);
+
+  /**
+   * StopIMEStateManagement() is called when the process should stop managing
+   * IME state.
+   */
+  static void StopIMEStateManagement();
+
   static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
   static nsresult OnRemoveContent(nsPresContext* aPresContext,
                                   nsIContent* aContent);
   /**
    * OnChangeFocus() should be called when focused content is changed or
    * IME enabled state is changed.  If nobody has focus, set both aPresContext
    * and aContent nullptr.  E.g., all windows are deactivated.
    */
@@ -158,30 +193,44 @@ public:
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
                           InputContextAction aAction);
+  static void SetInputContext(nsIWidget* aWidget,
+                              const InputContext& aInputContext,
+                              const InputContextAction& aAction);
   static IMEState GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static void EnsureTextCompositionArray();
   static void CreateIMEContentObserver(nsIEditor* aEditor);
   static void DestroyIMEContentObserver();
 
   static bool IsEditable(nsINode* node);
 
   static bool IsIMEObserverNeeded(const IMEState& aState);
 
-  static nsIContent*    sContent;
+  static StaticRefPtr<nsIContent> sContent;
   static nsPresContext* sPresContext;
   static StaticRefPtr<nsIWidget> sFocusedIMEWidget;
+  static StaticRefPtr<TabParent> sActiveTabParent;
+  // sActiveIMEContentObserver points to the currently active
+  // IMEContentObserver.  This is null if there is no focused editor.
+  static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver;
+
+  // All active compositions in the process are stored by this array.
+  // When you get an item of this array and use it, please be careful.
+  // The instances in this array can be destroyed automatically if you do
+  // something to cause committing or canceling the composition.
+  static TextCompositionArray* sTextCompositions;
+
   static bool           sInstalledMenuKeyboardListener;
   static bool           sIsGettingNewIMEState;
   static bool           sCheckForIMEUnawareWebApps;
   static bool           sRemoteHasFocus;
 
   class MOZ_STACK_CLASS GettingNewIMEStateBlocker final
   {
   public:
@@ -192,21 +241,13 @@ protected:
     }
     ~GettingNewIMEStateBlocker()
     {
       IMEStateManager::sIsGettingNewIMEState = mOldValue;
     }
   private:
     bool mOldValue;
   };
-
-  static IMEContentObserver* sActiveIMEContentObserver;
-
-  // All active compositions in the process are stored by this array.
-  // When you get an item of this array and use it, please be careful.
-  // The instances in this array can be destroyed automatically if you do
-  // something to cause committing or canceling the composition.
-  static TextCompositionArray* sTextCompositions;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_IMEStateManager_h_
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -430,17 +430,24 @@ const kEventConstructors = {
   SpeechRecognitionError:                    { create: function (aName, aProps) {
                                                          return new SpeechRecognitionError(aName, aProps);
                                                        },
                                              },
   SpeechRecognitionEvent:                    { create: function (aName, aProps) {
                                                          return new SpeechRecognitionEvent(aName, aProps);
                                                        },
                                              },
+  SpeechSynthesisErrorEvent:                 { create: function (aName, aProps) {
+                                                         aProps.error = "synthesis-unavailable";
+                                                         aProps.utterance = new SpeechSynthesisUtterance("Hello World");
+                                                         return new SpeechSynthesisErrorEvent(aName, aProps);
+                                                       },
+                                             },
   SpeechSynthesisEvent:                      { create: function (aName, aProps) {
+                                                         aProps.utterance = new SpeechSynthesisUtterance("Hello World");
                                                          return new SpeechSynthesisEvent(aName, aProps);
                                                        },
                                              },
   StorageEvent:                              { create: function (aName, aProps) {
                                                          return new StorageEvent(aName, aProps);
                                                        },
                                              },
   StyleRuleChangeEvent:                      { create: function (aName, aProps) {
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -572,16 +572,28 @@ child:
      */
     Activate();
 
     Deactivate();
 
     ParentActivated(bool aActivated);
 
     /**
+     * StopIMEStateManagement() is called when the process loses focus and
+     * should stop managing IME state.
+     */
+    StopIMEStateManagement();
+
+    /**
+     * MenuKeyboardListenerInstalled() is called when menu keyboard listener
+     * is installed in the parent process.
+     */
+    MenuKeyboardListenerInstalled(bool aInstalled);
+
+    /**
      * @see nsIDOMWindowUtils sendMouseEvent.
      */
     MouseEvent(nsString aType,
                float aX,
                float aY,
                int32_t aButton,
                int32_t aClickCount,
                int32_t aModifiers,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -15,16 +15,17 @@
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/plugins/PluginWidgetChild.h"
+#include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
@@ -2195,16 +2196,30 @@ bool TabChild::RecvParentActivated(const
   NS_ENSURE_TRUE(fm, true);
 
   nsCOMPtr<nsIDOMWindow> window = do_GetInterface(WebNavigation());
   fm->ParentActivated(window, aActivated);
   return true;
 }
 
 bool
+TabChild::RecvStopIMEStateManagement()
+{
+  IMEStateManager::StopIMEStateManagement();
+  return true;
+}
+
+bool
+TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled)
+{
+  IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled);
+  return true;
+}
+
+bool
 TabChild::RecvMouseEvent(const nsString& aType,
                          const float&    aX,
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -524,16 +524,20 @@ protected:
     virtual bool RecvDestroy() override;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
     virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
 
     virtual bool RecvRequestNotifyAfterRemotePaint() override;
 
     virtual bool RecvParentActivated(const bool& aActivated) override;
 
+    virtual bool RecvStopIMEStateManagement() override;
+    virtual bool RecvMenuKeyboardListenerInstalled(
+                   const bool& aInstalled) override;
+
 #ifdef MOZ_WIDGET_GONK
     void MaybeRequestPreinitCamera();
 #endif
 
 private:
     /**
      * Create a new TabChild object.
      *
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -243,17 +243,16 @@ private:
         }
         mFD = nullptr;
     }
 };
 
 namespace mozilla {
 namespace dom {
 
-TabParent *TabParent::mIMETabParent = nullptr;
 TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
 
 NS_IMPL_ISUPPORTS(TabParent,
                   nsITabParent,
                   nsIAuthPromptProvider,
                   nsISecureBrowserUI,
                   nsISupportsWeakReference)
 
@@ -476,19 +475,18 @@ TabParent::Recv__delete__()
   }
 
   return true;
 }
 
 void
 TabParent::ActorDestroy(ActorDestroyReason why)
 {
-  if (mIMETabParent == this) {
-    mIMETabParent = nullptr;
-  }
+  IMEStateManager::OnTabParentDestroying(this);
+
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (frameLoader) {
     nsCOMPtr<Element> frameElement(mFrameElement);
     ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
                    nullptr);
     frameLoader->DestroyComplete();
 
@@ -1929,17 +1927,16 @@ TabParent::RecvNotifyIMEFocus(const bool
                               nsIMEUpdatePreference* aPreference)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     *aPreference = nsIMEUpdatePreference();
     return true;
   }
 
-  mIMETabParent = aFocus ? this : nullptr;
   IMENotification notification(aFocus ? NOTIFY_IME_OF_FOCUS :
                                         NOTIFY_IME_OF_BLUR);
   mContentCache.AssignContent(aContentCache, &notification);
   IMEStateManager::NotifyIME(notification, widget, true);
 
   if (aFocus) {
     *aPreference = widget->GetIMEUpdatePreference();
   }
@@ -2383,56 +2380,29 @@ bool
 TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
                                const int32_t& aIMEOpen,
                                const nsString& aType,
                                const nsString& aInputmode,
                                const nsString& aActionHint,
                                const int32_t& aCause,
                                const int32_t& aFocusChange)
 {
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (!widget || !AllowContentIME()) {
-    return true;
-  }
-
-  InputContext oldContext = widget->GetInputContext();
-
-  // Ignore if current widget IME setting is not DISABLED and didn't come
-  // from remote content.  Chrome content may have taken over.
-  if (oldContext.mIMEState.mEnabled != IMEState::DISABLED &&
-      oldContext.IsOriginMainProcess()) {
-    return true;
-  }
-
-  // mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus
-  // When the input mode is set to anything but IMEState::DISABLED,
-  // mIMETabParent should be set to this
-  mIMETabParent =
-    aIMEEnabled != static_cast<int32_t>(IMEState::DISABLED) ? this : nullptr;
-
   InputContext context;
   context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
   context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
   context.mHTMLInputType.Assign(aType);
   context.mHTMLInputInputmode.Assign(aInputmode);
   context.mActionHint.Assign(aActionHint);
   context.mOrigin = InputContext::ORIGIN_CONTENT;
 
   InputContextAction action(
     static_cast<InputContextAction::Cause>(aCause),
     static_cast<InputContextAction::FocusChange>(aFocusChange));
-  widget->SetInputContext(context, action);
-
-  nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
-  if (!observerService)
-    return true;
-
-  nsAutoString state;
-  state.AppendInt(aIMEEnabled);
-  observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get());
+
+  IMEStateManager::SetInputContextForChildProcess(this, context, action);
 
   return true;
 }
 
 bool
 TabParent::RecvIsParentWindowMainWidgetVisible(bool* aIsVisible)
 {
   nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement);
@@ -2612,29 +2582,16 @@ TabParent::RecvGetRenderFrameInfo(PRende
   if (mNeedLayerTreeReadyNotification) {
     RequestNotifyLayerTreeReady();
     mNeedLayerTreeReadyNotification = false;
   }
 
   return true;
 }
 
-bool
-TabParent::AllowContentIME()
-{
-  nsFocusManager* fm = nsFocusManager::GetFocusManager();
-  NS_ENSURE_TRUE(fm, false);
-
-  nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
-  if (focusedContent && focusedContent->IsEditable())
-    return false;
-
-  return true;
-}
-
 already_AddRefed<nsFrameLoader>
 TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
 {
   if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
     return nullptr;
   }
 
   if (mFrameLoader) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -361,17 +361,16 @@ public:
                                       override;
 
     bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
 
-    static TabParent *GetIMETabParent() { return mIMETabParent; }
     bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
     bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event);
     bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event);
 
     static TabParent* GetFrom(nsFrameLoader* aFrameLoader);
     static TabParent* GetFrom(nsIFrameLoader* aFrameLoader);
     static TabParent* GetFrom(nsITabParent* aTabParent);
     static TabParent* GetFrom(PBrowserParent* aTabParent);
@@ -442,18 +441,16 @@ protected:
 
     virtual bool Recv__delete__() override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override;
 
     Element* mFrameElement;
     nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 
-    bool AllowContentIME();
-
     virtual PRenderFrameParent* AllocPRenderFrameParent() override;
     virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override;
 
     virtual bool RecvRemotePaintIsReady() override;
 
     virtual bool RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
                                         TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                         uint64_t* aLayersId) override;
@@ -462,18 +459,16 @@ protected:
                                    const int32_t& aX, const int32_t& aY,
                                    const int32_t& aCx, const int32_t& aCy) override;
 
     bool InitBrowserConfiguration(const nsCString& aURI,
                                   BrowserConfiguration& aConfiguration);
 
     void SetHasContentOpener(bool aHasContentOpener);
 
-    // IME
-    static TabParent *mIMETabParent;
     ContentCacheInParent mContentCache;
 
     nsIntRect mRect;
     ScreenIntSize mDimensions;
     ScreenOrientation mOrientation;
     float mDPI;
     CSSToLayoutDeviceScale mDefaultScale;
     bool mUpdatedDimensions;
--- a/dom/media/webspeech/synth/SpeechSynthesis.cpp
+++ b/dom/media/webspeech/synth/SpeechSynthesis.cpp
@@ -220,17 +220,19 @@ SpeechSynthesis::OnEnd(const nsSpeechTas
 
 void
 SpeechSynthesis::GetVoices(nsTArray< nsRefPtr<SpeechSynthesisVoice> >& aResult)
 {
   aResult.Clear();
   uint32_t voiceCount = 0;
 
   nsresult rv = nsSynthVoiceRegistry::GetInstance()->GetVoiceCount(&voiceCount);
-  NS_ENSURE_SUCCESS_VOID(rv);
+  if(NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
 
   for (uint32_t i = 0; i < voiceCount; i++) {
     nsAutoString uri;
     rv = nsSynthVoiceRegistry::GetInstance()->GetVoice(i, uri);
 
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to retrieve voice from registry");
       continue;
--- a/dom/media/webspeech/synth/SpeechSynthesisUtterance.cpp
+++ b/dom/media/webspeech/synth/SpeechSynthesisUtterance.cpp
@@ -157,16 +157,17 @@ void
 SpeechSynthesisUtterance::DispatchSpeechSynthesisEvent(const nsAString& aEventType,
                                                        uint32_t aCharIndex,
                                                        float aElapsedTime,
                                                        const nsAString& aName)
 {
   SpeechSynthesisEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
+  init.mUtterance = this;
   init.mCharIndex = aCharIndex;
   init.mElapsedTime = aElapsedTime;
   init.mName = aName;
 
   nsRefPtr<SpeechSynthesisEvent> event =
     SpeechSynthesisEvent::Constructor(this, aEventType, init);
   DispatchTrustedEvent(event);
 }
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
@@ -118,79 +118,89 @@ SpeechSynthesisRequestParent::RecvCancel
 }
 
 // SpeechTaskParent
 
 nsresult
 SpeechTaskParent::DispatchStartImpl(const nsAString& aUri)
 {
   MOZ_ASSERT(mActor);
-  NS_ENSURE_TRUE(mActor->SendOnStart(nsString(aUri)), NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mActor->SendOnStart(nsString(aUri))))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
   SpeechSynthesisRequestParent* actor = mActor;
   mActor = nullptr;
-  NS_ENSURE_TRUE(actor->Send__delete__(actor, false, aElapsedTime, aCharIndex),
-                 NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(actor->Send__delete__(actor, false, aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
-  NS_ENSURE_TRUE(mActor->SendOnPause(aElapsedTime, aCharIndex), NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mActor->SendOnPause(aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
-  NS_ENSURE_TRUE(mActor->SendOnResume(aElapsedTime, aCharIndex), NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mActor->SendOnResume(aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
   SpeechSynthesisRequestParent* actor = mActor;
   mActor = nullptr;
-  NS_ENSURE_TRUE(actor->Send__delete__(actor, true, aElapsedTime, aCharIndex),
-                 NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(actor->Send__delete__(actor, true, aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchBoundaryImpl(const nsAString& aName,
                                        float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
-  NS_ENSURE_TRUE(mActor->SendOnBoundary(nsString(aName), aElapsedTime, aCharIndex),
-                 NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mActor->SendOnBoundary(nsString(aName), aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 nsresult
 SpeechTaskParent::DispatchMarkImpl(const nsAString& aName,
                                    float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mActor);
-  NS_ENSURE_TRUE(mActor->SendOnMark(nsString(aName), aElapsedTime, aCharIndex),
-                 NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mActor->SendOnMark(nsString(aName), aElapsedTime, aCharIndex)))) {
+    return NS_ERROR_FAILURE;
+  }
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -160,17 +160,19 @@ nsSpeechTask::Setup(nsISpeechTaskCallbac
   }
 
   // mStream is set up in BindStream() that should be called before this.
   MOZ_ASSERT(mStream);
 
   mStream->AddListener(new SynthStreamListener(this));
 
   // XXX: Support more than one channel
-  NS_ENSURE_TRUE(aChannels == 1, NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(aChannels == 1))) {
+    return NS_ERROR_FAILURE;
+  }
 
   mChannels = aChannels;
 
   AudioSegment* segment = new AudioSegment();
   mStream->AddAudioTrack(1, aRate, 0, segment);
   mStream->AddAudioOutput(this);
   mStream->SetAudioOutputVolume(this, mVolume);
 
@@ -192,20 +194,28 @@ makeSamples(int16_t* aData, uint32_t aDa
 }
 
 NS_IMETHODIMP
 nsSpeechTask::SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLandmarks,
                         JSContext* aCx)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
-  NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_FALSE(mStream->IsDestroyed(), NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_TRUE(mChannels, NS_ERROR_FAILURE);
-  NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_INVALID_ARG);
+  if(NS_WARN_IF(!(mStream))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(mStream->IsDestroyed())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(!(mChannels))) {
+    return NS_ERROR_FAILURE;
+  }
+  if(NS_WARN_IF(!(aData.isObject()))) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   if (mIndirectAudio) {
     NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
     return NS_ERROR_FAILURE;
   }
 
   JS::Rooted<JSObject*> darray(aCx, &aData.toObject());
   JSAutoCompartment ac(aCx, darray);
@@ -234,19 +244,25 @@ nsSpeechTask::SendAudio(JS::Handle<JS::V
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::SendAudioNative(int16_t* aData, uint32_t aDataLen)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
-  NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_FALSE(mStream->IsDestroyed(), NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_TRUE(mChannels, NS_ERROR_FAILURE);
+  if(NS_WARN_IF(!(mStream))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(mStream->IsDestroyed())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(!(mChannels))) {
+    return NS_ERROR_FAILURE;
+  }
 
   if (mIndirectAudio) {
     NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<mozilla::SharedBuffer> samples = makeSamples(aData, aDataLen);
   SendAudioImpl(samples, aDataLen);
@@ -288,18 +304,19 @@ nsSpeechTask::DispatchStartImpl()
 }
 
 nsresult
 nsSpeechTask::DispatchStartImpl(const nsAString& aUri)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchStart"));
 
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING,
-                 NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
   mUtterance->mChosenVoiceURI = aUri;
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0, 0,
                                            EmptyString());
 
   return NS_OK;
 }
@@ -316,18 +333,19 @@ nsSpeechTask::DispatchEnd(float aElapsed
 }
 
 nsresult
 nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n"));
 
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
-                  NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   // XXX: This should not be here, but it prevents a crash in MSG.
   if (mStream) {
     mStream->Destroy();
   }
 
   nsRefPtr<SpeechSynthesisUtterance> utterance = mUtterance;
 
@@ -358,19 +376,22 @@ nsSpeechTask::DispatchPause(float aElaps
   return DispatchPauseImpl(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchPause"));
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_FALSE(mUtterance->mPaused, NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
-                  NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(mUtterance->mPaused)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->mPaused = true;
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
                                            aCharIndex, aElapsedTime,
                                            EmptyString());
   return NS_OK;
 }
 
@@ -385,19 +406,22 @@ nsSpeechTask::DispatchResume(float aElap
   return DispatchResumeImpl(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchResume"));
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_TRUE(mUtterance->mPaused, NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
-                  NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(mUtterance->mPaused))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->mPaused = false;
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
                                            aCharIndex, aElapsedTime,
                                            EmptyString());
   return NS_OK;
 }
 
@@ -411,18 +435,19 @@ nsSpeechTask::DispatchError(float aElaps
 
   return DispatchErrorImpl(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
-                  NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
                                            aCharIndex, aElapsedTime,
                                            EmptyString());
   return NS_OK;
 }
 
@@ -438,18 +463,19 @@ nsSpeechTask::DispatchBoundary(const nsA
   return DispatchBoundaryImpl(aName, aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchBoundaryImpl(const nsAString& aName,
                                    float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING,
-                 NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("boundary"),
                                            aCharIndex, aElapsedTime,
                                            aName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -464,18 +490,19 @@ nsSpeechTask::DispatchMark(const nsAStri
   return DispatchMarkImpl(aName, aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchMarkImpl(const nsAString& aName,
                                float aElapsedTime, uint32_t aCharIndex)
 {
   MOZ_ASSERT(mUtterance);
-  NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING,
-                 NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("mark"),
                                            aCharIndex, aElapsedTime,
                                            aName);
   return NS_OK;
 }
 
 void
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -231,18 +231,19 @@ nsSynthVoiceRegistry::AddVoice(nsISpeech
                                bool aLocalService)
 {
   LOG(LogLevel::Debug,
       ("nsSynthVoiceRegistry::AddVoice uri='%s' name='%s' lang='%s' local=%s",
        NS_ConvertUTF16toUTF8(aUri).get(), NS_ConvertUTF16toUTF8(aName).get(),
        NS_ConvertUTF16toUTF8(aLang).get(),
        aLocalService ? "true" : "false"));
 
-  NS_ENSURE_FALSE(XRE_GetProcessType() == GeckoProcessType_Content,
-                  NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(XRE_GetProcessType() == GeckoProcessType_Content)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   return AddVoiceImpl(aService, aUri, aName, aLang,
                       aLocalService);
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService,
                                   const nsAString& aUri)
@@ -250,18 +251,22 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpe
   LOG(LogLevel::Debug,
       ("nsSynthVoiceRegistry::RemoveVoice uri='%s' (%s)",
        NS_ConvertUTF16toUTF8(aUri).get(),
        (XRE_GetProcessType() == GeckoProcessType_Content) ? "child" : "parent"));
 
   bool found = false;
   VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
 
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
-  NS_ENSURE_TRUE(aService == retval->mService, NS_ERROR_INVALID_ARG);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if(NS_WARN_IF(!(aService == retval->mService))) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   mVoices.RemoveElement(retval);
   mDefaultVoices.RemoveElement(retval);
   mUriVoiceMap.Remove(aUri);
 
   nsTArray<SpeechSynthesisParent*> ssplist;
   GetAllSpeechSynthActors(ssplist);
 
@@ -272,17 +277,19 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpe
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri,
                                       bool aIsDefault)
 {
   bool found = false;
   VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mDefaultVoices.RemoveElement(retval);
 
   LOG(LogLevel::Debug, ("nsSynthVoiceRegistry::SetDefaultVoice %s %s",
                      NS_ConvertUTF16toUTF8(aUri).get(),
                      aIsDefault ? "true" : "false"));
 
   if (aIsDefault) {
@@ -307,29 +314,33 @@ nsSynthVoiceRegistry::GetVoiceCount(uint
   *aRetval = mVoices.Length();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::GetVoice(uint32_t aIndex, nsAString& aRetval)
 {
-  NS_ENSURE_TRUE(aIndex < mVoices.Length(), NS_ERROR_INVALID_ARG);
+  if(NS_WARN_IF(!(aIndex < mVoices.Length()))) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   aRetval = mVoices[aIndex]->mUri;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::IsDefaultVoice(const nsAString& aUri, bool* aRetval)
 {
   bool found;
   VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   for (int32_t i = mDefaultVoices.Length(); i > 0; ) {
     VoiceData* defaultVoice = mDefaultVoices[--i];
 
     if (voice->mLang.Equals(defaultVoice->mLang)) {
       *aRetval = voice == defaultVoice;
       return NS_OK;
     }
@@ -339,54 +350,62 @@ nsSynthVoiceRegistry::IsDefaultVoice(con
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::IsLocalVoice(const nsAString& aUri, bool* aRetval)
 {
   bool found;
   VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   *aRetval = voice->mIsLocal;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::GetVoiceLang(const nsAString& aUri, nsAString& aRetval)
 {
   bool found;
   VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   aRetval = voice->mLang;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::GetVoiceName(const nsAString& aUri, nsAString& aRetval)
 {
   bool found;
   VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   aRetval = voice->mName;
   return NS_OK;
 }
 
 nsresult
 nsSynthVoiceRegistry::AddVoiceImpl(nsISpeechService* aService,
                                    const nsAString& aUri,
                                    const nsAString& aName,
                                    const nsAString& aLang,
                                    bool aLocalService)
 {
   bool found = false;
   mUriVoiceMap.GetWeak(aUri, &found);
-  NS_ENSURE_FALSE(found, NS_ERROR_INVALID_ARG);
+  if(NS_WARN_IF(found)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   nsRefPtr<VoiceData> voice = new VoiceData(aService, aUri, aName, aLang,
                                             aLocalService);
 
   mVoices.AppendElement(voice);
   mUriVoiceMap.Put(aUri, voice);
 
   nsTArray<SpeechSynthesisParent*> ssplist;
@@ -472,21 +491,25 @@ nsSynthVoiceRegistry::FindBestMatch(cons
 
       return retval;
     }
   }
 
   // Try UI language.
   nsresult rv;
   nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
 
   nsAutoString uiLang;
   rv = localeService->GetLocaleComponentForUserAgent(uiLang);
-  NS_ENSURE_SUCCESS(rv, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
 
   if (FindVoiceByLang(uiLang, &retval)) {
     LOG(LogLevel::Debug,
         ("nsSynthVoiceRegistry::FindBestMatch - Matched UI language (%s ~= %s)",
          NS_ConvertUTF16toUTF8(uiLang).get(),
          NS_ConvertUTF16toUTF8(retval->mLang).get()));
 
     return retval;
--- a/dom/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp
@@ -446,17 +446,19 @@ nsPicoService::~nsPicoService()
 
 // nsIObserver
 
 NS_IMETHODIMP
 nsPicoService::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
+  if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
+    return NS_ERROR_UNEXPECTED;
+  }
 
   if (!Preferences::GetBool("media.webspeech.synth.enabled") ||
       Preferences::GetBool("media.webspeech.synth.test")) {
     return NS_OK;
   }
 
   DebugOnly<nsresult> rv = NS_NewNamedThread("Pico Worker", getter_AddRefs(mThread));
   MOZ_ASSERT(NS_SUCCEEDED(rv));
@@ -465,22 +467,26 @@ nsPicoService::Observe(nsISupports* aSub
 }
 // nsISpeechService
 
 NS_IMETHODIMP
 nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
                      float aVolume, float aRate, float aPitch,
                      nsISpeechTask* aTask)
 {
-  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(mInitialized))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   MonitorAutoLock autoLock(mVoicesMonitor);
   bool found = false;
   PicoVoice* voice = mVoices.GetWeak(aUri, &found);
-  NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
+  if(NS_WARN_IF(!(found))) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   mCurrentTask = aTask;
   nsRefPtr<PicoCallbackRunnable> cb = new PicoCallbackRunnable(aText, voice, aRate, aPitch, aTask, this);
   return mThread->Dispatch(cb, NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
 nsPicoService::GetServiceType(SpeechServiceType* aServiceType)
--- a/dom/media/webspeech/synth/test/file_setup.html
+++ b/dom/media/webspeech/synth/test/file_setup.html
@@ -22,16 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 ok(SpeechSynthesis, "SpeechSynthesis exists in global scope");
 ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope");
+ok(SpeechSynthesisErrorEvent, "SpeechSynthesisErrorEvent exists in global scope");
 ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope");
 
 // SpeechSynthesisUtterance is the only type that has a constructor
 //  and writable properties
 ok(SpeechSynthesisUtterance, "SpeechSynthesisUtterance exists in global scope");
 var ssu = new SpeechSynthesisUtterance("hello world");
 is(typeof ssu, "object", "SpeechSynthesisUtterance instance is an object");
 is(ssu.text, "hello world", "SpeechSynthesisUtterance.text is correct");
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -306,17 +306,19 @@ nsFakeSynthServices::Init()
 
 // nsIObserver
 
 NS_IMETHODIMP
 nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
                              const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
+  if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
+    return NS_ERROR_UNEXPECTED;
+  }
 
   if (Preferences::GetBool("media.webspeech.synth.test")) {
     Init();
   }
 
   return NS_OK;
 }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -960,16 +960,18 @@ var interfaceNamesInGlobalScope =
     "SimpleGestureEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleTest", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SourceBuffer", linux: false, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SourceBufferList", linux: false, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "SpeechSynthesisErrorEvent", b2g: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesis", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisUtterance", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisVoice", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SpeechSynthesisErrorEvent.webidl
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/.
+ *
+ * The origin of this IDL file is
+ * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+enum SpeechSynthesisErrorCode {
+  "canceled",
+  "interrupted",
+  "audio-busy",
+  "audio-hardware",
+  "network",
+  "synthesis-unavailable",
+  "synthesis-failed",
+  "language-unavailable",
+  "voice-unavailable",
+  "text-too-long",
+  "invalid-argument",
+};
+
+[Constructor(DOMString type, optional SpeechSynthesisErrorEventInit eventInitDict),
+ Pref="media.webspeech.synth.enabled"]
+interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
+  readonly attribute SpeechSynthesisErrorCode error;
+};
+
+dictionary SpeechSynthesisErrorEventInit : SpeechSynthesisEventInit
+{
+  required SpeechSynthesisErrorCode error;
+};
--- a/dom/webidl/SpeechSynthesisEvent.webidl
+++ b/dom/webidl/SpeechSynthesisEvent.webidl
@@ -9,19 +9,21 @@
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Constructor(DOMString type, optional SpeechSynthesisEventInit eventInitDict),
  Pref="media.webspeech.synth.enabled"]
 interface SpeechSynthesisEvent : Event
 {
+  readonly attribute SpeechSynthesisUtterance utterance;
   readonly attribute unsigned long charIndex;
   readonly attribute float elapsedTime;
   readonly attribute DOMString? name;
 };
 
 dictionary SpeechSynthesisEventInit : EventInit
 {
+  required SpeechSynthesisUtterance utterance;
   unsigned long charIndex = 0;
   float elapsedTime = 0;
   DOMString name = "";
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -606,16 +606,17 @@ if CONFIG['MOZ_WEBSPEECH']:
         'SpeechGrammarList.webidl',
         'SpeechRecognition.webidl',
         'SpeechRecognitionAlternative.webidl',
         'SpeechRecognitionError.webidl',
         'SpeechRecognitionEvent.webidl',
         'SpeechRecognitionResult.webidl',
         'SpeechRecognitionResultList.webidl',
         'SpeechSynthesis.webidl',
+        'SpeechSynthesisErrorEvent.webidl',
         'SpeechSynthesisEvent.webidl',
         'SpeechSynthesisUtterance.webidl',
         'SpeechSynthesisVoice.webidl',
     ]
 
 if CONFIG['MOZ_GAMEPAD']:
     WEBIDL_FILES += [
         'Gamepad.webidl',
@@ -783,16 +784,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'UDPMessageEvent.webidl',
     'UserProximityEvent.webidl',
     'USSDReceivedEvent.webidl',
 ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     GENERATED_EVENTS_WEBIDL_FILES += [
         'SpeechRecognitionEvent.webidl',
+        'SpeechSynthesisErrorEvent.webidl',
         'SpeechSynthesisEvent.webidl',
     ]
 
 if CONFIG['MOZ_GAMEPAD']:
     GENERATED_EVENTS_WEBIDL_FILES += [
         'GamepadAxisMoveEvent.webidl',
         'GamepadButtonEvent.webidl',
         'GamepadEvent.webidl',
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -55,21 +55,16 @@ static inline int floor_div(int a, int b
     int sub;
     sub = a ^ b;
     // The results of this shift is either 0 or -1.
     sub >>= 8*sizeof(int)-1;
     return div+sub;
   }
 }
 
-// An abstract implementation of a tile buffer. This code covers the logic of
-// moving and reusing tiles and leaves the validation up to the implementor. To
-// avoid the overhead of virtual dispatch, we employ the curiously recurring
-// template pattern.
-//
 // Tiles are aligned to a grid with one of the grid points at (0,0) and other
 // grid points spaced evenly in the x- and y-directions by GetTileSize()
 // multiplied by mResolution. GetScaledTileSize() provides convenience for
 // accessing these values.
 //
 // This tile buffer stores a valid region, which defines the areas that have
 // up-to-date content. The contents of tiles within this region will be reused
 // from paint to paint. It also stores the region that was modified in the last
@@ -77,40 +72,16 @@ static inline int floor_div(int a, int b
 // (as in an off-main-thread-compositing scenario), so that the shadow tiled
 // layer buffer can correctly reflect the updates of the master layer buffer.
 //
 // The associated Tile may be of any type as long as the derived class can
 // validate and return tiles of that type. Tiles will be frequently copied, so
 // the tile type should be a reference or some other type with an efficient
 // copy constructor.
 //
-// It is required that the derived class specify the base class as a friend. It
-// must also implement the following public method:
-//
-//   Tile GetPlaceholderTile() const;
-//
-//   Returns a temporary placeholder tile used as a marker. This placeholder tile
-//   must never be returned by validateTile and must be == to every instance
-//   of a placeholder tile.
-//
-// Additionally, it must implement the following protected methods:
-//
-//   Tile ValidateTile(Tile aTile, const nsIntPoint& aTileOrigin,
-//                     const nsIntRegion& aDirtyRect);
-//
-//   Validates the dirtyRect. The returned Tile will replace the tile.
-//
-//   void ReleaseTile(Tile aTile);
-//
-//   Destroys the given tile.
-//
-//   void SwapTiles(Tile& aTileA, Tile& aTileB);
-//
-//   Swaps two tiles.
-//
 // The contents of the tile buffer will be rendered at the resolution specified
 // in mResolution, which can be altered with SetResolution. The resolution
 // should always be a factor of the tile length, to avoid tiles covering
 // non-integer amounts of pixels.
 
 // Size and Point in number of tiles rather than in pixels
 typedef gfx::IntSizeTyped<TileUnit> TileIntSize;
 typedef gfx::IntPointTyped<TileUnit> TileIntPoint;
@@ -141,543 +112,110 @@ struct TilesPlacement {
 
   TileIntPoint TilePosition(size_t aIndex) const {
     return TileIntPoint(
       mFirst.x + aIndex / mSize.height,
       mFirst.y + aIndex % mSize.height
     );
   }
 
-  bool HasTile(TileIntPoint aPosition) {
+  bool HasTile(TileIntPoint aPosition) const {
     return aPosition.x >= mFirst.x && aPosition.x < mFirst.x + mSize.width &&
            aPosition.y >= mFirst.y && aPosition.y < mFirst.y + mSize.height;
   }
 };
 
+
+// Given a position i, this function returns the position inside the current tile.
+inline int GetTileStart(int i, int aTileLength) {
+  return (i >= 0) ? (i % aTileLength)
+                  : ((aTileLength - (-i % aTileLength)) %
+                     aTileLength);
+}
+
+// Rounds the given coordinate down to the nearest tile boundary.
+inline int RoundDownToTileEdge(int aX, int aTileLength) { return aX - GetTileStart(aX, aTileLength); }
+
 template<typename Derived, typename Tile>
 class TiledLayerBuffer
 {
 public:
   TiledLayerBuffer()
     : mTiles(0, 0, 0, 0)
     , mResolution(1)
     , mTileSize(gfxPlatform::GetPlatform()->GetTileWidth(),
                 gfxPlatform::GetPlatform()->GetTileHeight())
   {}
 
   ~TiledLayerBuffer() {}
 
-  // Given a tile origin aligned to a multiple of GetScaledTileSize,
-  // return the tile that describes that region.
-  // NOTE: To get the valid area of that tile you must intersect
-  //       (aTileOrigin.x, aTileOrigin.y,
-  //        GetScaledTileSize().width, GetScaledTileSize().height)
-  //       and GetValidRegion() to get the area of the tile that is valid.
-  Tile& GetTile(const gfx::IntPoint& aTileOrigin);
-  // Given a tile x, y relative to the top left of the layer, this function
-  // will return the tile for
-  // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
-  //  GetScaledTileSize().width, GetScaledTileSize().height)
-  Tile& GetTile(int x, int y);
-
-  Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
-
   gfx::IntPoint GetTileOffset(TileIntPoint aPosition) const {
     gfx::IntSize scaledTileSize = GetScaledTileSize();
     return gfx::IntPoint(aPosition.x * scaledTileSize.width,
                          aPosition.y * scaledTileSize.height);
   }
 
   const TilesPlacement& GetPlacement() const { return mTiles; }
 
-  int TileIndex(const gfx::IntPoint& aTileOrigin) const;
-  int TileIndex(int x, int y) const { return x * mTiles.mSize.height + y; }
-
-  bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
-  bool HasTile(const gfx::IntPoint& aTileOrigin) const;
-  bool HasTile(int x, int y) const {
-    return x >= 0 && x < mTiles.mSize.width && y >= 0 && y < mTiles.mSize.height;
-  }
-
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
   gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
 
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
+  Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
+
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
   void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
 
-  void ResetPaintedAndValidState() {
-    mPaintedRegion.SetEmpty();
-    mValidRegion.SetEmpty();
-    mTiles.mSize.width = 0;
-    mTiles.mSize.height = 0;
-    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-      if (!mRetainedTiles[i].IsPlaceholderTile()) {
-        AsDerived().ReleaseTile(mRetainedTiles[i]);
-      }
-    }
-    mRetainedTiles.Clear();
-  }
-
-  // Given a position i, this function returns the position inside the current tile.
-  int GetTileStart(int i, int aTileLength) const {
-    return (i >= 0) ? (i % aTileLength)
-                    : ((aTileLength - (-i % aTileLength)) %
-                       aTileLength);
-  }
-
-  // Rounds the given coordinate down to the nearest tile boundary.
-  int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
-
   // Get and set draw scaling. mResolution affects the resolution at which the
   // contents of the buffer are drawn. mResolution has no effect on the
   // coordinate space of the valid region, but does affect the size of an
   // individual tile's rect in relation to the valid region.
   // Setting the resolution will invalidate the buffer.
   float GetResolution() const { return mResolution; }
   bool IsLowPrecision() const { return mResolution < 1; }
 
-  typedef Tile* Iterator;
-  Iterator TilesBegin() { return mRetainedTiles.Elements(); }
-  Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
-
   void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml);
 
 protected:
-  // The implementor should call Update() to change
-  // the new valid region. This implementation will call
-  // validateTile on each tile that is dirty, which is left
-  // to the implementor.
-  void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
-
-  // Return a reference to this tile in GetTile when the requested tile offset
-  // does not exist.
-  Tile mPlaceHolderTile;
 
   nsIntRegion     mValidRegion;
   nsIntRegion     mPaintedRegion;
 
   /**
    * mRetainedTiles is a rectangular buffer of mTiles.mSize.width x mTiles.mSize.height
    * stored as column major with the same origin as mValidRegion.GetBounds().
    * Any tile that does not intersect mValidRegion is a PlaceholderTile.
    * Only the region intersecting with mValidRegion should be read from a tile,
    * another other region is assumed to be uninitialized. The contents of the
    * tiles is scaled by mResolution.
    */
   nsTArray<Tile>  mRetainedTiles;
   TilesPlacement  mTiles;
   float           mResolution;
   gfx::IntSize    mTileSize;
-
-private:
-  const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
-  Derived& AsDerived() { return *static_cast<Derived*>(this); }
 };
 
-class ClientTiledLayerBuffer;
-class SurfaceDescriptorTiles;
-class ISurfaceAllocator;
-
-// Shadow layers may implement this interface in order to be notified when a
-// tiled layer buffer is updated.
-class TiledLayerComposer
-{
-public:
-  /**
-   * Update the current retained layer with the updated layer data.
-   * It is expected that the tiles described by aTiledDescriptor are all in the
-   * ReadLock state, so that the locks can be adopted when recreating a
-   * ClientTiledLayerBuffer locally. This lock will be retained until the buffer
-   * has completed uploading.
-   *
-   * Returns false if a deserialization error happened, in which case we will
-   * have to kill the child process.
-   */
-  virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
-                                   const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
-
-  /**
-   * If some part of the buffer is being rendered at a lower precision, this
-   * returns that region. If it is not, an empty region will be returned.
-   */
-  virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
-
-  virtual const nsIntRegion& GetValidRegion() const = 0;
-};
-
-template<typename Derived, typename Tile> bool
-TiledLayerBuffer<Derived, Tile>::HasTile(const gfx::IntPoint& aTileOrigin) const {
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-  return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mTiles.mFirst.x,
-                 floor_div(aTileOrigin.y, scaledTileSize.height) - mTiles.mFirst.y);
-}
-
-template<typename Derived, typename Tile> Tile&
-TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin)
-{
-  if (HasTile(aTileOrigin)) {
-    return mRetainedTiles[TileIndex(aTileOrigin)];
-  }
-  return mPlaceHolderTile;
-}
-
-template<typename Derived, typename Tile> int
-TiledLayerBuffer<Derived, Tile>::TileIndex(const gfx::IntPoint& aTileOrigin) const
-{
-  // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
-  // origin, the difference is the tile x/y relative to the start of the tile buffer.
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-  return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mTiles.mFirst.x,
-                   floor_div(aTileOrigin.y, scaledTileSize.height) - mTiles.mFirst.y);
-}
-
-template<typename Derived, typename Tile> Tile&
-TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y)
-{
-  if (HasTile(x, y)) {
-    return mRetainedTiles[TileIndex(x, y)];
-  }
-  return mPlaceHolderTile;
-}
-
 template<typename Derived, typename Tile> void
 TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
                                       const char* aPrefix,
                                       bool aDumpHtml)
 {
-  gfx::IntRect visibleRect = GetValidRegion().GetBounds();
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-  for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
-    int32_t tileStartX = GetTileStart(x, scaledTileSize.width);
-    int32_t w = scaledTileSize.width - tileStartX;
-
-    for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
-      int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
-      nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
-                                         RoundDownToTileEdge(y, scaledTileSize.height));
-      Tile& tileTexture = GetTile(tileOrigin);
-      int32_t h = scaledTileSize.height - tileStartY;
-
-      aStream << "\n" << aPrefix << "Tile (x=" <<
-        RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
-        RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
-      if (!tileTexture.IsPlaceholderTile()) {
-        tileTexture.DumpTexture(aStream);
-      } else {
-        aStream << "empty tile";
-      }
-      y += h;
-    }
-    x += w;
-  }
-}
-
-template<typename Derived, typename Tile> void
-TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& newValidRegion,
-                                        const nsIntRegion& aPaintRegion)
-{
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-
-  nsTArray<Tile>  newRetainedTiles;
-  nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
-  const gfx::IntRect oldBound = mValidRegion.GetBounds();
-  const gfx::IntRect newBound = newValidRegion.GetBounds();
-  const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
-                                   RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
-  const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
-                                   RoundDownToTileEdge(newBound.y, scaledTileSize.height));
-
-  // This is the reason we break the style guide with newValidRegion instead
-  // of aNewValidRegion - so that the names match better and code easier to read
-  const nsIntRegion& oldValidRegion = mValidRegion;
-  const int oldRetainedHeight = mTiles.mSize.height;
-
-#ifdef GFX_TILEDLAYER_RETAINING_LOG
-  { // scope ss
-    std::stringstream ss;
-    ss << "TiledLayerBuffer " << this << " starting update"
-       << " on bounds ";
-    AppendToString(ss, newBound);
-    ss << " with mResolution=" << mResolution << "\n";
-    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-      ss << "mRetainedTiles[" << i << "] = ";
-      mRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    print_stderr(ss);
-  }
-#endif
-
-  // Pass 1: Recycle valid content from the old buffer
-  // Recycle tiles from the old buffer that contain valid regions.
-  // Insert placeholders tiles if we have no valid area for that tile
-  // which we will allocate in pass 2.
-  // TODO: Add a tile pool to reduce new allocation
-  int tileX = 0;
-  int tileY = 0;
-  int tilesMissing = 0;
-  // Iterate over the new drawing bounds in steps of tiles.
-  for (int32_t x = newBound.x; x < newBound.XMost(); tileX++) {
-    // Compute tileRect(x,y,width,height) in layer space coordinate
-    // giving us the rect of the tile that hits the newBounds.
-    int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
-    if (x + width > newBound.XMost()) {
-      width = newBound.x + newBound.width - x;
-    }
+  for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
+    const TileIntPoint tilePosition = mTiles.TilePosition(i);
+    gfx::IntPoint tileOffset = GetTileOffset(tilePosition);
 
-    tileY = 0;
-    for (int32_t y = newBound.y; y < newBound.YMost(); tileY++) {
-      int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
-      if (y + height > newBound.y + newBound.height) {
-        height = newBound.y + newBound.height - y;
-      }
-
-      const gfx::IntRect tileRect(x,y,width,height);
-      if (oldValidRegion.Intersects(tileRect) && newValidRegion.Intersects(tileRect)) {
-        // This old tiles contains some valid area so move it to the new tile
-        // buffer. Replace the tile in the old buffer with a placeholder
-        // to leave the old buffer index unaffected.
-        int tileX = floor_div(x - oldBufferOrigin.x, scaledTileSize.width);
-        int tileY = floor_div(y - oldBufferOrigin.y, scaledTileSize.height);
-        int index = tileX * oldRetainedHeight + tileY;
-
-        // The tile may have been removed, skip over it in this case.
-        if (oldRetainedTiles.
-                          SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile()) {
-          newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
-        } else {
-          Tile tileWithPartialValidContent = oldRetainedTiles[index];
-          newRetainedTiles.AppendElement(tileWithPartialValidContent);
-          oldRetainedTiles[index] = AsDerived().GetPlaceholderTile();
-        }
-
-      } else {
-        // This tile is either:
-        // 1) Outside the new valid region and will simply be an empty
-        // placeholder forever.
-        // 2) The old buffer didn't have any data for this tile. We postpone
-        // the allocation of this tile after we've reused any tile with
-        // valid content because then we know we can safely recycle
-        // with taking from a tile that has recyclable content.
-        newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
-
-        if (aPaintRegion.Intersects(tileRect)) {
-          tilesMissing++;
-        }
-      }
-
-      y += height;
-    }
-
-    x += width;
-  }
-
-  // Keep track of the number of horizontal/vertical tiles
-  // in the buffer so that we can easily look up a tile.
-  mTiles.mSize.width = tileX;
-  mTiles.mSize.height = tileY;
-
-#ifdef GFX_TILEDLAYER_RETAINING_LOG
-  { // scope ss
-    std::stringstream ss;
-    ss << "TiledLayerBuffer " << this << " finished pass 1 of update;"
-       << " tilesMissing=" << tilesMissing << "\n";
-    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
-      ss << "oldRetainedTiles[" << i << "] = ";
-      oldRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    print_stderr(ss);
-  }
-#endif
-
-
-  // Pass 1.5: Release excess tiles in oldRetainedTiles
-  // Tiles in oldRetainedTiles that aren't in newRetainedTiles will be recycled
-  // before creating new ones, but there could still be excess unnecessary
-  // tiles. As tiles may not have a fixed memory cost (for example, due to
-  // double-buffering), we should release these excess tiles first.
-  int oldTileCount = 0;
-  for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
-    Tile oldTile = oldRetainedTiles[i];
-    if (oldTile.IsPlaceholderTile()) {
-      continue;
-    }
-
-    if (oldTileCount >= tilesMissing) {
-      oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
-      AsDerived().ReleaseTile(oldTile);
+    aStream << "\n" << aPrefix << "Tile (x=" <<
+      tileOffset.x << ", y=" << tileOffset.y << "): ";
+    if (!mRetainedTiles[i].IsPlaceholderTile()) {
+      mRetainedTiles[i].DumpTexture(aStream);
     } else {
-      oldTileCount ++;
+      aStream << "empty tile";
     }
   }
-
-  if (!newValidRegion.Contains(aPaintRegion)) {
-    gfxCriticalError() << "Painting outside visible:"
-		       << " paint " << aPaintRegion.ToString().get()
-                       << " old valid " << oldValidRegion.ToString().get()
-                       << " new valid " << newValidRegion.ToString().get();
-  }
-#ifdef DEBUG
-  nsIntRegion oldAndPainted(oldValidRegion);
-  oldAndPainted.Or(oldAndPainted, aPaintRegion);
-  if (!oldAndPainted.Contains(newValidRegion)) {
-    gfxCriticalError() << "Not fully painted:"
-		       << " paint " << aPaintRegion.ToString().get()
-                       << " old valid " << oldValidRegion.ToString().get()
-                       << " old painted " << oldAndPainted.ToString().get()
-                       << " new valid " << newValidRegion.ToString().get();
-  }
-#endif
-
-  nsIntRegion regionToPaint(aPaintRegion);
-
-#ifdef GFX_TILEDLAYER_RETAINING_LOG
-  { // scope ss
-    std::stringstream ss;
-    ss << "TiledLayerBuffer " << this << " finished pass 1.5 of update\n";
-    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
-      ss << "oldRetainedTiles[" << i << "] = ";
-      oldRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
-      ss << "newRetainedTiles[" << i << "] = ";
-      newRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    print_stderr(ss);
-  }
-#endif
-
-  // Pass 2: Validate
-  // We know at this point that any tile in the new buffer that had valid content
-  // from the previous buffer is placed correctly in the new buffer.
-  // We know that any tile in the old buffer that isn't a place holder is
-  // of no use and can be recycled.
-  // We also know that any place holder tile in the new buffer must be
-  // allocated.
-  tileX = 0;
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  printf_stderr("Update %i, %i, %i, %i\n", newBound.x, newBound.y, newBound.width, newBound.height);
-#endif
-  for (int x = newBound.x; x < newBound.x + newBound.width; tileX++) {
-    // Compute tileRect(x,y,width,height) in layer space coordinate
-    // giving us the rect of the tile that hits the newBounds.
-    int tileStartX = RoundDownToTileEdge(x, scaledTileSize.width);
-    int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
-    if (x + width > newBound.XMost())
-      width = newBound.XMost() - x;
-
-    tileY = 0;
-    for (int y = newBound.y; y < newBound.y + newBound.height; tileY++) {
-      int tileStartY = RoundDownToTileEdge(y, scaledTileSize.height);
-      int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
-      if (y + height > newBound.YMost()) {
-        height = newBound.YMost() - y;
-      }
-
-      const gfx::IntRect tileRect(x, y, width, height);
-
-      nsIntRegion tileDrawRegion;
-      tileDrawRegion.And(tileRect, regionToPaint);
-
-      if (tileDrawRegion.IsEmpty()) {
-        // We have a tile but it doesn't hit the draw region
-        // because we can reuse all of the content from the
-        // previous buffer.
-#ifdef DEBUG
-        int currTileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
-        int currTileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
-        int index = TileIndex(currTileX, currTileY);
-        // If allocating a tile failed we can run into this assertion.
-        // Rendering is going to be glitchy but we don't want to crash.
-        NS_ASSERTION(!newValidRegion.Intersects(tileRect) ||
-                     !newRetainedTiles.
-                                    SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile(),
-                     "Unexpected placeholder tile");
-
-#endif
-        y += height;
-        continue;
-      }
-
-      int tileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
-      int tileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
-      int index = TileIndex(tileX, tileY);
-      MOZ_ASSERT(index >= 0 &&
-                 static_cast<unsigned>(index) < newRetainedTiles.Length(),
-                 "index out of range");
-
-      Tile newTile = newRetainedTiles[index];
-
-      // Try to reuse a tile from the old retained tiles that had no partially
-      // valid content.
-      while (newTile.IsPlaceholderTile() && oldRetainedTiles.Length() > 0) {
-        AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]);
-        oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1);
-        if (!newTile.IsPlaceholderTile()) {
-          oldTileCount--;
-        }
-      }
-
-      // We've done our best effort to recycle a tile but it can be null
-      // in which case it's up to the derived class's ValidateTile()
-      // implementation to allocate a new tile before drawing
-      nsIntPoint tileOrigin(tileStartX, tileStartY);
-      newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY),
-                                         tileDrawRegion);
-      NS_ASSERTION(!newTile.IsPlaceholderTile(), "Unexpected placeholder tile - failed to allocate?");
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-      printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index);
-#endif
-      newRetainedTiles[index] = newTile;
-
-      y += height;
-    }
-
-    x += width;
-  }
-
-  AsDerived().PostValidate(aPaintRegion);
-  for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
-    AsDerived().UnlockTile(newRetainedTiles[i]);
-  }
-
-#ifdef GFX_TILEDLAYER_RETAINING_LOG
-  { // scope ss
-    std::stringstream ss;
-    ss << "TiledLayerBuffer " << this << " finished pass 2 of update;"
-       << " oldTileCount=" << oldTileCount << "\n";
-    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
-      ss << "oldRetainedTiles[" << i << "] = ";
-      oldRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
-      ss << "newRetainedTiles[" << i << "] = ";
-      newRetainedTiles[i].Dump(ss);
-      ss << "\n";
-    }
-    print_stderr(ss);
-  }
-#endif
-
-  // At this point, oldTileCount should be zero
-  MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
-
-  mRetainedTiles = newRetainedTiles;
-  mValidRegion = newValidRegion;
-
-  mTiles.mFirst.x = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  mTiles.mFirst.y = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-
-  mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1095,16 +1095,81 @@ ClientTiledLayerBuffer::UnlockTile(TileC
   if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
     aTile.mBackBuffer->Unlock();
   }
   if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
     aTile.mBackBufferOnWhite->Unlock();
   }
 }
 
+void ClientTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
+                                    const nsIntRegion& aPaintRegion)
+{
+  const IntSize scaledTileSize = GetScaledTileSize();
+  const gfx::IntRect newBounds = newValidRegion.GetBounds();
+
+  const TilesPlacement oldTiles = mTiles;
+  const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
+                          floor_div(newBounds.y, scaledTileSize.height),
+                          floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
+                                    + newBounds.width, scaledTileSize.width) + 1,
+                          floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
+                                    + newBounds.height, scaledTileSize.height) + 1);
+
+  const size_t oldTileCount = mRetainedTiles.Length();
+  const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
+
+  nsTArray<TileClient> oldRetainedTiles;
+  mRetainedTiles.SwapElements(oldRetainedTiles);
+  mRetainedTiles.SetLength(newTileCount);
+
+  for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
+    const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
+    const size_t newIndex = newTiles.TileIndex(tilePosition);
+    // First, get the already existing tiles to the right place in the new array.
+    // Leave placeholders (default constructor) where there was no tile.
+    if (newTiles.HasTile(tilePosition)) {
+      mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
+    } else {
+      // release tiles that we are not going to reuse before allocating new ones
+      // to avoid allocating unnecessarily.
+      oldRetainedTiles[oldIndex].Release();
+    }
+  }
+
+  oldRetainedTiles.Clear();
+
+  for (size_t i = 0; i < newTileCount; ++i) {
+    const TileIntPoint tilePosition = newTiles.TilePosition(i);
+
+    IntPoint tileOffset = GetTileOffset(tilePosition);
+    nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+    tileDrawRegion.AndWith(aPaintRegion);
+
+    if (tileDrawRegion.IsEmpty()) {
+      continue;
+    }
+
+    TileClient tile = mRetainedTiles[i];
+    tile = ValidateTile(tile, GetTileOffset(tilePosition),
+                        tileDrawRegion);
+
+    mRetainedTiles[i] = tile;
+  }
+
+  PostValidate(aPaintRegion);
+  for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
+    UnlockTile(mRetainedTiles[i]);
+  }
+
+  mTiles = newTiles;
+  mValidRegion = newValidRegion;
+  mPaintedRegion.OrWith(aPaintRegion);
+}
+
 TileClient
 ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
                                     const nsIntPoint& aTileOrigin,
                                     const nsIntRegion& aDirtyRegion)
 {
   PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile",
     js::ProfileEntry::Category::GRAPHICS);
 
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -414,16 +414,18 @@ public:
                     std::numeric_limits<int32_t>::max())
   {}
 
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData);
 
+  void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
+
   void ReadLock();
 
   void Release();
 
   void DiscardBuffers();
 
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
@@ -448,29 +450,38 @@ public:
     if (mResolution == aResolution) {
       return;
     }
 
     Update(nsIntRegion(), nsIntRegion());
     mResolution = aResolution;
   }
 
+  void ResetPaintedAndValidState() {
+    mPaintedRegion.SetEmpty();
+    mValidRegion.SetEmpty();
+    mTiles.mSize.width = 0;
+    mTiles.mSize.height = 0;
+    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+      if (!mRetainedTiles[i].IsPlaceholderTile()) {
+        mRetainedTiles[i].Release();
+      }
+    }
+    mRetainedTiles.Clear();
+  }
+
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
                           const nsIntRegion& dirtyRect);
 
   void PostValidate(const nsIntRegion& aPaintRegion);
 
   void UnlockTile(TileClient aTile);
 
-  void ReleaseTile(TileClient aTile) { aTile.Release(); }
-
-  void SwapTiles(TileClient& aTileA, TileClient& aTileB) { std::swap(aTileA, aTileB); }
-
   TileClient GetPlaceholderTile() const { return TileClient(); }
 
 private:
   gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
   ClientTiledPaintedLayer* mPaintedLayer;
   CompositableClient* mCompositableClient;
   ClientLayerManager* mManager;
   LayerManager::DrawPaintedLayerCallback mCallback;
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -34,17 +34,17 @@ class Matrix4x4;
 class DataSourceSurface;
 }
 
 namespace layers {
 
 class Layer;
 class Compositor;
 class ThebesBufferData;
-class TiledLayerComposer;
+class TiledContentHost;
 class CompositableParentManager;
 class PCompositableParent;
 struct EffectChain;
 
 /**
  * The compositor-side counterpart to CompositableClient. Responsible for
  * updating textures and data about textures from IPC and how textures are
  * composited (tiling, double buffering, etc.).
@@ -127,17 +127,17 @@ public:
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   Layer* GetLayer() const { return mLayer; }
   void SetLayer(Layer* aLayer) { mLayer = aLayer; }
 
-  virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
+  virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
 
   typedef uint32_t AttachFlags;
   static const AttachFlags NO_FLAGS = 0;
   static const AttachFlags ALLOW_REATTACH = 1;
   static const AttachFlags KEEP_ATTACHED = 2;
   static const AttachFlags FORCE_DETACH = 2;
 
   virtual void Attach(Layer* aLayer,
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -35,34 +35,29 @@
 
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 }
 namespace layers {
 class Compositor;
 class ThebesBufferData;
-class TiledLayerComposer;
 struct EffectChain;
 
 struct TexturedEffect;
 
 /**
  * ContentHosts are used for compositing Painted layers, always matched by a
  * ContentClient of the same type.
  *
  * ContentHosts support only UpdateThebes(), not Update().
  */
 class ContentHost : public CompositableHost
 {
 public:
-  // Subclasses should implement this method if they support being used as a
-  // tiling.
-  virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
-
   virtual bool UpdateThebes(const ThebesBufferData& aData,
                             const nsIntRegion& aUpdated,
                             const nsIntRegion& aOldValidRegionBack,
                             nsIntRegion* aUpdatedRegionBack) = 0;
 
   virtual void SetPaintWillResample(bool aResample) { mPaintWillResample = aResample; }
   bool PaintWillResample() { return mPaintWillResample; }
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -14,17 +14,17 @@
 #include "FPSCounter.h"                 // for FPSState, FPSCounter
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "GeckoProfiler.h"              // for profiler_set_frame_number, etc
 #include "ImageLayerComposite.h"        // for ImageLayerComposite
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "LayerScope.h"                 // for LayerScope Tool
 #include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
 #include "PaintedLayerComposite.h"      // for PaintedLayerComposite
-#include "TiledLayerBuffer.h"           // for TiledLayerComposer
+#include "TiledContentHost.h"
 #include "Units.h"                      // for ScreenIntRect
 #include "UnitTransforms.h"             // for ViewAs
 #include "gfx2DGlue.h"                  // for ToMatrix4x4
 #include "gfxPrefs.h"                   // for gfxPrefs
 #ifdef XP_MACOSX
 #include "gfxPlatformMac.h"
 #endif
 #include "gfxRect.h"                    // for gfxRect
@@ -1011,20 +1011,20 @@ LayerManagerComposite::ComputeRenderInte
   if (!incompleteRegion.IsEmpty()) {
     // Calculate the transform to get between screen and layer space
     Matrix4x4 transformToScreen = aLayer->GetEffectiveTransform();
     transformToScreen = aTransform * transformToScreen;
 
     SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen);
 
     // See if there's any incomplete low-precision rendering
-    TiledLayerComposer* composer = nullptr;
+    TiledContentHost* composer = nullptr;
     LayerComposite* shadow = aLayer->AsLayerComposite();
     if (shadow) {
-      composer = shadow->GetTiledLayerComposer();
+      composer = shadow->GetCompositableHost()->AsTiledContentHost();
       if (composer) {
         incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion());
         if (!incompleteRegion.IsEmpty()) {
           SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen);
         }
       }
     }
 
@@ -1333,17 +1333,18 @@ LayerComposite::SetLayerManager(LayerMan
 bool
 LayerManagerComposite::AsyncPanZoomEnabled() const
 {
   return mCompositor->GetWidget()->AsyncPanZoomEnabled();
 }
 
 nsIntRegion
 LayerComposite::GetFullyRenderedRegion() {
-  if (TiledLayerComposer* tiled = GetTiledLayerComposer()) {
+  if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
+                                                        : nullptr) {
     nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion();
     // Discard the region which hasn't been drawn yet when doing
     // progressive drawing. Note that if the shadow visible region
     // shrunk the tiled valig region may not have discarded this yet.
     shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
     return shadowVisibleRegion;
   } else {
     return GetShadowVisibleRegion();
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -51,17 +51,16 @@ class CompositableHost;
 class Compositor;
 class ContainerLayerComposite;
 struct EffectChain;
 class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class PaintedLayerComposite;
-class TiledLayerComposer;
 class TextRenderer;
 class CompositingRenderTarget;
 struct FPSState;
 
 static const int kVisualWarningDuration = 150; // ms
 
 class LayerManagerComposite final : public LayerManager
 {
@@ -374,18 +373,16 @@ public:
     // We must handle this gracefully, see bug 967824
     NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
     return false;
   }
   virtual CompositableHost* GetCompositableHost() = 0;
 
   virtual void CleanupResources() = 0;
 
-  virtual TiledLayerComposer* GetTiledLayerComposer() { return nullptr; }
-
   virtual void DestroyFrontBuffer() { }
 
   void AddBlendModeEffect(EffectChain& aEffectChain);
 
   virtual void GenEffectChain(EffectChain& aEffect) { }
 
   /**
    * The following methods are
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -24,18 +24,16 @@
 #include "nsMathUtils.h"                // for NS_lround
 #include "nsString.h"                   // for nsAutoCString
 #include "TextRenderer.h"
 #include "GeckoProfiler.h"
 
 namespace mozilla {
 namespace layers {
 
-class TiledLayerComposer;
-
 PaintedLayerComposite::PaintedLayerComposite(LayerManagerComposite *aManager)
   : PaintedLayer(aManager, nullptr)
   , LayerComposite(aManager)
   , mBuffer(nullptr)
 {
   MOZ_COUNT_CTOR(PaintedLayerComposite);
   mImplData = static_cast<LayerComposite*>(this);
 }
@@ -86,26 +84,16 @@ PaintedLayerComposite::SetLayerManager(L
 {
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mBuffer && mCompositor) {
     mBuffer->SetCompositor(mCompositor);
   }
 }
 
-TiledLayerComposer*
-PaintedLayerComposite::GetTiledLayerComposer()
-{
-  if (!mBuffer) {
-    return nullptr;
-  }
-  MOZ_ASSERT(mBuffer->IsAttached());
-  return mBuffer->AsTiledLayerComposer();
-}
-
 LayerRenderState
 PaintedLayerComposite::GetRenderState()
 {
   if (!mBuffer || !mBuffer->IsAttached() || mDestroyed) {
     return LayerRenderState();
   }
   return mBuffer->GetRenderState();
 }
--- a/gfx/layers/composite/PaintedLayerComposite.h
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -23,17 +23,16 @@ namespace layers {
 /**
  * PaintedLayers use ContentHosts for their compsositable host.
  * By using different ContentHosts, PaintedLayerComposite support tiled and
  * non-tiled PaintedLayers and single or double buffering.
  */
 
 class CompositableHost;
 class ContentHost;
-class TiledLayerComposer;
 
 class PaintedLayerComposite : public PaintedLayer,
                               public LayerComposite
 {
 public:
   explicit PaintedLayerComposite(LayerManagerComposite *aManager);
 
 protected:
@@ -47,18 +46,16 @@ public:
   CompositableHost* GetCompositableHost() override;
 
   virtual void Destroy() override;
 
   virtual Layer* GetLayer() override;
 
   virtual void SetLayerManager(LayerManagerComposite* aManager) override;
 
-  virtual TiledLayerComposer* GetTiledLayerComposer() override;
-
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -493,30 +493,30 @@ TiledContentHost::RenderLayerBuffer(Tile
 {
   if (!mCompositor) {
     NS_WARNING("Can't render tiled content host - no compositor");
     return;
   }
   float resolution = aLayerBuffer.GetResolution();
   gfx::Size layerScale(1, 1);
 
-  // Make sure we don't render at low resolution where we have valid high
-  // resolution content, to avoid overdraw and artifacts with semi-transparent
-  // layers.
+  // We assume that the current frame resolution is the one used in our high
+  // precision layer buffer. Compensate for a changing frame resolution when
+  // rendering the low precision buffer.
   if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
     const CSSToParentLayerScale2D& layerResolution = aLayerBuffer.GetFrameResolution();
     const CSSToParentLayerScale2D& localResolution = mTiledBuffer.GetFrameResolution();
     layerScale.width = layerResolution.xScale / localResolution.xScale;
     layerScale.height = layerResolution.yScale / localResolution.yScale;
     aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
   }
 
-  // If we're drawing the low precision buffer, make sure the high precision
-  // buffer is masked out to avoid overdraw and rendering artifacts with
-  // non-opaque layers.
+  // Make sure we don't render at low resolution where we have valid high
+  // resolution content, to avoid overdraw and artifacts with semi-transparent
+  // layers.
   nsIntRegion maskRegion;
   if (resolution != mTiledBuffer.GetResolution()) {
     maskRegion = mTiledBuffer.GetValidRegion();
     // XXX This should be ScaleRoundIn, but there is no such function on
     //     nsIntRegion.
     maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
   }
 
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -162,18 +162,16 @@ public:
 
   void SetCompositor(Compositor* aCompositor);
 
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
-  void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
-
   CSSToParentLayerScale2D mFrameResolution;
 };
 
 /**
  * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
  * flakes, we have a unique update process. All the textures that back the
  * tiles are added in the usual way, but Updated is called on the host side
  * in response to a message that describes the transaction for every tile.
@@ -187,18 +185,17 @@ protected:
  *
  * When the content host is composited, we first validate the TiledLayerBuffer
  * (Upload), which calls Updated on each tile's texture host to make sure the
  * texture data has been uploaded. For single-buffered tiles, we unlock at this
  * point, for double-buffered tiles we unlock and discard the last composited
  * buffer after compositing a new one. Rendering takes us to RenderTile which
  * is similar to Composite for non-tiled ContentHosts.
  */
-class TiledContentHost : public ContentHost,
-                         public TiledLayerComposer
+class TiledContentHost : public ContentHost
 {
 public:
   explicit TiledContentHost(const TextureInfo& aTextureInfo);
 
 protected:
   ~TiledContentHost();
 
 public:
@@ -212,47 +209,47 @@ public:
                             const nsIntRegion& aUpdated,
                             const nsIntRegion& aOldValidRegionBack,
                             nsIntRegion* aUpdatedRegionBack) override
   {
     NS_ERROR("N/A for tiled layers");
     return false;
   }
 
-  const nsIntRegion& GetValidLowPrecisionRegion() const override
+  const nsIntRegion& GetValidLowPrecisionRegion() const
   {
     return mLowPrecisionTiledBuffer.GetValidRegion();
   }
 
-  const nsIntRegion& GetValidRegion() const override
+  const nsIntRegion& GetValidRegion() const
   {
     return mTiledBuffer.GetValidRegion();
   }
 
   virtual void SetCompositor(Compositor* aCompositor) override
   {
     MOZ_ASSERT(aCompositor);
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
-  virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
-                                   const SurfaceDescriptorTiles& aTiledDescriptor) override;
+  bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                           const SurfaceDescriptorTiles& aTiledDescriptor);
 
   void Composite(EffectChain& aEffectChain,
                  float aOpacity,
                  const gfx::Matrix4x4& aTransform,
                  const gfx::Filter& aFilter,
                  const gfx::Rect& aClipRect,
                  const nsIntRegion* aVisibleRegion = nullptr) override;
 
   virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
 
-  virtual TiledLayerComposer* AsTiledLayerComposer() override { return this; }
+  virtual TiledContentHost* AsTiledContentHost() override { return this; }
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
 
   virtual void Detach(Layer* aLayer = nullptr,
                       AttachFlags aFlags = NO_FLAGS) override;
 
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -6,28 +6,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CompositableTransactionParent.h"
 #include "CompositableHost.h"           // for CompositableParent, etc
 #include "CompositorParent.h"           // for CompositorParent
 #include "GLContext.h"                  // for GLContext
 #include "Layers.h"                     // for Layer
 #include "RenderTrace.h"                // for RenderTraceInvalidateEnd, etc
-#include "TiledLayerBuffer.h"           // for TiledLayerComposer
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ContentHost.h"  // for ContentHostBase
 #include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/TextureHost.h"  // for TextureHost
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
+#include "mozilla/layers/TiledContentHost.h"
 #include "mozilla/layers/PaintedLayerComposite.h"
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/unused.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsRegion.h"                   // for nsIntRegion
 
 namespace mozilla {
 namespace layers {
@@ -107,23 +107,22 @@ CompositableParentManager::ReceiveCompos
       CompositableHost* compositable = AsCompositable(op);
       MOZ_ASSERT(compositable);
       compositable->SetPictureRect(op.picture());
       break;
     }
     case CompositableOperation::TOpUseTiledLayerBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
       const OpUseTiledLayerBuffer& op = aEdit.get_OpUseTiledLayerBuffer();
-      CompositableHost* compositable = AsCompositable(op);
+      TiledContentHost* compositable = AsCompositable(op)->AsTiledContentHost();
 
-      TiledLayerComposer* tileComposer = compositable->AsTiledLayerComposer();
-      NS_ASSERTION(tileComposer, "compositable is not a tile composer");
+      NS_ASSERTION(compositable, "The compositable is not tiled");
 
       const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
-      bool success = tileComposer->UseTiledLayerBuffer(this, tileDesc);
+      bool success = compositable->UseTiledLayerBuffer(this, tileDesc);
       if (!success) {
         return false;
       }
       break;
     }
     case CompositableOperation::TOpRemoveTexture: {
       const OpRemoveTexture& op = aEdit.get_OpRemoveTexture();
       CompositableHost* compositable = AsCompositable(op);
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -126,16 +126,17 @@ EXPORTS.mozilla.layers += [
     'composite/ContainerLayerComposite.h',
     'composite/ContentHost.h',
     'composite/FrameUniformityData.h',
     'composite/ImageHost.h',
     'composite/ImageLayerComposite.h',
     'composite/LayerManagerComposite.h',
     'composite/PaintedLayerComposite.h',
     'composite/TextureHost.h',
+    'composite/TiledContentHost.h',
     'Compositor.h',
     'CompositorTypes.h',
     'D3D11ShareHandleImage.h',
     'D3D9SurfaceImage.h',
     'Effects.h',
     'ImageDataSerializer.h',
     'ipc/AsyncTransactionTracker.h',
     'ipc/CompositableForwarder.h',
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -36,17 +36,16 @@
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsMathUtils.h"                // for NS_roundf
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsString, nsAutoCString, etc
 #include "ScopedGLHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "GLBlitTextureImageHelper.h"
-#include "TiledLayerBuffer.h"           // for TiledLayerComposer
 #include "HeapCopyOfStackArray.h"
 
 #if MOZ_WIDGET_ANDROID
 #include "TexturePoolOGL.h"
 #endif
 
 #ifdef XP_MACOSX
 #include "nsCocoaFeatures.h"
--- a/gfx/tests/gtest/TestTiledLayerBuffer.cpp
+++ b/gfx/tests/gtest/TestTiledLayerBuffer.cpp
@@ -5,87 +5,62 @@
 
 #include "TiledLayerBuffer.h"
 
 #include "gtest/gtest.h"
 
 namespace mozilla {
 namespace layers {
 
-struct TestTiledLayerTile {
-  int value;
-  explicit TestTiledLayerTile(int v = 0) {
-    value = v;
-  }
-  bool operator== (const TestTiledLayerTile& o) const {
-    return value == o.value;
-  }
-  bool operator!= (const TestTiledLayerTile& o) const {
-    return value != o.value;
-  }
-
-  bool IsPlaceholderTile() const {
-    return value == -1;
-  }
-};
-
-class TestTiledLayerBuffer : public TiledLayerBuffer<TestTiledLayerBuffer, TestTiledLayerTile>
-{
-  friend class TiledLayerBuffer<TestTiledLayerBuffer, TestTiledLayerTile>;
-
-public:
-  TestTiledLayerTile GetPlaceholderTile() const {
-    return TestTiledLayerTile(-1);
-  }
-
-  TestTiledLayerTile ValidateTile(TestTiledLayerTile aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRect) {
-    return TestTiledLayerTile();
-  }
-
-  void ReleaseTile(TestTiledLayerTile aTile)
-  {
-
-  }
-
-  void SwapTiles(TestTiledLayerTile& aTileA, TestTiledLayerTile& aTileB)
-  {
-    TestTiledLayerTile oldTileA = aTileA;
-    aTileA = aTileB;
-    aTileB = oldTileA;
-  }
-
-  void TestUpdate(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion)
-  {
-    Update(aNewValidRegion, aPaintRegion);
-  }
-
-  void UnlockTile(TestTiledLayerTile aTile) {}
-  void PostValidate(const nsIntRegion& aPaintRegion) {}
-};
-
-TEST(TiledLayerBuffer, TileConstructor) {
-  gfxPlatform::GetPlatform()->ComputeTileSize();
-
-  TestTiledLayerBuffer buffer;
-}
-
 TEST(TiledLayerBuffer, TileStart) {
   gfxPlatform::GetPlatform()->ComputeTileSize();
 
-  TestTiledLayerBuffer buffer;
-
-  ASSERT_EQ(buffer.RoundDownToTileEdge(10, 256), 0);
-  ASSERT_EQ(buffer.RoundDownToTileEdge(-10, 256), -256);
+  ASSERT_EQ(RoundDownToTileEdge(10, 256), 0);
+  ASSERT_EQ(RoundDownToTileEdge(-10, 256), -256);
 }
 
-TEST(TiledLayerBuffer, EmptyUpdate) {
-  gfxPlatform::GetPlatform()->ComputeTileSize();
+TEST(TiledLayerBuffer, TilesPlacement) {
+  for (int firstY = -10; firstY < 10; ++firstY) {
+    for (int firstX = -10; firstX < 10; ++firstX) {
+      for (int height = 1; height < 10; ++height) {
+        for (int width = 1; width < 10; ++width) {
 
-  TestTiledLayerBuffer buffer;
+          const TilesPlacement p1 = TilesPlacement(firstX, firstY, width, height);
+          // Check that HasTile returns false with some positions that we know
+          // not to be in the rectangle of the TilesPlacement.
+          ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX - 1, 0)));
+          ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY - 1)));
+          ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX + width + 1,  0)));
+          ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY + height + 1)));
 
-  nsIntRegion validRegion(gfx::IntRect(0, 0, 10, 10));
-  buffer.TestUpdate(validRegion, validRegion);
+          // Verify that all positions within the rect that defines the
+          // TilesPlacement map to indices between 0 and width*height.
+          for (int y = firstY; y < (firstY+height); ++y) {
+            for (int x = firstX; x < (firstX+width); ++x) {
+              ASSERT_TRUE(p1.HasTile(TileIntPoint(x,y)));
+              ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) >= 0);
+              ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) < width * height);
+            }
+          }
 
-  ASSERT_EQ(buffer.GetValidRegion(), validRegion);
+          // XXX - This causes some versions of gcc to warn that it optimizes
+          // away the test, which gets caught in -WError in PGO builds.
+          // The lazy thing to do is to just comment this out since this specific
+          // test isn't critically important, but we should remove the warning instead.
+          // cf. bug 1179287
+          //
+          // Verify that indices map to positions that are within the rect that
+          // defines the TilesPlacement.
+          // for (int i = 0; i < width * height; ++i) {
+          //   ASSERT_TRUE(p1.TilePosition(i).x >= firstX);
+          //   ASSERT_TRUE(p1.TilePosition(i).x < firstX + width);
+          //   ASSERT_TRUE(p1.TilePosition(i).y >= firstY);
+          //   ASSERT_TRUE(p1.TilePosition(i).y < firstY + height);
+          // }
+
+        }
+      }
+    }
+  }
 }
 
 }
 }
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -19,17 +19,18 @@ UNIFIED_SOURCES += [
     'TestRect.cpp',
     'TestRegion.cpp',
     'TestSkipChars.cpp',
     # Hangs on linux in ApplyGdkScreenFontOptions
     #'gfxFontSelectionTest.cpp',
     'TestTextures.cpp',
     # Test works but it doesn't assert anything
     #'gfxTextRunPerfTest.cpp',
-    'TestTiledLayerBuffer.cpp',
+    # Bug 1179287 - PGO bustage on Linux
+    #'TestTiledLayerBuffer.cpp',
     'TestVsync.cpp',
 ]
 
 UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [
     'TestBase.cpp',
     'TestBugs.cpp',
     'TestCairo.cpp',
     'TestPoint.cpp',
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1179078-1-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+p {
+  border: 2px solid transparent;
+  border-image: linear-gradient(to right, orange, blue) 1 1;
+}
+</style>
+<p>This paragraph must have an orange/blue gradient border.</p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1179078-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+p {
+  border: 2px solid transparent;
+  border-image: linear-gradient(to right, orange, blue) 1 1;
+  border-image: linear-gradient(to right, garbage) 1 1;
+}
+</style>
+<p>This paragraph must have an orange/blue gradient border.</p>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1922,9 +1922,10 @@ skip-if(!B2G) fuzzy-if(B2G,102,577) == 1
 skip-if(!B2G) fuzzy-if(B2G,101,887) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul
 == 1151145-1.html 1151145-1-ref.html
 == 1151306-1.html 1151306-1-ref.html
 == 1153845-1.html 1153845-1-ref.html
 == 1155828-1.html 1155828-1-ref.html
 == 1156129-1.html 1156129-1-ref.html
 == 1169331-1.html 1169331-1-ref.html
+== 1179078-1.html 1179078-1-ref.html
 fuzzy(1,74) == 1174332-1.html 1174332-1-ref.html
--- a/layout/reftests/w3c-css/submitted/variables/reftest.list
+++ b/layout/reftests/w3c-css/submitted/variables/reftest.list
@@ -99,16 +99,17 @@ default-preferences pref(layout.css.vari
 == variable-reference-32.html support/color-green-ref.html
 == variable-reference-33.html support/color-green-ref.html
 == variable-reference-34.html support/color-green-ref.html
 == variable-reference-35.html support/color-green-ref.html
 == variable-reference-36.html variable-reference-36-ref.html
 == variable-reference-37.html variable-reference-37-ref.html
 == variable-reference-38.html variable-reference-38-ref.html
 == variable-reference-39.html support/color-green-ref.html
+== variable-reference-40.html variable-reference-40-ref.html
 == variable-supports-01.html support/color-green-ref.html
 == variable-supports-02.html support/color-green-ref.html
 == variable-supports-03.html support/color-green-ref.html
 == variable-supports-04.html support/color-green-ref.html
 == variable-supports-05.html support/color-green-ref.html
 == variable-supports-06.html support/color-green-ref.html
 == variable-supports-07.html support/color-green-ref.html
 == variable-supports-08.html support/color-green-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/variables/variable-reference-40-ref.html
@@ -0,0 +1,14 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<title>CSS Reftest Reference</title>
+<link rel="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
+<style>
+p {
+  border: 2px solid transparent;
+  border-image: linear-gradient(to right, orange, blue) 1 1;
+}
+</style>
+<p>This paragraph must have an orange/blue gradient border.</p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/variables/variable-reference-40.html
@@ -0,0 +1,17 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<title>CSS Test: Test that a variable reference within a gradient value in a border-image shorthand parses correctly.</title>
+<link rel="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
+<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables">
+<link rel="match" href="variable-reference-40-ref.html">
+<style>
+p {
+  --orange: orange;
+  border: 2px solid transparent;
+  border-image: linear-gradient(to right, var(--orange), blue) 1 1;
+}
+</style>
+<p>This paragraph must have an orange/blue gradient border.</p>
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -11342,20 +11342,24 @@ CSSParserImpl::ParseBorderImage()
   bool foundSliceWidthOutset = false;
   bool foundRepeat = false;
 
   // This loop is used to handle the parsing of border-image properties which
   // can appear in any order.
   nsCSSValue imageSourceValue;
   while (!CheckEndProperty()) {
     // <border-image-source>
-    if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
-      AppendValue(eCSSProperty_border_image_source, imageSourceValue);
-      foundSource = true;
-      continue;
+    if (!foundSource) {
+      nsAutoCSSParserInputStateRestorer stateRestorer(this);
+      if (ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
+        AppendValue(eCSSProperty_border_image_source, imageSourceValue);
+        foundSource = true;
+        stateRestorer.DoNotRestore();
+        continue;
+      }
     }
 
     // <border-image-slice>
     // ParseBorderImageSlice is weird.  It may consume tokens and then return
     // false, because it parses a property with two required components that
     // can appear in either order.  Since the tokens that were consumed cannot
     // parse as anything else we care about, this isn't a problem.
     if (!foundSliceWidthOutset) {
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -281,17 +281,17 @@ nsBaseChannel::ClassifyURI()
   // classify URIs.
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   if (mLoadFlags & LOAD_CLASSIFY_URI) {
     nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
     if (classifier) {
-      classifier->Start(this, false);
+      classifier->Start(this);
     } else {
       Cancel(NS_ERROR_OUT_OF_MEMORY);
     }
   }
 }
 
 //-----------------------------------------------------------------------------
 // nsBaseChannel::nsISupports
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -232,22 +232,19 @@ nsChannelClassifier::NotifyTrackingProte
     securityUI->GetState(&state);
     state |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
     eventSink->OnSecurityChange(nullptr, state);
 
     return NS_OK;
 }
 
 void
-nsChannelClassifier::Start(nsIChannel *aChannel, bool aContinueBeginConnect)
+nsChannelClassifier::Start(nsIChannel *aChannel)
 {
   mChannel = aChannel;
-  if (aContinueBeginConnect) {
-    mChannelInternal = do_QueryInterface(aChannel);
-  }
 
   nsresult rv = StartInternal();
   if (NS_FAILED(rv)) {
     // If we aren't getting a callback for any reason, assume a good verdict and
     // make sure we resume the channel if necessary.
     OnClassifyComplete(NS_OK);
   }
 }
@@ -525,18 +522,12 @@ nsChannelClassifier::OnClassifyComplete(
 
             mChannel->Cancel(aErrorCode);
         }
         LOG(("nsChannelClassifier[%p]: resuming channel %p from "
              "OnClassifyComplete", this, mChannel.get()));
         mChannel->Resume();
     }
 
-    // Even if we have cancelled the channel, we may need to call
-    // ContinueBeginConnect so that we abort appropriately.
-    if (mChannelInternal) {
-        mChannelInternal->ContinueBeginConnect();
-    }
     mChannel = nullptr;
-    mChannelInternal = nullptr;
 
     return NS_OK;
 }
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -16,30 +16,27 @@ class nsChannelClassifier final : public
 {
 public:
     nsChannelClassifier();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIURICLASSIFIERCALLBACK
 
     // Calls nsIURIClassifier.Classify with the principal of the given channel,
-    // and cancels the channel on a bad verdict.  If callContinueBeginConnect is true,
-    // and aChannel is an nsIHttpChannelInternal, nsChannelClassifier must call
-    // nsIHttpChannelInternal.ContinueBeginConnect once Start has returned.
-    void Start(nsIChannel *aChannel, bool aContinueBeginConnect);
+    // and cancels the channel on a bad verdict.
+    void Start(nsIChannel *aChannel);
     // Whether or not tracking protection should be enabled on this channel.
     nsresult ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result);
 
 private:
     // True if the channel is on the allow list.
     bool mIsAllowListed;
     // True if the channel has been suspended.
     bool mSuspendedChannel;
     nsCOMPtr<nsIChannel> mChannel;
-    nsCOMPtr<nsIHttpChannelInternal> mChannelInternal;
 
     ~nsChannelClassifier() {}
     // Caches good classifications for the channel principal.
     void MarkEntryClassified(nsresult status);
     bool HasBeenClassified(nsIChannel *aChannel);
     // Helper function so that we ensure we call ContinueBeginConnect once
     // Start is called. Returns NS_OK if and only if we will get a callback
     // from the classifier service.
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1531,25 +1531,16 @@ HttpBaseChannel::RedirectTo(nsIURI *newU
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-HttpBaseChannel::ContinueBeginConnect()
-{
-  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default,
-             "The parent overrides this");
-  MOZ_ASSERT(false, "This method must be overridden");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 HttpBaseChannel::GetTopWindowURI(nsIURI **aTopWindowURI)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<mozIThirdPartyUtil> util;
   // Only compute the top window URI once. In e10s, this must be computed in the
   // child. The parent gets the top window URI through HttpChannelOpenArgs.
   if (!mTopWindowURI) {
     util = do_GetService(THIRDPARTYUTIL_CONTRACTID);
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -192,17 +192,16 @@ public:
   NS_IMETHOD ForcePending(bool aForcePending) override;
   NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) override;
   NS_IMETHOD ForceNoIntercept() override;
   NS_IMETHOD GetCorsIncludeCredentials(bool* aInclude) override;
   NS_IMETHOD SetCorsIncludeCredentials(bool aInclude) override;
   NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override;
   NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
-  NS_IMETHOD ContinueBeginConnect() override;
   NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4777,16 +4777,21 @@ NS_IMETHODIMP
 nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
 {
     NS_ENSURE_ARG_POINTER(securityInfo);
     *securityInfo = mSecurityInfo;
     NS_IF_ADDREF(*securityInfo);
     return NS_OK;
 }
 
+// If any of the functions that AsyncOpen calls returns immediately an error
+// AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
+// To be sure that they are not call ReleaseListeners() is called.
+// If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
+// any error.
 NS_IMETHODIMP
 nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
 {
     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
 
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
@@ -4850,18 +4855,21 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
 
     rv = BeginConnect();
     if (NS_FAILED(rv))
         ReleaseListeners();
 
     return rv;
 }
 
-// On error BeginConnect() should call AsyncAbort() before exiting until
-// ContineBeginConnect after that it should not call it.
+// BeginConnect() will not call AsyncAbort() on an error and if AsyncAbort needs
+// to be called the function calling BeginConnect will need to call AsyncAbort.
+// If BeginConnect is called from AsyncOpen, AsyncnAbort doesn't need to be
+// called. If it is called form another function (e.g. the function is called
+// from OnProxyAvailable) that function should call AsyncOpen.
 nsresult
 nsHttpChannel::BeginConnect()
 {
     LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
     nsresult rv;
 
     // Construct connection info object
     nsAutoCString host;
@@ -4876,24 +4884,22 @@ nsHttpChannel::BeginConnect()
         rv = mURI->GetAsciiHost(host);
     if (NS_SUCCEEDED(rv))
         rv = mURI->GetPort(&port);
     if (NS_SUCCEEDED(rv))
         mURI->GetUsername(mUsername);
     if (NS_SUCCEEDED(rv))
         rv = mURI->GetAsciiSpec(mSpec);
     if (NS_FAILED(rv)) {
-        AsyncAbort(rv);
         return rv;
     }
 
     // Reject the URL if it doesn't specify a host
     if (host.IsEmpty()) {
         rv = NS_ERROR_MALFORMED_URI;
-        AsyncAbort(rv);
         return rv;
     }
     LOG(("host=%s port=%d\n", host.get(), port));
     LOG(("uri=%s\n", mSpec.get()));
 
     nsCOMPtr<nsProxyInfo> proxyInfo;
     if (mProxyInfo)
         proxyInfo = do_QueryInterface(mProxyInfo);
@@ -4959,34 +4965,29 @@ nsHttpChannel::BeginConnect()
     }
 
     mAuthProvider =
         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
                           &rv);
     if (NS_SUCCEEDED(rv))
         rv = mAuthProvider->Init(this);
     if (NS_FAILED(rv)) {
-        AsyncAbort(rv);
         return rv;
     }
 
     // check to see if authorization headers should be included
     mAuthProvider->AddAuthorizationHeaders();
 
     // notify "http-on-modify-request" observers
     CallOnModifyRequestObservers();
 
     // Check to see if we should redirect this channel elsewhere by
     // nsIHttpChannel.redirectTo API request
     if (mAPIRedirectToURI) {
-        rv = AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
-        if (NS_FAILED(rv)) {
-            AsyncAbort(rv);
-        }
-        return rv;
+        return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
     }
     // Check to see if this principal exists on local blocklists.
     nsRefPtr<nsChannelClassifier> channelClassifier = new nsChannelClassifier();
     if (mLoadFlags & LOAD_CLASSIFY_URI) {
         nsCOMPtr<nsIURIClassifier> classifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
         if (classifier) {
             bool tp = false;
             channelClassifier->ShouldEnableTrackingProtection(this, &tp);
@@ -5089,43 +5090,53 @@ nsHttpChannel::BeginConnect()
     if (mLoadFlags & LOAD_FRESH_CONNECTION) {
         // just the initial document resets the whole pool
         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
             gHttpHandler->ConnMgr()->ClearAltServiceMappings();
             gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo);
         }
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     }
+
+    // We may have been cancelled already, either by on-modify-request
+    // listeners or load group observers; in that case, we should not send the
+    // request to the server
+    if (mCanceled) {
+        return mStatus;
+    }
+
     if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
-        // On error ContinueBeginConnect() will call AsyncAbort so do not do it
-        // here
-        return ContinueBeginConnect();
-    }
+        ContinueBeginConnect();
+        return NS_OK;
+    }
+
     // mLocalBlocklist is true only if tracking protection is enabled and the
     // URI is a tracking domain, it makes no guarantees about phishing or
     // malware, so if LOAD_CLASSIFY_URI is true we must call
     // nsChannelClassifier to catch phishing and malware URIs.
     bool callContinueBeginConnect = true;
-    if (mCanceled || !mLocalBlocklist) {
-       rv = ContinueBeginConnect();
-       if (NS_FAILED(rv)) {
-           // On error ContinueBeginConnect() will call AsyncAbort so do not do
-           // it here
-           return rv;
-       }
-       callContinueBeginConnect = false;
+    if (!mLocalBlocklist) {
+        // Here we call ContinueBeginConnectWithResult and not
+        // ContinueBeginConnect so that in the case of an error we do not start
+        // channelClassifier.
+        rv = ContinueBeginConnectWithResult();
+        if (NS_FAILED(rv)) {
+            return rv;
+        }
+        callContinueBeginConnect = false;
     }
     // nsChannelClassifier calls ContinueBeginConnect if it has not already
     // been called, after optionally cancelling the channel once we have a
     // remote verdict. We call a concrete class instead of an nsI* that might
     // be overridden.
-    if (!mCanceled) {
-        LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
-           channelClassifier.get(), this));
-        channelClassifier->Start(this, callContinueBeginConnect);
+    LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
+         channelClassifier.get(), this));
+    channelClassifier->Start(this);
+    if (callContinueBeginConnect) {
+        ContinueBeginConnect();
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
@@ -5153,38 +5164,49 @@ nsHttpChannel::SetPriority(int32_t value
     if (mPriority == newValue)
         return NS_OK;
     mPriority = newValue;
     if (mTransaction)
         gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
     return NS_OK;
 }
 
-//-----------------------------------------------------------------------------
-// nsHttpChannel::nsIHttpChannelInternal
-//-----------------------------------------------------------------------------
-NS_IMETHODIMP
-nsHttpChannel::ContinueBeginConnect()
-{
-    LOG(("nsHttpChannel::ContinueBeginConnect [this=%p]", this));
+nsresult
+nsHttpChannel::ContinueBeginConnectWithResult()
+{
+    LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]", this));
+    NS_PRECONDITION(!mCallOnResume, "How did that happen?");
+
     nsresult rv;
-    // We may have been cancelled already, either by on-modify-request
-    // listeners or load group observers or nsChannelClassifier; in that case,
-    // we should not send the request to the server
-    if (mCanceled) {
+
+    if (mSuspendCount) {
+        LOG(("Waiting until resume to do async connect [this=%p]\n", this));
+        mCallOnResume = &nsHttpChannel::ContinueBeginConnect;
+        rv = NS_OK;
+    } else if (mCanceled) {
+        // We may have been cancelled already, by nsChannelClassifier in that
+        // case, we should not send the request to the server
         rv = mStatus;
     } else {
         rv = Connect();
     }
+
+    LOG(("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p rv=%x "
+         "mCanceled=%i]\n", this, rv, mCanceled));
+    return rv;
+}
+
+void
+nsHttpChannel::ContinueBeginConnect()
+{
+    nsresult rv = ContinueBeginConnectWithResult();
     if (NS_FAILED(rv)) {
-        LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
         CloseCacheEntry(true);
         AsyncAbort(rv);
     }
-    return rv;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannel::nsIClassOfService
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 nsHttpChannel::SetClassFlags(uint32_t inFlags)
 {
@@ -5227,24 +5249,23 @@ nsHttpChannel::OnProxyAvailable(nsICance
 
     if (NS_SUCCEEDED(status))
         mProxyInfo = pi;
 
     if (!gHttpHandler->Active()) {
         LOG(("nsHttpChannel::OnProxyAvailable [this=%p] "
              "Handler no longer active.\n", this));
         rv = NS_ERROR_NOT_AVAILABLE;
-        AsyncAbort(rv);
     }
     else {
-        // On error BeginConnect() will call AsyncAbort.
         rv = BeginConnect();
     }
 
     if (NS_FAILED(rv)) {
+        AsyncAbort(rv);
         Cancel(rv);
     }
     return rv;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIProxiedChannel
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -121,17 +121,16 @@ public:
     NS_IMETHOD Cancel(nsresult status) override;
     NS_IMETHOD Suspend() override;
     NS_IMETHOD Resume() override;
     // nsIChannel
     NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
     NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
     // nsIHttpChannelInternal
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
-    NS_IMETHOD ContinueBeginConnect() override;
     // nsISupportsPriority
     NS_IMETHOD SetPriority(int32_t value) override;
     // nsIClassOfService
     NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
     NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
     NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
 
     // nsIResumableChannel
@@ -236,16 +235,18 @@ public: /* internal necko use only */
 protected:
     virtual ~nsHttpChannel();
 
 private:
     typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
 
     bool     RequestIsConditional();
     nsresult BeginConnect();
+    nsresult ContinueBeginConnectWithResult();
+    void     ContinueBeginConnect();
     nsresult Connect();
     void     SpeculativeConnect();
     nsresult SetupTransaction();
     void     SetupTransactionLoadGroupInfo();
     nsresult CallOnStartRequest();
     nsresult ProcessResponse();
     nsresult ContinueProcessResponse(nsresult);
     nsresult ProcessNormal();
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -33,17 +33,17 @@ interface nsIHttpUpgradeListener : nsISu
                               in nsIAsyncOutputStream aSocketOut);
 };
 
 /**
  * Dumping ground for http.  This interface will never be frozen.  If you are
  * using any feature exposed by this interface, be aware that this interface
  * will change and you will be broken.  You have been warned.
  */
-[scriptable, uuid(26833ec7-4555-4f23-9281-3a12d4b76db1)]
+[scriptable, uuid(c025c35a-dda3-4a1d-9e6c-e02d7149ac79)]
 
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
@@ -249,19 +249,13 @@ interface nsIHttpChannelInternal : nsISu
     readonly attribute nsIURI topWindowURI;
 
     /**
      * The network interface id that's associated with this channel.
      */
     attribute ACString networkInterfaceId;
 
     /**
-     * Used only by nsChannelClassifier to resume connecting or abort the
-     * channel after a remote classification verdict.
-     */
-    void continueBeginConnect();
-
-    /**
      * Read the proxy URI, which, if non-null, will be used to resolve
      * proxies for this channel.
      */
     readonly attribute nsIURI proxyURI;
 };
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_suspend_channel_before_connect.js
@@ -0,0 +1,108 @@
+
+const CC = Components.Constructor;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const ServerSocket = CC("@mozilla.org/network/server-socket;1",
+                        "nsIServerSocket",
+                        "init");
+
+var obs = Cc["@mozilla.org/observer-service;1"]
+            .getService(Ci.nsIObserverService);
+
+var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Components.interfaces.nsIIOService);
+
+// A server that waits for a connect. If a channel is suspended it should not
+// try to connect to the server until it is is resumed or not try at all if it
+// is cancelled as in this test.
+function TestServer() {
+  this.listener = ServerSocket(-1, true, -1);
+  this.port = this.listener.port;
+  this.listener.asyncListen(this);
+}
+
+TestServer.prototype = {
+  onSocketAccepted: function(socket, trans) {
+    do_check_true(false, "Socket should not have tried to connect!");
+  },
+
+  onStopListening: function(socket) {
+  },
+
+  stop: function() {
+    try { this.listener.close(); } catch(ignore) {}
+  }
+}
+
+var requestListenerObserver = {
+
+  QueryInterface: function queryinterface(iid) {
+    if (iid.equals(Ci.nsISupports) ||
+        iid.equals(Ci.nsIObserver))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  observe: function(subject, topic, data) {
+    if (topic === "http-on-modify-request" &&
+        subject instanceof Ci.nsIHttpChannel) {
+
+      var chan = subject.QueryInterface(Ci.nsIHttpChannel);
+      chan.suspend();
+      var obs = Cc["@mozilla.org/observer-service;1"].getService();
+      obs = obs.QueryInterface(Ci.nsIObserverService);
+      obs.removeObserver(this, "http-on-modify-request");
+
+      // Timers are bad, but we need to wait to see that we are not trying to
+      // connect to the server. There are no other event since nothing should
+      // happen until we resume the channel.
+      let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      timer.initWithCallback(() => {
+        chan.cancel(Cr.NS_BINDING_ABORTED);
+        chan.resume();
+      }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+  }
+};
+
+var listener = {
+  onStartRequest: function test_onStartR(request, ctx) {
+  },
+
+  onDataAvailable: function test_ODA() {
+    do_throw("Should not get any data!");
+  },
+
+  onStopRequest: function test_onStopR(request, ctx, status) {
+    do_execute_soon(run_next_test);
+  }
+};
+
+// Add observer and start a channel. Observer is going to suspend the channel on
+// "http-on-modify-request" even. If a channel is suspended so early it should
+// not try to connect at all until it is resumed. In this case we are going to
+// wait for some time and cancel the channel before resuming it.
+add_test(function testNoConnectChannelCanceledEarly() {
+
+  serv = new TestServer();
+
+  obs.addObserver(requestListenerObserver, "http-on-modify-request", false);
+
+  var chan = ios.newChannel2("http://localhost:" + serv.port,
+                             "",
+                             null,
+                             null,      // aLoadingNode
+                             Services.scriptSecurityManager.getSystemPrincipal(),
+                             null,      // aTriggeringPrincipal
+                             Ci.nsILoadInfo.SEC_NORMAL,
+                             Ci.nsIContentPolicy.TYPE_OTHER);
+
+  chan.asyncOpen(listener, chan);
+
+  do_register_cleanup(function(){ serv.stop(); });
+});
+
+function run_test() {
+  run_next_test();
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -314,8 +314,9 @@ skip-if = os != "win"
 [test_websocket_offline.js]
 [test_tls_server.js]
 # The local cert service used by this test is not currently shipped on Android
 skip-if = os == "android"
 [test_1073747.js]
 [test_multipart_streamconv_application_package.js]
 [test_safeoutputstream_append.js]
 [test_packaged_app_service.js]
+[test_suspend_channel_before_connect.js]
--- a/testing/docker/b2g-build/Dockerfile
+++ b/testing/docker/b2g-build/Dockerfile
@@ -1,29 +1,38 @@
 FROM          centos:centos6
 MAINTAINER    Dustin J. Mitchell <dustin@mozilla.com>
 
 # Run majority of yum installs here so we cache them!
 COPY releng.repo /etc/yum.repos.d/releng.repo
 RUN yum install -y epel-release && \
   yum update -y && \
   yum makecache && \
-  yum install -y \
+  yum install -y  wget
+
+# Install updated curl libraries
+RUN cd /tmp && \
+  wget https://s3-us-west-2.amazonaws.com/test-caching/libcurl-7.29.0-19.el6.x86_64.rpm && \
+  wget https://s3-us-west-2.amazonaws.com/test-caching/libcurl-devel-7.29.0-19.el6.x86_64.rpm && \
+  wget https://s3-us-west-2.amazonaws.com/test-caching/curl-7.29.0-19.el6.x86_64.rpm && \
+  yum install -y libcurl-7.29.0-19.el6.x86_64.rpm libcurl-devel-7.29.0-19.el6.x86_64.rpm curl-7.29.0-19.el6.x86_64.rpm && \
+  rm -f libcurl-7.29.0-19.el6.x86_64.rpm libcurl-devel-7.29.0-19.el6.x86_64.rpm curl-7.29.0-19.el6.x86_64.rpm && \
+  cd -
+
+RUN yum install -y \
   # From Building B2G docs
   # cmake \
   # cmake: is disabled intentionally to work around: bug 1141417
   GConf2-devel \
   alsa-lib-devel \
   autoconf213 \
   bc \
   bison \
   bzip2 \
   ccache \
-  curl \
-  curl-devel \
   dbus-devel \
   dbus-glib-devel \
   dbus-glib-devel \
   dbus-python \
   expat-devel \
   file \
   flex \
   gawk \
@@ -36,17 +45,16 @@ RUN yum install -y epel-release && \
   gstreamer-plugins-base-devel \
   gtk2-devel \
   install \
   iw \
   libX11-devel \
   libX11-devel.i686 \
   libXrandr.i686 \
   libXt-devel \
-  libcurl-devel \
   libnotify-devel \
   libstdc++-static \
   libstdc++-static \
   libstdc++.i686 \
   make \
   mesa-libGL-devel \
   mesa-libGL-devel.i686 \
   mozilla-python27 \
@@ -67,17 +75,16 @@ RUN yum install -y epel-release && \
   screen \
   subversion-perl \
   tar \
   tcl \
   tk \
   unzip \
   uuid \
   vim \
-  wget \
   wireless-tools-devel \
   xorg-x11-server-Xvfb \
   xorg-x11-server-utils \
   xz \
   yasm \
   zip \
   zlib-devel \
   zlib-devel.i686 && \
--- a/testing/docker/b2g-build/VERSION
+++ b/testing/docker/b2g-build/VERSION
@@ -1,1 +1,1 @@
-0.2.8
+0.2.9
--- a/testing/docker/builder/Dockerfile
+++ b/testing/docker/builder/Dockerfile
@@ -1,9 +1,9 @@
-FROM          quay.io/mozilla/b2g-build:0.2.8
+FROM          quay.io/mozilla/b2g-build:0.2.9
 MAINTAINER    Dustin J. Mitchell <dustin@mozilla.com>
 
 ENV PYTHONPATH /tools/tools/lib/python:$PYTHONPATH
 ENV TOOLTOOL_CACHE  /home/worker/tools/tooltool-cache
 
 ADD https://raw.githubusercontent.com/taskcluster/buildbot-step/58a16f7370a8b4de7a4458436a4a5fad9905f5d9/buildbot_step.js /home/worker/bin/buildbot_step
 
 # Add utilities and configuration
@@ -16,16 +16,16 @@ RUN hg clone http://hg.mozilla.org/build
       cd /tools/tools && \
       python setup.py install
 
 # Initialize git (makes repo happy)
 RUN git config --global user.email "mozilla@example.com" && \
     git config --global user.name "mozilla"
 
 # VCS Tools
-RUN npm install -g taskcluster-vcs@2.3.5
+RUN npm install -g taskcluster-vcs@2.3.6
 
 # TODO enable worker
 # TODO volume mount permissions will be an issue
 # USER worker
 
 COPY bin /home/worker/bin
 RUN chmod a+x /home/worker/bin/*
--- a/testing/docker/builder/VERSION
+++ b/testing/docker/builder/VERSION
@@ -1,1 +1,1 @@
-0.5.5
+0.5.6
--- a/testing/docker/phone-builder/Dockerfile
+++ b/testing/docker/phone-builder/Dockerfile
@@ -1,9 +1,9 @@
-FROM          quay.io/mozilla/builder:0.5.5
+FROM          taskcluster/builder:0.5.6
 MAINTAINER    Wander Lairson Costa <wcosta@mozilla.com>
 
 ENV           SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE /home/worker/socorro.token
 
 # Add utilities and configuration
 ADD           bin                   /home/worker/bin
 ADD           config                /home/worker/.aws/config
 ADD           socorro.token         /home/worker/socorro.token
--- a/testing/docker/phone-builder/VERSION
+++ b/testing/docker/phone-builder/VERSION
@@ -1,1 +1,1 @@
-0.0.15
+0.0.16
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -1073,17 +1073,17 @@
            this._lastSelectedRow = cell.row;
 
            if (cell.row == -1)
              return;
 
            if (cell.childElt == "twisty")
              return;
 
-           if (cell.col) {
+           if (cell.col && event.button == 0) {
              if (cell.col.cycler) {
                view.cycleCell(cell.row, cell.col);
                return;
              } else if (cell.col.type == Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) {
                if (this.parentNode.editable && cell.col.editable &&
                    view.isEditable(cell.row, cell.col)) {
                  var value = view.getCellValue(cell.row, cell.col);
                  value = value == "true" ? "false" : "true";
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -360,18 +360,22 @@ void UniqueStacks::Stack::AppendFrame(co
 uint32_t UniqueStacks::Stack::GetOrAddIndex() const
 {
   return mUniqueStacks.GetOrAddStackIndex(mStack);
 }
 
 uint32_t UniqueStacks::FrameKey::Hash() const
 {
   uint32_t hash = 0;
-  if (!mLocation.empty()) {
+  if (!mLocation.IsEmpty()) {
+#ifdef SPS_STANDALONE
     hash = mozilla::HashString(mLocation.c_str());
+#else
+    hash = mozilla::HashString(mLocation.get());
+#endif
   }
   if (mLine.isSome()) {
     hash = mozilla::AddToHash(hash, *mLine);
   }
   if (mCategory.isSome()) {
     hash = mozilla::AddToHash(hash, *mCategory);
   }
   if (mJITAddress.isSome()) {
@@ -534,17 +538,21 @@ void UniqueStacks::StreamFrame(const OnS
   //   [location, implementation, optimizations, line, category]
 
   mFrameTableWriter.StartArrayElement();
 #ifndef SPS_STANDALONE
   if (!aFrame.mJITFrameHandle) {
 #else
   {
 #endif
+#ifdef SPS_STANDALONE
     mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.c_str());
+#else
+    mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.get());
+#endif
     if (aFrame.mLine.isSome()) {
       mFrameTableWriter.NullElement(); // implementation
       mFrameTableWriter.NullElement(); // optimizations
       mFrameTableWriter.IntElement(*aFrame.mLine);
     }
     if (aFrame.mCategory.isSome()) {
       if (aFrame.mLine.isNothing()) {
         mFrameTableWriter.NullElement(); // line
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -143,17 +143,23 @@ private:
   SpliceableChunkedJSONWriter mStringTableWriter;
   std::map<StringKey, uint32_t> mStringToIndexMap;
 };
 
 class UniqueStacks
 {
 public:
   struct FrameKey {
+#ifdef SPS_STANDALONE
     std::string mLocation;
+#else
+    // This cannot be a std::string, as it is not memmove compatible, which
+    // is used by nsHashTable
+    nsCString mLocation;
+#endif
     mozilla::Maybe<unsigned> mLine;
     mozilla::Maybe<unsigned> mCategory;
     mozilla::Maybe<void*> mJITAddress;
     mozilla::Maybe<uint32_t> mJITDepth;
 
     explicit FrameKey(const char* aLocation)
      : mLocation(aLocation)
     {
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -44,17 +44,17 @@ pref("offline-apps.allow_by_default", tr
 
 // TCPSocket
 pref("dom.mozTCPSocket.enabled", true);
 
 // Enable smooth scrolling
 pref("general.smoothScroll", true);
 
 // WebPayment
-pref("dom.mozPay.enabled", true);
+pref("dom.mozPay.enabled", false);
 
 // System messages
 pref("dom.sysmsg.enabled", true);
 
 // Alarm API
 pref("dom.mozAlarms.enabled", true);
 
 // Disable slow script dialog for apps
--- a/webapprt/test/chrome/browser_debugger.js
+++ b/webapprt/test/chrome/browser_debugger.js
@@ -14,19 +14,16 @@ function test() {
       client.listTabs((aResponse) => {
         is(aResponse.tabs[0].title, "Debugger Test Webapp", "Title correct");
         is(aResponse.tabs[0].url, "http://test/webapprtChrome/webapprt/test/chrome/debugger.html", "URL correct");
         ok(aResponse.tabs[0].consoleActor, "consoleActor set");
         ok(aResponse.tabs[0].gcliActor, "gcliActor set");
         ok(aResponse.tabs[0].styleEditorActor, "styleEditorActor set");
         ok(aResponse.tabs[0].inspectorActor, "inspectorActor set");
         ok(aResponse.tabs[0].traceActor, "traceActor set");
-        ok(aResponse.chromeDebugger, "chromeDebugger set");
-        ok(aResponse.consoleActor, "consoleActor set");
-        ok(aResponse.profilerActor, "profilerActor set");
         ok(aResponse.deviceActor, "deviceActor set");
 
         client.close(() => {
           finish();
         });
       });
     });
   });
--- a/webapprt/test/chrome/browser_mozpay.js
+++ b/webapprt/test/chrome/browser_mozpay.js
@@ -7,20 +7,20 @@ function test() {
 
   let curTest = 0;
 
   let tests = [];
   tests.push({
     providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-success.html?req=",
     message: "Success."
   });
-  tests.push({
-    providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-failure.html?req=",
-    message: "Chocolate rejected."
-  });
+  // tests.push({
+  //   providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-failure.html?req=",
+  //   message: "Chocolate rejected."
+  // });
 
   let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" +
             "a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
             "HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" +
             "llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" +
             "iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" +
             "Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" +
             "WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" +
@@ -31,57 +31,59 @@ function test() {
   PaymentManager.registeredProviders = {};
   PaymentManager.registeredProviders["mock/payments/inapp/v1"] = {
     name: "mockprovider",
     description: "Mock Payment Provider",
     uri: tests[curTest].providerUri,
     requestMethod: "GET"
   };
 
-  let providerWindow;
-
-  let winObserver = function(win, topic) {
-    if (topic == "domwindowopened") {
-      win.addEventListener("load", function onLoadWindow() {
-        win.removeEventListener("load", onLoadWindow, false);
-
-        if (win.document.getElementById("content").getAttribute("src") ==
-            (tests[curTest].providerUri + jwt)) {
-          ok(true, "Payment provider window shown.");
-          providerWindow = win;
-        }
-      }, false);
-    }
-  }
-
-  Services.ww.registerNotification(winObserver);
+  // Disabled because the mozPay API is disabled, so the provider window
+  // won't be shown.
+  //
+  // let providerWindow;
+  // let winObserver = function(win, topic) {
+  //   if (topic == "domwindowopened") {
+  //     win.addEventListener("load", function onLoadWindow() {
+  //       win.removeEventListener("load", onLoadWindow, false);
+  //
+  //       if (win.document.getElementById("content") &&
+  //           win.document.getElementById("content").getAttribute("src") ==
+  //             (tests[curTest].providerUri + jwt)) {
+  //         ok(true, "Payment provider window shown.");
+  //         providerWindow = win;
+  //       }
+  //     }, false);
+  //   }
+  // }
+  // Services.ww.registerNotification(winObserver);
 
   let mutObserver = null;
 
   function onLoad() {
     let msg = gAppBrowser.contentDocument.getElementById("msg");
     mutObserver = new MutationObserver(function(mutations) {
       is(msg.textContent, tests[curTest].message, "Got: " + tests[curTest].message);
 
-      if (!providerWindow) {
-        ok(false, "Payment provider window shown.");
-      } else {
-        providerWindow.close();
-        providerWindow = null;
-      }
+      // if (!providerWindow) {
+      //   ok(false, "Payment provider window shown.");
+      // } else {
+      //   providerWindow.close();
+      //   providerWindow = null;
+      // }
 
       runNextTest();
     });
     mutObserver.observe(msg, { childList: true });
   }
 
   loadWebapp("mozpay.webapp", undefined, onLoad);
 
   function runNextTest() {
-    providerWindow = null;
+    // providerWindow = null;
     if (mutObserver) {
       mutObserver.disconnect();
     }
 
     curTest++;
 
     if (curTest < tests.length) {
       PaymentManager.registeredProviders["mock/payments/inapp/v1"].uri = tests[curTest].providerUri;
@@ -92,12 +94,12 @@ function test() {
       }, true);
       gAppBrowser.reload();
     } else {
       finish();
     }
   }
 
   registerCleanupFunction(function() {
-    Services.ww.unregisterNotification(winObserver);
+    // Services.ww.unregisterNotification(winObserver);
     mutObserver.disconnect();
   });
 }
--- a/webapprt/test/chrome/mozpay.html
+++ b/webapprt/test/chrome/mozpay.html
@@ -27,19 +27,27 @@
               "llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" +
               "iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" +
               "Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" +
               "WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" +
               "15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" +
               "gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" +
               "IyKIC1TKelVhNklvk-Ou1l_daKntaFI";
 
-    var request = navigator.mozPay(jwt);
-    request.onsuccess = function onsuccess() {
-      document.getElementById("msg").textContent = "Success.";
-    };
-    request.onerror = function onerror() {
-      document.getElementById("msg").textContent = request.error.name;
-    };
+    // mozPay is currently disabled in the desktop runtime, so we check
+    // that the property is set to null on the navigator object.
+    window.addEventListener("load", function() {
+        document.getElementById("msg").textContent =
+          (navigator.mozPay === null) ? "Success." : "navigator.mozPay defined";
+    }, false);
+
+    // This is the old code for checking the behavior of the API when enabled:
+    // var request = navigator.mozPay(jwt);
+    // request.onsuccess = function onsuccess() {
+    //   document.getElementById("msg").textContent = "Success.";
+    // };
+    // request.onerror = function onerror() {
+    //   document.getElementById("msg").textContent = request.error.name;
+    // };
     </script>
     <p id="msg">Webapp waiting to be paid...</p>
   </body>
 </html>