Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 16 Oct 2014 15:03:32 -0400
changeset 210780 57c461500a0cdbab5071b04ab7419f51d6238366
parent 210779 7fac025c8cdb86ee91fb3abfec6ea6a9ed330e79 (current diff)
parent 210767 cc7fc5918561b47d77404e4beffa31ede57a9522 (diff)
child 210781 2a0d75a59098a3a1699c7700a7eaec8f56bf462a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone36.0a1
Merge m-c to fx-team. a=merge CLOSED TREE
dom/events/BeforeAfterKeyboardEvent.cpp
dom/events/BeforeAfterKeyboardEvent.h
dom/events/test/bug989198_embedded.html
dom/events/test/bug989198_helper.js
dom/events/test/test_dom_before_after_keyboard_event.html
dom/events/test/test_dom_before_after_keyboard_event_remote.html
dom/webidl/BeforeAfterKeyboardEvent.webidl
testing/web-platform/meta/html/syntax/parsing/html5lib_tests11.html.ini
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -171,22 +171,18 @@ const WorkerSandbox = Class({
     let options = 'contentScriptOptions' in worker ?
       JSON.stringify(worker.contentScriptOptions) :
       undefined;
 
     // Then call `inject` method and communicate with this script
     // by trading two methods that allow to send events to the other side:
     //   - `onEvent` called by content script
     //   - `result.emitToContent` called by addon script
-    // Bug 758203: We have to explicitely define `__exposedProps__` in order
-    // to allow access to these chrome object attributes from this sandbox with
-    // content priviledges
-    // https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers
     let onEvent = onContentEvent.bind(null, this);
-    let chromeAPI = createChromeAPI();
+    let chromeAPI = createChromeAPI(ContentWorker);
     let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
 
     // Merge `emitToContent` into our private model of the
     // WorkerSandbox so we can communicate with content script
     model.emitToContent = result;
 
     let console = new PlainTextConsole(null, getInnerId(window));
 
@@ -374,34 +370,21 @@ function modelFor (workerSandbox) {
 function getUnsafeWindow (win) {
   return win.wrappedJSObject || win;
 }
 
 function emitToContent (workerSandbox, args) {
   return modelFor(workerSandbox).emitToContent(args);
 }
 
-function createChromeAPI () {
-  return {
+function createChromeAPI (scope) {
+  return Cu.cloneInto({
     timers: {
-      setTimeout: timer.setTimeout,
-      setInterval: timer.setInterval,
-      clearTimeout: timer.clearTimeout,
-      clearInterval: timer.clearInterval,
-      __exposedProps__: {
-        setTimeout: 'r',
-        setInterval: 'r',
-        clearTimeout: 'r',
-        clearInterval: 'r'
-      },
+      setTimeout: timer.setTimeout.bind(timer),
+      setInterval: timer.setInterval.bind(timer),
+      clearTimeout: timer.clearTimeout.bind(timer),
+      clearInterval: timer.clearInterval.bind(timer),
     },
     sandbox: {
       evaluate: evaluate,
-      __exposedProps__: {
-        evaluate: 'r'
-      }
     },
-    __exposedProps__: {
-      timers: 'r',
-      sandbox: 'r'
-    }
-  };
+  }, scope, {cloneFunctions: true});
 }
--- a/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
+++ b/addon-sdk/source/lib/sdk/deprecated/traits-worker.js
@@ -179,44 +179,27 @@ const WorkerSandbox = EventEmitter.compo
     let options = 'contentScriptOptions' in worker ?
       JSON.stringify( worker.contentScriptOptions ) :
       undefined;
 
     // Then call `inject` method and communicate with this script
     // by trading two methods that allow to send events to the other side:
     //   - `onEvent` called by content script
     //   - `result.emitToContent` called by addon script
-    // Bug 758203: We have to explicitely define `__exposedProps__` in order
-    // to allow access to these chrome object attributes from this sandbox with
-    // content priviledges
-    // https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers
-    let chromeAPI = {
+    let chromeAPI = Cu.cloneInto({
       timers: {
-        setTimeout: timer.setTimeout,
-        setInterval: timer.setInterval,
-        clearTimeout: timer.clearTimeout,
-        clearInterval: timer.clearInterval,
-        __exposedProps__: {
-          setTimeout: 'r',
-          setInterval: 'r',
-          clearTimeout: 'r',
-          clearInterval: 'r'
-        }
+        setTimeout: timer.setTimeout.bind(timer),
+        setInterval: timer.setInterval.bind(timer),
+        clearTimeout: timer.clearTimeout.bind(timer),
+        clearInterval: timer.clearInterval.bind(timer),
       },
       sandbox: {
         evaluate: evaluate,
-        __exposedProps__: {
-          evaluate: 'r',
-        }
       },
-      __exposedProps__: {
-        timers: 'r',
-        sandbox: 'r',
-      }
-    };
+    }, ContentWorker, {cloneFunctions: true});
     let onEvent = this._onContentEvent.bind(this);
     let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
     this._emitToContent = result;
 
     // Handle messages send by this script:
     let self = this;
     // console.xxx calls
     this.on("console", function consoleListener(kind) {
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -783,19 +783,16 @@ pref("dom.ipc.reuse_parent_app", false);
 // When a process receives a system message, we hold a CPU wake lock on its
 // behalf for this many seconds, or until it handles the system message,
 // whichever comes first.
 pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
 
 // Ignore the "dialog=1" feature in window.open.
 pref("dom.disable_window_open_dialog_feature", true);
 
-// Enable before keyboard events and after keyboard events.
-pref("dom.beforeAfterKeyboardEvent.enabled", true);
-
 // Screen reader support
 pref("accessibility.accessfu.activate", 2);
 pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
 // Active quicknav mode, index value of list from quicknav_modes
 pref("accessibility.accessfu.quicknav_index", 0);
 // Setting for an utterance order (0 - description first, 1 - description last).
 pref("accessibility.accessfu.utterance", 1);
 // Whether to skip images with empty alt text
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -319,18 +319,16 @@ var shell = {
     // Capture all key events so we can filter out hardware buttons
     // And send them to Gaia via mozChromeEvents.
     // Ideally, hardware buttons wouldn't generate key events at all, or
     // if they did, they would use keycodes that conform to DOM 3 Events.
     // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
     chromeEventHandler.addEventListener('keydown', this, true);
     chromeEventHandler.addEventListener('keypress', this, true);
     chromeEventHandler.addEventListener('keyup', this, true);
-    chromeEventHandler.addEventListener('mozbrowserbeforekeydown', this, true);
-    chromeEventHandler.addEventListener('mozbrowserbeforekeyup', this, true);
 
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
@@ -353,18 +351,16 @@ var shell = {
     ppmm.addMessageListener("file-picker", this);
   },
 
   stop: function shell_stop() {
     window.removeEventListener('unload', this);
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
-    window.removeEventListener('mozbrowserbeforekeydown', this, true);
-    window.removeEventListener('mozbrowserbeforekeyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
     ppmm.removeMessageListener("content-handler", this);
@@ -419,24 +415,27 @@ var shell = {
       isMediaKey = true;
       type = mediaKeys[evt.key];
     }
 
     if (!type) {
       return;
     }
 
+    // If we didn't return, then the key event represents a hardware key
+    // and we need to prevent it from propagating to Gaia
+    evt.stopImmediatePropagation();
+    evt.preventDefault(); // Prevent keypress events (when #501496 is fixed).
+
     // If it is a key down or key up event, we send a chrome event to Gaia.
     // If it is a keypress event we just ignore it.
     switch (evt.type) {
-      case 'mozbrowserbeforekeydown':
       case 'keydown':
         type = type + '-press';
         break;
-      case 'mozbrowserbeforekeyup':
       case 'keyup':
         type = type + '-release';
         break;
       case 'keypress':
         return;
     }
 
     // Let applications receive the headset button key press/release event.
@@ -469,18 +468,16 @@ var shell = {
   visibleNormalAudioActive: false,
 
   handleEvent: function shell_handleEvent(evt) {
     let content = this.contentBrowser.contentWindow;
     switch (evt.type) {
       case 'keydown':
       case 'keyup':
       case 'keypress':
-      case 'mozbrowserbeforekeydown':
-      case 'mozbrowserbeforekeyup':
         this.filterHardwareKeys(evt);
         break;
       case 'mozfullscreenchange':
         // When the screen goes fullscreen make sure to set the focus to the
         // main window so noboby can prevent the ESC key to get out fullscreen
         // mode
         if (document.mozFullScreen)
           Services.fm.focusedWindow = window;
--- 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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- 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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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"/>
@@ -129,12 +129,12 @@
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="125ccf9bd5986c7728ea44508b3e1d1185ac028b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d259117b4976decbe2f76eeed85218bf0109190f"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="85e864c4abc80e08bc82f0ac53c042e0d27839b0"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7047fcaa8bb55888ec7b84997e1bab41185ba3a3"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- 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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- 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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
@@ -127,17 +127,17 @@
   <remove-project name="platform/hardware/libhardware"/>
   <remove-project name="platform/external/bluetooth/bluedroid"/>
   <!--original fetch url was git://github.com/t2m-foxfone/-->
   <remote fetch="https://git.mozilla.org/external/t2m-foxfone" name="t2m"/>
   <default remote="caf" revision="LNX.LA.3.5.2.1.1" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="54c32c2ddef066fbdf611d29e4b7c47e0363599e"/>
-  <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="52c909e821d107d414f851e267dedcd7aae2cebf"/>
+  <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="05aa7b98d3f891b334031dc710d48d0d6b82ec1d"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="7731d63c809dbca4da408e1de0c1a044f0765e52"/>
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="30b96dfca99cb384bf520a16b81f3aba56f09907"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="ea2f399b3ca0a23524d2828f85f69902caefc22e"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="6b58ab45e3e56c1fc20708cc39fa2264c52558df"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="a46a9f1ac0ed5662d614c277cbb14eb3f332f365"/>
@@ -146,13 +146,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="e5a971282719907f73fb1da964ca40aad67a3be0"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="350eac5403124dacb2a5fd9e28ac290a59fc3b8e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="85e864c4abc80e08bc82f0ac53c042e0d27839b0"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7047fcaa8bb55888ec7b84997e1bab41185ba3a3"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="7704e16da545f4207812e593743d6743e1afb9c5"/>
 </manifest>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
@@ -140,13 +140,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="cd76b19aafd4229ccf83853d02faef8c51ca8b34"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="85e864c4abc80e08bc82f0ac53c042e0d27839b0"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7047fcaa8bb55888ec7b84997e1bab41185ba3a3"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "8017299c3eb7b82bdaee1334994f546c11eb16ab", 
+    "revision": "63436aa17e7fa3ad521fdeffdc22b81c36e5d69b", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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"/>
@@ -124,17 +124,17 @@
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Nexus 4 specific things -->
   <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="85e864c4abc80e08bc82f0ac53c042e0d27839b0"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7047fcaa8bb55888ec7b84997e1bab41185ba3a3"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="b0a528d839cfd9d170d092fe3743b5252b4243a6"/>
   <project name="platform/hardware/qcom/bt" path="hardware/qcom/bt" revision="380945eaa249a2dbdde0daa4c8adb8ca325edba6"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="6f3b0272cefaffeaed2a7d2bb8f633059f163ddc"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="16da8262c997a5a0d797885788a64a0771b26910"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="689b476ba3eb46c34b81343295fe144a0e81a18e"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="5c636a7a54b2c86d8ff6bc1aa1e5f9594c7bc586"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="abef62c0623e5504a97b4fd411e879a67b285b52"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -20,17 +20,17 @@ DEFINES += \
 
 ifdef LIBXUL_SDK #{
 PREF_JS_EXPORTS += $(srcdir)/profile/channel-prefs.js
 endif #} LIBXUL_SDK
 
 # Build a binary bootstrapping with XRE_main
 
 ifndef MOZ_WINCONSOLE
-ifdef MOZ_DEBUG
+ifneq (,$(MOZ_DEBUG)$(MOZ_ASAN))
 MOZ_WINCONSOLE = 1
 else
 MOZ_WINCONSOLE = 0
 endif
 endif
 
 # This switches $(INSTALL) to copy mode, like $(SYSINSTALL), so things that
 # shouldn't get 755 perms need $(IFLAGS1) for either way of calling nsinstall.
--- a/browser/base/content/test/general/browser_bug767836_perwindowpb.js
+++ b/browser/base/content/test/general/browser_bug767836_perwindowpb.js
@@ -24,18 +24,23 @@ function test() {
       is(aWindow.gBrowser.selectedBrowser.currentURI.spec, newTabURL,
         "URL of NewTab should be " + newTabURL + " in " + mode +  " mode");
       // Set the custom newtab url
       Services.prefs.setCharPref(newTabPrefName, testURL);
       ok(Services.prefs.prefHasUserValue(newTabPrefName), "Custom newtab url is set");
 
       // Open a newtab after setting the custom newtab url
       openNewTab(aWindow, function () {
-        is(aWindow.gBrowser.selectedBrowser.currentURI.spec, testURL,
-           "URL of NewTab should be the custom url");
+        if (aIsPrivateMode) {
+          is(aWindow.gBrowser.selectedBrowser.currentURI.spec, newTabURL,
+             "URL of NewTab should always be " + newTabURL + " in " + mode +  " mode");
+        } else {
+          is(aWindow.gBrowser.selectedBrowser.currentURI.spec, testURL,
+             "URL of NewTab should be the custom url");
+        }
 
         // clear the custom url preference
         Services.prefs.clearUserPref(newTabPrefName);
         ok(!Services.prefs.prefHasUserValue(newTabPrefName), "No custom newtab url is set");
 
         aWindow.gBrowser.removeTab(aWindow.gBrowser.selectedTab);
         aWindow.gBrowser.removeTab(aWindow.gBrowser.selectedTab);
         aWindow.close();
@@ -75,9 +80,9 @@ function openNewTab(aWindow, aCallback) 
     executeSoon(aCallback);
     return;
   }
 
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
     executeSoon(aCallback);
   }, true);
-}
\ No newline at end of file
+}
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -8,21 +8,21 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Components.utils.import("resource:///modules/RecentWindow.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
   const PREF = "browser.newtab.url";
 
   function getNewTabPageURL() {
-    if (!Services.prefs.prefHasUserValue(PREF)) {
-      if (PrivateBrowsingUtils.isWindowPrivate(window) &&
-          !PrivateBrowsingUtils.permanentPrivateBrowsing)
-        return "about:privatebrowsing";
+    if (PrivateBrowsingUtils.isWindowPrivate(window) &&
+        !PrivateBrowsingUtils.permanentPrivateBrowsing) {
+      return "about:privatebrowsing";
     }
+
     let url = Services.prefs.getComplexValue(PREF, Ci.nsISupportsString).data;
     return url || "about:blank";
   }
 
   function update() {
     BROWSER_NEW_TAB_URL = getNewTabPageURL();
   }
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
@@ -1,70 +1,40 @@
 /* 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/. */
 
 // This test makes sure that the URL bar is focused when entering the private window.
 
-function test() {
-  waitForExplicitFinish();
+"use strict";
 
-  const TEST_URL = "data:text/plain,test";
+function checkUrlbarFocus(win) {
+  let urlbar = win.gURLBar;
+  is(win.document.activeElement, urlbar.inputField, "URL Bar should be focused");
+  is(urlbar.value, "", "URL Bar should be empty");
+}
 
-  function checkUrlbarFocus(aWin, aIsPrivate, aCallback) {
-    let urlbar = aWin.gURLBar;
-    if (aIsPrivate) {
-      is(aWin.document.commandDispatcher.focusedElement, urlbar.inputField,
-         "URL Bar should be focused inside the private window");
-      is(urlbar.value, "",
-         "URL Bar should be empty inside the private window");
-    } else {
-      isnot(aWin.document.commandDispatcher.focusedElement, urlbar.inputField,
-            "URL Bar should not be focused after opening window");
-      isnot(urlbar.value, "",
-            "URL Bar should not be empty after opening window");
-    }
-    aCallback();
-  }
-
-  let windowsToClose = [];
-  function testOnWindow(aPrivate, aCallback) {
-    whenNewWindowLoaded({private: aPrivate}, function(win) {
-      windowsToClose.push(win);
-      executeSoon(function() aCallback(win));
-    });
-  }
+function openNewPrivateWindow() {
+  let deferred = Promise.defer();
+  whenNewWindowLoaded({private: true}, win => {
+    executeSoon(() => deferred.resolve(win));
+  });
+  return deferred.promise;
+}
 
-  function doneWithTests() {
-    windowsToClose.forEach(function(win) {
-      win.close();
-    });
-    finish();
-  }
+add_task(function* () {
+  let win = yield openNewPrivateWindow();
+  checkUrlbarFocus(win);
+  win.close();
+});
 
-  function whenLoadTab(aPrivate, aCallback) {
-    testOnWindow(aPrivate, function(win) {
-      if (!aPrivate) {
-        let browser = win.gBrowser.selectedBrowser;
-        browser.addEventListener("load", function() {
-          browser.removeEventListener("load", arguments.callee, true);
-          aCallback(win);
-        }, true);
-        browser.focus();
-        browser.loadURI(TEST_URL);
-      } else {
-        aCallback(win);
-      }
-    });
-  }
+add_task(function* () {
+  Services.prefs.setCharPref("browser.newtab.url", "about:blank");
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("browser.newtab.url");
+  });
 
-  whenLoadTab(false, function(win) {
-    checkUrlbarFocus(win, false, function() {
-      whenLoadTab(true, function(win) {
-        checkUrlbarFocus(win, true, function() {
-          whenLoadTab(false, function(win) {
-            checkUrlbarFocus(win, false, doneWithTests);
-          });
-        });
-      });
-    });
-  });
-}
+  let win = yield openNewPrivateWindow();
+  checkUrlbarFocus(win);
+  win.close();
+
+  Services.prefs.clearUserPref("browser.newtab.url");
+});
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -117,17 +117,17 @@ MOZ_ARG_ENABLE_STRING(debug,
         MOZ_DEBUG_FLAGS=`echo $enableval | sed -e 's|\\\ | |g'`
         _MOZ_DEBUG_FLAGS_SET=1
     fi
   else
     MOZ_DEBUG=
   fi ],
   MOZ_DEBUG=)
 
-if test -z "$MOZ_DEBUG"; then
+if test -z "$MOZ_DEBUG" -o -n "$MOZ_ASAN"; then
     MOZ_NO_DEBUG_RTL=1
 fi
 
 AC_SUBST(MOZ_NO_DEBUG_RTL)
 
 MOZ_DEBUG_ENABLE_DEFS="-DDEBUG -DTRACING"
 MOZ_ARG_WITH_STRING(debug-label,
 [  --with-debug-label=LABELS
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -46,24 +46,30 @@ yes)
     ENABLE_INTL_API=1
     EXPOSE_INTL_API=1
     ;;
 *)
     AC_MSG_ERROR([Invalid value passed to --with-intl-api: $_INTL_API])
     ;;
 esac
 
+if test -n "$ENABLE_INTL_API"; then
+    USE_ICU=1
+fi
+
 if test -n "$EXPOSE_INTL_API"; then
     AC_DEFINE(EXPOSE_INTL_API)
 fi
 
-dnl Settings for the implementation of the ECMAScript Internationalization API
 if test -n "$ENABLE_INTL_API"; then
     AC_DEFINE(ENABLE_INTL_API)
+fi
 
+dnl Settings for the implementation of the ECMAScript Internationalization API
+if test -n "$USE_ICU"; then
     icudir="$_topsrcdir/intl/icu/source"
     if test ! -d "$icudir"; then
         icudir="$_topsrcdir/../../intl/icu/source"
         if test ! -d "$icudir"; then
             AC_MSG_ERROR([Cannot find the ICU directory])
         fi
     fi
 
@@ -95,36 +101,37 @@ if test -n "$ENABLE_INTL_API"; then
             *)
                 AC_MSG_ERROR([ECMAScript Internationalization API is not yet supported on this platform])
         esac
     fi
 fi
 
 AC_SUBST(MOZ_ICU_DBG_SUFFIX)
 AC_SUBST(ENABLE_INTL_API)
+AC_SUBST(USE_ICU)
 AC_SUBST_LIST(ICU_LIB_NAMES)
 
-if test -n "$ENABLE_INTL_API" -a -z "$MOZ_NATIVE_ICU"; then
+if test -n "$USE_ICU" -a -z "$MOZ_NATIVE_ICU"; then
     dnl We build ICU as a static library for non-shared js builds and as a shared library for shared js builds.
     if test -z "$MOZ_SHARED_ICU"; then
         AC_DEFINE(U_STATIC_IMPLEMENTATION)
     fi
     dnl Source files that use ICU should have control over which parts of the ICU
     dnl namespace they want to use.
     AC_DEFINE(U_USING_ICU_NAMESPACE,0)
 fi
 
 
 ])
 
 AC_DEFUN([MOZ_SUBCONFIGURE_ICU], [
 
 if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then
 
-    if test -n "$ENABLE_INTL_API" -a -z "$MOZ_NATIVE_ICU"; then
+    if test -n "$USE_ICU" -a -z "$MOZ_NATIVE_ICU"; then
         # Set ICU compile options
         ICU_CPPFLAGS=""
         # don't use icu namespace automatically in client code
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_USING_ICU_NAMESPACE=0"
         # don't include obsolete header files
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1"
         # remove chunks of the library that we don't need (yet)
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_LEGACY_CONVERSION"
--- a/config/external/icu/Makefile.in
+++ b/config/external/icu/Makefile.in
@@ -1,14 +1,14 @@
 # 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/.
 
 # Ensure that this happens before including rules.mk
-ifdef ENABLE_INTL_API
+ifdef USE_ICU
   ifndef MOZ_NATIVE_ICU
   # Library names: On Windows, ICU uses modified library names for static
   # and debug libraries.
     ifdef MOZ_SHARED_ICU
       ifeq ($(OS_ARCH),WINNT)
         ifdef JS_SHARED_LIBRARY
           ICU_FILES := $(foreach libname,$(ICU_LIB_NAMES),$(DEPTH)/intl/icu/target/lib/$(libname)$(MOZ_ICU_DBG_SUFFIX)$(MOZ_ICU_VERSION).dll)
         endif
@@ -31,21 +31,21 @@ ifdef ENABLE_INTL_API
       endif
     else # !MOZ_SHARED_ICU
       ifeq ($(OS_ARCH),WINNT)
         ICU_LIB_RENAME = $(foreach libname,$(ICU_LIB_NAMES),\
                              cp -p $(DEPTH)/intl/icu/target/lib/s$(libname)$(MOZ_ICU_DBG_SUFFIX).lib $(DEPTH)/intl/icu/target/lib/$(libname)$(MOZ_ICU_DBG_SUFFIX).lib;)
       endif
     endif # MOZ_SHARED_ICU
   endif # !MOZ_NATIVE_ICU
-endif # ENABLE_INTL_API
+endif # USE_ICU
 
 include $(topsrcdir)/config/rules.mk
 
-ifdef ENABLE_INTL_API
+ifdef USE_ICU
 ifndef MOZ_NATIVE_ICU
 target:: buildicu
 $(STATIC_LIBS): buildicu
 
 # - Force ICU to use the standard suffix for object files because expandlibs
 #   will discard all files with a non-standard suffix (bug 857450).
 # - Options for genrb: -k strict parsing; -R omit collation tailoring rules.
 buildicu::
--- a/configure.in
+++ b/configure.in
@@ -2201,16 +2201,20 @@ ia64*-hpux*)
         # MSVC warning C4800 warns when a value is implicitly cast to bool,
         # because this also forces narrowing to a single byte, which can be a
         # perf hit.  But this matters so little in practice (and often we want
         # that behavior) that it's better to turn it off.
         # MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign)
         # on non-Western system locales even if it is in a comment.
         CFLAGS="$CFLAGS -wd4244 -wd4267 -wd4819"
         CXXFLAGS="$CXXFLAGS -wd4251 -wd4244 -wd4267 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
+        if test -n "$CLANG_CL"; then
+            # Suppress the clang-cl warning for the inline 'new' and 'delete' in mozalloc
+            CXXFLAGS="$CXXFLAGS -Wno-inline-new-delete"
+        fi
         # make 'foo == bar;' error out
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib secur32.lib netapi32.lib"
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS='-O1 -Oi'
         MOZ_FIX_LINK_PATHS=
@@ -8020,16 +8024,21 @@ else
 MOZ_ENABLE_SKIA=
 fi
 
 MOZ_ARG_ENABLE_BOOL(skia,
 [  --enable-skia   Enable use of Skia],
 MOZ_ENABLE_SKIA=1,
 MOZ_ENABLE_SKIA=)
 
+MOZ_ARG_DISABLE_BOOL(skia-gpu,
+[  --disable-skia-gpu  Disable use of Skia-GPU],
+MOZ_DISABLE_SKIA_GPU=1,
+MOZ_DISABLE_SKIA_GPU=)
+
 if test "$USE_FC_FREETYPE"; then
     if test "$COMPILE_ENVIRONMENT"; then
         dnl ========================================================
         dnl = Check for freetype2 and its functionality
         dnl ========================================================
         PKG_CHECK_MODULES(FT2, freetype2 >= 6.1.0, _HAVE_FREETYPE2=1, _HAVE_FREETYPE2=)
 
         if test "$_HAVE_FREETYPE2"; then
@@ -8254,17 +8263,17 @@ dnl Skia
 dnl ========================================================
 if test "$MOZ_ENABLE_SKIA"; then
   AC_DEFINE(MOZ_ENABLE_SKIA)
   AC_DEFINE(USE_SKIA)
   if test "${MOZ_WIDGET_TOOLKIT}" = "android" -o x"$MOZ_WIDGET_TOOLKIT" = x"gonk"; then
     AC_DEFINE(SK_BUILD_FOR_ANDROID_NDK)
   fi
 
-  if test "${CPU_ARCH}" != "ppc" -a "${CPU_ARCH}" != "ppc64" -a "${CPU_ARCH}" != "sparc" ; then
+  if test "${CPU_ARCH}" != "ppc" -a "${CPU_ARCH}" != "ppc64" -a "${CPU_ARCH}" != "sparc" -a -z "$MOZ_DISABLE_SKIA_GPU" ; then
     MOZ_ENABLE_SKIA_GPU=1
     AC_DEFINE(USE_SKIA_GPU)
     AC_SUBST(MOZ_ENABLE_SKIA_GPU)
   fi
 fi
 AC_SUBST(MOZ_ENABLE_SKIA)
 
 dnl ========================================================
@@ -9022,29 +9031,27 @@ dnl ICU Support
 dnl ========================================================
 
 # Internationalization isn't built or exposed by default in non-desktop
 # builds.  Bugs to enable:
 #
 #   Android:  bug 864843
 #   B2G:      bug 866301
 
-if test "$MOZ_WIDGET_TOOLKIT" = "android"; then
+if test "$MOZ_WIDGET_TOOLKIT" = "android" ||
+   test "$MOZ_BUILD_APP" = "b2g"; then
     _INTL_API=no
-elif test "$MOZ_BUILD_APP" = "b2g"; then
-    if test "$MOZ_WIDGET_TOOLKIT" = "cocoa"; then
-        # nsCollationMacUC needs ICU
-        _INTL_API=yes
-    else
-        _INTL_API=no
-    fi
 else
     _INTL_API=yes
 fi
 
+if test "$MOZ_WIDGET_TOOLKIT" = "cocoa"; then
+    USE_ICU=1
+fi
+
 MOZ_CONFIG_ICU()
 
 if test -z "$JS_SHARED_LIBRARY"; then
   AC_DEFINE(MOZ_STATIC_JS)
 fi
 AC_SUBST(JS_SHARED_LIBRARY)
 
 MOZ_CREATE_CONFIG_STATUS()
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -789,20 +789,16 @@ GK_ATOM(onmousedown, "onmousedown")
 GK_ATOM(onmouseenter, "onmouseenter")
 GK_ATOM(onmouseleave, "onmouseleave")
 GK_ATOM(onmousemove, "onmousemove")
 GK_ATOM(onmouseout, "onmouseout")
 GK_ATOM(onmouseover, "onmouseover")
 GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
 GK_ATOM(onmouseup, "onmouseup")
 GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
-GK_ATOM(onmozbrowserafterkeydown, "onmozbrowserafterkeydown")
-GK_ATOM(onmozbrowserafterkeyup, "onmozbrowserafterkeyup")
-GK_ATOM(onmozbrowserbeforekeydown, "onmozbrowserbeforekeydown")
-GK_ATOM(onmozbrowserbeforekeyup, "onmozbrowserbeforekeyup")
 GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
 GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
 GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")
 GK_ATOM(onmozpointerlockerror, "onmozpointerlockerror")
 GK_ATOM(onmoztimechange, "onmoztimechange")
 GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
 GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
 GK_ATOM(onmoznetworkupload, "onmoznetworkupload")
@@ -1338,19 +1334,16 @@ GK_ATOM(fePointLight, "fePointLight")
 GK_ATOM(feSpecularLighting, "feSpecularLighting")
 GK_ATOM(feSpotLight, "feSpotLight")
 GK_ATOM(feTile, "feTile")
 GK_ATOM(feTurbulence, "feTurbulence")
 GK_ATOM(fill, "fill")
 GK_ATOM(fill_opacity, "fill-opacity")
 GK_ATOM(fill_rule, "fill-rule")
 GK_ATOM(filter, "filter")
-// Parsing for filterRes may be removed in the future.
-// https://bugzilla.mozilla.org/show_bug.cgi?id=979472
-GK_ATOM(filterRes, "filterRes")
 GK_ATOM(filterUnits, "filterUnits")
 GK_ATOM(_float, "float")
 GK_ATOM(flood_color, "flood-color")
 GK_ATOM(flood_opacity, "flood-opacity")
 GK_ATOM(font_face, "font-face")
 GK_ATOM(font_face_format, "font-face-format")
 GK_ATOM(font_face_name, "font-face-name")
 GK_ATOM(font_face_src, "font-face-src")
--- a/content/base/src/nsTreeSanitizer.cpp
+++ b/content/base/src/nsTreeSanitizer.cpp
@@ -388,18 +388,16 @@ nsIAtom** const kAttributesSVG[] = {
   // cap-height
   &nsGkAtoms::_class, // class
   &nsGkAtoms::clip_path, // clip-path
   &nsGkAtoms::clip_rule, // clip-rule
   &nsGkAtoms::clipPathUnits, // clipPathUnits
   &nsGkAtoms::color, // color
   &nsGkAtoms::colorInterpolation, // color-interpolation
   &nsGkAtoms::colorInterpolationFilters, // color-interpolation-filters
-  // contentScriptType
-  // contentStyleType
   &nsGkAtoms::cursor, // cursor
   &nsGkAtoms::cx, // cx
   &nsGkAtoms::cy, // cy
   &nsGkAtoms::d, // d
   // descent
   &nsGkAtoms::diffuseConstant, // diffuseConstant
   &nsGkAtoms::direction, // direction
   &nsGkAtoms::display, // display
@@ -411,17 +409,16 @@ nsIAtom** const kAttributesSVG[] = {
   &nsGkAtoms::edgeMode, // edgeMode
   &nsGkAtoms::elevation, // elevation
   // enable-background
   &nsGkAtoms::end, // end
   &nsGkAtoms::fill, // fill
   &nsGkAtoms::fill_opacity, // fill-opacity
   &nsGkAtoms::fill_rule, // fill-rule
   &nsGkAtoms::filter, // filter
-  &nsGkAtoms::filterRes, // filterRes
   &nsGkAtoms::filterUnits, // filterUnits
   &nsGkAtoms::flood_color, // flood-color
   &nsGkAtoms::flood_opacity, // flood-opacity
   // XXX focusable
   &nsGkAtoms::font, // font
   &nsGkAtoms::font_family, // font-family
   &nsGkAtoms::font_size, // font-size
   &nsGkAtoms::font_size_adjust, // font-size-adjust
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -297,17 +297,17 @@ support-files =
   wave_metadata_utf8.wav
   wave_metadata_utf8.wav^headers^
   wavedata_s16.wav
   wavedata_s16.wav^headers^
   wavedata_u8.wav
   wavedata_u8.wav^headers^
 
 [test_access_control.html]
-skip-if = buildapp == 'b2g' && toolkit != 'gonk' # crash on b2g-desktop
+skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_aspectratio_mp4.html]
 [test_audio1.html]
 [test_audio2.html]
 [test_audioDocumentTitle.html]
 skip-if = true # bug 475110 - disabled since we don't play Wave files standalone
 [test_autoplay.html]
 [test_autoplay_contentEditable.html]
 [test_buffered.html]
@@ -346,17 +346,17 @@ skip-if = toolkit == 'gonk' && debug
 [test_contentDuration6.html]
 [test_contentDuration7.html]
 [test_controls.html]
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
-skip-if = buildapp == 'b2g' # bug 1021676
+skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_encryptedMediaExtensions.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
--- a/content/media/test/test_delay_load.html
+++ b/content/media/test/test_delay_load.html
@@ -4,71 +4,58 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=479711
 -->
 <head>
   <title>Test for Bug 479711</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="manifest.js"></script>
   <script>
-  
+
   var gRegisteredElements = [];
-  var gLog = false;
   var testWindows = [];
 
-  function log(msg) {
-    if (gLog) {
-      document.getElementById('log').innerHTML += "<p>"+msg+"</p>";
-      dump(msg + "\n");
-    }
-  }
-  
   function register(v) {
     gRegisteredElements.push(v);
   }
 
   function loaded() {
-    log("onload fired!");
+    info("onload fired!");
 
     for (var i = 0; i < gRegisteredElements.length; ++i) {
       var v = gRegisteredElements[i];
       ok(v.readyState >= v.HAVE_CURRENT_DATA,
          v._name + ":" + v.id + " is not ready before onload fired (" + v.readyState + ")");
     }
 
     for (i=0; i<testWindows.length; ++i) {
       testWindows[i].close();
     }
 
-    mediaTestCleanup();    
+    mediaTestCleanup();
 
     SimpleTest.finish();
   }
-  
+
   addLoadEvent(loaded);
-  
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479711">Mozilla Bug 479711</a>
 <p id="display"></p>
-<div id="content" style="display: none">
-  
-</div>
-
-
-<div id="log" style="font-size: small;"></div>
+<div id="content" style="display: none"></div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 479711 **/
 
 function createVideo(name, type, id) {
   var v = document.createElement("video");
+  v.preload = "metadata";
   // Make sure each video is a unique resource
   v.src = name + "?" + id;
   v._name = name;
   v.id = id;
   register(v);
   return v;
 }
 
@@ -77,17 +64,17 @@ var test = getPlayableVideo(gSmallTests)
 // Straightforward add, causing a load.
 var v = createVideo(test.name, test.type, "1");
 document.body.appendChild(v);
 
 // Load, add, then remove.
 v = createVideo(test.name, test.type, "1");
 v.load();
 document.body.appendChild(v);
-v.parentNode.removeChild(v);
+v.remove();
 
 // Load and add.
 v = createVideo(test.name, test.type, "2");
 v.load();
 document.body.appendChild(v);
 
 // Load outside of doc.
 v = createVideo(test.name, test.type, "3");
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -488,22 +488,16 @@ this.PermissionsTable =  { geolocation: 
                              access: ["read", "write"],
                              additional: ["settings-api"]
                            },
                            "engineering-mode": {
                              app: DENY_ACTION,
                              trusted: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
-                           },
-                           "before-after-keyboard-event": {
-                             app: DENY_ACTION,
-                             trusted: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
                            }
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
--- a/dom/base/test/test_window_enumeration.html
+++ b/dom/base/test/test_window_enumeration.html
@@ -8,27 +8,24 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 807222</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=807222">Mozilla Bug 807222</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 807222 **/
 var expectedProps = [ "Image", "Audio", "Option",
                       "PerformanceTiming", "CSS2Properties", "SVGElement" ];
-if (navigator.mozTelephony) {
-  expectedProps.push("USSDReceivedEvent");
-}
 var actualProps = Object.getOwnPropertyNames(window);
 
 for (var i = 0; i < expectedProps.length; ++i) {
   isnot(actualProps.indexOf(expectedProps[i]), -1,
         "getOwnPropertyNames should include " + expectedProps[i]);
 }
 </script>
 </pre>
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -188,31 +188,71 @@ public:
     BluetoothA2dpManager* a2dpManager = BluetoothA2dpManager::Get();
     sBtAvrcpInterface->Init(a2dpManager, new InitAvrcpResultHandler(mRes));
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
 
+class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                      nsresult aRv)
+  : mRes(aRes)
+  , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    mRes->OnError(mRv);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
 /*
  * This function will be only called when Bluetooth is turning on.
  * It is important to register a2dp callbacks before enable() gets called.
  * It is required to register a2dp callbacks before a2dp media task
  * starts up.
  */
 // static
 void
 BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes)
 {
   BluetoothInterface* btInf = BluetoothInterface::GetInstance();
-  NS_ENSURE_TRUE_VOID(btInf);
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no HFP interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
+    }
+    return;
+  }
 
   sBtA2dpInterface = btInf->GetBluetoothA2dpInterface();
-  NS_ENSURE_TRUE_VOID(sBtA2dpInterface);
+  if (NS_WARN_IF(!sBtA2dpInterface)) {
+    // If there's no HFP interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
+    }
+    return;
+  }
 
   BluetoothA2dpManager* a2dpManager = BluetoothA2dpManager::Get();
   sBtA2dpInterface->Init(a2dpManager, new InitA2dpResultHandler(aRes));
 }
 
 BluetoothA2dpManager::~BluetoothA2dpManager()
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -336,16 +376,21 @@ public:
     }
   }
 
   void Cleanup() MOZ_OVERRIDE
   {
     sBtA2dpInterface = nullptr;
     if (sBtAvrcpInterface) {
       sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes));
+    } else if (mRes) {
+      /* Not all backends support AVRCP. If it's not available
+       * we signal success from here.
+       */
+      mRes->Deinit();
     }
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class CleanupA2dpResultHandlerRunnable MOZ_FINAL : public nsRunnable
@@ -355,16 +400,21 @@ public:
   : mRes(aRes)
   { }
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
     sBtA2dpInterface = nullptr;
     if (sBtAvrcpInterface) {
       sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes));
+    } else if (mRes) {
+      /* Not all backends support AVRCP. If it's not available
+       * we signal success from here.
+       */
+      mRes->Deinit();
     }
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -324,26 +324,66 @@ public:
     mRes->RunInit();
     return NS_OK;
   }
 
 private:
   nsRefPtr<CleanupInitResultHandler> mRes;
 };
 
+class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                      nsresult aRv)
+  : mRes(aRes)
+  , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    mRes->OnError(mRv);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
 // static
 void
 BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
 {
   BluetoothInterface* btInf = BluetoothInterface::GetInstance();
-  NS_ENSURE_TRUE_VOID(btInf);
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no backend interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
+    }
+    return;
+  }
 
   BluetoothHandsfreeInterface *interface =
     btInf->GetBluetoothHandsfreeInterface();
-  NS_ENSURE_TRUE_VOID(interface);
+  if (NS_WARN_IF(!interface)) {
+    // If there's no HFP interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
+    }
+    return;
+  }
 
   nsRefPtr<CleanupInitResultHandler> res =
     new CleanupInitResultHandler(interface, aRes);
 
   if (sBluetoothHfpInterface) {
     // Cleanup an initialized HFP before initializing again.
     sBluetoothHfpInterface->Cleanup(res);
   } else {
--- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
@@ -192,40 +192,68 @@ public:
     BluetoothA2dpManager* a2dpManager = BluetoothA2dpManager::Get();
     sBtAvrcpInterface->Init(a2dpManager, new InitAvrcpResultHandler(mRes));
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
 
+class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                      nsresult aRv)
+  : mRes(aRes)
+  , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    mRes->OnError(mRv);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
 /*
  * This function will be only called when Bluetooth is turning on.
  * It is important to register a2dp callbacks before enable() gets called.
  * It is required to register a2dp callbacks before a2dp media task
  * starts up.
  */
 // static
 void
 BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes)
 {
   BluetoothInterface* btInf = BluetoothInterface::GetInstance();
-  if (!btInf) {
-    BT_LOGR("Error: Bluetooth interface not available");
-    if (aRes) {
-      aRes->OnError(NS_ERROR_FAILURE);
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no backend interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   sBtA2dpInterface = btInf->GetBluetoothA2dpInterface();
-  if (!sBtA2dpInterface) {
-    BT_LOGR("Error: Bluetooth A2DP interface not available");
-    if (aRes) {
-      aRes->OnError(NS_ERROR_FAILURE);
+  if (NS_WARN_IF(!sBtA2dpInterface)) {
+    // If there's no A2DP interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   BluetoothA2dpManager* a2dpManager = BluetoothA2dpManager::Get();
   sBtA2dpInterface->Init(a2dpManager, new InitA2dpResultHandler(aRes));
 }
 
@@ -352,16 +380,21 @@ public:
     }
   }
 
   void Cleanup() MOZ_OVERRIDE
   {
     sBtA2dpInterface = nullptr;
     if (sBtAvrcpInterface) {
       sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes));
+    } else if (mRes) {
+      /* Not all backends support AVRCP. If it's not available
+       * we signal success from here.
+       */
+      mRes->Deinit();
     }
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
 
 class CleanupA2dpResultHandlerRunnable MOZ_FINAL : public nsRunnable
@@ -371,16 +404,21 @@ public:
   : mRes(aRes)
   { }
 
   NS_IMETHOD Run() MOZ_OVERRIDE
   {
     sBtA2dpInterface = nullptr;
     if (sBtAvrcpInterface) {
       sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes));
+    } else if (mRes) {
+      /* Not all backends support AVRCP. If it's not available
+       * we signal success from here.
+       */
+      mRes->Deinit();
     }
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<BluetoothProfileResultHandler> mRes;
 };
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -315,35 +315,63 @@ public:
     mRes->RunInit();
     return NS_OK;
   }
 
 private:
   nsRefPtr<CleanupInitResultHandler> mRes;
 };
 
+class OnErrorProfileResultHandlerRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  OnErrorProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
+                                      nsresult aRv)
+  : mRes(aRes)
+  , mRv(aRv)
+  {
+    MOZ_ASSERT(mRes);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    mRes->OnError(mRv);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<BluetoothProfileResultHandler> mRes;
+  nsresult mRv;
+};
+
 // static
 void
 BluetoothHfpManager::InitHfpInterface(BluetoothProfileResultHandler* aRes)
 {
   BluetoothInterface* btInf = BluetoothInterface::GetInstance();
-  if (!btInf) {
-    BT_LOGR("Error: Bluetooth interface not available");
-    if (aRes) {
-      aRes->OnError(NS_ERROR_FAILURE);
+  if (NS_WARN_IF(!btInf)) {
+    // If there's no backend interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   BluetoothHandsfreeInterface *interface =
     btInf->GetBluetoothHandsfreeInterface();
-  if (!interface) {
-    BT_LOGR("Error: Bluetooth Handsfree interface not available");
-    if (aRes) {
-      aRes->OnError(NS_ERROR_FAILURE);
+  if (NS_WARN_IF(!interface)) {
+    // If there's no HFP interface, we dispatch a runnable
+    // that calls the profile result handler.
+    nsRefPtr<nsRunnable> r =
+      new OnErrorProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
+    if (NS_FAILED(NS_DispatchToMainThread(r))) {
+      BT_LOGR("Failed to dispatch HFP OnError runnable");
     }
     return;
   }
 
   nsRefPtr<CleanupInitResultHandler> res =
     new CleanupInitResultHandler(interface, aRes);
 
   if (sBluetoothHfpInterface) {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1214,17 +1214,17 @@ CanvasRenderingContext2D::EnsureTarget(R
     }
 
      if (layerManager) {
       if (mode == RenderingMode::OpenGLBackendMode && CheckSizeForSkiaGL(size)) {
         DemoteOldestContextIfNecessary();
 
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
-#if USE_SKIA
+#if USE_SKIA_GPU
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
             AddDemotableContext(this);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
             mode = RenderingMode::SoftwareBackendMode;
           }
deleted file mode 100644
--- a/dom/events/BeforeAfterKeyboardEvent.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- Mode: C++; 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/. */
-
-#include "mozilla/dom/BeforeAfterKeyboardEvent.h"
-#include "mozilla/TextEvents.h"
-#include "prtime.h"
-
-namespace mozilla {
-namespace dom {
-
-BeforeAfterKeyboardEvent::BeforeAfterKeyboardEvent(
-                                       EventTarget* aOwner,
-                                       nsPresContext* aPresContext,
-                                       InternalBeforeAfterKeyboardEvent* aEvent)
-  : KeyboardEvent(aOwner, aPresContext,
-                  aEvent ? aEvent :
-                           new InternalBeforeAfterKeyboardEvent(false, 0,
-                                                                nullptr))
-{
-  MOZ_ASSERT(mEvent->mClass == eBeforeAfterKeyboardEventClass,
-             "event type mismatch eBeforeAfterKeyboardEventClass");
-
-  if (!aEvent) {
-    mEventIsInternal = true;
-    mEvent->time = PR_Now();
-  }
-}
-
-// static
-already_AddRefed<BeforeAfterKeyboardEvent>
-BeforeAfterKeyboardEvent::Constructor(
-                            EventTarget* aOwner,
-                            const nsAString& aType,
-                            const BeforeAfterKeyboardEventInit& aParam)
-{
-  nsRefPtr<BeforeAfterKeyboardEvent> event =
-    new BeforeAfterKeyboardEvent(aOwner, nullptr, nullptr);
-  ErrorResult rv;
-  event->InitWithKeyboardEventInit(aOwner, aType, aParam, rv);
-  NS_WARN_IF(rv.Failed());
-
-  event->mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled =
-    aParam.mEmbeddedCancelled;
-
-  return event.forget();
-}
-
-// static
-already_AddRefed<BeforeAfterKeyboardEvent>
-BeforeAfterKeyboardEvent::Constructor(
-                            const GlobalObject& aGlobal,
-                            const nsAString& aType,
-                            const BeforeAfterKeyboardEventInit& aParam,
-                            ErrorResult& aRv)
-{
-  nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
-  return Constructor(owner, aType, aParam);
-}
-
-Nullable<bool>
-BeforeAfterKeyboardEvent::GetEmbeddedCancelled()
-{
-  nsAutoString type;
-  GetType(type);
-  if (type.EqualsLiteral("mozbrowserafterkeydown") ||
-      type.EqualsLiteral("mozbrowserafterkeyup")) {
-    return mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled;
-  }
-  return Nullable<bool>();
-}
-
-} // namespace dom
-} // namespace mozilla
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-nsresult
-NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
-                                  EventTarget* aOwner,
-                                  nsPresContext* aPresContext,
-                                  InternalBeforeAfterKeyboardEvent* aEvent)
-{
-  BeforeAfterKeyboardEvent* it =
-    new BeforeAfterKeyboardEvent(aOwner, aPresContext, aEvent);
-
-  NS_ADDREF(it);
-  *aInstancePtrResult = static_cast<Event*>(it);
-  return NS_OK;
-}
deleted file mode 100644
--- a/dom/events/BeforeAfterKeyboardEvent.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; 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/. */
-
-#ifndef mozilla_dom_BeforeAfterKeyboardEvent_h_
-#define mozilla_dom_BeforeAfterKeyboardEvent_h_
-
-#include "mozilla/dom/KeyboardEvent.h"
-#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
-
-namespace mozilla {
-namespace dom {
-
-class BeforeAfterKeyboardEvent : public KeyboardEvent
-{
-public:
-  BeforeAfterKeyboardEvent(EventTarget* aOwner,
-                           nsPresContext* aPresContext,
-                           InternalBeforeAfterKeyboardEvent* aEvent);
-
-  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
-  {
-    return BeforeAfterKeyboardEventBinding::Wrap(aCx, this);
-  }
-
-  static already_AddRefed<BeforeAfterKeyboardEvent>
-  Constructor(const GlobalObject& aGlobal,
-              const nsAString& aType,
-              const BeforeAfterKeyboardEventInit& aParam,
-              ErrorResult& aRv);
-
-  static already_AddRefed<BeforeAfterKeyboardEvent>
-  Constructor(EventTarget* aOwner, const nsAString& aType,
-              const BeforeAfterKeyboardEventInit& aEventInitDict);
-
-  // This function returns a boolean value when event typs is either
-  // "mozbrowserafterkeydown" or "mozbrowserafterkeyup".
-  Nullable<bool> GetEmbeddedCancelled();
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_BeforeAfterKeyboardEvent_h_
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -696,19 +696,16 @@ EventDispatcher::CreateEvent(EventTarget
       return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext,
                               aEvent->AsGUIEvent());
     case eScrollAreaEventClass:
       return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext,
                                       aEvent->AsScrollAreaEvent());
     case eKeyboardEventClass:
       return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext,
                                     aEvent->AsKeyboardEvent());
-    case eBeforeAfterKeyboardEventClass:
-      return NS_NewDOMBeforeAfterKeyboardEvent(aDOMEvent, aOwner, aPresContext,
-                                               aEvent->AsBeforeAfterKeyboardEvent());
     case eCompositionEventClass:
       return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext,
                                        aEvent->AsCompositionEvent());
     case eMouseEventClass:
       return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext,
                                  aEvent->AsMouseEvent());
     case eFocusEventClass:
       return NS_NewDOMFocusEvent(aDOMEvent, aOwner, aPresContext,
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -232,32 +232,16 @@ EVENT(keydown,
 EVENT(keypress,
       NS_KEY_PRESS,
       EventNameType_HTMLXUL,
       eKeyboardEventClass)
 EVENT(keyup,
       NS_KEY_UP,
       EventNameType_HTMLXUL,
       eKeyboardEventClass)
-NON_IDL_EVENT(mozbrowserbeforekeydown,
-              NS_KEY_BEFORE_DOWN,
-              EventNameType_None,
-              eBeforeAfterKeyboardEventClass)
-NON_IDL_EVENT(mozbrowserafterkeydown,
-              NS_KEY_AFTER_DOWN,
-              EventNameType_None,
-              eBeforeAfterKeyboardEventClass)
-NON_IDL_EVENT(mozbrowserbeforekeyup,
-              NS_KEY_BEFORE_UP,
-              EventNameType_None,
-              eBeforeAfterKeyboardEventClass)
-NON_IDL_EVENT(mozbrowserafterkeyup,
-              NS_KEY_AFTER_UP,
-              EventNameType_None,
-              eBeforeAfterKeyboardEventClass)
 EVENT(loadeddata,
       NS_LOADEDDATA,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(loadedmetadata,
       NS_LOADEDMETADATA,
       EventNameType_HTML,
       eBasicEventClass)
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -646,22 +646,18 @@ EventStateManager::PreHandleEvent(nsPres
       if (modifierMask &&
           (modifierMask == Prefs::ChromeAccessModifierMask() ||
            modifierMask == Prefs::ContentAccessModifierMask())) {
         HandleAccessKey(aPresContext, keyEvent, aStatus, nullptr,
                         eAccessKeyProcessingNormal, modifierMask);
       }
     }
     // then fall through...
-  case NS_KEY_BEFORE_DOWN:
   case NS_KEY_DOWN:
-  case NS_KEY_AFTER_DOWN:
-  case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
-  case NS_KEY_AFTER_UP:
     {
       nsIContent* content = GetFocusedContent();
       if (content)
         mCurrentTargetContent = content;
 
       // NOTE: Don't refer TextComposition::IsComposing() since DOM Level 3
       //       Events defines that KeyboardEvent.isComposing is true when it's
       //       dispatched after compositionstart and compositionend.
@@ -3162,19 +3158,17 @@ EventStateManager::PostHandleEvent(nsPre
       break;
     }
   case NS_DRAGDROP_EXIT:
      // make sure to fire the enter and exit_synth events after the
      // NS_DRAGDROP_EXIT event, otherwise we'll clean up too early
     GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
     break;
 
-  case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
-  case NS_KEY_AFTER_UP:
     break;
 
   case NS_KEY_PRESS:
     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
       //This is to prevent keyboard scrolling while alt modifier in use.
       if (!keyEvent->IsAlt()) {
         switch(keyEvent->keyCode) {
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -12,18 +12,20 @@ namespace mozilla {
 namespace dom {
 
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
   : UIEvent(aOwner, aPresContext,
             aEvent ? aEvent : new WidgetKeyboardEvent(false, 0, nullptr))
   , mInitializedByCtor(false)
-  , mInitializedWhichValue(0)
+  , mInitialzedWhichValue(0)
 {
+  NS_ASSERTION(mEvent->mClass == eKeyboardEventClass, "event type mismatch");
+
   if (aEvent) {
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
     mEvent->time = PR_Now();
     mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   }
@@ -250,22 +252,18 @@ uint32_t
 KeyboardEvent::CharCode()
 {
   // If this event is initialized with ctor, we shouldn't check event type.
   if (mInitializedByCtor) {
     return mEvent->AsKeyboardEvent()->charCode;
   }
 
   switch (mEvent->message) {
-  case NS_KEY_BEFORE_DOWN:
+  case NS_KEY_UP:
   case NS_KEY_DOWN:
-  case NS_KEY_AFTER_DOWN:
-  case NS_KEY_BEFORE_UP:
-  case NS_KEY_UP:
-  case NS_KEY_AFTER_UP:
     return 0;
   case NS_KEY_PRESS:
     return mEvent->AsKeyboardEvent()->charCode;
   }
   return 0;
 }
 
 NS_IMETHODIMP
@@ -279,37 +277,36 @@ KeyboardEvent::GetKeyCode(uint32_t* aKey
 uint32_t
 KeyboardEvent::KeyCode()
 {
   // If this event is initialized with ctor, we shouldn't check event type.
   if (mInitializedByCtor) {
     return mEvent->AsKeyboardEvent()->keyCode;
   }
 
-  if (mEvent->HasKeyEventMessage()) {
+  switch (mEvent->message) {
+  case NS_KEY_UP:
+  case NS_KEY_PRESS:
+  case NS_KEY_DOWN:
     return mEvent->AsKeyboardEvent()->keyCode;
   }
   return 0;
 }
 
 uint32_t
 KeyboardEvent::Which()
 {
   // If this event is initialized with ctor, which can have independent value.
   if (mInitializedByCtor) {
-    return mInitializedWhichValue;
+    return mInitialzedWhichValue;
   }
 
   switch (mEvent->message) {
-    case NS_KEY_BEFORE_DOWN:
+    case NS_KEY_UP:
     case NS_KEY_DOWN:
-    case NS_KEY_AFTER_DOWN:
-    case NS_KEY_BEFORE_UP:
-    case NS_KEY_UP:
-    case NS_KEY_AFTER_UP:
       return KeyCode();
     case NS_KEY_PRESS:
       //Special case for 4xp bug 62878.  Try to make value of which
       //more closely mirror the values that 4.x gave for RETURN and BACKSPACE
       {
         uint32_t keyCode = mEvent->AsKeyboardEvent()->keyCode;
         if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
           return keyCode;
@@ -341,45 +338,36 @@ already_AddRefed<KeyboardEvent>
 KeyboardEvent::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aType,
                            const KeyboardEventInit& aParam,
                            ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
   nsRefPtr<KeyboardEvent> newEvent =
     new KeyboardEvent(target, nullptr, nullptr);
-  newEvent->InitWithKeyboardEventInit(target, aType, aParam, aRv);
-
-  return newEvent.forget();
-}
+  bool trusted = newEvent->Init(target);
+  aRv = newEvent->InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
+                               aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
+                               aParam.mShiftKey, aParam.mMetaKey,
+                               aParam.mKeyCode, aParam.mCharCode);
+  newEvent->SetTrusted(trusted);
+  newEvent->mDetail = aParam.mDetail;
+  newEvent->mInitializedByCtor = true;
+  newEvent->mInitialzedWhichValue = aParam.mWhich;
 
-void
-KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
-                                         const nsAString& aType,
-                                         const KeyboardEventInit& aParam,
-                                         ErrorResult& aRv)
-{
-  bool trusted = Init(aOwner);
-  aRv = InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
-                     aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
-                     aParam.mShiftKey, aParam.mMetaKey,
-                     aParam.mKeyCode, aParam.mCharCode);
-  SetTrusted(trusted);
-  mDetail = aParam.mDetail;
-  mInitializedByCtor = true;
-  mInitializedWhichValue = aParam.mWhich;
-
-  WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
+  WidgetKeyboardEvent* internalEvent = newEvent->mEvent->AsKeyboardEvent();
   internalEvent->location = aParam.mLocation;
   internalEvent->mIsRepeat = aParam.mRepeat;
   internalEvent->mIsComposing = aParam.mIsComposing;
   internalEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
+  internalEvent->mKeyValue = aParam.mKey;
   internalEvent->mCodeNameIndex = CODE_NAME_INDEX_USE_STRING;
-  internalEvent->mKeyValue = aParam.mKey;
   internalEvent->mCodeValue = aParam.mCode;
+
+  return newEvent.forget();
 }
 
 NS_IMETHODIMP
 KeyboardEvent::InitKeyEvent(const nsAString& aType,
                             bool aCanBubble,
                             bool aCancelable,
                             nsIDOMWindow* aView,
                             bool aCtrlKey,
--- a/dom/events/KeyboardEvent.h
+++ b/dom/events/KeyboardEvent.h
@@ -69,27 +69,21 @@ public:
     aRv = InitKeyEvent(aType, aCanBubble, aCancelable, aView,
                        aCtrlKey, aAltKey, aShiftKey,aMetaKey,
                        aKeyCode, aCharCode);
   }
 
 protected:
   ~KeyboardEvent() {}
 
-  void InitWithKeyboardEventInit(EventTarget* aOwner,
-                                 const nsAString& aType,
-                                 const KeyboardEventInit& aParam,
-                                 ErrorResult& aRv);
-
 private:
   // True, if the instance is created with Constructor().
   bool mInitializedByCtor;
-
   // If the instance is created with Constructor(), which may have independent
   // value.  mInitializedWhichValue stores it.  I.e., this is invalid when
   // mInitializedByCtor is false.
-  uint32_t mInitializedWhichValue;
+  uint32_t mInitialzedWhichValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyboardEvent_h_
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -27,17 +27,16 @@ EXPORTS.mozilla += [
     'KeyNameList.h',
     'PhysicalKeyCodeNameList.h',
     'TextComposition.h',
     'VirtualKeyCodeList.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'AnimationEvent.h',
-    'BeforeAfterKeyboardEvent.h',
     'BeforeUnloadEvent.h',
     'ClipboardEvent.h',
     'CommandEvent.h',
     'CompositionEvent.h',
     'CustomEvent.h',
     'DataContainerEvent.h',
     'DataTransfer.h',
     'DeviceMotionEvent.h',
@@ -67,17 +66,16 @@ EXPORTS.mozilla.dom += [
 ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     EXPORTS.mozilla.dom += ['SpeechRecognitionError.h']
 
 UNIFIED_SOURCES += [
     'AnimationEvent.cpp',
     'AsyncEventDispatcher.cpp',
-    'BeforeAfterKeyboardEvent.cpp',
     'BeforeUnloadEvent.cpp',
     'ClipboardEvent.cpp',
     'CommandEvent.cpp',
     'CompositionEvent.cpp',
     'ContentEventHandler.cpp',
     'DataContainerEvent.cpp',
     'DataTransfer.cpp',
     'DeviceMotionEvent.cpp',
deleted file mode 100644
--- a/dom/events/test/bug989198_embedded.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Embedded iframe</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body onload="getFocus();">
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <input id="input" style="display: block;">
-  <pre id="test">
-  <script type="application/javascript">
-    function getFocus() {
-      input = document.getElementById("input");
-      input.focus();
-    }
-  </script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/events/test/bug989198_helper.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Constants and helper functions for testing BeforeAfterKeyboardEvent.
- */
-
-const kUnknownEvent       = 0x000;
-const kKeyDownEvent       = 0x001;
-const kKeyUpEvent         = 0x002;
-const kBeforeEvent        = 0x010;
-const kAfterEvent         = 0x020;
-const kParent             = 0x100;
-const kChild              = 0x200;
-
-var gCurrentTest;
-
-function frameScript()
-{
-  function handler(e) {
-    var results = sendSyncMessage("forwardevent", { type: e.type });
-    if (results[0]) {
-      e.preventDefault();
-    }
-  }
-  addEventListener('keydown', handler);
-  addEventListener('keyup', handler);
-  addEventListener('mozbrowserbeforekeydown', handler);
-  addEventListener('mozbrowserbeforekeyup', handler);
-  addEventListener('mozbrowserafterkeydown', handler);
-  addEventListener('mozbrowserafterkeyup', handler);
-}
-
-function prepareTest(useRemote)
-{
-  setupHandlers(window, embedderHandler);
-
-  var iframe = document.createElement("iframe");
-  iframe.id = "embedded";
-  iframe.src = "bug989198_embedded.html";
-  iframe.setAttribute("remote", useRemote ? "true" : "false");
-  SpecialPowers.wrap(iframe).mozbrowser = true;
-
-  iframe.addEventListener("mozbrowserloadend", function onloadend() {
-    iframe.removeEventListener("mozbrowserloadend", onloadend);
-    iframe.focus();
-    var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
-    mm.addMessageListener("forwardevent", function(msg) {
-      return embeddedHandler(msg.json);
-    });
-    mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
-    runTests();
-    return;
-  });
-
-  document.body.appendChild(iframe);
-}
-
-function cleanupTest()
-{
-  teardownHandlers(window, embedderHandler);
-  runTests();
-}
-
-function setupHandlers(element, handler)
-{
-  element.addEventListener('keydown', handler);
-  element.addEventListener('keyup', handler);
-  element.addEventListener('mozbrowserbeforekeydown', handler);
-  element.addEventListener('mozbrowserbeforekeyup', handler);
-  element.addEventListener('mozbrowserafterkeydown', handler);
-  element.addEventListener('mozbrowserafterkeyup', handler);
-}
-
-function teardownHandlers(element, handler)
-{
-  element.removeEventListener('keydown', handler);
-  element.removeEventListener('keyup', handler);
-  element.removeEventListener('mozbrowserbeforekeydown', handler);
-  element.removeEventListener('mozbrowserbeforekeyup', handler);
-  element.removeEventListener('mozbrowserafterkeydown', handler);
-  element.removeEventListener('mozbrowserafterkeyup', handler);
-}
-
-function convertNameToCode(name)
-{
-  switch (name) {
-    case "mozbrowserbeforekeydown":
-      return kBeforeEvent | kKeyDownEvent;
-    case "mozbrowserafterkeydown":
-      return kAfterEvent | kKeyDownEvent;
-    case "mozbrowserbeforekeyup":
-      return kBeforeEvent | kKeyUpEvent;
-    case "mozbrowserafterkeyup":
-      return kAfterEvent | kKeyUpEvent;
-    case "keydown":
-      return kKeyDownEvent;
-    case "keyup":
-      return kKeyUpEvent;
-    default:
-      return kUnknownEvent;
-  }
-}
-
-function classifyEvents(test)
-{
-  // Categorize resultEvents into KEYDOWN group and KEYUP group.
-  for (var i = 0; i < gCurrentTest.resultEvents.length ; i++) {
-    var code = test.resultEvents[i];
-    if ((code & 0xF) == 0x1) { // KEYDOWN
-      test.classifiedEvents[0].push(code);
-    } else if ((code & 0xF) == 0x2) { // KEYUP
-      test.classifiedEvents[1].push(code);
-    } else {
-      ok(false, "Invalid code for events");
-    }
-  }
-}
-
-function verifyResults(test)
-{
-  for (var i = 0; i < gCurrentTest.expectedEvents.length; i++) {
-    is(test.classifiedEvents[i].length,
-       test.expectedEvents[i].length,
-       test.description + ": Wrong number of events");
-
-    for (var j = 0; j < gCurrentTest.classifiedEvents[i].length; j++) {
-      var item = test.classifiedEvents[i][j];
-      is(item, test.expectedEvents[i][j],
-         test.description + ": Wrong order of events");
-     }
-  }
-}
-
-function embeddedHandler(e)
-{
-  return handler(e, kChild);
-}
-
-function embedderHandler(e)
-{
-  // Verify value of attribute embeddedCancelled
-  handler(e, kParent, function checkEmbeddedCancelled(code){
-    switch (code) {
-      case kBeforeEvent | kKeyDownEvent:
-      case kBeforeEvent | kKeyUpEvent:
-        is(e.embeddedCancelled, null,
-           gCurrentTest.description + ': embeddedCancelled should be null');
-        break;
-      case kAfterEvent | kKeyDownEvent:
-        if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyDownEvent) {
-          is(e.embeddedCancelled, true,
-             gCurrentTest.description + ': embeddedCancelled should be true');
-        } else {
-          is(e.embeddedCancelled, false,
-             gCurrentTest.description + ': embeddedCancelled should be false');
-        }
-        break;
-      case kAfterEvent | kKeyUpEvent:
-        if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyUpEvent) {
-          is(e.embeddedCancelled, true,
-             gCurrentTest.description + ': embeddedCancelled should be true');
-        } else {
-          is(e.embeddedCancelled, false,
-             gCurrentTest.description + ': embeddedCancelled should be false');
-        }
-        break;
-      default:
-        break;
-    }
-  });
-}
-
-function handler(e, highBit, callback)
-{
-  var code = convertNameToCode(e.type);
-  var newCode = highBit | code;
-  gCurrentTest.resultEvents.push(newCode);
-
-  if (callback) {
-    callback(code);
-  }
-
-  // Return and let frameScript to handle
-  if (highBit == kChild) {
-    return newCode == gCurrentTest.doPreventDefaultAt;
-  }
-
-  if (newCode == gCurrentTest.doPreventDefaultAt) {
-    e.preventDefault();
-  }
-}
-
-function runTests()
-{
-  if (!tests.length) {
-    SimpleTest.finish();
-    return;
-  }
-
-  var test = tests.shift();
-  test();
-}
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -162,17 +162,8 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_focus_disabled.html]
 skip-if = buildapp == 'mulet'
 [test_messageEvent.html]
 [test_moz_mouse_pixel_scroll_event.html]
 skip-if = buildapp == 'mulet'
 [test_onerror_handler_args.html]
 [test_wheel_default_action.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s
-[test_dom_before_after_keyboard_event.html]
-support-files =
-  bug989198_embedded.html
-  bug989198_helper.js
-[test_dom_before_after_keyboard_event_remote.html]
-support-files =
-  bug989198_embedded.html
-  bug989198_helper.js
-skip-if = buildapp == 'b2g' || e10s
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -29,20 +29,16 @@ const kEventConstructors = {
                                                        },
                                              },
   AnimationEvent:                            { create: function (aName, aProps) {
                                                          return new AnimationEvent(aName, aProps);
                                                        },
                                              },
   AudioProcessingEvent:                      { create: null, // Cannot create untrusted event from JS.
                                              },
-  BeforeAfterKeyboardEvent:                  { create: function (aName, aProps) {
-                                                         return new BeforeAfterKeyboardEvent(aName, aProps);
-                                                       },
-                                             },
   BeforeUnloadEvent:                         { create: function (aName, aProps) {
                                                          var e = document.createEvent("beforeunloadevent");
                                                          e.initEvent(aName, aProps.bubbles, aProps.cancelable);
                                                          return e;
                                                        },
                                              },
   BlobEvent:                                 { create: function (aName, aProps) {
                                                          return new BlobEvent(aName, aProps);
deleted file mode 100644
--- a/dom/events/test/test_dom_before_after_keyboard_event.html
+++ /dev/null
@@ -1,117 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Bug 989198</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
-  <script type="text/javascript" src="bug989198_helper.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body onload="runTests();">
-<a target="_blank"
-   href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
-<p id="display"></p>
-<pre id="test">
-<script type="application/javascript">
-
-SimpleTest.waitForExplicitFinish();
-
-function testEventOrderAndAttr()
-{
-  const mainDesc = 'Testing the order of the events';
-  const kTests = [
-    {
-      description: mainDesc,
-      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                          kChild | kKeyDownEvent,
-                          kParent | kAfterEvent | kKeyDownEvent ],
-                        [ kParent | kBeforeEvent | kKeyUpEvent,
-                          kChild | kKeyUpEvent,
-                          kParent | kAfterEvent | kKeyUpEvent ] ],
-      resultEvents: [],
-      classifiedEvents: [ [], [] ],
-      doPreventDefaultAt: kUnknownEvent
-    },
-    {
-      description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
-      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                          kParent | kAfterEvent | kKeyDownEvent ],
-                        [ kParent | kBeforeEvent | kKeyUpEvent,
-                          kChild | kKeyUpEvent,
-                          kParent | kAfterEvent | kKeyUpEvent ] ],
-      resultEvents: [],
-      classifiedEvents: [ [], [] ],
-      doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
-    },
-    {
-      description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
-      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                          kChild | kKeyDownEvent,
-                          kParent | kAfterEvent | kKeyDownEvent ],
-                        [ kParent | kBeforeEvent | kKeyUpEvent,
-                          kParent | kAfterEvent | kKeyUpEvent ] ],
-      resultEvents: [],
-      classifiedEvents: [ [], [] ],
-      doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
-    },
-    {
-      description: mainDesc + ', calling preventDefault() at "keydown" event',
-      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                          kChild | kKeyDownEvent,
-                          kParent | kAfterEvent | kKeyDownEvent ],
-                        [ kParent | kBeforeEvent | kKeyUpEvent,
-                          kChild | kKeyUpEvent,
-                          kParent | kAfterEvent | kKeyUpEvent ] ],
-      resultEvents: [],
-      classifiedEvents: [ [], [] ],
-      doPreventDefaultAt: kChild | kKeyDownEvent
-    },
-    {
-      description: mainDesc + ', calling preventDefault() at "keyup" event',
-      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                          kChild | kKeyDownEvent,
-                          kParent | kAfterEvent | kKeyDownEvent ],
-                        [ kParent | kBeforeEvent | kKeyUpEvent,
-                          kChild | kKeyUpEvent,
-                          kParent | kAfterEvent | kKeyUpEvent ] ],
-      resultEvents: [],
-      classifiedEvents: [ [], [] ],
-      doPreventDefaultAt: kChild | kKeyUpEvent
-    }
-  ];
-
-  for (var k = 0; k < kTests.length; k++ ) {
-    gCurrentTest = kTests[k];
-    synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
-    classifyEvents(kTests[k]);
-    verifyResults(kTests[k]);
-  }
-
-  runTests();
-}
-
-var tests = [
-  function addPermissions() {
-    SpecialPowers.pushPermissions(
-      [{ type: "before-after-keyboard-event", allow: true, context: document },
-       { type: "browser", allow: true, context: document }],
-      runTests);
-  },
-  function addPreferences() {
-    SpecialPowers.pushPrefEnv(
-      { "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
-                ["dom.mozBrowserFramesEnabled", true],
-                ["dom.ipc.tabs.disabled", false]] },
-      runTests);
-  },
-
-  // Tests for in-process iframe, i.e. <iframe mozbrowser>.
-  ()=>prepareTest(false),
-  testEventOrderAndAttr,
-  cleanupTest,
-];
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/events/test/test_dom_before_after_keyboard_event_remote.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Bug 989198</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
-  <script type="text/javascript" src="bug989198_helper.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body onload="runTests();">
-<a target="_blank"
-   href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
-<p id="display"></p>
-<div id="content">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-SimpleTest.waitForExplicitFinish();
-
-const mainDesc = 'Testing the order of the events (OOP)';
-var testsForEventOrder = [
-  {
-    description: mainDesc,
-    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                        kParent | kKeyDownEvent,
-                        kChild | kKeyDownEvent,
-                        kParent | kAfterEvent | kKeyDownEvent ],
-                      [ kParent | kBeforeEvent | kKeyUpEvent,
-                        kParent | kKeyUpEvent,
-                        kChild | kKeyUpEvent,
-                        kParent | kAfterEvent | kKeyUpEvent ] ],
-    resultEvents: [],
-    classifiedEvents: [ [], [] ],
-    doPreventDefaultAt: kUnknownEvent
-  },
-  {
-    description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
-    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                        kParent | kAfterEvent | kKeyDownEvent ],
-                      [ kParent | kBeforeEvent | kKeyUpEvent,
-                        kParent | kKeyUpEvent,
-                        kChild | kKeyUpEvent,
-                        kParent | kAfterEvent | kKeyUpEvent ] ],
-    resultEvents: [],
-    classifiedEvents: [ [], [] ],
-    doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
-  },
-  {
-    description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
-    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                        kParent | kKeyDownEvent,
-                        kChild | kKeyDownEvent,
-                        kParent | kAfterEvent | kKeyDownEvent ],
-                      [ kParent | kBeforeEvent | kKeyUpEvent,
-                        kParent | kAfterEvent | kKeyUpEvent ] ],
-    resultEvents: [],
-    classifiedEvents: [ [], [] ],
-    doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
-  },
-  {
-    description: mainDesc + ', calling preventDefault() at "keydown" event',
-    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                        kParent | kKeyDownEvent,
-                        kChild | kKeyDownEvent,
-                        kParent | kAfterEvent | kKeyDownEvent ],
-                      [ kParent | kBeforeEvent | kKeyUpEvent,
-                        kParent | kKeyUpEvent,
-                        kChild | kKeyUpEvent,
-                        kParent | kAfterEvent | kKeyUpEvent ] ],
-    resultEvents: [],
-    classifiedEvents: [ [], [] ],
-    doPreventDefaultAt: kChild | kKeyDownEvent
-  },
-  {
-    description: mainDesc + ', calling preventDefault() at "keyup" event',
-    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
-                        kParent | kKeyDownEvent,
-                        kChild | kKeyDownEvent,
-                        kParent | kAfterEvent | kKeyDownEvent ],
-                      [ kParent | kBeforeEvent | kKeyUpEvent,
-                        kParent | kKeyUpEvent,
-                        kChild | kKeyUpEvent,
-                        kParent | kAfterEvent | kKeyUpEvent ] ],
-    resultEvents: [],
-    classifiedEvents: [ [], [] ],
-    doPreventDefaultAt: kChild | kKeyUpEvent
-  }
-];
-
-function testEventOrder()
-{
-  if (!testsForEventOrder.length) {
-    runTests();
-    return;
-  }
-  gCurrentTest = testsForEventOrder.shift();
-
-  synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
-  // It take some time to propagate events to a remote iframe.
-
-  waitAndVerifyResult(0);
-}
-
-function waitAndVerifyResult(count) {
-  expectedEventLength = gCurrentTest.expectedEvents[0].length +
-                        gCurrentTest.expectedEvents[1].length;
-  if (gCurrentTest.resultEvents.length >= expectedEventLength || count > 10) {
-    classifyEvents(gCurrentTest);
-    verifyResults(gCurrentTest);
-    testEventOrder();
-  }
-  else {
-    setTimeout(()=>waitAndVerifyResult(count + 1), 100);
-  }
-}
-
-var tests = [
-  function addPermissions() {
-    SpecialPowers.pushPermissions(
-      [{ type: "before-after-keyboard-event", allow: true, context: document },
-       { type: "browser", allow: true, context: document }],
-      runTests);
-  },
-  function addPreferences() {
-    SpecialPowers.pushPrefEnv(
-      { "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
-                ["dom.mozBrowserFramesEnabled", true],
-                ["dom.ipc.tabs.disabled", false]] },
-      runTests);
-  },
-
-  // Tests for out-of-process iframe, i.el. <iframe mozbrowser remote>
-  ()=>prepareTest(true),
-  testEventOrder,
-  cleanupTest
-];
-
-</script>
-</body>
-</html>
--- a/dom/interfaces/events/nsIDOMEvent.idl
+++ b/dom/interfaces/events/nsIDOMEvent.idl
@@ -267,23 +267,16 @@ NS_NewDOMInputEvent(nsIDOMEvent** aInsta
                     mozilla::dom::EventTarget* aOwner,
                     nsPresContext* aPresContext,
                     mozilla::InternalEditorInputEvent* aEvent);
 nsresult
 NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
                        mozilla::dom::EventTarget* aOwner,
                        nsPresContext* aPresContext,
                        mozilla::WidgetKeyboardEvent* aEvent);
-
-nsresult
-NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
-                                  mozilla::dom::EventTarget* aOwner,
-                                  nsPresContext* aPresContext,
-                                  mozilla::InternalBeforeAfterKeyboardEvent* aEvent);
-
 nsresult
 NS_NewDOMCompositionEvent(nsIDOMEvent** aInstancePtrResult,
                           mozilla::dom::EventTarget* aOwner,
                           nsPresContext* aPresContext,
                           mozilla::WidgetCompositionEvent* aEvent);
 nsresult
 NS_NewDOMMutationEvent(nsIDOMEvent** aResult,
                        mozilla::dom::EventTarget* aOwner,
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -21,27 +21,29 @@
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBlobStreamChild.h"
 #include "mozilla/dom/PBlobStreamParent.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
+#include "MultipartFileImpl.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIRemoteBlob.h"
 #include "nsISeekableStream.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
+#include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #ifdef DEBUG
 #include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
 #endif
 
 #define DISABLE_ASSERTS_FOR_FUZZING 0
@@ -54,16 +56,17 @@
 
 #define PRIVATE_REMOTE_INPUT_STREAM_IID \
   {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::ipc;
+using namespace mozilla::dom::indexedDB;
 
 namespace {
 
 const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1";
 
 StaticRefPtr<nsIUUIDGenerator> gUUIDGenerator;
 
 GeckoProcessType gProcessType = GeckoProcessType_Invalid;
@@ -156,16 +159,34 @@ ActorManagerProcessID(PBackgroundParent*
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
   return BackgroundParent::GetRawContentParentForComparison(aManager);
 }
 
 bool
+ActorManagerIsSameProcess(nsIContentParent* aManager)
+{
+  AssertCorrectThreadForManager(aManager);
+  MOZ_ASSERT(aManager);
+
+  return false;
+}
+
+bool
+ActorManagerIsSameProcess(PBackgroundParent* aManager)
+{
+  AssertCorrectThreadForManager(aManager);
+  MOZ_ASSERT(aManager);
+
+  return !BackgroundParent::IsOtherProcessActor(aManager);
+}
+
+bool
 EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget)
 {
   if (!aEventTarget) {
     return NS_IsMainThread();
   }
 
   bool current;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEventTarget->IsOnCurrentThread(&current)));
@@ -323,17 +344,18 @@ class RemoteInputStream MOZ_FINAL
   nsCOMPtr<nsIInputStream> mStream;
   nsRefPtr<FileImpl> mBlobImpl;
   nsCOMPtr<nsIEventTarget> mEventTarget;
   nsISeekableStream* mWeakSeekableStream;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  explicit RemoteInputStream(FileImpl* aBlobImpl)
+  explicit
+  RemoteInputStream(FileImpl* aBlobImpl)
     : mMonitor("RemoteInputStream.mMonitor")
     , mBlobImpl(aBlobImpl)
     , mWeakSeekableStream(nullptr)
   {
     MOZ_ASSERT(IsOnOwningThread());
     MOZ_ASSERT(aBlobImpl);
 
     if (!NS_IsMainThread()) {
@@ -697,21 +719,482 @@ private:
   // This method is only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
   {
     // Nothing needs to be done here.
   }
 };
 
+class EmptyBlobImpl MOZ_FINAL
+  : public FileImplBase
+{
+public:
+  EmptyBlobImpl(const nsAString& aContentType)
+    : FileImplBase(aContentType, 0)
+  {
+    mImmutable = true;
+  }
+
+  EmptyBlobImpl(const nsAString& aName,
+                const nsAString& aContentType,
+                uint64_t aLastModifiedDate)
+    : FileImplBase(aName, aContentType, 0, aLastModifiedDate)
+  {
+    mImmutable = true;
+  }
+
+private:
+  virtual already_AddRefed<FileImpl>
+  CreateSlice(uint64_t /* aStart */,
+              uint64_t aLength,
+              const nsAString& aContentType,
+              ErrorResult& /* aRv */) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(!aLength);
+
+    nsRefPtr<FileImpl> sliceImpl = new EmptyBlobImpl(aContentType);
+
+    DebugOnly<bool> isMutable;
+    MOZ_ASSERT(NS_SUCCEEDED(sliceImpl->GetMutable(&isMutable)));
+    MOZ_ASSERT(!isMutable);
+
+    return sliceImpl.forget();
+  }
+
+  virtual nsresult
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
+  {
+    NS_ENSURE_ARG_POINTER(aStream);
+
+    nsString emptyString;
+    nsresult rv = NS_NewStringInputStream(aStream, emptyString);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+};
+
+// This is only needed for IndexedDB FileImplSnapshot.
+class SameProcessInputStreamBlobImpl MOZ_FINAL
+  : public FileImplBase
+{
+  nsCOMPtr<nsIInputStream> mInputStream;
+
+public:
+  SameProcessInputStreamBlobImpl(const nsAString& aContentType,
+                                 uint64_t aLength,
+                                 nsIInputStream* aInputStream)
+    : FileImplBase(aContentType, aLength)
+    , mInputStream(aInputStream)
+  {
+    MOZ_ASSERT(aLength != UINT64_MAX);
+    MOZ_ASSERT(aInputStream);
+
+    mImmutable = true;
+  }
+
+  SameProcessInputStreamBlobImpl(const nsAString& aName,
+                                 const nsAString& aContentType,
+                                 uint64_t aLength,
+                                 uint64_t aLastModifiedDate,
+                                 nsIInputStream* aInputStream)
+    : FileImplBase(aName, aContentType, aLength, aLastModifiedDate)
+    , mInputStream(aInputStream)
+  {
+    MOZ_ASSERT(aLength != UINT64_MAX);
+    MOZ_ASSERT(aLastModifiedDate != UINT64_MAX);
+    MOZ_ASSERT(aInputStream);
+
+    mImmutable = true;
+  }
+
+private:
+  virtual already_AddRefed<FileImpl>
+  CreateSlice(uint64_t /* aStart */,
+              uint64_t /* aLength */,
+              const nsAString& /* aContentType */,
+              ErrorResult& /* aRv */) MOZ_OVERRIDE
+  {
+    MOZ_CRASH("Not implemented");
+  }
+
+  virtual nsresult
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
+  {
+    NS_ENSURE_ARG_POINTER(aStream);
+
+    nsCOMPtr<nsIInputStream> inputStream = mInputStream;
+    inputStream.forget(aStream);
+    return NS_OK;
+  }
+};
+
+struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL
+{
+  nsString mContentType;
+  nsString mName;
+  uint64_t mLength;
+  uint64_t mLastModifiedDate;
+  bool mHasRecursed;
+  bool mIsSameProcessActor;
+
+  CreateBlobImplMetadata()
+    : mLength(0)
+    , mLastModifiedDate(0)
+    , mHasRecursed(false)
+    , mIsSameProcessActor(false)
+  {
+    MOZ_COUNT_CTOR(CreateBlobImplMetadata);
+
+    mName.SetIsVoid(true);
+  }
+
+  ~CreateBlobImplMetadata()
+  {
+    MOZ_COUNT_DTOR(CreateBlobImplMetadata);
+  }
+
+  bool
+  IsFile() const
+  {
+    return !mName.IsVoid();
+  }
+};
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const StringInputStreamParams& aParams,
+                         const CreateBlobImplMetadata& aMetadata)
+{
+  static_assert(sizeof(aParams.data().Length()) <= sizeof(size_t),
+                "String length won't fit in size_t!");
+  static_assert(sizeof(size_t) <= sizeof(uint64_t),
+                "size_t won't fit in uint64_t!");
+
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+  nsRefPtr<FileImpl> blobImpl;
+
+  if (auto length = static_cast<size_t>(aParams.data().Length())) {
+    if (!aMetadata.mHasRecursed &&
+        NS_WARN_IF(aMetadata.mLength != uint64_t(length))) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    void* buffer = moz_malloc(aParams.data().Length());
+    if (NS_WARN_IF(!buffer)) {
+      return nullptr;
+    }
+
+    memcpy(buffer, aParams.data().get(), length);
+
+    if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+      blobImpl =
+        new FileImplMemory(buffer,
+                           uint64_t(length),
+                           aMetadata.mName,
+                           aMetadata.mContentType,
+                           aMetadata.mLastModifiedDate);
+    } else {
+      blobImpl =
+        new FileImplMemory(buffer, uint64_t(length), aMetadata.mContentType);
+    }
+  } else if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+    blobImpl =
+      new EmptyBlobImpl(aMetadata.mName,
+                        aMetadata.mContentType,
+                        aMetadata.mLastModifiedDate);
+  } else {
+    blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
+
+  return blobImpl.forget();
+}
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const RemoteInputStreamParams& aParams,
+                         const CreateBlobImplMetadata& aMetadata)
+{
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  MOZ_ASSERT(aMetadata.mHasRecursed);
+
+  nsRefPtr<FileImpl> blobImpl = BlobParent::GetBlobImplForID(aParams.id());
+  if (NS_WARN_IF(!blobImpl)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+  return blobImpl.forget();
+}
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const SameProcessInputStreamParams& aParams,
+                         const CreateBlobImplMetadata& aMetadata)
+{
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  MOZ_ASSERT(aMetadata.mIsSameProcessActor);
+  MOZ_ASSERT(aParams.addRefedInputStream());
+
+  nsCOMPtr<nsIInputStream> inputStream =
+    dont_AddRef(
+      reinterpret_cast<nsIInputStream*>(aParams.addRefedInputStream()));
+
+  nsRefPtr<FileImpl> blobImpl;
+  if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+    blobImpl =
+      new SameProcessInputStreamBlobImpl(aMetadata.mName,
+                                         aMetadata.mContentType,
+                                         aMetadata.mLength,
+                                         aMetadata.mLastModifiedDate,
+                                         inputStream);
+  } else {
+    blobImpl =
+      new SameProcessInputStreamBlobImpl(aMetadata.mContentType,
+                                         aMetadata.mLength,
+                                         inputStream);
+  }
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+  return blobImpl.forget();
+}
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
+                         CreateBlobImplMetadata& aMetadata);
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromInputStreamParams(const InputStreamParams& aParams,
+                                    CreateBlobImplMetadata& aMetadata)
+{
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+  nsRefPtr<FileImpl> blobImpl;
+
+  switch (aParams.type()) {
+    case InputStreamParams::TStringInputStreamParams: {
+      const StringInputStreamParams& params =
+        aParams.get_StringInputStreamParams();
+      blobImpl = CreateBlobImplFromParams(params, aMetadata);
+      break;
+    }
+
+    case InputStreamParams::TFileInputStreamParams: {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    case InputStreamParams::TPartialFileInputStreamParams: {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    case InputStreamParams::TBufferedInputStreamParams: {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    case InputStreamParams::TMIMEInputStreamParams: {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    case InputStreamParams::TMultiplexInputStreamParams: {
+      const MultiplexInputStreamParams& params =
+        aParams.get_MultiplexInputStreamParams();
+      blobImpl = CreateBlobImplFromParams(params, aMetadata);
+      break;
+    }
+
+    case InputStreamParams::TRemoteInputStreamParams: {
+      if (NS_WARN_IF(!aMetadata.mHasRecursed)) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+
+      const RemoteInputStreamParams& params =
+        aParams.get_RemoteInputStreamParams();
+      blobImpl = CreateBlobImplFromParams(params, aMetadata);
+      break;
+    }
+
+    case InputStreamParams::TSameProcessInputStreamParams: {
+      if (NS_WARN_IF(!aMetadata.mIsSameProcessActor)) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+
+      const SameProcessInputStreamParams& params =
+        aParams.get_SameProcessInputStreamParams();
+      blobImpl = CreateBlobImplFromParams(params, aMetadata);
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Unknown params!");
+  }
+
+  return blobImpl.forget();
+}
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams,
+                         CreateBlobImplMetadata& aMetadata)
+{
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+  if (NS_WARN_IF(aParams.currentStream())) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(aParams.status()))) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(aParams.startedReadingCurrent())) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  const nsTArray<InputStreamParams>& streams = aParams.streams();
+
+  // Special case for a multipart blob with only one part.
+  if (streams.Length() == 1) {
+    const InputStreamParams& params = streams[0];
+
+    nsRefPtr<FileImpl> blobImpl =
+      CreateBlobImplFromInputStreamParams(params, aMetadata);
+    if (NS_WARN_IF(!blobImpl)) {
+      return nullptr;
+    }
+
+    DebugOnly<bool> isMutable;
+    MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+    MOZ_ASSERT(!isMutable);
+
+    return blobImpl.forget();
+  }
+
+  FallibleTArray<nsRefPtr<FileImpl>> fallibleBlobImpls;
+  if (NS_WARN_IF(!fallibleBlobImpls.SetLength(streams.Length()))) {
+    return nullptr;
+  }
+
+  nsTArray<nsRefPtr<FileImpl>> blobImpls;
+  fallibleBlobImpls.SwapElements(blobImpls);
+
+  const bool hasRecursed = aMetadata.mHasRecursed;
+  aMetadata.mHasRecursed = true;
+
+  for (uint32_t count = streams.Length(), index = 0; index < count; index++) {
+    const InputStreamParams& params = streams[index];
+    nsRefPtr<FileImpl>& blobImpl = blobImpls[index];
+
+    blobImpl = CreateBlobImplFromParams(params, aMetadata);
+    if (NS_WARN_IF(!blobImpl)) {
+      return nullptr;
+    }
+
+    DebugOnly<bool> isMutable;
+    MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+    MOZ_ASSERT(!isMutable);
+  }
+
+  nsRefPtr<FileImpl> blobImpl;
+  if (!hasRecursed && aMetadata.IsFile()) {
+    blobImpl =
+      new MultipartFileImpl(blobImpls, aMetadata.mName, aMetadata.mContentType);
+  } else {
+    blobImpl =
+      new MultipartFileImpl(blobImpls, aMetadata.mContentType);
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
+
+  return blobImpl.forget();
+}
+
+already_AddRefed<FileImpl>
+CreateBlobImplFromParams(const ParentBlobConstructorParams& aParams,
+                         bool aIsSameProcessActor)
+{
+  MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+  MOZ_ASSERT(aParams.blobParams().type() ==
+               AnyBlobConstructorParams::TNormalBlobConstructorParams ||
+             aParams.blobParams().type() ==
+               AnyBlobConstructorParams::TFileBlobConstructorParams);
+  MOZ_ASSERT(aParams.optionalInputStreamParams().type() ==
+               OptionalInputStreamParams::TInputStreamParams);
+
+  CreateBlobImplMetadata metadata;
+
+  if (aParams.blobParams().type() ==
+        AnyBlobConstructorParams::TNormalBlobConstructorParams) {
+    const NormalBlobConstructorParams& params =
+      aParams.blobParams().get_NormalBlobConstructorParams();
+
+    if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    metadata.mContentType = params.contentType();
+    metadata.mLength = params.length();
+  } else {
+    const FileBlobConstructorParams& params =
+      aParams.blobParams().get_FileBlobConstructorParams();
+
+    if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    if (NS_WARN_IF(params.modDate() == UINT64_MAX)) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+
+    metadata.mContentType = params.contentType();
+    metadata.mName = params.name();
+    metadata.mLength = params.length();
+    metadata.mLastModifiedDate = params.modDate();
+  }
+
+  metadata.mIsSameProcessActor = aIsSameProcessActor;
+
+  const InputStreamParams& inputStreamParams =
+    aParams.optionalInputStreamParams().get_InputStreamParams();
+
+  nsRefPtr<FileImpl> blobImpl =
+    CreateBlobImplFromInputStreamParams(inputStreamParams, metadata);
+  return blobImpl.forget();
+}
+
 } // anonymous namespace
 
 StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
 StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
 
+/*******************************************************************************
+ * BlobParent::IDTableEntry Declaration
+ ******************************************************************************/
+
 class BlobParent::IDTableEntry MOZ_FINAL
 {
   const nsID mID;
   const intptr_t mProcessID;
   const nsRefPtr<FileImpl> mBlobImpl;
 
 public:
   static already_AddRefed<IDTableEntry>
@@ -798,16 +1281,20 @@ private:
   GetOrCreateInternal(const nsID& aID,
                       intptr_t aProcessID,
                       FileImpl* aBlobImpl,
                       bool aMayCreate,
                       bool aMayGet,
                       bool aIgnoreProcessID);
 };
 
+/*******************************************************************************
+ * BlobParent::OpenStreamRunnable Declaration
+ ******************************************************************************/
+
 // Each instance of this class will be dispatched to the network stream thread
 // pool to run the first time where it will open the file input stream. It will
 // then dispatch itself back to the owning thread to send the child process its
 // response (assuming that the child has not crashed). The runnable will then
 // dispatch itself to the thread pool again in order to close the file input
 // stream.
 class BlobParent::OpenStreamRunnable MOZ_FINAL
   : public nsRunnable
@@ -1047,60 +1534,46 @@ private:
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(BlobParent::OpenStreamRunnable, nsRunnable)
 
 /*******************************************************************************
  * BlobChild::RemoteBlobImpl Declaration
  ******************************************************************************/
 
-class BlobChild::RemoteBlobImpl MOZ_FINAL
+class BlobChild::RemoteBlobImpl
   : public FileImplBase
   , public nsIRemoteBlob
 {
-  class StreamHelper;
-  class SliceHelper;
+protected:
+  class CreateStreamHelper;
 
   BlobChild* mActor;
   nsCOMPtr<nsIEventTarget> mActorTarget;
+  const bool mIsSlice;
 
 public:
+  // For File.
   RemoteBlobImpl(BlobChild* aActor,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  uint64_t aLength,
-                 uint64_t aModDate)
-    : FileImplBase(aName, aContentType, aLength, aModDate)
-  {
-    CommonInit(aActor);
-  }
-
+                 uint64_t aModDate);
+
+  // For Blob.
   RemoteBlobImpl(BlobChild* aActor,
                  const nsAString& aContentType,
-                 uint64_t aLength)
-    : FileImplBase(aContentType, aLength)
-  {
-    CommonInit(aActor);
-  }
-
+                 uint64_t aLength);
+
+  // For mystery blobs.
   explicit
-  RemoteBlobImpl(BlobChild* aActor)
-    : FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
-  {
-    CommonInit(aActor);
-  }
+  RemoteBlobImpl(BlobChild* aActor);
 
   void
-  NoteDyingActor()
-  {
-    MOZ_ASSERT(mActor);
-    mActor->AssertIsOnOwningThread();
-
-    mActor = nullptr;
-  }
+  NoteDyingActor();
 
   BlobChild*
   GetActor() const
   {
     AssertActorEventTargetIsOnCurrentThread();
 
     return mActor;
   }
@@ -1109,328 +1582,368 @@ public:
   GetActorEventTarget() const
   {
     return mActorTarget;
   }
 
   void
   AssertActorEventTargetIsOnCurrentThread() const
   {
-    MOZ_ASSERT(EventTargetIsOnCurrentThread(mActorTarget));
+    MOZ_ASSERT(
+      EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget));
   }
 
+  bool
+  IsSlice() const
+  {
+    return mIsSlice;
+  }
+
+  RemoteBlobSliceImpl*
+  AsSlice() const;
+
+  RemoteBlobImpl*
+  BaseRemoteBlobImpl() const;
+
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual void
   GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) MOZ_OVERRIDE;
 
   virtual already_AddRefed<FileImpl>
-  CreateSlice(uint64_t aStart, uint64_t aLength,
-              const nsAString& aContentType, ErrorResult& aRv) MOZ_OVERRIDE;
+  CreateSlice(uint64_t aStart,
+              uint64_t aLength,
+              const nsAString& aContentType,
+              ErrorResult& aRv) MOZ_OVERRIDE;
 
   virtual nsresult
   GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
 
   virtual int64_t
   GetFileId() MOZ_OVERRIDE;
 
   virtual int64_t GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
 
   virtual BlobChild*
   GetBlobChild() MOZ_OVERRIDE;
 
   virtual BlobParent*
   GetBlobParent() MOZ_OVERRIDE;
 
+protected:
+  // For SliceImpl.
+  RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength);
+
+  ~RemoteBlobImpl()
+  {
+    MOZ_ASSERT_IF(mActorTarget,
+                  EventTargetIsOnCurrentThread(mActorTarget));
+  }
+
+  void
+  CommonInit(BlobChild* aActor);
+
+  void
+  Destroy();
+};
+
+class BlobChild::RemoteBlobImpl::CreateStreamHelper MOZ_FINAL
+  : public nsRunnable
+{
+  Monitor mMonitor;
+  nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl;
+  nsRefPtr<RemoteInputStream> mInputStream;
+  const uint64_t mStart;
+  const uint64_t mLength;
+  bool mDone;
+
+public:
+  CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
+
+  nsresult
+  GetStream(nsIInputStream** aInputStream);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIRUNNABLE
+
+private:
+  ~CreateStreamHelper()
+  {
+    MOZ_ASSERT(!mRemoteBlobImpl);
+    MOZ_ASSERT(!mInputStream);
+    MOZ_ASSERT(mDone);
+  }
+
+  void
+  RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, bool aNotify);
+};
+
+class BlobChild::RemoteBlobSliceImpl MOZ_FINAL
+  : public RemoteBlobImpl
+{
+  nsRefPtr<RemoteBlobImpl> mParent;
+  bool mActorWasCreated;
+
+public:
+  RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
+                      uint64_t aStart,
+                      uint64_t aLength,
+                      const nsAString& aContentType);
+
+  RemoteBlobImpl*
+  Parent() const
+  {
+    MOZ_ASSERT(mParent);
+
+    return const_cast<RemoteBlobImpl*>(mParent.get());
+  }
+
+  uint64_t
+  Start() const
+  {
+    return mStart;
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual BlobChild*
+  GetBlobChild() MOZ_OVERRIDE;
+
+private:
+  ~RemoteBlobSliceImpl()
+  { }
+};
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl Declaration
+ ******************************************************************************/
+
+class BlobParent::RemoteBlobImpl MOZ_FINAL
+  : public FileImpl
+  , public nsIRemoteBlob
+{
+  BlobParent* mActor;
+  nsCOMPtr<nsIEventTarget> mActorTarget;
+  nsRefPtr<FileImpl> mBlobImpl;
+
+public:
+  RemoteBlobImpl(BlobParent* aActor, FileImpl* aBlobImpl);
+
+  void
+  NoteDyingActor();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual void
+  GetName(nsAString& aName) MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetPath(nsAString& aPath) MOZ_OVERRIDE;
+
+  virtual int64_t
+  GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void
+  GetMozFullPath(nsAString& aName, ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void
+  GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual uint64_t
+  GetSize(ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual void
+  GetType(nsAString& aType) MOZ_OVERRIDE;
+
+  virtual already_AddRefed<FileImpl>
+  CreateSlice(uint64_t aStart,
+              uint64_t aLength,
+              const nsAString& aContentType,
+              ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual const nsTArray<nsRefPtr<FileImpl>>*
+  GetSubBlobImpls() const MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
+
+  virtual int64_t
+  GetFileId() MOZ_OVERRIDE;
+
+  virtual void
+  AddFileInfo(FileInfo* aFileInfo) MOZ_OVERRIDE;
+
+  virtual FileInfo*
+  GetFileInfo(FileManager* aFileManager) MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetSendInfo(nsIInputStream** aBody,
+              uint64_t* aContentLength,
+              nsACString& aContentType,
+              nsACString& aCharset) MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetMutable(bool* aMutable) const MOZ_OVERRIDE;
+
+  virtual nsresult
+  SetMutable(bool aMutable) MOZ_OVERRIDE;
+
+  virtual void
+  SetLazyData(const nsAString& aName,
+              const nsAString& aContentType,
+              uint64_t aLength,
+              uint64_t aLastModifiedDate) MOZ_OVERRIDE;
+
+  virtual bool
+  IsMemoryFile() const MOZ_OVERRIDE;
+
+  virtual bool
+  IsSizeUnknown() const MOZ_OVERRIDE;
+
+  virtual bool
+  IsDateUnknown() const MOZ_OVERRIDE;
+
+  virtual bool
+  IsFile() const MOZ_OVERRIDE;
+
+  virtual void
+  Unlink() MOZ_OVERRIDE;
+
+  virtual void
+  Traverse(nsCycleCollectionTraversalCallback& aCallback) MOZ_OVERRIDE;
+
+  virtual BlobChild*
+  GetBlobChild() MOZ_OVERRIDE;
+
+  virtual BlobParent*
+  GetBlobParent() MOZ_OVERRIDE;
+
 private:
   ~RemoteBlobImpl()
   {
     MOZ_ASSERT_IF(mActorTarget,
                   EventTargetIsOnCurrentThread(mActorTarget));
   }
 
   void
-  CommonInit(BlobChild* aActor)
-  {
-    MOZ_ASSERT(aActor);
-    aActor->AssertIsOnOwningThread();
-
-    mActor = aActor;
-    mActorTarget = aActor->EventTarget();
-
-    mImmutable = true;
-  }
-
-  void
-  Destroy()
-  {
-    if (EventTargetIsOnCurrentThread(mActorTarget)) {
-      if (mActor) {
-        mActor->AssertIsOnOwningThread();
-        mActor->NoteDyingRemoteBlobImpl();
-      }
-
-      delete this;
-      return;
-    }
-
-    nsCOMPtr<nsIRunnable> destroyRunnable =
-      NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
-
-    if (mActorTarget) {
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
-                                                          NS_DISPATCH_NORMAL)));
-    } else {
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
-    }
-  }
-};
-
-class BlobChild::RemoteBlobImpl::StreamHelper MOZ_FINAL
-  : public nsRunnable
-{
-  Monitor mMonitor;
-  nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl;
-  nsRefPtr<RemoteInputStream> mInputStream;
-  bool mDone;
-
-public:
-  StreamHelper(RemoteBlobImpl* aRemoteBlobImpl)
-    : mMonitor("BlobChild::RemoteBlobImpl::StreamHelper::mMonitor")
-    , mRemoteBlobImpl(aRemoteBlobImpl)
-    , mDone(false)
-  {
-    // This may be created on any thread.
-    MOZ_ASSERT(aRemoteBlobImpl);
-  }
-
-  nsresult
-  GetStream(nsIInputStream** aInputStream)
-  {
-    // This may be called on any thread.
-    MOZ_ASSERT(aInputStream);
-    MOZ_ASSERT(mRemoteBlobImpl);
-    MOZ_ASSERT(!mInputStream);
-    MOZ_ASSERT(!mDone);
-
-    if (EventTargetIsOnCurrentThread(mRemoteBlobImpl->GetActorEventTarget())) {
-      RunInternal(false);
-    } else {
-      nsCOMPtr<nsIEventTarget> target = mRemoteBlobImpl->GetActorEventTarget();
-      if (!target) {
-        target = do_GetMainThread();
-      }
-
-      MOZ_ASSERT(target);
-
-      nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      {
-        MonitorAutoLock lock(mMonitor);
-        while (!mDone) {
-          lock.Wait();
-        }
-      }
-    }
-
-    MOZ_ASSERT(!mRemoteBlobImpl);
-    MOZ_ASSERT(mDone);
-
-    if (!mInputStream) {
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    mInputStream.forget(aInputStream);
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mRemoteBlobImpl);
-    mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
-
-    RunInternal(true);
-    return NS_OK;
-  }
-
-private:
-  void
-  RunInternal(bool aNotify)
-  {
-    MOZ_ASSERT(mRemoteBlobImpl);
-    mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
-    MOZ_ASSERT(!mInputStream);
-    MOZ_ASSERT(!mDone);
-
-    if (BlobChild* actor = mRemoteBlobImpl->GetActor()) {
-      nsRefPtr<RemoteInputStream> stream =
-        new RemoteInputStream(mRemoteBlobImpl);
-
-      InputStreamChild* streamActor = new InputStreamChild(stream);
-      if (actor->SendPBlobStreamConstructor(streamActor)) {
-        stream.swap(mInputStream);
-      }
-    }
-
-    mRemoteBlobImpl = nullptr;
-
-    if (aNotify) {
-      MonitorAutoLock lock(mMonitor);
-      mDone = true;
-      lock.Notify();
-    }
-    else {
-      mDone = true;
-    }
-  }
-};
-
-class BlobChild::RemoteBlobImpl::SliceHelper MOZ_FINAL
-  : public nsRunnable
-{
-  Monitor mMonitor;
-  nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl;
-  nsRefPtr<FileImpl> mSlice;
-  uint64_t mStart;
-  uint64_t mLength;
-  nsString mContentType;
-  bool mDone;
-
-public:
-  explicit
-  SliceHelper(RemoteBlobImpl* aRemoteBlobImpl)
-    : mMonitor("BlobChild::RemoteBlobImpl::SliceHelper::mMonitor")
-    , mRemoteBlobImpl(aRemoteBlobImpl)
-    , mStart(0)
-    , mLength(0)
-    , mDone(false)
-  {
-    // This may be created on any thread.
-    MOZ_ASSERT(aRemoteBlobImpl);
-  }
-
-  already_AddRefed<FileImpl>
-  GetSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType)
-  {
-    // This may be called on any thread.
-    MOZ_ASSERT(mRemoteBlobImpl);
-    MOZ_ASSERT(!mSlice);
-    MOZ_ASSERT(!mDone);
-
-    mStart = aStart;
-    mLength = aLength;
-    mContentType = aContentType;
-
-    if (EventTargetIsOnCurrentThread(mRemoteBlobImpl->GetActorEventTarget())) {
-      RunInternal(false);
-    } else {
-      nsCOMPtr<nsIEventTarget> target = mRemoteBlobImpl->GetActorEventTarget();
-      if (!target) {
-        target = do_GetMainThread();
-      }
-
-      MOZ_ASSERT(target);
-
-      nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return nullptr;
-      }
-
-      {
-        MonitorAutoLock lock(mMonitor);
-        while (!mDone) {
-          lock.Wait();
-        }
-      }
-    }
-
-    MOZ_ASSERT(!mRemoteBlobImpl);
-    MOZ_ASSERT(mDone);
-
-    if (NS_WARN_IF(!mSlice)) {
-      return nullptr;
-    }
-
-    return mSlice.forget();
-  }
-
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mRemoteBlobImpl);
-    mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
-
-    RunInternal(true);
-    return NS_OK;
-  }
-
-private:
-  void
-  RunInternal(bool aNotify)
-  {
-    MOZ_ASSERT(mRemoteBlobImpl);
-    mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
-    MOZ_ASSERT(!mSlice);
-    MOZ_ASSERT(!mDone);
-
-    if (BlobChild* actor = mRemoteBlobImpl->GetActor()) {
-      MOZ_ASSERT(actor->HasManager());
-
-      nsID id;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
-
-      ChildBlobConstructorParams params(
-        id,
-        NormalBlobConstructorParams(mContentType /* contentType */,
-                                    mLength /* length */));
-
-      ParentBlobConstructorParams otherSideParams(
-        SlicedBlobConstructorParams(nullptr /* sourceParent */,
-                                    actor /* sourceChild */,
-                                    id /* optionalID */,
-                                    mStart /* begin */,
-                                    mStart + mLength /* end */,
-                                    mContentType /* contentType */),
-        void_t() /* optionalInputStream */);
-
-      BlobChild* newActor;
-      if (nsIContentChild* contentManager = actor->GetContentManager()) {
-        newActor = SendSliceConstructor(contentManager,
-                                        params,
-                                        otherSideParams);
-      } else {
-        newActor = SendSliceConstructor(actor->GetBackgroundManager(),
-                                        params,
-                                        otherSideParams);
-      }
-
-      if (newActor) {
-        mSlice = newActor->GetBlobImpl();
-      }
-    }
-
-    mRemoteBlobImpl = nullptr;
-
-    if (aNotify) {
-      MonitorAutoLock lock(mMonitor);
-      mDone = true;
-      lock.Notify();
-    }
-    else {
-      mDone = true;
-    }
-  }
+  Destroy();
 };
 
 /*******************************************************************************
- * BlobChild::RemoteBlobImpl Implementation
+ * BlobChild::RemoteBlobImpl
  ******************************************************************************/
 
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+                               const nsAString& aName,
+                               const nsAString& aContentType,
+                               uint64_t aLength,
+                               uint64_t aModDate)
+  : FileImplBase(aName, aContentType, aLength, aModDate)
+  , mIsSlice(false)
+{
+  CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+                               const nsAString& aContentType,
+                               uint64_t aLength)
+  : FileImplBase(aContentType, aLength)
+  , mIsSlice(false)
+{
+  CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
+  : FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
+  , mIsSlice(false)
+{
+  CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
+  : FileImplBase(aContentType, aLength)
+  , mActor(nullptr)
+  , mIsSlice(true)
+{
+  mImmutable = true;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::CommonInit(BlobChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  aActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(!mIsSlice);
+
+  mActor = aActor;
+  mActorTarget = aActor->EventTarget();
+
+  mImmutable = true;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::NoteDyingActor()
+{
+  MOZ_ASSERT(mActor);
+  mActor->AssertIsOnOwningThread();
+
+  mActor = nullptr;
+}
+
+BlobChild::RemoteBlobSliceImpl*
+BlobChild::
+RemoteBlobImpl::AsSlice() const
+{
+  MOZ_ASSERT(IsSlice());
+
+  return static_cast<RemoteBlobSliceImpl*>(const_cast<RemoteBlobImpl*>(this));
+}
+
+BlobChild::RemoteBlobImpl*
+BlobChild::
+RemoteBlobImpl::BaseRemoteBlobImpl() const
+{
+  if (IsSlice()) {
+    return AsSlice()->Parent()->BaseRemoteBlobImpl();
+  }
+
+  return const_cast<RemoteBlobImpl*>(this);
+}
+
+void
+BlobChild::
+RemoteBlobImpl::Destroy()
+{
+  if (EventTargetIsOnCurrentThread(mActorTarget)) {
+    if (mActor) {
+      mActor->AssertIsOnOwningThread();
+      mActor->NoteDyingRemoteBlobImpl();
+    }
+
+    delete this;
+    return;
+  }
+
+  nsCOMPtr<nsIRunnable> destroyRunnable =
+    NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+  if (mActorTarget) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
+                                                        NS_DISPATCH_NORMAL)));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
+  }
+}
+
 NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl)
 NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlobImpl, Destroy())
 NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl,
                                   FileImpl,
                                   nsIRemoteBlob)
 
 void
 BlobChild::
@@ -1452,35 +1965,33 @@ RemoteBlobImpl::GetMozFullPathInternal(n
     return;
   }
 
   aFilePath = filePath;
 }
 
 already_AddRefed<FileImpl>
 BlobChild::
-RemoteBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
-                            const nsAString& aContentType, ErrorResult& aRv)
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+                            uint64_t aLength,
+                            const nsAString& aContentType,
+                            ErrorResult& aRv)
 {
-  nsRefPtr<SliceHelper> helper = new SliceHelper(this);
-
-  nsRefPtr<FileImpl> impl = helper->GetSlice(aStart, aLength, aContentType);
-  if (NS_WARN_IF(!impl)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  return impl.forget();
+  // May be called on any thread.
+   nsRefPtr<RemoteBlobSliceImpl> slice =
+     new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
+   return slice.forget();
 }
 
 nsresult
 BlobChild::
 RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
 {
-  nsRefPtr<StreamHelper> helper = new StreamHelper(this);
+  // May be called on any thread.
+  nsRefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this);
   return helper->GetStream(aStream);
 }
 
 int64_t
 BlobChild::
 RemoteBlobImpl::GetFileId()
 {
   if (!EventTargetIsOnCurrentThread(mActorTarget)) {
@@ -1516,16 +2027,454 @@ RemoteBlobImpl::GetBlobChild()
 BlobParent*
 BlobChild::
 RemoteBlobImpl::GetBlobParent()
 {
   return nullptr;
 }
 
 /*******************************************************************************
+ * BlobChild::RemoteBlobImpl::CreateStreamHelper
+ ******************************************************************************/
+
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl)
+  : mMonitor("BlobChild::RemoteBlobImpl::CreateStreamHelper::mMonitor")
+  , mRemoteBlobImpl(aRemoteBlobImpl)
+  , mStart(aRemoteBlobImpl->IsSlice() ? aRemoteBlobImpl->AsSlice()->Start() : 0)
+  , mLength(0)
+  , mDone(false)
+{
+  // This may be created on any thread.
+  MOZ_ASSERT(aRemoteBlobImpl);
+
+  ErrorResult rv;
+  const_cast<uint64_t&>(mLength) = aRemoteBlobImpl->GetSize(rv);
+  MOZ_ASSERT(!rv.Failed());
+}
+
+nsresult
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::GetStream(nsIInputStream** aInputStream)
+{
+  // This may be called on any thread.
+  MOZ_ASSERT(aInputStream);
+  MOZ_ASSERT(mRemoteBlobImpl);
+  MOZ_ASSERT(!mInputStream);
+  MOZ_ASSERT(!mDone);
+
+  nsRefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
+    mRemoteBlobImpl->BaseRemoteBlobImpl();
+  MOZ_ASSERT(baseRemoteBlobImpl);
+
+  if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) {
+    RunInternal(baseRemoteBlobImpl, false);
+  } else {
+    nsCOMPtr<nsIEventTarget> target = baseRemoteBlobImpl->GetActorEventTarget();
+    if (!target) {
+      target = do_GetMainThread();
+    }
+
+    MOZ_ASSERT(target);
+
+    nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    {
+      MonitorAutoLock lock(mMonitor);
+      while (!mDone) {
+        lock.Wait();
+      }
+    }
+  }
+
+  MOZ_ASSERT(!mRemoteBlobImpl);
+  MOZ_ASSERT(mDone);
+
+  if (!mInputStream) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mInputStream.forget(aInputStream);
+  return NS_OK;
+}
+
+void
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl,
+                                bool aNotify)
+{
+  MOZ_ASSERT(aBaseRemoteBlobImpl);
+  aBaseRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
+  MOZ_ASSERT(!mInputStream);
+  MOZ_ASSERT(!mDone);
+
+  if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) {
+    nsRefPtr<RemoteInputStream> stream =
+      new RemoteInputStream(aBaseRemoteBlobImpl);
+
+    InputStreamChild* streamActor = new InputStreamChild(stream);
+    if (actor->SendPBlobStreamConstructor(streamActor, mStart, mLength)) {
+      stream.swap(mInputStream);
+    }
+  }
+
+  mRemoteBlobImpl = nullptr;
+
+  if (aNotify) {
+    MonitorAutoLock lock(mMonitor);
+    mDone = true;
+    lock.Notify();
+  } else {
+    mDone = true;
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobImpl::CreateStreamHelper,
+                             nsRunnable)
+
+NS_IMETHODIMP
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::Run()
+{
+  MOZ_ASSERT(mRemoteBlobImpl);
+  mRemoteBlobImpl->AssertActorEventTargetIsOnCurrentThread();
+
+  nsRefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
+    mRemoteBlobImpl->BaseRemoteBlobImpl();
+  MOZ_ASSERT(baseRemoteBlobImpl);
+
+  RunInternal(baseRemoteBlobImpl, true);
+  return NS_OK;
+}
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobSliceImpl
+ ******************************************************************************/
+
+BlobChild::
+RemoteBlobSliceImpl::RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
+                                         uint64_t aStart,
+                                         uint64_t aLength,
+                                         const nsAString& aContentType)
+  : RemoteBlobImpl(aContentType, aLength)
+  , mParent(aParent->BaseRemoteBlobImpl())
+  , mActorWasCreated(false)
+{
+  MOZ_ASSERT(mParent);
+  MOZ_ASSERT(mParent->BaseRemoteBlobImpl() == mParent);
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(aParent->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+#ifdef DEBUG
+  {
+    ErrorResult rv;
+    uint64_t parentSize = aParent->GetSize(rv);
+    MOZ_ASSERT(!rv.Failed());
+    MOZ_ASSERT(parentSize >= aStart + aLength);
+  }
+#endif
+
+  // Account for the offset of the parent slice, if any.
+  mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
+                             BlobChild::RemoteBlobImpl)
+
+BlobChild*
+BlobChild::
+RemoteBlobSliceImpl::GetBlobChild()
+{
+  AssertActorEventTargetIsOnCurrentThread();
+
+  if (mActorWasCreated) {
+    return RemoteBlobImpl::GetBlobChild();
+  }
+
+  mActorWasCreated = true;
+
+  BlobChild* baseActor = mParent->GetActor();
+  MOZ_ASSERT(baseActor);
+  MOZ_ASSERT(baseActor->HasManager());
+
+  nsID id;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
+
+  ParentBlobConstructorParams params(
+    SlicedBlobConstructorParams(nullptr /* sourceParent */,
+                                baseActor /* sourceChild */,
+                                id /* id */,
+                                mStart /* begin */,
+                                mStart + mLength /* end */,
+                                mContentType /* contentType */),
+    void_t() /* optionalInputStream */);
+
+  if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
+    mActor = SendSliceConstructor(contentManager, this, params);
+  } else {
+    mActor =
+      SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
+  }
+
+  return mActor;
+}
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl
+ ******************************************************************************/
+
+BlobParent::
+RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, FileImpl* aBlobImpl)
+  : mActor(aActor)
+  , mActorTarget(aActor->EventTarget())
+  , mBlobImpl(aBlobImpl)
+{
+  MOZ_ASSERT(aActor);
+  aActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(aBlobImpl);
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::NoteDyingActor()
+{
+  MOZ_ASSERT(mActor);
+  mActor->AssertIsOnOwningThread();
+
+  mActor = nullptr;
+}
+
+void
+BlobParent::
+RemoteBlobImpl::Destroy()
+{
+  if (EventTargetIsOnCurrentThread(mActorTarget)) {
+    if (mActor) {
+      mActor->AssertIsOnOwningThread();
+      mActor->NoteDyingRemoteBlobImpl();
+    }
+
+    delete this;
+    return;
+  }
+
+  nsCOMPtr<nsIRunnable> destroyRunnable =
+    NS_NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+  if (mActorTarget) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
+                                                        NS_DISPATCH_NORMAL)));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
+  }
+}
+
+NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
+NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy())
+NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
+                                  FileImpl,
+                                  nsIRemoteBlob)
+
+void
+BlobParent::
+RemoteBlobImpl::GetName(nsAString& aName)
+{
+  mBlobImpl->GetName(aName);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetPath(nsAString& aPath)
+{
+  return mBlobImpl->GetPath(aPath);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
+{
+  return mBlobImpl->GetLastModified(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv)
+{
+  mBlobImpl->GetMozFullPath(aName, aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv)
+{
+  mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
+}
+
+uint64_t
+BlobParent::
+RemoteBlobImpl::GetSize(ErrorResult& aRv)
+{
+  return mBlobImpl->GetSize(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetType(nsAString& aType)
+{
+  mBlobImpl->GetType(aType);
+}
+
+already_AddRefed<FileImpl>
+BlobParent::
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+                            uint64_t aLength,
+                            const nsAString& aContentType,
+                            ErrorResult& aRv)
+{
+  return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
+}
+
+const nsTArray<nsRefPtr<FileImpl>>*
+BlobParent::
+RemoteBlobImpl::GetSubBlobImpls() const
+{
+  return mBlobImpl->GetSubBlobImpls();
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
+{
+  return mBlobImpl->GetInternalStream(aStream);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetFileId()
+{
+  return mBlobImpl->GetFileId();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::AddFileInfo(FileInfo* aFileInfo)
+{
+  return mBlobImpl->AddFileInfo(aFileInfo);
+}
+
+FileInfo*
+BlobParent::
+RemoteBlobImpl::GetFileInfo(FileManager* aFileManager)
+{
+  return mBlobImpl->GetFileInfo(aFileManager);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetSendInfo(nsIInputStream** aBody,
+                            uint64_t* aContentLength,
+                            nsACString& aContentType,
+                            nsACString& aCharset)
+{
+  return mBlobImpl->GetSendInfo(aBody,
+                                aContentLength,
+                                aContentType,
+                                aCharset);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetMutable(bool* aMutable) const
+{
+  return mBlobImpl->GetMutable(aMutable);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::SetMutable(bool aMutable)
+{
+  return mBlobImpl->SetMutable(aMutable);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetLazyData(const nsAString& aName,
+                            const nsAString& aContentType,
+                            uint64_t aLength,
+                            uint64_t aLastModifiedDate)
+{
+  MOZ_CRASH("This should never be called!");
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsMemoryFile() const
+{
+  return mBlobImpl->IsMemoryFile();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsSizeUnknown() const
+{
+  return mBlobImpl->IsSizeUnknown();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsDateUnknown() const
+{
+  return mBlobImpl->IsDateUnknown();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsFile() const
+{
+  return mBlobImpl->IsFile();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::Unlink()
+{
+  return mBlobImpl->Unlink();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::Traverse(nsCycleCollectionTraversalCallback& aCallback)
+{
+  return mBlobImpl->Traverse(aCallback);
+}
+
+BlobChild*
+BlobParent::
+RemoteBlobImpl::GetBlobChild()
+{
+  return nullptr;
+}
+
+BlobParent*
+BlobParent::
+RemoteBlobImpl::GetBlobParent()
+{
+  return mActor;
+}
+
+/*******************************************************************************
  * BlobChild
  ******************************************************************************/
 
 BlobChild::BlobChild(nsIContentChild* aManager, FileImpl* aBlobImpl)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
   AssertCorrectThreadForManager(aManager);
@@ -1596,16 +2545,45 @@ BlobChild::BlobChild(PBackgroundChild* a
   CommonInit(aParams);
 
   if (!NS_IsMainThread()) {
     mEventTarget = do_GetCurrentThread();
     MOZ_ASSERT(mEventTarget);
   }
 }
 
+BlobChild::BlobChild(nsIContentChild* aManager,
+                     const nsID& aParentID,
+                     RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
+  : mBackgroundManager(nullptr)
+  , mContentManager(aManager)
+{
+  AssertCorrectThreadForManager(aManager);
+  MOZ_ASSERT(aManager);
+
+  CommonInit(aParentID, aRemoteBlobSliceImpl);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager,
+                     const nsID& aParentID,
+                     RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
+  : mBackgroundManager(aManager)
+  , mContentManager(nullptr)
+{
+  AssertCorrectThreadForManager(aManager);
+  MOZ_ASSERT(aManager);
+
+  CommonInit(aParentID, aRemoteBlobSliceImpl);
+
+  if (!NS_IsMainThread()) {
+    mEventTarget = do_GetCurrentThread();
+    MOZ_ASSERT(mEventTarget);
+  }
+}
+
 BlobChild::~BlobChild()
 {
   AssertIsOnOwningThread();
 
   MOZ_COUNT_DTOR(BlobChild);
 }
 
 void
@@ -1628,18 +2606,16 @@ BlobChild::CommonInit(FileImpl* aBlobImp
 void
 BlobChild::CommonInit(BlobChild* aOther)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aOther);
   MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager());
   MOZ_ASSERT_IF(mBackgroundManager, aOther->GetContentManager());
 
-  MOZ_COUNT_CTOR(BlobChild);
-
   nsRefPtr<FileImpl> otherImpl = aOther->GetBlobImpl();
   MOZ_ASSERT(otherImpl);
 
   nsString contentType;
   otherImpl->GetType(contentType);
 
   ErrorResult rv;
   uint64_t length = otherImpl->GetSize(rv);
@@ -1653,28 +2629,17 @@ BlobChild::CommonInit(BlobChild* aOther)
     uint64_t modDate = otherImpl->GetLastModified(rv);
     MOZ_ASSERT(!rv.Failed());
 
     remoteBlob = new RemoteBlobImpl(this, name, contentType, length, modDate);
   } else {
     remoteBlob = new RemoteBlobImpl(this, contentType, length);
   }
 
-  MOZ_ASSERT(remoteBlob);
-
-  DebugOnly<bool> isMutable;
-  MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable)));
-  MOZ_ASSERT(!isMutable);
-
-  mRemoteBlobImpl = remoteBlob;
-
-  remoteBlob.forget(&mBlobImpl);
-  mOwnsBlobImpl = true;
-
-  mParentID = aOther->ParentID();
+  CommonInit(aOther->ParentID(), remoteBlob);
 }
 
 void
 BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
 {
   AssertIsOnOwningThread();
 
   MOZ_COUNT_CTOR(BlobChild);
@@ -1726,16 +2691,38 @@ BlobChild::CommonInit(const ChildBlobCon
   mRemoteBlobImpl = remoteBlob;
 
   remoteBlob.forget(&mBlobImpl);
   mOwnsBlobImpl = true;
 
   mParentID = aParams.id();
 }
 
+void
+BlobChild::CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aRemoteBlobImpl);
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(aRemoteBlobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+  MOZ_COUNT_CTOR(BlobChild);
+
+  nsRefPtr<RemoteBlobImpl> remoteBlob = aRemoteBlobImpl;
+
+  mRemoteBlobImpl = remoteBlob;
+
+  remoteBlob.forget(&mBlobImpl);
+  mOwnsBlobImpl = true;
+
+  mParentID = aParentID;
+}
+
 #ifdef DEBUG
 
 void
 BlobChild::AssertIsOnOwningThread() const
 {
   MOZ_ASSERT(IsOnOwningThread());
 }
 
@@ -1878,76 +2865,50 @@ BlobChild::CreateFromParams(ChildManager
   switch (blobParams.type()) {
     case AnyBlobConstructorParams::TNormalBlobConstructorParams:
     case AnyBlobConstructorParams::TFileBlobConstructorParams:
     case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
       return new BlobChild(aManager, aParams);
     }
 
     case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
-      const SlicedBlobConstructorParams& params =
-        blobParams.get_SlicedBlobConstructorParams();
-      MOZ_ASSERT(params.optionalID().type() == OptionalID::Tvoid_t);
-
-      auto* actor =
-        const_cast<BlobChild*>(
-          static_cast<const BlobChild*>(params.sourceChild()));
-      MOZ_ASSERT(actor);
-
-      nsRefPtr<FileImpl> source = actor->GetBlobImpl();
-      MOZ_ASSERT(source);
-
-      Optional<int64_t> start;
-      start.Construct(params.begin());
-
-      Optional<int64_t> end;
-      start.Construct(params.end());
-
-      ErrorResult rv;
-      nsRefPtr<FileImpl> slice =
-        source->Slice(start, end, params.contentType(), rv);
-      if (NS_WARN_IF(rv.Failed())) {
-        return nullptr;
-      }
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(slice->SetMutable(false)));
-
-      actor = new BlobChild(aManager, slice);
-
-      actor->mParentID = aParams.id();
-
-      return actor;
+      MOZ_CRASH("Parent should never send SlicedBlobConstructorParams!");
     }
 
     case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
-      MOZ_CRASH("Parent should never send this type!");
+      MOZ_CRASH("Parent should never send KnownBlobConstructorParams!");
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
 
 // static
 template <class ChildManagerType>
 BlobChild*
-BlobChild::SendSliceConstructor(
-                            ChildManagerType* aManager,
-                            const ChildBlobConstructorParams& aParams,
-                            const ParentBlobConstructorParams& aOtherSideParams)
+BlobChild::SendSliceConstructor(ChildManagerType* aManager,
+                                RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
+                                const ParentBlobConstructorParams& aParams)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
-
-  BlobChild* newActor = BlobChild::Create(aManager, aParams);
-  MOZ_ASSERT(newActor);
-
-  if (aManager->SendPBlobConstructor(newActor, aOtherSideParams)) {
+  MOZ_ASSERT(aRemoteBlobSliceImpl);
+  MOZ_ASSERT(aParams.blobParams().type() ==
+               AnyBlobConstructorParams::TSlicedBlobConstructorParams);
+  MOZ_ASSERT(aParams.optionalInputStreamParams().type() ==
+               OptionalInputStreamParams::Tvoid_t);
+
+  const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id();
+
+  BlobChild* newActor = new BlobChild(aManager, id, aRemoteBlobSliceImpl);
+
+  if (aManager->SendPBlobConstructor(newActor, aParams)) {
     if (gProcessType != GeckoProcessType_Default || !NS_IsMainThread()) {
       newActor->SendWaitForSliceCreation();
     }
     return newActor;
   }
 
   BlobChild::Destroy(newActor);
   return nullptr;
@@ -2135,588 +3096,34 @@ BlobChild::ActorDestroy(ActorDestroyReas
   mRemoteBlobImpl = nullptr;
   mBackgroundManager = nullptr;
   mContentManager = nullptr;
   mOwnsBlobImpl = false;
 #endif
 }
 
 PBlobStreamChild*
-BlobChild::AllocPBlobStreamChild()
+BlobChild::AllocPBlobStreamChild(const uint64_t& aStart,
+                                 const uint64_t& aLength)
 {
   AssertIsOnOwningThread();
 
   return new InputStreamChild();
 }
 
 bool
 BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
 {
   AssertIsOnOwningThread();
 
   delete static_cast<InputStreamChild*>(aActor);
   return true;
 }
 
 /*******************************************************************************
- * BlobParent::RemoteBlob Declaration
- ******************************************************************************/
-
-class BlobParent::RemoteBlobImplBase
-  : public nsISupports
-{
-  friend class BlobParent;
-
-  BlobParent* mActor;
-  nsCOMPtr<nsIEventTarget> mActorTarget;
-  bool mForwards;
-
-protected:
-  RemoteBlobImplBase(BlobParent* aActor, bool aForwards)
-    : mActor(aActor)
-    , mActorTarget(aActor->EventTarget())
-    , mForwards(aForwards)
-  {
-    MOZ_ASSERT(aActor);
-    aActor->AssertIsOnOwningThread();
-
-    MOZ_COUNT_CTOR(BlobParent::RemoteBlobImplBase);
-  }
-
-  virtual
-  ~RemoteBlobImplBase()
-  {
-    MOZ_ASSERT_IF(mActorTarget,
-                  EventTargetIsOnCurrentThread(mActorTarget));
-
-    MOZ_COUNT_DTOR(BlobParent::RemoteBlobImplBase);
-  }
-
-  void
-  Destroy()
-  {
-    if (EventTargetIsOnCurrentThread(mActorTarget)) {
-      if (mActor) {
-        mActor->AssertIsOnOwningThread();
-        mActor->NoteDyingRemoteBlobImpl();
-      }
-
-      delete this;
-      return;
-    }
-
-    nsCOMPtr<nsIRunnable> destroyRunnable =
-      NS_NewNonOwningRunnableMethod(this, &RemoteBlobImplBase::Destroy);
-
-    if (mActorTarget) {
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mActorTarget->Dispatch(destroyRunnable,
-                                                          NS_DISPATCH_NORMAL)));
-    } else {
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
-    }
-  }
-
-private:
-  void
-  NoteDyingActor()
-  {
-    MOZ_ASSERT(mActor);
-    mActor->AssertIsOnOwningThread();
-
-    mActor = nullptr;
-  }
-};
-
-class BlobParent::RemoteBlobImpl MOZ_FINAL
-  : public RemoteBlobImplBase
-  , public FileImplBase
-  , public nsIRemoteBlob
-{
-  friend class mozilla::dom::BlobParent;
-
-  class SliceHelper;
-
-  InputStreamParams mInputStreamParams;
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  virtual already_AddRefed<FileImpl>
-  CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
-              ErrorResult& aRv) MOZ_OVERRIDE;
-
-  virtual nsresult
-  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
-
-  virtual int64_t
-  GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
-
-  virtual BlobChild*
-  GetBlobChild() MOZ_OVERRIDE;
-
-  virtual BlobParent*
-  GetBlobParent() MOZ_OVERRIDE;
-
-private:
-  RemoteBlobImpl(BlobParent* aActor,
-                 const InputStreamParams& aInputStreamParams,
-                 const nsAString& aName,
-                 const nsAString& aContentType,
-                 uint64_t aLength,
-                 uint64_t aModDate)
-    : RemoteBlobImplBase(aActor, /* aForwards */ false)
-    , FileImplBase(aName, aContentType, aLength, aModDate)
-  {
-    CommonInit(aInputStreamParams);
-  }
-
-  RemoteBlobImpl(BlobParent* aActor,
-                 const InputStreamParams& aInputStreamParams,
-                 const nsAString& aContentType,
-                 uint64_t aLength)
-    : RemoteBlobImplBase(aActor, /* aForwards */ false)
-    , FileImplBase(aContentType, aLength)
-  {
-    CommonInit(aInputStreamParams);
-  }
-
-  ~RemoteBlobImpl()
-  { }
-
-  void
-  CommonInit(const InputStreamParams& aInputStreamParams)
-  {
-    MOZ_ASSERT(aInputStreamParams.type() != InputStreamParams::T__None);
-
-    mInputStreamParams = aInputStreamParams;
-
-    mImmutable = true;
-  }
-};
-
-class BlobParent::RemoteBlobImpl::SliceHelper MOZ_FINAL
-  : public nsRunnable
-{
-  Monitor mMonitor;
-  BlobParent* mActor;
-  nsRefPtr<FileImpl> mSlice;
-  uint64_t mStart;
-  uint64_t mLength;
-  nsString mContentType;
-  bool mDone;
-
-public:
-  explicit
-  SliceHelper(BlobParent* aActor)
-    : mMonitor("BlobParent::RemoteBlobImpl::SliceHelper::mMonitor")
-    , mActor(aActor)
-    , mStart(0)
-    , mLength(0)
-    , mDone(false)
-  {
-    // This may be created on any thread.
-    MOZ_ASSERT(aActor);
-  }
-
-  FileImpl*
-  GetSlice(uint64_t aStart,
-           uint64_t aLength,
-           const nsAString& aContentType)
-  {
-    // This may be called on any thread.
-    MOZ_ASSERT(mActor);
-    MOZ_ASSERT(!mSlice);
-    MOZ_ASSERT(!mDone);
-
-    mStart = aStart;
-    mLength = aLength;
-    mContentType = aContentType;
-
-    if (mActor->IsOnOwningThread()) {
-      RunInternal(false);
-    } else {
-      nsCOMPtr<nsIEventTarget> target = mActor->EventTarget();
-      if (!target) {
-        target = do_GetMainThread();
-      }
-
-      MOZ_ASSERT(target);
-
-      nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return nullptr;
-      }
-
-      {
-        MonitorAutoLock lock(mMonitor);
-        while (!mDone) {
-          lock.Wait();
-        }
-      }
-    }
-
-    MOZ_ASSERT(!mActor);
-    MOZ_ASSERT(mDone);
-
-    if (NS_WARN_IF(!mSlice)) {
-      return nullptr;
-    }
-
-    return mSlice;
-  }
-
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mActor);
-    mActor->AssertIsOnOwningThread();
-
-    RunInternal(true);
-    return NS_OK;
-  }
-
-private:
-  void
-  RunInternal(bool aNotify)
-  {
-    MOZ_ASSERT(mActor);
-    mActor->AssertIsOnOwningThread();
-    MOZ_ASSERT(!mSlice);
-    MOZ_ASSERT(!mDone);
-
-    NS_ENSURE_TRUE_VOID(mActor->HasManager());
-
-    nsID id;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
-
-    ParentBlobConstructorParams params(
-      NormalBlobConstructorParams(mContentType /* contentType */,
-                                  mLength /* length */),
-      void_t() /* optionalInputStreamParams */);
-
-    ChildBlobConstructorParams otherSideParams(
-      id,
-      SlicedBlobConstructorParams(mActor /* sourceParent*/,
-                                  nullptr /* sourceChild */,
-                                  void_t() /* optionalID */,
-                                  mStart /* begin */,
-                                  mStart + mLength /* end */,
-                                  mContentType /* contentType */));
-
-    BlobParent* newActor;
-    if (nsIContentParent* contentManager = mActor->GetContentManager()) {
-      newActor = SendSliceConstructor(contentManager, params, otherSideParams);
-    } else {
-      newActor = SendSliceConstructor(mActor->GetBackgroundManager(),
-                                      params,
-                                      otherSideParams);
-    }
-
-    if (newActor) {
-      mSlice = newActor->GetBlobImpl();
-    }
-
-    mActor = nullptr;
-
-    if (aNotify) {
-      MonitorAutoLock lock(mMonitor);
-      mDone = true;
-      lock.Notify();
-    }
-    else {
-      mDone = true;
-    }
-  }
-};
-
-class BlobParent::ForwardingRemoteBlobImpl MOZ_FINAL
-  : public RemoteBlobImplBase
-  , public FileImpl
-  , public nsIRemoteBlob
-{
-  friend class mozilla::dom::BlobParent;
-
-  typedef mozilla::dom::indexedDB::FileInfo FileInfo;
-  typedef mozilla::dom::indexedDB::FileManager FileManager;
-
-  nsRefPtr<FileImpl> mBlobImpl;
-  nsCOMPtr<nsIRemoteBlob> mRemoteBlob;
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  virtual void
-  GetName(nsAString& aName) MOZ_OVERRIDE
-  {
-    mBlobImpl->GetName(aName);
-  }
-
-  virtual nsresult
-  GetPath(nsAString& aPath) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetPath(aPath);
-  }
-
-  virtual int64_t
-  GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetLastModified(aRv);
-  }
-
-  virtual void
-  GetMozFullPath(nsAString& aName, ErrorResult& aRv) MOZ_OVERRIDE
-  {
-    mBlobImpl->GetMozFullPath(aName, aRv);
-  }
-
-  virtual void
-  GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) MOZ_OVERRIDE
-  {
-    mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
-  }
-
-  virtual uint64_t
-  GetSize(ErrorResult& aRv) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetSize(aRv);
-  }
-
-  virtual void
-  GetType(nsAString& aType) MOZ_OVERRIDE
-  {
-    mBlobImpl->GetType(aType);
-  }
-
-  virtual already_AddRefed<FileImpl>
-  CreateSlice(uint64_t aStart,
-              uint64_t aLength,
-              const nsAString& aContentType,
-              ErrorResult& aRv) MOZ_OVERRIDE
-  {
-    return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
-  }
-
-  virtual const nsTArray<nsRefPtr<FileImpl>>*
-  GetSubBlobImpls() const MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetSubBlobImpls();
-  }
-
-  virtual nsresult
-  GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetInternalStream(aStream);
-  }
-
-  virtual int64_t
-  GetFileId() MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetFileId();
-  }
-
-  virtual void
-  AddFileInfo(FileInfo* aFileInfo) MOZ_OVERRIDE
-  {
-    return mBlobImpl->AddFileInfo(aFileInfo);
-  }
-
-  virtual FileInfo*
-  GetFileInfo(FileManager* aFileManager) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetFileInfo(aFileManager);
-  }
-
-  virtual nsresult
-  GetSendInfo(nsIInputStream** aBody,
-              uint64_t* aContentLength,
-              nsACString& aContentType,
-              nsACString& aCharset) MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetSendInfo(aBody,
-                                  aContentLength,
-                                  aContentType,
-                                  aCharset);
-  }
-
-  virtual nsresult
-  GetMutable(bool* aMutable) const MOZ_OVERRIDE
-  {
-    return mBlobImpl->GetMutable(aMutable);
-  }
-
-  virtual nsresult
-  SetMutable(bool aMutable) MOZ_OVERRIDE
-  {
-    return mBlobImpl->SetMutable(aMutable);
-  }
-
-  virtual void
-  SetLazyData(const nsAString& aName,
-              const nsAString& aContentType,
-              uint64_t aLength,
-              uint64_t aLastModifiedDate) MOZ_OVERRIDE
-  {
-    MOZ_CRASH("This should never be called!");
-  }
-
-  virtual bool
-  IsMemoryFile() const MOZ_OVERRIDE
-  {
-    return mBlobImpl->IsMemoryFile();
-  }
-
-  virtual bool
-  IsSizeUnknown() const MOZ_OVERRIDE
-  {
-    return mBlobImpl->IsSizeUnknown();
-  }
-
-  virtual bool
-  IsDateUnknown() const MOZ_OVERRIDE
-  {
-    return mBlobImpl->IsDateUnknown();
-  }
-
-  virtual bool
-  IsFile() const MOZ_OVERRIDE
-  {
-    return mBlobImpl->IsFile();
-  }
-
-  virtual void
-  Unlink() MOZ_OVERRIDE
-  {
-    return mBlobImpl->Unlink();
-  }
-
-  virtual void
-  Traverse(nsCycleCollectionTraversalCallback& aCallback) MOZ_OVERRIDE
-  {
-    return mBlobImpl->Traverse(aCallback);
-  }
-
-  virtual BlobChild*
-  GetBlobChild() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mRemoteBlob);
-
-    return mRemoteBlob->GetBlobChild();
-  }
-
-  virtual BlobParent*
-  GetBlobParent() MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(mRemoteBlob);
-
-    return mRemoteBlob->GetBlobParent();
-  }
-
-private:
-  ForwardingRemoteBlobImpl(BlobParent* aActor,
-                           FileImpl* aBlobImpl)
-    : RemoteBlobImplBase(aActor, /* aForwards */ true)
-    , mBlobImpl(aBlobImpl)
-    , mRemoteBlob(do_QueryObject(aBlobImpl))
-  {
-    MOZ_ASSERT(aBlobImpl);
-
-    DebugOnly<bool> isMutable;
-    MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
-    MOZ_ASSERT(!isMutable);
-  }
-
-  ~ForwardingRemoteBlobImpl()
-  { }
-};
-
-/*******************************************************************************
- * BlobParent::RemoteBlobImpl Implementation
- ******************************************************************************/
-
-NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
-NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy())
-NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
-                                  FileImplBase,
-                                  nsIRemoteBlob)
-
-already_AddRefed<FileImpl>
-BlobParent::
-RemoteBlobImpl::CreateSlice(uint64_t aStart,
-                            uint64_t aLength,
-                            const nsAString& aContentType,
-                            ErrorResult& aRv)
-{
-  if (!mActor) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  nsRefPtr<SliceHelper> helper = new SliceHelper(mActor);
-
-  nsRefPtr<FileImpl> impl = helper->GetSlice(aStart, aLength, aContentType);
-  if (NS_WARN_IF(!impl)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  return impl.forget();
-}
-
-nsresult
-BlobParent::
-RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream)
-{
-  MOZ_ASSERT(mInputStreamParams.type() != InputStreamParams::T__None);
-
-  nsTArray<FileDescriptor> fds;
-  nsCOMPtr<nsIInputStream> realStream =
-    DeserializeInputStream(mInputStreamParams, fds);
-  if (!realStream) {
-    NS_WARNING("Failed to deserialize stream!");
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  nsCOMPtr<nsIInputStream> stream =
-    new BlobInputStreamTether(realStream, this);
-  stream.forget(aStream);
-  return NS_OK;
-}
-
-int64_t
-BlobParent::
-RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
-{
-  if (IsDateUnknown()) {
-    return 0;
-  }
-
-  return mLastModificationDate;
-}
-
-BlobChild*
-BlobParent::
-RemoteBlobImpl::GetBlobChild()
-{
-  return nullptr;
-}
-
-BlobParent*
-BlobParent::
-RemoteBlobImpl::GetBlobParent()
-{
-  return mActor;
-}
-
-NS_IMPL_ADDREF(BlobParent::ForwardingRemoteBlobImpl)
-NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::ForwardingRemoteBlobImpl, Destroy())
-NS_INTERFACE_MAP_BEGIN(BlobParent::ForwardingRemoteBlobImpl)
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIRemoteBlob, mRemoteBlob)
-NS_INTERFACE_MAP_END_INHERITING(FileImpl)
-
-/*******************************************************************************
  * BlobParent
  ******************************************************************************/
 
 BlobParent::BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
   AssertCorrectThreadForManager(aManager);
@@ -2733,39 +3140,39 @@ BlobParent::BlobParent(PBackgroundParent
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mEventTarget);
 
   CommonInit(aIDTableEntry);
 }
 
 BlobParent::BlobParent(nsIContentParent* aManager,
-                       const ParentBlobConstructorParams& aParams,
+                       FileImpl* aBlobImpl,
                        IDTableEntry* aIDTableEntry)
   : mBackgroundManager(nullptr)
   , mContentManager(aManager)
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
 
-  CommonInit(aParams, aIDTableEntry);
+  CommonInit(aBlobImpl, aIDTableEntry);
 }
 
 BlobParent::BlobParent(PBackgroundParent* aManager,
-                       const ParentBlobConstructorParams& aParams,
+                       FileImpl* aBlobImpl,
                        IDTableEntry* aIDTableEntry)
   : mBackgroundManager(aManager)
   , mContentManager(nullptr)
   , mEventTarget(do_GetCurrentThread())
 {
   AssertCorrectThreadForManager(aManager);
   MOZ_ASSERT(aManager);
   MOZ_ASSERT(mEventTarget);
 
-  CommonInit(aParams, aIDTableEntry);
+  CommonInit(aBlobImpl, aIDTableEntry);
 }
 
 BlobParent::~BlobParent()
 {
   AssertIsOnOwningThread();
 
   MOZ_COUNT_DTOR(BlobParent);
 }
@@ -2784,110 +3191,34 @@ BlobParent::CommonInit(IDTableEntry* aID
 
   mBlobImpl->AddRef();
   mOwnsBlobImpl = true;
 
   mIDTableEntry = aIDTableEntry;
 }
 
 void
-BlobParent::CommonInit(const ParentBlobConstructorParams& aParams,
-                       IDTableEntry* aIDTableEntry)
+BlobParent::CommonInit(FileImpl* aBlobImpl, IDTableEntry* aIDTableEntry)
 {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(aBlobImpl);
+  MOZ_ASSERT(aIDTableEntry);
 
   MOZ_COUNT_CTOR(BlobParent);
 
-  const AnyBlobConstructorParams& blobParams = aParams.blobParams();
-
-  const AnyBlobConstructorParams::Type paramsType = blobParams.type();
-  MOZ_ASSERT(paramsType ==
-               AnyBlobConstructorParams::TNormalBlobConstructorParams ||
-             paramsType ==
-               AnyBlobConstructorParams::TFileBlobConstructorParams ||
-             paramsType ==
-               AnyBlobConstructorParams::TKnownBlobConstructorParams);
-  MOZ_ASSERT_IF(paramsType ==
-                  AnyBlobConstructorParams::TKnownBlobConstructorParams,
-                aIDTableEntry);
-  MOZ_ASSERT_IF(paramsType !=
-                  AnyBlobConstructorParams::TKnownBlobConstructorParams,
-                !aIDTableEntry);
-  MOZ_ASSERT_IF(paramsType ==
-                  AnyBlobConstructorParams::TKnownBlobConstructorParams,
-                aParams.optionalInputStreamParams().type() ==
-                   OptionalInputStreamParams::Tvoid_t);
-  MOZ_ASSERT_IF(paramsType !=
-                  AnyBlobConstructorParams::TKnownBlobConstructorParams,
-                aParams.optionalInputStreamParams().type() ==
-                   OptionalInputStreamParams::TInputStreamParams);
-
-  nsRefPtr<FileImpl> remoteBlobImpl;
-  RemoteBlobImplBase* remoteBlobBase = nullptr;
-
-  switch (paramsType) {
-    case AnyBlobConstructorParams::TNormalBlobConstructorParams: {
-      const InputStreamParams& inputStreamParams =
-        aParams.optionalInputStreamParams().get_InputStreamParams();
-
-      const NormalBlobConstructorParams& params =
-        blobParams.get_NormalBlobConstructorParams();
-
-      nsRefPtr<RemoteBlobImpl> impl =
-        new RemoteBlobImpl(this,
-                           inputStreamParams,
-                           params.contentType(),
-                           params.length());
-
-      remoteBlobBase = impl;
-      remoteBlobImpl = impl.forget();
-      break;
-    }
-
-    case AnyBlobConstructorParams::TFileBlobConstructorParams: {
-      const InputStreamParams& inputStreamParams =
-        aParams.optionalInputStreamParams().get_InputStreamParams();
-
-      const FileBlobConstructorParams& params =
-        blobParams.get_FileBlobConstructorParams();
-
-      nsRefPtr<RemoteBlobImpl> impl =
-        new RemoteBlobImpl(this,
-                           inputStreamParams,
-                           params.name(),
-                           params.contentType(),
-                           params.length(),
-                           params.modDate());
-
-      remoteBlobBase = impl;
-      remoteBlobImpl = impl.forget();
-      break;
-    }
-
-    case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
-      nsRefPtr<ForwardingRemoteBlobImpl> impl =
-        new ForwardingRemoteBlobImpl(this, aIDTableEntry->BlobImpl());
-
-      remoteBlobBase = impl;
-      remoteBlobImpl = impl.forget();
-      break;
-    }
-
-    default:
-      MOZ_CRASH("Unknown params!");
-  }
-
-  MOZ_ASSERT(remoteBlobImpl);
-  MOZ_ASSERT(remoteBlobBase);
-
   DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
+  nsRefPtr<RemoteBlobImpl> remoteBlobImpl = new RemoteBlobImpl(this, aBlobImpl);
+
   MOZ_ASSERT(NS_SUCCEEDED(remoteBlobImpl->GetMutable(&isMutable)));
   MOZ_ASSERT(!isMutable);
 
-  mRemoteBlobImpl = remoteBlobBase;
+  mRemoteBlobImpl = remoteBlobImpl;
 
   remoteBlobImpl.forget(&mBlobImpl);
   mOwnsBlobImpl = true;
 
   mIDTableEntry = aIDTableEntry;
 }
 
 #ifdef DEBUG
@@ -3067,66 +3398,82 @@ BlobParent::CreateFromParams(ParentManag
     case AnyBlobConstructorParams::TNormalBlobConstructorParams:
     case AnyBlobConstructorParams::TFileBlobConstructorParams: {
       if (aParams.optionalInputStreamParams().type() !=
             OptionalInputStreamParams::TInputStreamParams) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
-      return new BlobParent(aManager, aParams, /* aIDTableEntry */ nullptr);
+      nsRefPtr<FileImpl> blobImpl =
+        CreateBlobImplFromParams(aParams, ActorManagerIsSameProcess(aManager));
+      if (NS_WARN_IF(!blobImpl)) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+
+      nsID id;
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
+
+      nsRefPtr<IDTableEntry> idTableEntry =
+        IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
+      if (NS_WARN_IF(!idTableEntry)) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+
+      return new BlobParent(aManager, blobImpl, idTableEntry);
     }
 
     case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
       if (aParams.optionalInputStreamParams().type() !=
             OptionalInputStreamParams::Tvoid_t) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
       const SlicedBlobConstructorParams& params =
         blobParams.get_SlicedBlobConstructorParams();
-      if (NS_WARN_IF(params.optionalID().type() != OptionalID::TnsID)) {
+
+      if (NS_WARN_IF(params.end() < params.begin())) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
       auto* actor =
         const_cast<BlobParent*>(
           static_cast<const BlobParent*>(params.sourceParent()));
       MOZ_ASSERT(actor);
 
       nsRefPtr<FileImpl> source = actor->GetBlobImpl();
       MOZ_ASSERT(source);
 
-      Optional<int64_t> start;
-      start.Construct(params.begin());
-
-      Optional<int64_t> end;
-      end.Construct(params.end());
-
       ErrorResult rv;
       nsRefPtr<FileImpl> slice =
-        source->Slice(start, end, params.contentType(), rv);
+        source->CreateSlice(params.begin(),
+                            params.end() - params.begin(),
+                            params.contentType(),
+                            rv);
       if (NS_WARN_IF(rv.Failed())) {
+        ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(slice->SetMutable(false)));
 
       nsRefPtr<IDTableEntry> idTableEntry =
-        IDTableEntry::Create(params.optionalID(),
+        IDTableEntry::Create(params.id(),
                              ActorManagerProcessID(aManager),
                              slice);
       if (NS_WARN_IF(!idTableEntry)) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
-      return new BlobParent(aManager, idTableEntry);
+      return new BlobParent(aManager, slice, idTableEntry);
     }
 
     case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
       if (aParams.optionalInputStreamParams().type() !=
             OptionalInputStreamParams::Tvoid_t) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
@@ -3136,17 +3483,20 @@ BlobParent::CreateFromParams(ParentManag
 
       nsRefPtr<IDTableEntry> idTableEntry =
         IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager));
       if (NS_WARN_IF(!idTableEntry)) {
         ASSERT_UNLESS_FUZZING();
         return nullptr;
       }
 
-      return new BlobParent(aManager, aParams, idTableEntry);
+      nsRefPtr<FileImpl> blobImpl = idTableEntry->BlobImpl();
+      MOZ_ASSERT(blobImpl);
+
+      return new BlobParent(aManager, blobImpl, idTableEntry);
     }
 
     default:
       MOZ_CRASH("Unknown params!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
@@ -3307,40 +3657,74 @@ BlobParent::ActorDestroy(ActorDestroyRea
   mRemoteBlobImpl = nullptr;
   mBackgroundManager = nullptr;
   mContentManager = nullptr;
   mOwnsBlobImpl = false;
 #endif
 }
 
 PBlobStreamParent*
-BlobParent::AllocPBlobStreamParent()
+BlobParent::AllocPBlobStreamParent(const uint64_t& aStart,
+                                   const uint64_t& aLength)
 {
   AssertIsOnOwningThread();
 
-  if (NS_WARN_IF(mRemoteBlobImpl && !mRemoteBlobImpl->mForwards)) {
+  if (NS_WARN_IF(mRemoteBlobImpl)) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
   return new InputStreamParent();
 }
 
 bool
-BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor)
+BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
+                                       const uint64_t& aStart,
+                                       const uint64_t& aLength)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(mBlobImpl);
-  MOZ_ASSERT_IF(mRemoteBlobImpl, mRemoteBlobImpl->mForwards);
+  MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
+  // Make sure we can't overflow.
+  if (NS_WARN_IF(UINT64_MAX - aLength < aStart)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  ErrorResult errorResult;
+  uint64_t blobLength = mBlobImpl->GetSize(errorResult);
+  MOZ_ASSERT(!errorResult.Failed());
+
+  if (NS_WARN_IF(aStart + aLength > blobLength)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  nsRefPtr<FileImpl> blobImpl;
+
+  if (!aStart && aLength == blobLength) {
+    blobImpl = mBlobImpl;
+  } else {
+    nsString type;
+    mBlobImpl->GetType(type);
+
+    blobImpl = mBlobImpl->CreateSlice(aStart, aLength, type, errorResult);
+    if (NS_WARN_IF(errorResult.Failed())) {
+      return false;
+    }
+  }
+
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = mBlobImpl->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, false);
+  errorResult = blobImpl->GetInternalStream(getter_AddRefs(stream));
+  if (NS_WARN_IF(errorResult.Failed())) {
+    return false;
+  }
 
   // If the stream is entirely backed by memory then we can serialize and send
   // it immediately.
   if (mBlobImpl->IsMemoryFile()) {
     InputStreamParams params;
     nsTArray<FileDescriptor> fds;
     SerializeInputStream(stream, params, fds);
 
@@ -3373,26 +3757,28 @@ BlobParent::RecvPBlobStreamConstructor(P
     serializableStream = do_QueryInterface(stream);
     if (!serializableStream) {
       MOZ_ASSERT(false, "Must be serializable!");
       return false;
     }
   }
 
   nsCOMPtr<nsIThread> target;
-  rv = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  errorResult = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
+  if (NS_WARN_IF(errorResult.Failed())) {
     return false;
   }
 
   nsRefPtr<OpenStreamRunnable> runnable =
     new OpenStreamRunnable(this, aActor, stream, serializableStream, target);
 
-  rv = runnable->Dispatch();
-  NS_ENSURE_SUCCESS(rv, false);
+  errorResult = runnable->Dispatch();
+  if (NS_WARN_IF(errorResult.Failed())) {
+    return false;
+  }
 
   // nsRevocableEventPtr lacks some of the operators needed for anything nicer.
   *mOpenStreamRunnables.AppendElement() = runnable;
   return true;
 }
 
 bool
 BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor)
@@ -3464,17 +3850,16 @@ BlobParent::RecvResolveMystery(const Res
   MOZ_CRASH("Should never get here!");
 }
 
 bool
 BlobParent::RecvWaitForSliceCreation()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
-  MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
   // The whole point of this message is to ensure that the sliced blob created
   // by the child has been inserted into our IDTable.
   MOZ_ASSERT(mIDTableEntry);
 
 #ifdef DEBUG
   {
@@ -3487,18 +3872,16 @@ BlobParent::RecvWaitForSliceCreation()
 #endif
 
   return true;
 }
 
 bool
 BlobParent::RecvGetFileId(int64_t* aFileId)
 {
-  using namespace mozilla::dom::indexedDB;
-
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
   if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
     ASSERT_UNLESS_FUZZING();
     return false;
@@ -3506,18 +3889,16 @@ BlobParent::RecvGetFileId(int64_t* aFile
 
   *aFileId = mBlobImpl->GetFileId();
   return true;
 }
 
 bool
 BlobParent::RecvGetFilePath(nsString* aFilePath)
 {
-  using namespace mozilla::dom::indexedDB;
-
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
   // In desktop e10s the file picker code sends this message.
 #ifdef MOZ_CHILD_PERMISSIONS
   if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
@@ -3532,41 +3913,19 @@ BlobParent::RecvGetFilePath(nsString* aF
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
   *aFilePath = filePath;
   return true;
 }
 
-bool
-InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
-                                 const OptionalFileDescriptorSet& aFDs)
-{
-  MOZ_ASSERT(mRemoteStream);
-  mRemoteStream->AssertIsOnOwningThread();
-
-  nsTArray<FileDescriptor> fds;
-  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
-    FileDescriptorSetChild* fdSetActor =
-      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
-    MOZ_ASSERT(fdSetActor);
-
-    fdSetActor->ForgetFileDescriptors(fds);
-    MOZ_ASSERT(!fds.IsEmpty());
-
-    fdSetActor->Send__delete__(fdSetActor);
-  }
-
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
-  MOZ_ASSERT(stream);
-
-  mRemoteStream->SetStream(stream);
-  return true;
-}
+/*******************************************************************************
+ * BlobParent::IDTableEntry
+ ******************************************************************************/
 
 BlobParent::
 IDTableEntry::IDTableEntry(const nsID& aID,
                            intptr_t aProcessID,
                            FileImpl* aBlobImpl)
   : mID(aID)
   , mProcessID(aProcessID)
   , mBlobImpl(aBlobImpl)
@@ -3645,10 +4004,40 @@ IDTableEntry::GetOrCreateInternal(const 
     }
   }
 
   MOZ_ASSERT(entry);
 
   return entry.forget();
 }
 
+/*******************************************************************************
+ * Other stuff
+ ******************************************************************************/
+
+bool
+InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
+                                 const OptionalFileDescriptorSet& aFDs)
+{
+  MOZ_ASSERT(mRemoteStream);
+  mRemoteStream->AssertIsOnOwningThread();
+
+  nsTArray<FileDescriptor> fds;
+  if (aFDs.type() == OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+    FileDescriptorSetChild* fdSetActor =
+      static_cast<FileDescriptorSetChild*>(aFDs.get_PFileDescriptorSetChild());
+    MOZ_ASSERT(fdSetActor);
+
+    fdSetActor->ForgetFileDescriptors(fds);
+    MOZ_ASSERT(!fds.IsEmpty());
+
+    fdSetActor->Send__delete__(fdSetActor);
+  }
+
+  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  MOZ_ASSERT(stream);
+
+  mRemoteStream->SetStream(stream);
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/BlobChild.h
+++ b/dom/ipc/BlobChild.h
@@ -32,16 +32,19 @@ class PBlobStreamChild;
 class BlobChild MOZ_FINAL
   : public PBlobChild
 {
   typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
 
   class RemoteBlobImpl;
   friend class RemoteBlobImpl;
 
+  class RemoteBlobSliceImpl;
+  friend class RemoteBlobSliceImpl;
+
   FileImpl* mBlobImpl;
   RemoteBlobImpl* mRemoteBlobImpl;
 
   // One of these will be null and the other non-null.
   PBackgroundChild* mBackgroundManager;
   nsCOMPtr<nsIContentChild> mContentManager;
 
   nsCOMPtr<nsIEventTarget> mEventTarget;
@@ -136,42 +139,54 @@ private:
 
   // These constructors are called on the receiving side.
   BlobChild(nsIContentChild* aManager,
             const ChildBlobConstructorParams& aParams);
 
   BlobChild(PBackgroundChild* aManager,
             const ChildBlobConstructorParams& aParams);
 
+  // These constructors are called for slices.
+  BlobChild(nsIContentChild* aManager,
+            const nsID& aParentID,
+            RemoteBlobSliceImpl* aRemoteBlobSliceImpl);
+
+  BlobChild(PBackgroundChild* aManager,
+            const nsID& aParentID,
+            RemoteBlobSliceImpl* aRemoteBlobSliceImpl);
+
   // Only called by Destroy().
   ~BlobChild();
 
   void
   CommonInit(FileImpl* aBlobImpl);
 
   void
   CommonInit(BlobChild* aOther);
 
   void
   CommonInit(const ChildBlobConstructorParams& aParams);
 
+  void
+  CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl);
+
   template <class ChildManagerType>
   static BlobChild*
   GetOrCreateFromImpl(ChildManagerType* aManager, FileImpl* aBlobImpl);
 
   template <class ChildManagerType>
   static BlobChild*
   CreateFromParams(ChildManagerType* aManager,
                    const ChildBlobConstructorParams& aParams);
 
   template <class ChildManagerType>
   static BlobChild*
   SendSliceConstructor(ChildManagerType* aManager,
-                       const ChildBlobConstructorParams& aParams,
-                       const ParentBlobConstructorParams& aOtherSideParams);
+                       RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
+                       const ParentBlobConstructorParams& aParams);
 
   static BlobChild*
   MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
                               nsIContentChild* aManager);
 
   static BlobChild*
   MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
                               PBackgroundChild* aManager);
@@ -188,17 +203,18 @@ private:
   bool
   IsOnOwningThread() const;
 
   // These methods are only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PBlobStreamChild*
-  AllocPBlobStreamChild() MOZ_OVERRIDE;
+  AllocPBlobStreamChild(const uint64_t& aStart,
+                        const uint64_t& aLength) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBlobStreamChild(PBlobStreamChild* aActor) MOZ_OVERRIDE;
 };
 
 // Only let ContentChild call BlobChild::Startup() and ensure that
 // ContentChild can't access any other BlobChild internals.
 class BlobChild::FriendKey MOZ_FINAL
--- a/dom/ipc/BlobParent.h
+++ b/dom/ipc/BlobParent.h
@@ -43,27 +43,25 @@ class BlobParent MOZ_FINAL
   typedef mozilla::ipc::PBackgroundParent PBackgroundParent;
 
   class IDTableEntry;
   typedef nsDataHashtable<nsIDHashKey, IDTableEntry*> IDTable;
 
   class OpenStreamRunnable;
   friend class OpenStreamRunnable;
 
-  class RemoteBlobImplBase;
-  friend class RemoteBlobImplBase;
+  class RemoteBlobImpl;
 
-  class RemoteBlobImpl;
-  class ForwardingRemoteBlobImpl;
+  struct CreateBlobImplMetadata;
 
   static StaticAutoPtr<IDTable> sIDTable;
   static StaticAutoPtr<Mutex> sIDTableMutex;
 
   FileImpl* mBlobImpl;
-  RemoteBlobImplBase* mRemoteBlobImpl;
+  RemoteBlobImpl* mRemoteBlobImpl;
 
   // One of these will be null and the other non-null.
   PBackgroundParent* mBackgroundManager;
   nsCOMPtr<nsIContentParent> mContentManager;
 
   nsCOMPtr<nsIEventTarget> mEventTarget;
 
   // nsIInputStreams backed by files must ensure that the files are actually
@@ -143,32 +141,31 @@ public:
 private:
   // These constructors are called on the sending side.
   BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry);
 
   BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry);
 
   // These constructors are called on the receiving side.
   BlobParent(nsIContentParent* aManager,
-             const ParentBlobConstructorParams& aParams,
+             FileImpl* aBlobImpl,
              IDTableEntry* aIDTableEntry);
 
   BlobParent(PBackgroundParent* aManager,
-             const ParentBlobConstructorParams& aParams,
+             FileImpl* aBlobImpl,
              IDTableEntry* aIDTableEntry);
 
   // Only destroyed by BackgroundParentImpl and ContentParent.
   ~BlobParent();
 
   void
   CommonInit(IDTableEntry* aIDTableEntry);
 
   void
-  CommonInit(const ParentBlobConstructorParams& aParams,
-             IDTableEntry* aIDTableEntry);
+  CommonInit(FileImpl* aBlobImpl, IDTableEntry* aIDTableEntry);
 
   template <class ParentManagerType>
   static BlobParent*
   GetOrCreateFromImpl(ParentManagerType* aManager,
                       FileImpl* aBlobImpl);
 
   template <class ParentManagerType>
   static BlobParent*
@@ -204,20 +201,23 @@ private:
   bool
   IsOnOwningThread() const;
 
   // These methods are only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PBlobStreamParent*
-  AllocPBlobStreamParent() MOZ_OVERRIDE;
+  AllocPBlobStreamParent(const uint64_t& aStart,
+                         const uint64_t& aLength) MOZ_OVERRIDE;
 
   virtual bool
-  RecvPBlobStreamConstructor(PBlobStreamParent* aActor) MOZ_OVERRIDE;
+  RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
+                             const uint64_t& aStart,
+                             const uint64_t& aLength) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBlobStreamParent(PBlobStreamParent* aActor) MOZ_OVERRIDE;
 
   virtual bool
   RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
 
   virtual bool
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -40,18 +40,17 @@ struct FileBlobConstructorParams
   nsString contentType;
   uint64_t length;
   uint64_t modDate;
 };
 
 struct SlicedBlobConstructorParams
 {
   PBlob source;
-  // Must be void_t for parent -> child and nsID for child -> parent.
-  OptionalID optionalID;
+  nsID id;
   uint64_t begin;
   uint64_t end;
   nsString contentType;
 };
 
 struct MysteryBlobConstructorParams
 {
   // Nothing is known about this type of blob.
@@ -59,28 +58,33 @@ struct MysteryBlobConstructorParams
 
 struct KnownBlobConstructorParams
 {
   nsID id;
 };
 
 union AnyBlobConstructorParams
 {
+  // These types may be sent to/from parent and child.
   NormalBlobConstructorParams;
   FileBlobConstructorParams;
+
+  // This type may only be sent from parent to child.
+  MysteryBlobConstructorParams;
+
+  // These types may only be sent from child to parent.
   SlicedBlobConstructorParams;
-  MysteryBlobConstructorParams;
   KnownBlobConstructorParams;
 };
 
 struct ChildBlobConstructorParams
 {
   nsID id;
 
-  // May never be KnownBlobConstructorParams.
+  // May not be SlicedBlobConstructorParams or KnownBlobConstructorParams.
   AnyBlobConstructorParams blobParams;
 };
 
 struct ParentBlobConstructorParams
 {
   // May not be MysteryBlobConstructorParams.
   AnyBlobConstructorParams blobParams;
 
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -22,17 +22,17 @@ sync protocol PBlob
 {
   manager PBackground or PContent or PContentBridge;
   manages PBlobStream;
 
 both:
   __delete__();
 
 parent:
-  PBlobStream();
+  PBlobStream(uint64_t begin, uint64_t length);
 
   ResolveMystery(ResolveMysteryParams params);
 
   sync WaitForSliceCreation();
 
   // Use only for testing!
   sync GetFileId()
     returns (int64_t fileId);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -393,18 +393,16 @@ parent:
      * OnAuthAvailable or OnAuthCancelled message.
      */
     AsyncAuthPrompt(nsCString uri, nsString realm, uint64_t aCallbackId);
 
     __delete__();
 
     ReplyKeyEvent(WidgetKeyboardEvent event);
 
-    DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
-
     sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
         returns (MaybeNativeKeyBinding bindings);
 
     /**
      * Child informs the parent that the graphics objects are ready for
      * compositing.  This is sent when all pending changes have been
      * sent to the compositor and are ready to be shown on the next composite.
      * @see PCompositor
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/layers/ActiveElementManager.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
-#include "mozilla/layers/AsyncPanZoomController.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
@@ -76,18 +76,18 @@
 #include "JavaScriptChild.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
 #include "LayersLogging.h"
 #include "nsIOService.h"
+
 #include "nsColorPickerProxy.h"
-#include "nsPresShell.h"
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
@@ -426,17 +426,17 @@ TabChildBase::HandlePossibleViewportChan
   if (!pageSize.width) {
     // Return early rather than divide by 0.
     return false;
   }
   metrics.mScrollableRect = CSSRect(CSSPoint(), pageSize);
 
   // Calculate a display port _after_ having a scrollable rect because the
   // display port is clamped to the scrollable rect.
-  metrics.SetDisplayPortMargins(AsyncPanZoomController::CalculatePendingDisplayPort(
+  metrics.SetDisplayPortMargins(APZCTreeManager::CalculatePendingDisplayPort(
     // The page must have been refreshed in some way such as a new document or
     // new CSS viewport, so we know that there's no velocity, acceleration, and
     // we have no idea how long painting will take.
     metrics, ScreenPoint(0.0f, 0.0f), 0.0));
   metrics.SetUseDisplayPortMargins();
 
   // Force a repaint with these metrics. This, among other things, sets the
   // displayport, so we start with async painting.
@@ -2357,20 +2357,16 @@ TabChild::RecvRealKeyEvent(const WidgetK
   if (event.message == NS_KEY_DOWN) {
     mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
   }
 
   if (localEvent.mFlags.mWantReplyFromContentProcess) {
     SendReplyKeyEvent(localEvent);
   }
 
-  if (PresShell::BeforeAfterKeyboardEventEnabled()) {
-    SendDispatchAfterKeyboardEvent(localEvent);
-  }
-
   return true;
 }
 
 bool
 TabChild::RecvKeyEvent(const nsString& aType,
                        const int32_t& aKeyCode,
                        const int32_t& aCharCode,
                        const int32_t& aModifiers,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -47,17 +47,16 @@
 #include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowWatcher.h"
-#include "nsPresShell.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsWindowWatcher.h"
 #include "private/pprio.h"
 #include "PermissionMessageUtils.h"
 #include "StructuredCloneUtils.h"
 #include "ColorPickerParent.h"
@@ -1459,39 +1458,16 @@ TabParent::RecvReplyKeyEvent(const Widge
   NS_ENSURE_TRUE(presShell, true);
   nsPresContext* presContext = presShell->GetPresContext();
   NS_ENSURE_TRUE(presContext, true);
 
   EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
   return true;
 }
 
-bool
-TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
-{
-  NS_ENSURE_TRUE(mFrameElement, true);
-
-  WidgetKeyboardEvent localEvent(aEvent);
-  localEvent.widget = GetWidget();
-
-  nsIDocument* doc = mFrameElement->OwnerDoc();
-  nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
-  NS_ENSURE_TRUE(presShell, true);
-
-  if (mFrameElement &&
-      PresShell::BeforeAfterKeyboardEventEnabled() &&
-      localEvent.message != NS_KEY_PRESS) {
-    nsCOMPtr<nsINode> node(do_QueryInterface(mFrameElement));
-    presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent,
-                                          aEvent.mFlags.mDefaultPrevented);
-  }
-
-  return true;
-}
-
 /**
  * Try to answer query event using cached text.
  *
  * For NS_QUERY_SELECTED_TEXT, fail if the cache doesn't contain the whole
  *  selected range. (This shouldn't happen because PuppetWidget should have
  *  already sent the whole selection.)
  *
  * For NS_QUERY_TEXT_CONTENT, fail only if the cache doesn't overlap with
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -117,17 +117,16 @@ public:
      */
     bool TryCapture(const WidgetGUIEvent& aEvent);
 
     void Destroy();
 
     virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
     virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& event);
-    virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& event);
     virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* aActor,
                                              ScrollingBehavior* aScrolling,
                                              TextureFactoryIdentifier* aFactoryIdentifier,
                                              uint64_t* aLayersId,
                                              bool* aSuccess) MOZ_OVERRIDE;
     virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                             const nsString& aURL,
                                             const nsString& aName,
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -1,8 +1,14 @@
+[test_blob_sliced_from_child_process.html]
+# This test is only supposed to run in the main process.
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
+[test_blob_sliced_from_parent_process.html]
+# This test is only supposed to run in the main process.
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
 [test_NuwaProcessCreation.html]
 run-if = toolkit == 'gonk'
 [test_NuwaProcessDeadlock.html]
 run-if = toolkit == 'gonk'
 [test_child_docshell.html]
 run-if = toolkit != 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf
 [test_CrashService_crash.html]
 run-if = crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows') && (buildapp != 'b2g' || toolkit == 'gonk')
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_child_process.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test for slicing blobs that originated in a child process</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
+    </script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  </head>
+  <body onload="setup();">
+    <script type="application/javascript;version=1.7">
+"use strict";
+
+function childFrameScript() {
+  "use strict";
+
+  Components.utils.importGlobalProperties(["Blob"]);
+
+  const messageName = "test:blob-slice-test";
+  const blobData = ["So", " ", "many", " ", "blobs!"];
+  const blobText = blobData.join("");
+  const blobType = "text/plain";
+
+  let blob = new Blob(blobData, { type: blobType });
+
+  let sliceText = blobData[2][1] + blobData[2][2];
+  let sliceIndex = blobText.indexOf(sliceText);
+
+  let firstSliceStart = blobData[0].length + blobData[1].length;
+  let firstSliceEnd = firstSliceStart + blobData[2].length;
+
+  let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType);
+
+  let secondSliceStart = blobData[2].indexOf("a");
+  let secondSliceEnd = secondSliceStart + 2;
+
+  slice = slice.slice(secondSliceStart, secondSliceEnd, blobType);
+
+  sendAsyncMessage(messageName, { blob: blob });
+  sendAsyncMessage(messageName, { slice: slice });
+}
+
+function parentFrameScript(mm) {
+  const messageName = "test:blob-slice-test";
+  const blobData = ["So", " ", "many", " ", "blobs!"];
+  const blobText = blobData.join("");
+  const blobType = "text/plain";
+
+  const sliceText = "an";
+
+  let receivedBlob = false;
+  let receivedSlice = false;
+
+  let finishedTestingBlob = false;
+  let finishedTestingSlice = false;
+
+  mm.addMessageListener(messageName, function(message) {
+    if ("blob" in message.data) {
+      is(receivedBlob, false, "Have not yet received Blob");
+      is(receivedSlice, false, "Have not yet received Slice");
+      is(finishedTestingBlob, false, "Have not yet finished testing Blob");
+      is(finishedTestingSlice, false, "Have not yet finished testing Slice");
+
+      receivedBlob = true;
+
+      let blob = message.data.blob;
+
+      ok(blob instanceof Blob, "Received a Blob");
+      is(blob.size, blobText.length, "Blob has correct size");
+      is(blob.type, blobType, "Blob has correct type");
+
+      let slice = blob.slice(blobText.length -
+                                blobData[blobData.length - 1].length,
+                              blob.size,
+                              blobType);
+
+      ok(slice instanceof Blob, "Slice returned a Blob");
+      is(slice.size,
+          blobData[blobData.length - 1].length,
+          "Slice has correct size");
+      is(slice.type, blobType, "Slice has correct type");
+
+      let reader = new FileReader();
+      reader.onload = function() {
+        is(reader.result,
+            blobData[blobData.length - 1],
+            "Slice has correct data");
+
+        finishedTestingBlob = true;
+
+        if (finishedTestingSlice) {
+          SimpleTest.finish();
+        }
+      };
+      reader.readAsText(slice);
+
+      return;
+    }
+
+    if ("slice" in message.data) {
+      is(receivedBlob, true, "Already received Blob");
+      is(receivedSlice, false, "Have not yet received Slice");
+      is(finishedTestingSlice, false, "Have not yet finished testing Slice");
+
+      receivedSlice = true;
+
+      let slice = message.data.slice;
+
+      ok(slice instanceof Blob, "Received a Blob for slice");
+      is(slice.size, sliceText.length, "Slice has correct size");
+      is(slice.type, blobType, "Slice has correct type");
+
+      let reader = new FileReader();
+      reader.onload = function() {
+        is(reader.result, sliceText, "Slice has correct data");
+
+        let slice2 = slice.slice(1, 2, blobType);
+
+        ok(slice2 instanceof Blob, "Slice returned a Blob");
+        is(slice2.size, 1, "Slice has correct size");
+        is(slice2.type, blobType, "Slice has correct type");
+
+        let reader2 = new FileReader();
+        reader2.onload = function() {
+          is(reader2.result, sliceText[1], "Slice has correct data");
+
+          finishedTestingSlice = true;
+
+          if (finishedTestingBlob) {
+            SimpleTest.finish();
+          }
+        };
+        reader2.readAsText(slice2);
+      };
+      reader.readAsText(slice);
+
+      return;
+    }
+
+    ok(false, "Received a bad message: " + JSON.stringify(message.data));
+  });
+
+  mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+                      false);
+}
+
+function setup() {
+  info("Got load event");
+
+  SpecialPowers.pushPrefEnv(
+    { set: [ ["dom.ipc.browser_frames.oop_by_default", true],
+              ["dom.mozBrowserFramesEnabled", true],
+              ["browser.pagethumbnails.capturing_disabled", true] ] },
+    function() {
+      info("Prefs set");
+
+      SpecialPowers.pushPermissions(
+        [ { type: "browser", allow: true, context: document } ],
+        function() {
+          info("Permissions set");
+
+          let iframe = document.createElement("iframe");
+          SpecialPowers.wrap(iframe).mozbrowser = true;
+          iframe.id = "iframe";
+          iframe.src =
+            "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+          iframe.addEventListener("mozbrowserloadend", function() {
+            info("Starting tests");
+
+            let mm = SpecialPowers.getBrowserFrameMessageManager(iframe)
+            parentFrameScript(mm);
+          });
+
+          document.body.appendChild(iframe);
+        }
+      );
+    }
+  );
+}
+
+SimpleTest.waitForExplicitFinish();
+  </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test for slicing blobs that originated in a parent process</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
+    </script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  </head>
+  <body onload="setup();">
+    <script type="application/javascript;version=1.7">
+"use strict";
+
+function childFrameScript() {
+  "use strict";
+
+  const { classes: Cc, interfaces: Ci } = Components;
+
+  const messageName = "test:blob-slice-test";
+  const blobData = ["So", " ", "many", " ", "blobs!"];
+  const blobText = blobData.join("");
+  const blobType = "text/plain";
+
+  const sliceText = "an";
+
+  function info(msg) {
+    sendAsyncMessage(messageName, { op: "info", msg: msg });
+  }
+
+  function ok(condition, name, diag) {
+    sendAsyncMessage(messageName,
+                     { op: "ok",
+                       condition: condition,
+                       name: name,
+                       diag: diag });
+  }
+
+  function is(a, b, name) {
+    let pass = a == b;
+    let diag = pass ? "" : "got " + a + ", expected " + b;
+    ok(pass, name, diag);
+  }
+
+  function finish(result) {
+    sendAsyncMessage(messageName, { op: "done", result: result });
+  }
+
+  function createFileReader() {
+    return Cc["@mozilla.org/files/filereader;1"]
+             .createInstance(Ci.nsIDOMFileReader);
+  }
+
+  function grabAndContinue(arg) {
+    testGenerator.send(arg);
+  }
+
+  function testSteps() {
+    addMessageListener(messageName, grabAndContinue);
+    let message = yield undefined;
+
+    let blob = message.data;
+
+    ok(blob instanceof Ci.nsIDOMBlob, "Received a Blob");
+    is(blob.size, blobText.length, "Blob has correct length");
+    is(blob.type, blobType, "Blob has correct type");
+
+    info("Reading blob");
+
+    let reader = createFileReader();
+    reader.addEventListener("load", grabAndContinue);
+    reader.readAsText(blob);
+
+    yield undefined;
+
+    is(reader.result, blobText, "Blob has correct data");
+
+    let firstSliceStart = blobData[0].length + blobData[1].length;
+    let firstSliceEnd = firstSliceStart + blobData[2].length;
+
+    let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType);
+
+    ok(slice instanceof Ci.nsIDOMBlob, "Slice returned a Blob");
+    is(slice.size, blobData[2].length, "Slice has correct length");
+    is(slice.type, blobType, "Slice has correct type");
+
+    info("Reading slice");
+
+    reader = createFileReader();
+    reader.addEventListener("load", grabAndContinue);
+    reader.readAsText(slice);
+
+    yield undefined;
+
+    is(reader.result, blobData[2], "Slice has correct data");
+
+    let secondSliceStart = blobData[2].indexOf("a");
+    let secondSliceEnd = secondSliceStart + sliceText.length;
+
+    slice = slice.slice(secondSliceStart, secondSliceEnd, blobType);
+
+    ok(slice instanceof Ci.nsIDOMBlob, "Second slice returned a Blob");
+    is(slice.size, sliceText.length, "Second slice has correct length");
+    is(slice.type, blobType, "Second slice has correct type");
+
+    info("Sending second slice");
+    finish(slice);
+
+    yield undefined;
+  }
+
+  let testGenerator = testSteps();
+  testGenerator.next();
+}
+
+function parentFrameScript(mm) {
+  const messageName = "test:blob-slice-test";
+  const blobData = ["So", " ", "many", " ", "blobs!"];
+  const blobText = blobData.join("");
+  const blobType = "text/plain";
+
+  const sliceText = "an";
+
+  function grabAndContinue(arg) {
+    testGenerator.send(arg);
+  }
+
+  function testSteps() {
+    let slice = yield undefined;
+
+    ok(slice instanceof Blob, "Received a Blob");
+    is(slice.size, sliceText.length, "Slice has correct size");
+    is(slice.type, blobType, "Slice has correct type");
+
+    let reader = new FileReader();
+    reader.onload = grabAndContinue;
+    reader.readAsText(slice);
+    yield undefined;
+
+    is(reader.result, sliceText, "Slice has correct data");
+    SimpleTest.finish();
+
+    yield undefined;
+  }
+
+  let testGenerator = testSteps();
+  testGenerator.next();
+
+  mm.addMessageListener(messageName, function(message) {
+    let data = message.data;
+    switch (data.op) {
+      case "info": {
+        info(data.msg);
+        break;
+      }
+
+      case "ok": {
+        ok(data.condition, data.name, data.diag);
+        break;
+      }
+
+      case "done": {
+        testGenerator.send(data.result);
+        break;
+      }
+
+      default: {
+        ok(false, "Unknown op: " + data.op);
+        SimpleTest.finish();
+      }
+    }
+  });
+
+  mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+                      false);
+
+  let blob = new Blob(blobData, { type: blobType });
+  mm.sendAsyncMessage(messageName, blob);
+}
+
+function setup() {
+  info("Got load event");
+
+  SpecialPowers.pushPrefEnv(
+    { set: [ ["dom.ipc.browser_frames.oop_by_default", true],
+             ["dom.mozBrowserFramesEnabled", true],
+             ["browser.pagethumbnails.capturing_disabled", true] ] },
+    function() {
+      info("Prefs set");
+
+      SpecialPowers.pushPermissions(
+        [ { type: "browser", allow: true, context: document } ],
+        function() {
+          info("Permissions set");
+
+          let iframe = document.createElement("iframe");
+          SpecialPowers.wrap(iframe).mozbrowser = true;
+          iframe.id = "iframe";
+          iframe.src =
+            "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+          iframe.addEventListener("mozbrowserloadend", function() {
+            info("Starting tests");
+
+            let mm = SpecialPowers.getBrowserFrameMessageManager(iframe)
+            parentFrameScript(mm);
+          });
+
+          document.body.appendChild(iframe);
+        }
+      );
+    }
+  );
+}
+
+SimpleTest.waitForExplicitFinish();
+    </script>
+  </body>
+</html>
--- a/dom/mobileconnection/gonk/MobileConnectionService.js
+++ b/dom/mobileconnection/gonk/MobileConnectionService.js
@@ -22,18 +22,18 @@ const GONK_MOBILECONNECTIONSERVICE_CID =
 const MOBILECONNECTIONINFO_CID =
   Components.ID("{8162b3c0-664b-45f6-96cd-f07b4e193b0e}");
 const MOBILENETWORKINFO_CID =
   Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
 const MOBILECELLINFO_CID =
   Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}");
 const MOBILECALLFORWARDINGOPTIONS_CID =
   Components.ID("{e0cf4463-ee63-4b05-ab2e-d94bf764836c}");
-const TELEPHONYCALLBACK_CID =
-  Components.ID("{6e1af17e-37f3-11e4-aed3-60a44c237d2b}");
+const TELEPHONYDIALCALLBACK_CID =
+  Components.ID("{c2af1a5d-3649-44ef-a1ff-18e9ac1dec51}");
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID  = "nsPref:changed";
 const NS_NETWORK_ACTIVE_CHANGED_TOPIC_ID = "network-active-changed";
 
 const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
@@ -134,24 +134,24 @@ MobileCallForwardingOptions.prototype = 
   action: Ci.nsIMobileConnection.CALL_FORWARD_ACTION_UNKNOWN,
   reason: Ci.nsIMobileConnection.CALL_FORWARD_REASON_UNKNOWN,
   number: null,
   timeSeconds: -1,
   serviceClass: Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE
 }
 
 /**
- * Wrap a MobileConnectionCallback to a TelephonyCallback.
+ * Wrap a MobileConnectionCallback to a TelephonyDialCallback.
  */
-function TelephonyCallback(aCallback) {
+function TelephonyDialCallback(aCallback) {
   this.callback = aCallback;
 }
-TelephonyCallback.prototype = {
-  QueryInterface:   XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
-  classID:          TELEPHONYCALLBACK_CID,
+TelephonyDialCallback.prototype = {
+  QueryInterface:   XPCOMUtils.generateQI([Ci.nsITelephonyDialCallback]),
+  classID:          TELEPHONYDIALCALLBACK_CID,
 
   _notifySendCancelMmiSuccess: function(aResult) {
     // No additional information.
     if (aResult.additionalInformation === undefined) {
       this.callback.notifySendCancelMmiSuccess(aResult.serviceCode,
                                                aResult.statusMessage);
       return;
     }
@@ -687,18 +687,18 @@ MobileConnectionProvider.prototype = {
       }
 
       aCallback.notifySuccessWithBoolean(aResponse.enabled);
       return false;
     }).bind(this));
   },
 
   sendMMI: function(aMmi, aCallback) {
-    let telephonyCallback = new TelephonyCallback(aCallback);
-    gGonkTelephonyService.dialMMI(this._clientId, aMmi, telephonyCallback);
+    let callback = new TelephonyDialCallback(aCallback);
+    gGonkTelephonyService.dialMMI(this._clientId, aMmi, callback);
   },
 
   cancelMMI: function(aCallback) {
     this._radioInterface.sendWorkerMessage("cancelUSSD", null,
                                            (function(aResponse) {
       if (aResponse.errorMsg) {
         aCallback.notifyError(aResponse.errorMsg);
         return false;
@@ -995,24 +995,16 @@ MobileConnectionService.prototype = {
   notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
     if (DEBUG) {
       debug("notifyUssdReceived for " + aClientId + ": " +
             aMessage + " (sessionEnded : " + aSessionEnded + ")");
     }
 
     this.getItemByServiceId(aClientId)
         .deliverListenerEvent("notifyUssdReceived", [aMessage, aSessionEnded]);
-
-    let info = {
-      message: aMessage,
-      sessionEnded: aSessionEnded,
-      serviceId: aClientId
-    };
-
-    gSystemMessenger.broadcastMessage("ussd-received", info);
   },
 
   notifyDataError: function(aClientId, aMessage) {
     if (DEBUG) {
       debug("notifyDataError for " + aClientId + ": " + aMessage);
     }
 
     this.getItemByServiceId(aClientId)
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -625,16 +625,17 @@ Nfc.prototype = {
       case "NFC:PowerOff":
         this.setConfig({powerLevel: NFC.NFC_POWER_LEVEL_DISABLED,
                         requestId: message.data.requestId});
         break;
       case "NFC:ReadNDEF":
         this.sendToNfcService("readNDEF", message.data);
         break;
       case "NFC:WriteNDEF":
+        message.data.isP2P = SessionHelper.isP2PSession(message.data.sessionId);
         this.sendToNfcService("writeNDEF", message.data);
         break;
       case "NFC:MakeReadOnlyNDEF":
         this.sendToNfcService("makeReadOnlyNDEF", message.data);
         break;
       case "NFC:Connect":
         this.sendToNfcService("connect", message.data);
         break;
--- a/dom/nfc/gonk/NfcGonkMessage.h
+++ b/dom/nfc/gonk/NfcGonkMessage.h
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NfcGonkMessage_h
 #define NfcGonkMessage_h
 
 namespace mozilla {
 
 #define NFCD_MAJOR_VERSION 1
-#define NFCD_MINOR_VERSION 10
+#define NFCD_MINOR_VERSION 12
 
 enum NfcRequest {
   ConfigReq = 0,
   ConnectReq,
   CloseReq,
   ReadNDEFReq,
   WriteNDEFReq,
   MakeReadOnlyNDEFReq,
--- a/dom/nfc/gonk/NfcMessageHandler.cpp
+++ b/dom/nfc/gonk/NfcMessageHandler.cpp
@@ -191,16 +191,17 @@ NfcMessageHandler::ReadNDEFResponse(cons
   return true;
 }
 
 bool
 NfcMessageHandler::WriteNDEFRequest(Parcel& aParcel, const CommandOptions& aOptions)
 {
   aParcel.writeInt32(NfcRequest::WriteNDEFReq);
   aParcel.writeInt32(aOptions.mSessionId);
+  aParcel.writeInt32(aOptions.mIsP2P);
   WriteNDEFMessage(aParcel, aOptions);
   mRequestIdQueue.AppendElement(aOptions.mRequestId);
   return true;
 }
 
 bool
 NfcMessageHandler::MakeReadOnlyNDEFRequest(Parcel& aParcel, const CommandOptions& aOptions)
 {
--- a/dom/nfc/gonk/NfcOptions.h
+++ b/dom/nfc/gonk/NfcOptions.h
@@ -31,16 +31,17 @@ struct CommandOptions
     prop = defaultValue;                              \
   }
 
     COPY_FIELD(mType)
     COPY_FIELD(mRequestId)
     COPY_OPT_FIELD(mSessionId, 0)
     COPY_OPT_FIELD(mPowerLevel, 0)
     COPY_OPT_FIELD(mTechType, 0)
+    COPY_OPT_FIELD(mIsP2P, false)
 
     if (!aOther.mRecords.WasPassed()) {
       return;
     }
 
     mozilla::dom::Sequence<mozilla::dom::MozNDEFRecordOptions> const & currentValue = aOther.mRecords.InternalValue();
     int count = currentValue.Length();
     for (int32_t i = 0; i < count; i++) {
@@ -72,16 +73,17 @@ struct CommandOptions
 #undef COPY_OPT_FIELD
   }
 
   nsString mType;
   int32_t mSessionId;
   nsString mRequestId;
   int32_t mPowerLevel;
   int32_t mTechType;
+  bool mIsP2P;
   nsTArray<NDEFRecordStruct> mRecords;
 };
 
 struct EventOptions
 {
   EventOptions()
     : mType(EmptyString()), mStatus(-1), mSessionId(-1), mRequestId(EmptyString()), mMajorVersion(-1), mMinorVersion(-1),
       mIsReadOnly(-1), mCanBeMadeReadOnly(-1), mMaxSupportedLength(-1), mPowerLevel(-1),
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -26,42 +26,26 @@ XPCOMUtils.defineLazyServiceGetter(this,
 /**
  * NFCTag
  */
 function MozNFCTag() {
   debug("In MozNFCTag Constructor");
   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                              .getService(Ci.nsINfcContentHelper);
   this.session = null;
-
-  // Map WebIDL declared enum map names to integer
-  this._techTypesMap = [];
-  this._techTypesMap['NFC_A'] = 0;
-  this._techTypesMap['NFC_B'] = 1;
-  this._techTypesMap['NFC_ISO_DEP'] = 2;
-  this._techTypesMap['NFC_F'] = 3;
-  this._techTypesMap['NFC_V'] = 4;
-  this._techTypesMap['NDEF'] = 5;
-  this._techTypesMap['NDEF_FORMATABLE'] = 6;
-  this._techTypesMap['MIFARE_CLASSIC'] = 7;
-  this._techTypesMap['MIFARE_ULTRALIGHT'] = 8;
-  this._techTypesMap['NFC_BARCODE'] = 9;
-  this._techTypesMap['P2P'] = 10;
 }
 MozNFCTag.prototype = {
   _nfcContentHelper: null,
   _window: null,
 
   initialize: function(aWindow, aSessionToken) {
     this._window = aWindow;
     this.session = aSessionToken;
   },
 
-  _techTypesMap: null,
-
   // NFCTag interface:
   readNDEF: function readNDEF() {
     return this._nfcContentHelper.readNDEF(this._window, this.session);
   },
   writeNDEF: function writeNDEF(records) {
     return this._nfcContentHelper.writeNDEF(this._window, records, this.session);
   },
   makeReadOnlyNDEF: function makeReadOnlyNDEF() {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1980,23 +1980,28 @@ RadioInterface.prototype = {
         break;
       case "callDisconnected":
         gTelephonyService.notifyCallDisconnected(this.clientId, message.call);
         break;
       case "conferenceCallStateChanged":
         gTelephonyService.notifyConferenceCallStateChanged(message.state);
         break;
       case "cdmaCallWaiting":
-        gTelephonyService.notifyCdmaCallWaiting(this.clientId, message.waitingCall);
+        gTelephonyService.notifyCdmaCallWaiting(this.clientId,
+                                                message.waitingCall);
         break;
       case "suppSvcNotification":
         gTelephonyService.notifySupplementaryService(this.clientId,
                                                      message.callIndex,
                                                      message.notification);
         break;
+      case "ussdreceived":
+        gTelephonyService.notifyUssdReceived(this.clientId, message.message,
+                                             message.sessionEnded);
+        break;
       case "datacallerror":
         connHandler.handleDataCallError(message);
         break;
       case "datacallstatechange":
         let addresses = [];
         for (let i = 0; i < message.addresses.length; i++) {
           let [address, prefixLength] = message.addresses[i].split("/");
           // From AOSP hardware/ril/include/telephony/ril.h, that address prefix
@@ -2040,21 +2045,16 @@ RadioInterface.prototype = {
         gMobileConnectionService.notifyOtaStatusChanged(this.clientId, message.status);
         break;
       case "radiostatechange":
         // gRadioEnabledController should know the radio state for each client,
         // so notify gRadioEnabledController here.
         gRadioEnabledController.notifyRadioStateChanged(this.clientId,
                                                         message.radioState);
         break;
-      case "ussdreceived":
-        gMobileConnectionService.notifyUssdReceived(this.clientId,
-                                                    message.message,
-                                                    message.sessionEnded);
-        break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
         gRadioEnabledController.receiveCardState(this.clientId);
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
         this.handleSmsMultipart(message);
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2636,32 +2636,58 @@ RilObject.prototype = {
     }
 
     // If the MMI code is not a known code, it is treated as an ussd.
     if (!_isRadioAvailable()) {
       return;
     }
 
     options.ussd = mmi.fullMMI;
+
+    if (options.startNewSession && this._ussdSession) {
+      if (DEBUG) this.context.debug("Cancel existing ussd session.");
+      this.cachedUSSDRequest = options;
+      this.cancelUSSD({});
+      return;
+    }
+
     this.sendUSSD(options);
   },
 
   /**
+   * Cache the request for send out a new ussd when there is an existing
+   * session. We should do cancelUSSD first.
+   */
+  cachedUSSDRequest : null,
+
+  /**
    * Send USSD.
    *
    * @param ussd
    *        String containing the USSD code.
-   *
-   */
-   sendUSSD: function(options) {
-     let Buf = this.context.Buf;
-     Buf.newParcel(REQUEST_SEND_USSD, options);
-     Buf.writeString(options.ussd);
-     Buf.sendParcel();
-   },
+   * @param checkSession
+   *        True if an existing session should be there.
+   */
+  sendUSSD: function(options) {
+    if (options.checkSession && !this._ussdSession) {
+      options.success = false;
+      options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
+      this.sendChromeMessage(options);
+      return;
+    }
+
+    this.sendRilRequestSendUSSD(options);
+  },
+
+  sendRilRequestSendUSSD: function(options) {
+    let Buf = this.context.Buf;
+    Buf.newParcel(REQUEST_SEND_USSD, options);
+    Buf.writeString(options.ussd);
+    Buf.sendParcel();
+  },
 
   /**
    * Cancel pending USSD.
    */
    cancelUSSD: function(options) {
      this.context.Buf.simpleRequest(REQUEST_CANCEL_USSD, options);
    },
 
@@ -5674,19 +5700,29 @@ RilObject.prototype[REQUEST_SEND_USSD] =
   options.success = (this._ussdSession = options.rilRequestError === 0);
   options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
   this.sendChromeMessage(options);
 };
 RilObject.prototype[REQUEST_CANCEL_USSD] = function REQUEST_CANCEL_USSD(length, options) {
   if (DEBUG) {
     this.context.debug("REQUEST_CANCEL_USSD" + JSON.stringify(options));
   }
+
   options.success = (options.rilRequestError === 0);
   this._ussdSession = !options.success;
   options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
+
+  // The cancelUSSD is triggered by ril_worker itself.
+  if (this.cachedUSSDRequest) {
+    if (DEBUG) this.context.debug("Send out the cached ussd request");
+    this.sendUSSD(this.cachedUSSDRequest);
+    this.cachedUSSDRequest = null;
+    return;
+  }
+
   this.sendChromeMessage(options);
 };
 RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, options) {
   options.success = (options.rilRequestError === 0);
   if (!options.success) {
     options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
     this.sendChromeMessage(options);
     return;
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -21,17 +21,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
 #include "CallsList.h"
 #include "TelephonyCall.h"
 #include "TelephonyCallGroup.h"
 #include "TelephonyCallId.h"
-#include "TelephonyCallback.h"
+#include "TelephonyDialCallback.h"
 
 // Service instantiation
 #include "ipc/TelephonyIPCService.h"
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
 #include "nsIGonkTelephonyService.h"
 #endif
 #include "nsXULAppAPI.h" // For XRE_GetProcessType()
 
@@ -230,18 +230,18 @@ Telephony::DialInternal(uint32_t aServic
   }
 
   // We only support one outgoing call at a time.
   if (HasDialingCall()) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
-  nsCOMPtr<nsITelephonyCallback> callback =
-    new TelephonyCallback(GetOwner(), this, promise, aServiceId);
+  nsCOMPtr<nsITelephonyDialCallback> callback =
+    new TelephonyDialCallback(GetOwner(), this, promise, aServiceId);
 
   nsresult rv = mService->Dial(aServiceId, aNumber, aEmergency, callback);
   if (NS_FAILED(rv)) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   return promise.forget();
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -18,17 +18,17 @@
 #include "TelephonyCall.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 namespace telephony {
 
-class TelephonyCallback;
+class TelephonyDialCallback;
 
 } // namespace telephony
 
 class OwningTelephonyCallOrTelephonyCallGroup;
 
 class Telephony MOZ_FINAL : public DOMEventTargetHelper,
                             private nsITelephonyListener
 {
@@ -38,17 +38,17 @@ class Telephony MOZ_FINAL : public DOMEv
    * and passes it to nsITelephonyService. The onreceived events are first
    * delivered to mListener and then forwarded to its owner, Telephony. See
    * also bug 775997 comment #51.
    */
   class Listener;
   class EnumerationAck;
 
   friend class EnumerationAck;
-  friend class telephony::TelephonyCallback;
+  friend class telephony::TelephonyDialCallback;
 
   nsCOMPtr<nsITelephonyService> mService;
   nsRefPtr<Listener> mListener;
 
   nsTArray<nsRefPtr<TelephonyCall> > mCalls;
   nsRefPtr<CallsList> mCallsList;
 
   nsRefPtr<TelephonyCallGroup> mGroup;
--- a/dom/telephony/TelephonyCallback.cpp
+++ b/dom/telephony/TelephonyCallback.cpp
@@ -1,139 +1,34 @@
 /* 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 "TelephonyCallback.h"
 
-#include "mozilla/dom/DOMMMIError.h"
-#include "nsServiceManagerUtils.h"
+#include "mozilla/dom/Promise.h"
+#include "nsJSUtils.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::telephony;
 
 NS_IMPL_ISUPPORTS(TelephonyCallback, nsITelephonyCallback)
 
-TelephonyCallback::TelephonyCallback(nsPIDOMWindow* aWindow,
-                                     Telephony* aTelephony,
-                                     Promise* aPromise,
-                                     uint32_t aServiceId)
-  : mWindow(aWindow), mTelephony(aTelephony), mPromise(aPromise),
-    mServiceId(aServiceId)
-{
-  MOZ_ASSERT(mTelephony);
-}
-
-nsresult
-TelephonyCallback::NotifyDialMMISuccess(const nsAString& aStatusMessage)
+TelephonyCallback::TelephonyCallback(Promise* aPromise)
+  : mPromise(aPromise)
 {
-  AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  JSContext* cx = jsapi.cx();
-
-  MozMMIResult result;
-
-  result.mServiceCode.Assign(mServiceCode);
-  result.mStatusMessage.Assign(aStatusMessage);
-
-  return NotifyDialMMISuccess(cx, result);
-}
-
-nsresult
-TelephonyCallback::NotifyDialMMISuccess(JSContext* aCx,
-                                        const nsAString& aStatusMessage,
-                                        JS::Handle<JS::Value> aInfo)
-{
-  RootedDictionary<MozMMIResult> result(aCx);
-
-  result.mServiceCode.Assign(mServiceCode);
-  result.mStatusMessage.Assign(aStatusMessage);
-  result.mAdditionalInformation.Construct().SetAsObject() = &aInfo.toObject();
-
-  return NotifyDialMMISuccess(aCx, result);
-}
-
-nsresult
-TelephonyCallback::NotifyDialMMISuccess(JSContext* aCx,
-                                        const MozMMIResult& aResult)
-{
-  JS::Rooted<JS::Value> jsResult(aCx);
-
-  if (!ToJSValue(aCx, aResult, &jsResult)) {
-    JS_ClearPendingException(aCx);
-    return NS_ERROR_TYPE_ERR;
-  }
-
-  return NotifyDialMMISuccess(jsResult);
 }
 
 // nsITelephonyCallback
 
 NS_IMETHODIMP
-TelephonyCallback::NotifyDialMMI(const nsAString& aServiceCode)
-{
-  mMMIRequest = new DOMRequest(mWindow);
-  mServiceCode.Assign(aServiceCode);
-
-  mPromise->MaybeResolve(mMMIRequest);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-TelephonyCallback::NotifyDialError(const nsAString& aError)
+TelephonyCallback::NotifySuccess()
 {
-  mPromise->MaybeRejectBrokenly(aError);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-TelephonyCallback::NotifyDialCallSuccess(uint32_t aCallIndex,
-                                         const nsAString& aNumber)
-{
-  nsRefPtr<TelephonyCallId> id = mTelephony->CreateCallId(aNumber);
-  nsRefPtr<TelephonyCall> call =
-      mTelephony->CreateCall(id, mServiceId, aCallIndex,
-                             nsITelephonyService::CALL_STATE_DIALING);
-
-  mPromise->MaybeResolve(call);
+  mPromise->MaybeResolve(JS::UndefinedHandleValue);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TelephonyCallback::NotifyDialMMISuccess(JS::Handle<JS::Value> aResult)
-{
-  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
-
-  return rs->FireSuccessAsync(mMMIRequest, aResult);
-}
-
-NS_IMETHODIMP
-TelephonyCallback::NotifyDialMMIError(const nsAString& aError)
+TelephonyCallback::NotifyError(const nsAString& aError)
 {
-  Nullable<int16_t> info;
-
-  nsRefPtr<DOMError> error =
-      new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
-
-  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
-
-  return rs->FireDetailedError(mMMIRequest, error);
+  mPromise->MaybeRejectBrokenly(aError);
+  return NS_OK;
 }
-
-NS_IMETHODIMP
-TelephonyCallback::NotifyDialMMIErrorWithInfo(const nsAString& aError,
-                                              uint16_t aInfo)
-{
-  Nullable<int16_t> info(aInfo);
-
-  nsRefPtr<DOMError> error =
-      new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
-
-  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
-
-  return rs->FireDetailedError(mMMIRequest, error);
-}
--- a/dom/telephony/TelephonyCallback.h
+++ b/dom/telephony/TelephonyCallback.h
@@ -1,81 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TelephonyCallback_h
 #define mozilla_dom_TelephonyCallback_h
 
-#include "Telephony.h"
-#include "mozilla/dom/DOMRequest.h"
-#include "mozilla/dom/MozMobileConnectionBinding.h"
-#include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ToJSValue.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsITelephonyService.h"
-#include "nsJSUtils.h"
-#include "nsString.h"
-
-class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
+
+class Promise;
+
 namespace telephony {
 
-class TelephonyCallback MOZ_FINAL : public nsITelephonyCallback
+class TelephonyCallback : public nsITelephonyCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYCALLBACK
 
-  TelephonyCallback(nsPIDOMWindow* aWindow, Telephony* aTelephony,
-                    Promise* aPromise, uint32_t aServiceId);
-
-  nsresult
-  NotifyDialMMISuccess(const nsAString& aStatusMessage);
-
-  template<typename T>
-  nsresult
-  NotifyDialMMISuccess(const nsAString& aStatusMessage, const T& aInfo)
-  {
-    AutoJSAPI jsapi;
-    if (!NS_WARN_IF(jsapi.Init(mWindow))) {
-      return NS_ERROR_FAILURE;
-    }
-
-    JSContext* cx = jsapi.cx();
-    JS::Rooted<JS::Value> info(cx);
+  TelephonyCallback(Promise* aPromise);
 
-    if (!ToJSValue(cx, aInfo, &info)) {
-      JS_ClearPendingException(cx);
-      return NS_ERROR_TYPE_ERR;
-    }
-
-    return NotifyDialMMISuccess(cx, aStatusMessage, info);
-  }
-
-private:
-  ~TelephonyCallback() {}
+protected:
+  virtual ~TelephonyCallback() {}
 
-  nsresult
-  NotifyDialMMISuccess(JSContext* aCx, const nsAString& aStatusMessage,
-                       JS::Handle<JS::Value> aInfo);
-
-  nsresult
-  NotifyDialMMISuccess(JSContext* aCx, const MozMMIResult& aResult);
-
-
-  nsCOMPtr<nsPIDOMWindow> mWindow;
-  nsRefPtr<Telephony> mTelephony;
+protected:
   nsRefPtr<Promise> mPromise;
-  uint32_t mServiceId;
-
-  nsRefPtr<DOMRequest> mMMIRequest;
-  nsString mServiceCode;
 };
 
 } // namespace telephony
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TelephonyCallback_h
new file mode 100644
--- /dev/null
+++ b/dom/telephony/TelephonyDialCallback.cpp
@@ -0,0 +1,133 @@
+/* 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 "TelephonyDialCallback.h"
+
+#include "mozilla/dom/DOMMMIError.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::telephony;
+
+NS_IMPL_ISUPPORTS_INHERITED(TelephonyDialCallback, TelephonyCallback,
+                            nsITelephonyDialCallback)
+
+TelephonyDialCallback::TelephonyDialCallback(nsPIDOMWindow* aWindow,
+                                             Telephony* aTelephony,
+                                             Promise* aPromise,
+                                             uint32_t aServiceId)
+  : TelephonyCallback(aPromise), mWindow(aWindow), mTelephony(aTelephony),
+    mServiceId(aServiceId)
+{
+  MOZ_ASSERT(mTelephony);
+}
+
+nsresult
+TelephonyDialCallback::NotifyDialMMISuccess(const nsAString& aStatusMessage)
+{
+  AutoJSAPI jsapi;
+  if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSContext* cx = jsapi.cx();
+
+  MozMMIResult result;
+
+  result.mServiceCode.Assign(mServiceCode);
+  result.mStatusMessage.Assign(aStatusMessage);
+
+  return NotifyDialMMISuccess(cx, result);
+}
+
+nsresult
+TelephonyDialCallback::NotifyDialMMISuccess(JSContext* aCx,
+                                            const nsAString& aStatusMessage,
+                                            JS::Handle<JS::Value> aInfo)
+{
+  RootedDictionary<MozMMIResult> result(aCx);
+
+  result.mServiceCode.Assign(mServiceCode);
+  result.mStatusMessage.Assign(aStatusMessage);
+  result.mAdditionalInformation.Construct().SetAsObject() = &aInfo.toObject();
+
+  return NotifyDialMMISuccess(aCx, result);
+}
+
+nsresult
+TelephonyDialCallback::NotifyDialMMISuccess(JSContext* aCx,
+                                            const MozMMIResult& aResult)
+{
+  JS::Rooted<JS::Value> jsResult(aCx);
+
+  if (!ToJSValue(aCx, aResult, &jsResult)) {
+    JS_ClearPendingException(aCx);
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  return NotifyDialMMISuccess(jsResult);
+}
+
+// nsITelephonyDialCallback
+
+NS_IMETHODIMP
+TelephonyDialCallback::NotifyDialMMI(const nsAString& aServiceCode)
+{
+  mMMIRequest = new DOMRequest(mWindow);
+  mServiceCode.Assign(aServiceCode);
+
+  mPromise->MaybeResolve(mMMIRequest);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyDialCallback::NotifyDialCallSuccess(uint32_t aCallIndex,
+                                             const nsAString& aNumber)
+{
+  nsRefPtr<TelephonyCallId> id = mTelephony->CreateCallId(aNumber);
+  nsRefPtr<TelephonyCall> call =
+      mTelephony->CreateCall(id, mServiceId, aCallIndex,
+                             nsITelephonyService::CALL_STATE_DIALING);
+
+  mPromise->MaybeResolve(call);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TelephonyDialCallback::NotifyDialMMISuccess(JS::Handle<JS::Value> aResult)
+{
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireSuccessAsync(mMMIRequest, aResult);
+}
+
+NS_IMETHODIMP
+TelephonyDialCallback::NotifyDialMMIError(const nsAString& aError)
+{
+  Nullable<int16_t> info;
+
+  nsRefPtr<DOMError> error =
+      new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
+
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireDetailedError(mMMIRequest, error);
+}
+
+NS_IMETHODIMP
+TelephonyDialCallback::NotifyDialMMIErrorWithInfo(const nsAString& aError,
+                                                  uint16_t aInfo)
+{
+  Nullable<int16_t> info(aInfo);
+
+  nsRefPtr<DOMError> error =
+      new DOMMMIError(mWindow, aError, EmptyString(), mServiceCode, info);
+
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE);
+
+  return rs->FireDetailedError(mMMIRequest, error);
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/TelephonyDialCallback.h
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TelephonyDialCallback_h
+#define mozilla_dom_TelephonyDialCallback_h
+
+#include "Telephony.h"
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/telephony/TelephonyCallback.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsITelephonyService.h"
+#include "nsJSUtils.h"
+#include "nsString.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+namespace telephony {
+
+class TelephonyDialCallback MOZ_FINAL : public TelephonyCallback,
+                                        public nsITelephonyDialCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSITELEPHONYDIALCALLBACK
+
+  TelephonyDialCallback(nsPIDOMWindow* aWindow, Telephony* aTelephony,
+                        Promise* aPromise, uint32_t aServiceId);
+
+  NS_FORWARD_NSITELEPHONYCALLBACK(TelephonyCallback::)
+
+  nsresult
+  NotifyDialMMISuccess(const nsAString& aStatusMessage);
+
+  template<typename T>
+  nsresult
+  NotifyDialMMISuccess(const nsAString& aStatusMessage, const T& aInfo)
+  {
+    AutoJSAPI jsapi;
+    if (!NS_WARN_IF(jsapi.Init(mWindow))) {
+      return NS_ERROR_FAILURE;
+    }
+
+    JSContext* cx = jsapi.cx();
+    JS::Rooted<JS::Value> info(cx);
+
+    if (!ToJSValue(cx, aInfo, &info)) {
+      JS_ClearPendingException(cx);
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    return NotifyDialMMISuccess(cx, aStatusMessage, info);
+  }
+
+private:
+  ~TelephonyDialCallback() {}
+
+  nsresult
+  NotifyDialMMISuccess(JSContext* aCx, const nsAString& aStatusMessage,
+                       JS::Handle<JS::Value> aInfo);
+
+  nsresult
+  NotifyDialMMISuccess(JSContext* aCx, const MozMMIResult& aResult);
+
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<Telephony> mTelephony;
+  uint32_t mServiceId;
+
+  nsRefPtr<DOMRequest> mMMIRequest;
+  nsString mServiceCode;
+};
+
+} // namespace telephony
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TelephonyDialCallback_h
new file mode 100644
--- /dev/null
+++ b/dom/telephony/USSDSession.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/dom/USSDSession.h"
+
+#include "mozilla/dom/USSDSessionBinding.h"
+#include "mozilla/dom/telephony/TelephonyCallback.h"
+#include "nsIGlobalObject.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::telephony;
+using mozilla::ErrorResult;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USSDSession, mWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(USSDSession)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(USSDSession)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USSDSession)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+USSDSession::USSDSession(nsPIDOMWindow* aWindow, nsITelephonyService* aService,
+                         uint32_t aServiceId)
+  : mWindow(aWindow), mService(aService), mServiceId(aServiceId)
+{
+}
+
+USSDSession::~USSDSession()
+{
+}
+
+nsPIDOMWindow*
+USSDSession::GetParentObject() const
+{
+  return mWindow;
+}
+
+JSObject*
+USSDSession::WrapObject(JSContext* aCx)
+{
+  return USSDSessionBinding::Wrap(aCx, this);
+}
+
+// WebIDL
+
+already_AddRefed<USSDSession>
+USSDSession::Constructor(const GlobalObject& aGlobal, uint32_t aServiceId,
+                         ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!window) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsITelephonyService> ril =
+    do_GetService(TELEPHONY_SERVICE_CONTRACTID);
+  if (!ril) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsRefPtr<USSDSession> session = new USSDSession(window, ril, aServiceId);
+  return session.forget();
+}
+
+already_AddRefed<Promise>
+USSDSession::Send(const nsAString& aUssd, ErrorResult& aRv)
+{
+  if (!mService) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
+  if (!global) {
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
+
+  nsresult rv = mService->SendUSSD(mServiceId, aUssd, callback);
+  if (NS_FAILED(rv)) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+  }
+
+  return promise.forget();
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/USSDSession.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_USSDSession_h
+#define mozilla_dom_USSDSession_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Promise.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsITelephonyService.h"
+#include "nsPIDOMWindow.h"
+#include "nsWrapperCache.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+
+class USSDSession MOZ_FINAL : public nsISupports,
+                              public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USSDSession)
+
+  USSDSession(nsPIDOMWindow* aWindow, nsITelephonyService* aService,
+              uint32_t aServiceId);
+
+  nsPIDOMWindow*
+  GetParentObject() const;
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  // WebIDL
+  static already_AddRefed<USSDSession>
+  Constructor(const GlobalObject& aGlobal, uint32_t aServiceId,
+              ErrorResult& aRv);
+
+  already_AddRefed<Promise>
+  Send(const nsAString& aUssd, ErrorResult& aRv);
+
+private:
+  ~USSDSession();
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsITelephonyService> mService;
+  uint32_t mServiceId;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_USSDSession_h
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -559,75 +559,75 @@ TelephonyService.prototype = {
     if (!aIsDialEmergency) {
       aNumber = gPhoneNumberUtils.normalize(aNumber);
     }
 
     // Validate the number.
     // Note: isPlainPhoneNumber also accepts USSD and SS numbers
     if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
       if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
-      aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
+      aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
       return;
     }
 
     let mmi = this._parseMMI(aNumber, this._hasCalls(aClientId));
     if (!mmi) {
       this._dialCall(aClientId,
                      { number: aNumber,
                        isDialEmergency: aIsDialEmergency }, aCallback);
     } else if (this._isTemporaryCLIR(mmi)) {
       this._dialCall(aClientId,
                      { number: mmi.dialNumber,
                        clirMode: this._getTemporaryCLIRMode(mmi.procedure),
                        isDialEmergency: aIsDialEmergency }, aCallback);
     } else {
       // Reject MMI code from dialEmergency api.
       if (aIsDialEmergency) {
-        aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
+        aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
         return;
       }
 
-      this._dialMMI(aClientId, mmi, aCallback);
+      this._dialMMI(aClientId, mmi, aCallback, true);
     }
   },
 
   /**
    * @param aOptions.number
    * @param aOptions.clirMode (optional)
    * @param aOptions.isDialEmergency
    */
   _dialCall: function(aClientId, aOptions, aCallback) {
     if (this._isDialing) {
       if (DEBUG) debug("Error: Already has a dialing call.");
-      aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
+      aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
       return;
     }
 
     // We can only have at most two calls on the same line (client).
     if (this._numCallsOnLine(aClientId) >= 2) {
       if (DEBUG) debug("Error: Already has more than 2 calls on line.");
-      aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
+      aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
       return;
     }
 
     // For DSDS, if there is aleady a call on SIM 'aClientId', we cannot place
     // any new call on other SIM.
     if (this._hasCallsOnOtherClient(aClientId)) {
       if (DEBUG) debug("Error: Already has a call on other sim.");
-      aCallback.notifyDialError(DIAL_ERROR_OTHER_CONNECTION_IN_USE);
+      aCallback.notifyError(DIAL_ERROR_OTHER_CONNECTION_IN_USE);
       return;
     }
 
     aOptions.isEmergency = this._isEmergencyNumber(aOptions.number);
     if (aOptions.isEmergency) {
       // Automatically select a proper clientId for emergency call.
       aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
       if (aClientId === -1) {
         if (DEBUG) debug("Error: No client is avaialble for emergency call.");
-        aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
+        aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
         return;
       }
     }
 
     // Before we dial, we have to hold the active call first.
     let activeCall = this._getOneActiveCall(aClientId);
     if (!activeCall) {
       this._sendDialCallRequest(aClientId, aOptions, aCallback);
@@ -650,17 +650,17 @@ TelephonyService.prototype = {
 
   _sendDialCallRequest: function(aClientId, aOptions, aCallback) {
     this._isDialing = true;
 
     this._sendToRilWorker(aClientId, "dial", aOptions, response => {
       this._isDialing = false;
 
       if (!response.success) {
-        aCallback.notifyDialError(response.errorMsg);
+        aCallback.notifyError(response.errorMsg);
         return;
       }
 
       let currentCdmaCallIndex = !response.isCdma ? null :
         Object.keys(this._currentCalls[aClientId])[0];
 
       if (currentCdmaCallIndex == null) {
         aCallback.notifyDialCallSuccess(response.callIndex, response.number);
@@ -668,26 +668,34 @@ TelephonyService.prototype = {
         // RIL doesn't hold the 2nd call. We create one by ourselves.
         aCallback.notifyDialCallSuccess(CDMA_SECOND_CALL_INDEX, response.number);
         this._addCdmaChildCall(aClientId, response.number, currentCdmaCallIndex);
       }
     });
   },
 
   /**
+   * @param aClientId
+   *        Client id.
    * @param aMmi
    *        Parsed MMI structure.
+   * @param aCallback
+   *        A nsITelephonyDialCallback object.
+   * @param aStartNewSession
+   *        True to start a new session for ussd request.
    */
-  _dialMMI: function(aClientId, aMmi, aCallback) {
+  _dialMMI: function(aClientId, aMmi, aCallback, aStartNewSession) {
     let mmiServiceCode = aMmi ?
       this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD;
 
     aCallback.notifyDialMMI(mmiServiceCode);
 
-    this._sendToRilWorker(aClientId, "sendMMI", { mmi: aMmi }, response => {
+    this._sendToRilWorker(aClientId, "sendMMI",
+                          { mmi: aMmi,
+                            startNewSession: aStartNewSession }, response => {
       if (DEBUG) debug("MMI response: " + JSON.stringify(response));
 
       if (!response.success) {
         if (response.additionalInformation != null) {
           aCallback.notifyDialMMIErrorWithInfo(response.errorMsg,
                                                response.additionalInformation);
         } else {
           aCallback.notifyDialMMIError(response.errorMsg);
@@ -1043,16 +1051,28 @@ TelephonyService.prototype = {
   holdConference: function(aClientId) {
     this._sendToRilWorker(aClientId, "holdConference");
   },
 
   resumeConference: function(aClientId) {
     this._sendToRilWorker(aClientId, "resumeConference");
   },
 
+  sendUSSD: function(aClientId, aUssd, aCallback) {
+    this._sendToRilWorker(aClientId, "sendUSSD",
+                          { ussd: aUssd, checkSession: true },
+                          response => {
+      if (!response.success) {
+        aCallback.notifyError(response.errorMsg);
+      } else {
+        aCallback.notifySuccess();
+      }
+    });
+  },
+
   get microphoneMuted() {
     return gAudioManager.microphoneMuted;
   },
 
   set microphoneMuted(aMuted) {
     if (aMuted == this.microphoneMuted) {
       return;
     }
@@ -1261,19 +1281,37 @@ TelephonyService.prototype = {
   },
 
   notifyConferenceCallStateChanged: function(aState) {
     if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
     aState = this._convertRILCallState(aState);
     this._notifyAllListeners("conferenceCallStateChanged", [aState]);
   },
 
+  notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
+    if (DEBUG) {
+      debug("notifyUssdReceived for " + aClientId + ": " +
+            aMessage + " (sessionEnded : " + aSessionEnded + ")");
+    }
+
+    let info = {
+      serviceId: aClientId,
+      message: aMessage,
+      sessionEnded: aSessionEnded
+    };
+
+    gSystemMessenger.broadcastMessage("ussd-received", info);
+
+    gGonkMobileConnectionService.notifyUssdReceived(aClientId, aMessage,
+                                                    aSessionEnded);
+  },
+
   dialMMI: function(aClientId, aMmiString, aCallback) {
     let mmi = this._parseMMI(aMmiString, this._hasCalls(aClientId));
-    this._dialMMI(aClientId, mmi, aCallback);
+    this._dialMMI(aClientId, mmi, aCallback, false);
   },
 
   /**
    * nsIObserver interface.
    */
 
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
@@ -1290,9 +1328,43 @@ TelephonyService.prototype = {
         this._releaseCallRingWakeLock();
 
         Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
         break;
     }
   }
 };
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService]);
+/**
+ * This implements nsISystemMessagesWrapper.wrapMessage(), which provides a
+ * plugable way to wrap a "ussd-received" type system message.
+ *
+ * Please see SystemMessageManager.js to know how it customizes the wrapper.
+ */
+function USSDReceivedWrapper() {
+  if (DEBUG) debug("USSDReceivedWrapper()");
+}
+USSDReceivedWrapper.prototype = {
+  // nsISystemMessagesWrapper implementation.
+  wrapMessage: function(aMessage, aWindow) {
+    if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage));
+
+    let session = aMessage.sessionEnded ? null :
+      new aWindow.USSDSession(aMessage.serviceId);
+
+    let event = new aWindow.USSDReceivedEvent("ussdreceived", {
+      serviceId: aMessage.serviceId,
+      message: aMessage.message,
+      sessionEnded: aMessage.sessionEnded,
+      session: session
+    });
+
+    return event;
+  },
+
+  classDescription: "USSDReceivedWrapper",
+  classID: Components.ID("{d03684ed-ede4-4210-8206-f4f32772d9f5}"),
+  contractID: "@mozilla.org/dom/system-messages/wrapper/ussd-received;1",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService,
+                                                    USSDReceivedWrapper]);
--- a/dom/telephony/gonk/TelephonyService.manifest
+++ b/dom/telephony/gonk/TelephonyService.manifest
@@ -1,2 +1,5 @@
 component {67d26434-d063-4d28-9f48-5b3189788155} TelephonyService.js
 contract @mozilla.org/telephony/gonktelephonyservice;1 {67d26434-d063-4d28-9f48-5b3189788155}
+
+component {d03684ed-ede4-4210-8206-f4f32772d9f5} TelephonyService.js
+contract @mozilla.org/dom/system-messages/wrapper/ussd-received;1 {d03684ed-ede4-4210-8206-f4f32772d9f5}
--- a/dom/telephony/ipc/PTelephony.ipdl
+++ b/dom/telephony/ipc/PTelephony.ipdl
@@ -19,20 +19,27 @@ struct EnumerateCallsRequest
 
 struct DialRequest
 {
   uint32_t clientId;
   nsString number;
   bool isEmergency;
 };
 
+struct USSDRequest
+{
+  uint32_t clientId;
+  nsString ussd;
+};
+
 union IPCTelephonyRequest
 {
   EnumerateCallsRequest;
   DialRequest;
+  USSDRequest;
 };
 
 sync protocol PTelephony {
   manager PContent;
   manages PTelephonyRequest;
 
 child:
   NotifyCallError(uint32_t aClientId, int32_t aCallIndex, nsString aError);
--- a/dom/telephony/ipc/PTelephonyRequest.ipdl
+++ b/dom/telephony/ipc/PTelephonyRequest.ipdl
@@ -11,17 +11,22 @@ namespace mozilla {
 namespace dom {
 namespace telephony {
 
 struct EnumerateCallsResponse
 {
   // empty.
 };
 
-struct DialResponseError
+struct SuccessResponse
+{
+  // empty.
+};
+
+struct ErrorResponse
 {
   nsString name;
 };
 
 struct DialResponseCallSuccess
 {
   uint32_t callIndex;
   nsString number;
@@ -37,18 +42,22 @@ struct DialResponseMMIError
 {
   nsString name;
   AdditionalInformation additionalInformation;
 };
 
 union IPCTelephonyResponse
 {
   EnumerateCallsResponse;
+
+  // General.
+  SuccessResponse;
+  ErrorResponse;
+
   // dial
-  DialResponseError;
   DialResponseCallSuccess;
   DialResponseMMISuccess;
   DialResponseMMIError;
 };
 
 protocol PTelephonyRequest
 {
   manager PTelephony;
--- a/dom/telephony/ipc/TelephonyChild.cpp
+++ b/dom/telephony/ipc/TelephonyChild.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; 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/. */
 
 #include "TelephonyChild.h"
 
-#include "mozilla/dom/telephony/TelephonyCallback.h"
+#include "mozilla/dom/telephony/TelephonyDialCallback.h"
 #include "TelephonyIPCService.h"
 
 USING_TELEPHONY_NAMESPACE
 
 /*******************************************************************************
  * TelephonyChild
  ******************************************************************************/
 
@@ -142,18 +142,20 @@ TelephonyRequestChild::ActorDestroy(Acto
 
 bool
 TelephonyRequestChild::Recv__delete__(const IPCTelephonyResponse& aResponse)
 {
   switch (aResponse.type()) {
     case IPCTelephonyResponse::TEnumerateCallsResponse:
       mListener->EnumerateCallStateComplete();
       break;
-    case IPCTelephonyResponse::TDialResponseError:
-      return DoResponse(aResponse.get_DialResponseError());
+    case IPCTelephonyResponse::TSuccessResponse:
+      return DoResponse(aResponse.get_SuccessResponse());
+    case IPCTelephonyResponse::TErrorResponse:
+      return DoResponse(aResponse.get_ErrorResponse());
     case IPCTelephonyResponse::TDialResponseCallSuccess:
       return DoResponse(aResponse.get_DialResponseCallSuccess());
     case IPCTelephonyResponse::TDialResponseMMISuccess:
       return DoResponse(aResponse.get_DialResponseMMISuccess());
     case IPCTelephonyResponse::TDialResponseMMIError:
       return DoResponse(aResponse.get_DialResponseMMIError());
     default:
       MOZ_CRASH("Unknown type!");
@@ -182,45 +184,55 @@ TelephonyRequestChild::RecvNotifyEnumera
                                 aData.isMergeable());
   return true;
 }
 
 bool
 TelephonyRequestChild::RecvNotifyDialMMI(const nsString& aServiceCode)
 {
   MOZ_ASSERT(mCallback);
-
-  mCallback->NotifyDialMMI(aServiceCode);
+  nsCOMPtr<nsITelephonyDialCallback> callback = do_QueryInterface(mCallback);
+  callback->NotifyDialMMI(aServiceCode);
   return true;
 }
 
 bool
-TelephonyRequestChild::DoResponse(const DialResponseError& aResponse)
+TelephonyRequestChild::DoResponse(const SuccessResponse& aResponse)
 {
   MOZ_ASSERT(mCallback);
-  mCallback->NotifyDialError(aResponse.name());
+  mCallback->NotifySuccess();
+  return true;
+}
+
+bool
+TelephonyRequestChild::DoResponse(const ErrorResponse& aResponse)
+{
+  MOZ_ASSERT(mCallback);
+  mCallback->NotifyError(aResponse.name());
   return true;
 }
 
 bool
 TelephonyRequestChild::DoResponse(const DialResponseCallSuccess& aResponse)
 {
   MOZ_ASSERT(mCallback);
-  mCallback->NotifyDialCallSuccess(aResponse.callIndex(), aResponse.number());
+  nsCOMPtr<nsITelephonyDialCallback> callback = do_QueryInterface(mCallback);
+  callback->NotifyDialCallSuccess(aResponse.callIndex(), aResponse.number());
   return true;
 }
 
 bool
 TelephonyRequestChild::DoResponse(const DialResponseMMISuccess& aResponse)
 {
   MOZ_ASSERT(mCallback);
 
   // FIXME: Need to overload NotifyDialMMISuccess in the IDL. mCallback is not
   // necessarily an instance of TelephonyCallback.
-  nsRefPtr<TelephonyCallback> callback = static_cast<TelephonyCallback*>(mCallback.get());
+  nsCOMPtr<nsITelephonyDialCallback> dialCallback = do_QueryInterface(mCallback);
+  nsRefPtr<TelephonyDialCallback> callback = static_cast<TelephonyDialCallback*>(dialCallback.get());
 
   nsAutoString statusMessage(aResponse.statusMessage());
   AdditionalInformation info(aResponse.additionalInformation());
 
   switch (info.type()) {
     case AdditionalInformation::Tvoid_t:
       callback->NotifyDialMMISuccess(statusMessage);
       break;
@@ -237,26 +249,27 @@ TelephonyRequestChild::DoResponse(const 
 
   return true;
 }
 
 bool
 TelephonyRequestChild::DoResponse(const DialResponseMMIError& aResponse)
 {
   MOZ_ASSERT(mCallback);
+  nsCOMPtr<nsITelephonyDialCallback> callback = do_QueryInterface(mCallback);
 
   nsAutoString name(aResponse.name());
   AdditionalInformation info(aResponse.additionalInformation());
 
   switch (info.type()) {
     case AdditionalInformation::Tvoid_t:
-      mCallback->NotifyDialMMIError(name);
+      callback->NotifyDialMMIError(name);
       break;
     case AdditionalInformation::Tuint16_t:
-      mCallback->NotifyDialMMIErrorWithInfo(name, info.get_uint16_t());
+      callback->NotifyDialMMIErrorWithInfo(name, info.get_uint16_t());
       break;
     default:
       MOZ_CRASH("Received invalid type!");
       break;
   }
 
   return true;
 }
--- a/dom/telephony/ipc/TelephonyChild.h
+++ b/dom/telephony/ipc/TelephonyChild.h
@@ -79,17 +79,20 @@ protected:
   RecvNotifyEnumerateCallState(const uint32_t& aClientId,
                                const IPCCallStateData& aData) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyDialMMI(const nsString& aServiceCode) MOZ_OVERRIDE;
 
 private:
   bool
-  DoResponse(const DialResponseError& aResponse);
+  DoResponse(const SuccessResponse& aResponse);
+
+  bool
+  DoResponse(const ErrorResponse& aResponse);
 
   bool
   DoResponse(const DialResponseCallSuccess& aResponse);
 
   bool
   DoResponse(const DialResponseMMISuccess& aResponse);
 
   bool
--- a/dom/telephony/ipc/TelephonyIPCService.cpp
+++ b/dom/telephony/ipc/TelephonyIPCService.cpp
@@ -157,17 +157,18 @@ TelephonyIPCService::SendRequest(nsITele
 NS_IMETHODIMP
 TelephonyIPCService::EnumerateCalls(nsITelephonyListener *aListener)
 {
   return SendRequest(aListener, nullptr, EnumerateCallsRequest());
 }
 
 NS_IMETHODIMP
 TelephonyIPCService::Dial(uint32_t aClientId, const nsAString& aNumber,
-                           bool aIsEmergency, nsITelephonyCallback *aCallback)
+                           bool aIsEmergency,
+                           nsITelephonyDialCallback *aCallback)
 {
   return SendRequest(nullptr, aCallback,
                      DialRequest(aClientId, nsString(aNumber), aIsEmergency));
 }
 
 NS_IMETHODIMP
 TelephonyIPCService::HangUp(uint32_t aClientId, uint32_t aCallIndex)
 {
@@ -296,16 +297,24 @@ TelephonyIPCService::StopTone(uint32_t a
     return NS_ERROR_FAILURE;
   }
 
   mPTelephonyChild->SendStopTone(aClientId);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TelephonyIPCService::SendUSSD(uint32_t aClientId, const nsAString& aUssd,
+                              nsITelephonyCallback *aCallback)
+{
+  return SendRequest(nullptr, aCallback,
+                     USSDRequest(aClientId, nsString(aUssd)));
+}
+
+NS_IMETHODIMP
 TelephonyIPCService::GetMicrophoneMuted(bool* aMuted)
 {
   if (!mPTelephonyChild) {
     NS_WARNING("TelephonyService used after shutdown has begun!");
     return NS_ERROR_FAILURE;
   }
 
   mPTelephonyChild->SendGetMicrophoneMuted(aMuted);
--- a/dom/telephony/ipc/TelephonyParent.cpp
+++ b/dom/telephony/ipc/TelephonyParent.cpp
@@ -38,16 +38,18 @@ TelephonyParent::RecvPTelephonyRequestCo
 {
   TelephonyRequestParent* actor = static_cast<TelephonyRequestParent*>(aActor);
 
   switch (aRequest.type()) {
     case IPCTelephonyRequest::TEnumerateCallsRequest:
       return actor->DoRequest(aRequest.get_EnumerateCallsRequest());
     case IPCTelephonyRequest::TDialRequest:
       return actor->DoRequest(aRequest.get_DialRequest());
+    case IPCTelephonyRequest::TUSSDRequest:
+      return actor->DoRequest(aRequest.get_USSDRequest());
     default:
       MOZ_CRASH("Unknown type!");
   }
 
   return false;
 }
 
 PTelephonyRequestParent*
@@ -380,17 +382,18 @@ TelephonyParent::SupplementaryServiceNot
 }
 
 /*******************************************************************************
  * TelephonyRequestParent
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(TelephonyRequestParent,
                   nsITelephonyListener,
-                  nsITelephonyCallback)
+                  nsITelephonyCallback,
+                  nsITelephonyDialCallback)
 
 TelephonyRequestParent::TelephonyRequestParent()
   : mActorDestroyed(false)
 {
 }
 
 void
 TelephonyRequestParent::ActorDestroy(ActorDestroyReason why)
@@ -421,19 +424,33 @@ TelephonyRequestParent::DoRequest(const 
 
 bool
 TelephonyRequestParent::DoRequest(const DialRequest& aRequest)
 {
   nsCOMPtr<nsITelephonyService> service =
     do_GetService(TELEPHONY_SERVICE_CONTRACTID);
   if (service) {
     service->Dial(aRequest.clientId(), aRequest.number(),
-                   aRequest.isEmergency(), this);
+                  aRequest.isEmergency(), this);
   } else {
-    return NS_SUCCEEDED(NotifyDialError(NS_LITERAL_STRING("InvalidStateError")));
+    return NS_SUCCEEDED(NotifyError(NS_LITERAL_STRING("InvalidStateError")));
+  }
+
+  return true;
+}
+
+bool
+TelephonyRequestParent::DoRequest(const USSDRequest& aRequest)
+{
+  nsCOMPtr<nsITelephonyService> service =
+    do_GetService(TELEPHONY_SERVICE_CONTRACTID);
+  if (service) {
+    service->SendUSSD(aRequest.clientId(), aRequest.ussd(), this);
+  } else {
+    return NS_SUCCEEDED(NotifyError(NS_LITERAL_STRING("InvalidStateError")));
   }
 
   return true;
 }
 
 nsresult
 TelephonyRequestParent::SendResponse(const IPCTelephonyResponse& aResponse)
 {
@@ -527,30 +544,36 @@ TelephonyRequestParent::NotifyError(uint
 NS_IMETHODIMP
 TelephonyRequestParent::SupplementaryServiceNotification(uint32_t aClientId,
                                                          int32_t aCallIndex,
                                                          uint16_t aNotification)
 {
   MOZ_CRASH("Not a TelephonyParent!");
 }
 
-// nsITelephonyCallback
+// nsITelephonyDialCallback
 
 NS_IMETHODIMP
 TelephonyRequestParent::NotifyDialMMI(const nsAString& aServiceCode)
 {
   NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
 
   return SendNotifyDialMMI(nsAutoString(aServiceCode)) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-TelephonyRequestParent::NotifyDialError(const nsAString& aError)
+TelephonyRequestParent::NotifySuccess()
 {
-  return SendResponse(DialResponseError(nsAutoString(aError)));
+  return SendResponse(SuccessResponse());
+}
+
+NS_IMETHODIMP
+TelephonyRequestParent::NotifyError(const nsAString& aError)
+{
+  return SendResponse(ErrorResponse(nsAutoString(aError)));
 }
 
 NS_IMETHODIMP
 TelephonyRequestParent::NotifyDialCallSuccess(uint32_t aCallIndex,
                                               const nsAString& aNumber)
 {
   return SendResponse(DialResponseCallSuccess(aCallIndex, nsAutoString(aNumber)));
 }
--- a/dom/telephony/ipc/TelephonyParent.h
+++ b/dom/telephony/ipc/TelephonyParent.h
@@ -93,24 +93,25 @@ protected:
 
 private:
   bool mActorDestroyed;
   bool mRegistered;
 };
 
 class TelephonyRequestParent : public PTelephonyRequestParent
                              , public nsITelephonyListener
-                             , public nsITelephonyCallback
+                             , public nsITelephonyDialCallback
 {
   friend class TelephonyParent;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYLISTENER
   NS_DECL_NSITELEPHONYCALLBACK
+  NS_DECL_NSITELEPHONYDIALCALLBACK
 
 protected:
   TelephonyRequestParent();
   virtual ~TelephonyRequestParent() {}
 
   virtual void
   ActorDestroy(ActorDestroyReason why);
 
@@ -120,13 +121,16 @@ protected:
 private:
   bool mActorDestroyed;
 
   bool
   DoRequest(const EnumerateCallsRequest& aRequest);
 
   bool
   DoRequest(const DialRequest& aRequest);
+
+  bool
+  DoRequest(const USSDRequest& aRequest);
 };
 
 END_TELEPHONY_NAMESPACE
 
 #endif /* mozilla_dom_telephony_TelephonyParent_h */
--- a/dom/telephony/moz.build
+++ b/dom/telephony/moz.build
@@ -11,35 +11,39 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'dom_telephony'
 
 EXPORTS.mozilla.dom += [
     'CallsList.h',
     'Telephony.h',
     'TelephonyCall.h',
     'TelephonyCallGroup.h',
     'TelephonyCallId.h',
+    'USSDSession.h'
 ]
 
 EXPORTS.mozilla.dom.telephony += [
     'ipc/TelephonyChild.h',
     'ipc/TelephonyParent.h',
     'TelephonyCallback.h',
     'TelephonyCommon.h',
+    'TelephonyDialCallback.h',
 ]
 
 UNIFIED_SOURCES += [
     'CallsList.cpp',
     'ipc/TelephonyChild.cpp',
     'ipc/TelephonyIPCService.cpp',
     'ipc/TelephonyParent.cpp',
     'Telephony.cpp',
     'TelephonyCall.cpp',
     'TelephonyCallback.cpp',
     'TelephonyCallGroup.cpp',
     'TelephonyCallId.cpp',
+    'TelephonyDialCallback.cpp',
+    'USSDSession.cpp',
 ]
 
 IPDL_SOURCES += [
     'ipc/PTelephony.ipdl',
     'ipc/PTelephonyRequest.ipdl',
     'ipc/TelephonyTypes.ipdlh'
 ]
 
--- a/dom/telephony/nsIGonkTelephonyService.idl
+++ b/dom/telephony/nsIGonkTelephonyService.idl
@@ -5,28 +5,31 @@
 
 #include "nsITelephonyService.idl"
 
 %{C++
 #define GONK_TELEPHONY_SERVICE_CONTRACTID \
         "@mozilla.org/telephony/gonktelephonyservice;1"
 %}
 
-[scriptable, uuid(8790e2cc-2c68-4ce9-90dc-f68e1b6e4886)]
+[scriptable, uuid(79eec3c3-2dfc-4bbf-b106-af5457651ae0)]
 interface nsIGonkTelephonyService : nsITelephonyService
 {
   void notifyCallDisconnected(in unsigned long clientId, in jsval call);
 
   void notifyCallRing();
 
   void notifyCallStateChanged(in unsigned long clientId, in jsval call,
                               [optional] in boolean skipStateConversion);
 
   void notifyCdmaCallWaiting(in unsigned long clientId, in jsval waitingCall);
 
   void notifySupplementaryService(in unsigned long clientId, in long callIndex,
                                   in AString notification);
 
   void notifyConferenceCallStateChanged(in short state);
 
+  void notifyUssdReceived(in unsigned long clientId, in DOMString message,
+                          in boolean sessionEnded);
+
   void dialMMI(in unsigned long clientId, in AString mmiString,
-               in nsITelephonyCallback callback);
+               in nsITelephonyDialCallback callback);
 };
--- a/dom/telephony/nsITelephonyService.idl
+++ b/dom/telephony/nsITelephonyService.idl
@@ -172,39 +172,41 @@ interface nsITelephonyListener : nsISupp
    * @param message
    *        Detailed error message from RIL.
    */
   void notifyConferenceError(in AString name,
                              in AString message);
 };
 
 /**
- * A callback interface for handling asynchronous response of Telephony.dial().
+ * A callback interface for handling asynchronous response.
  */
-[scriptable, uuid(67533db3-cd38-475c-a774-8d0bbf9169fb)]
+[scriptable, uuid(cffc3f9d-2c88-4a14-8ebc-f216caf0cc1d)]
 interface nsITelephonyCallback : nsISupports
 {
+  void notifySuccess();
+  void notifyError(in AString error);
+};
+
+/**
+ * A callback interface for handling asynchronous response for telephony.dial.
+ */
+[scriptable, uuid(4481212f-72e9-4aef-97e1-5397af782113)]
+interface nsITelephonyDialCallback : nsITelephonyCallback
+{
   /**
    * Called when a dial request is treated as an MMI code and it is about to
    * process the request.
    *
    * @param serviceCode
    *        MMI service code key string that defined in MMI_KS_SC_*
    */
   void notifyDialMMI(in AString serviceCode);
 
   /**
-   * Called when a dial request fails.
-   *
-   * @param error
-   *        Error from RIL.
-   */
-  void notifyDialError(in AString error);
-
-  /**
    * Called when a dial request is treated as a call setup and the result
    * succeeds.
    *
    * @param callIndex
    *        Call index from RIL.
    * @param number
    *        Dialed out phone number (ex: Temporary CLIR prefix will be removed)
    */
@@ -233,17 +235,17 @@ interface nsITelephonyCallback : nsISupp
 #define TELEPHONY_SERVICE_CONTRACTID \
   "@mozilla.org/telephony/telephonyservice;1"
 %}
 
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(4ff3ecb7-b024-4752-9dd6-c3623c6e6b8a)]
+[scriptable, uuid(79188caa-046a-48e1-b9c5-2e891504dc7a)]
 interface nsITelephonyService : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_CONNECTING = 3;
   const unsigned short CALL_STATE_CONNECTED = 4;
   const unsigned short CALL_STATE_HOLDING = 5;
@@ -276,32 +278,42 @@ interface nsITelephonyService : nsISuppo
    * returns false.
    */
   void enumerateCalls(in nsITelephonyListener listener);
 
   /**
    * Functionality for making and managing phone calls.
    */
   void dial(in unsigned long clientId, in DOMString number,
-            in boolean isEmergency, in nsITelephonyCallback callback);
+            in boolean isEmergency, in nsITelephonyDialCallback callback);
   void hangUp(in unsigned long clientId, in unsigned long callIndex);
 
   void startTone(in unsigned long clientId, in DOMString dtmfChar);
   void stopTone(in unsigned long clientId);
 
   void answerCall(in unsigned long clientId, in unsigned long callIndex);
   void rejectCall(in unsigned long clientId, in unsigned long callIndex);
   void holdCall(in unsigned long clientId, in unsigned long callIndex);
   void resumeCall(in unsigned long clientId, in unsigned long callIndex);
 
   void conferenceCall(in unsigned long clientId);
   void separateCall(in unsigned long clientId, in unsigned long callIndex);
   void holdConference(in unsigned long clientId);
   void resumeConference(in unsigned long clientId);
 
+  /**
+   * Send an USSD on existing session. It results in error if the session is
+   * not existed.
+   *
+   * If successful, callback.notifySuccess() will be called.
+   * Otherwise, callback.notifyError() will be called.
+   */
+  void sendUSSD(in unsigned long clientId, in DOMString ussd,
+                in nsITelephonyCallback callback);
+
   attribute bool microphoneMuted;
   attribute bool speakerEnabled;
 };
 
 %{C++
 template<typename T> struct already_AddRefed;
 
 already_AddRefed<nsITelephonyService>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -167,40 +167,36 @@ var interfaceNamesInGlobalScope =
     "AudioProcessingEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AudioStreamTrack",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BarProp",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BatteryManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BeforeAfterKeyboardEvent", b2g: true,
-     pref: "dom.beforeAfterKeyboardEvent.enabled",
-     permission: ["embed-apps", "before-after-keyboard-event"]},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "BeforeUnloadEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BiquadFilterNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Blob",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BlobEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothAdapter", b2g: true, permission: ["bluetooth"]},
+    {name: "BluetoothAdapter", b2g: true, permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothDevice", b2g: true, permission: ["bluetooth"]},
+    {name: "BluetoothDevice", b2g: true, permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothDeviceEvent", b2g: true, permission: ["bluetooth"]},
+    {name: "BluetoothDeviceEvent", b2g: true, permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothDiscoveryStateChangedEvent", b2g: true,
-     permission: ["bluetooth"]},
+     permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
+    {name: "BluetoothManager", b2g: true, permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothStatusChangedEvent", b2g: true, permission: ["bluetooth"]},
+    {name: "BluetoothStatusChangedEvent", b2g: true, permission: "bluetooth"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallGroupErrorEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraCapabilities", b2g: true},
@@ -752,17 +748,17 @@ var interfaceNamesInGlobalScope =
     {name: "mozRTCIceCandidate", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "mozRTCPeerConnection", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "mozRTCSessionDescription", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSettingsEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozSettingsTransactionEvent", permission: ["settings-api-read"]},
+    {name: "MozSettingsTransactionEvent", permission: "settings-api-read"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSmsEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSmsMessage",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozSpeakerManager", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"},
@@ -772,33 +768,33 @@ var interfaceNamesInGlobalScope =
     {name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozVoicemailEvent", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozVoicemailStatus", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWakeLock", b2g: true, pref: "dom.wakelock.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiCapabilities", b2g: true, permission: ["wifi-manage"]},
+    {name: "MozWifiCapabilities", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiConnectionInfoEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiStationInfoEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-   {name: "MozWifiManager", b2g: true, permission: ["wifi-manage"]},
+   {name: "MozWifiManager", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiNetwork", b2g: true, permission: ["wifi-manage"]},
+    {name: "MozWifiNetwork", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiStatusChangeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pGroupOwner", b2g: true, permission: ["wifi-manage"]},
+    {name: "MozWifiP2pGroupOwner", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pManager", b2g: true, permission: ["wifi-manage"]},
+    {name: "MozWifiP2pManager", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: ["wifi-manage"]},
+    {name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: "wifi-manage"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationObserver",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationRecord",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "NamedNodeMap",
@@ -846,19 +842,19 @@ var interfaceNamesInGlobalScope =
     "PerformanceNavigation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceResourceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PeriodicWave",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PermissionSettings", b2g: true, permission: ["permissions"]},
+    {name: "PermissionSettings", b2g: true, permission: "permissions"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PhoneNumberService", permission: ["phonenumberservice"]},
+    {name: "PhoneNumberService", permission: "phonenumberservice"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Plugin",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PluginArray",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PointerEvent", pref: "dom.w3c_pointer_events.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PopStateEvent",
@@ -1230,34 +1226,30 @@ var interfaceNamesInGlobalScope =
     {name: "TreeColumns", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TreeContentView", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TreeSelection", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TreeWalker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "UDPMessageEvent", pref: "dom.udpsocket.enabled",
-     permission: ["udp-socket"]},
+    {name: "UDPMessageEvent", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "UDPSocket", pref: "dom.udpsocket.enabled",
-     permission: ["udp-socket"]},
+    {name: "UDPSocket", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UIEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UndoManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URL",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URLSearchParams",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UserProximityEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "USSDReceivedEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "ValidityState",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VideoPlaybackQuality", pref: "media.mediasource.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VideoStreamTrack",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VTTCue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -1344,22 +1336,18 @@ var interfaceNamesInGlobalScope =
 
 function createInterfaceMap(isXBLScope) {
   var prefs = SpecialPowers.Services.prefs;
   var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
   var isNightly = version.endsWith("a1");
   var isRelease = !version.contains("a");
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
-  var hasPermission = function (aPermissions) {
-    var result = false;
-    for (var p of aPermissions) {
-      result = result || SpecialPowers.hasPermission(p, window.document);
-    }
-    return result;
+  var hasPermission = function (aPermission) {
+    return SpecialPowers.hasPermission(aPermission, window.document);
   };
 
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
deleted file mode 100644
--- a/dom/webidl/BeforeAfterKeyboardEvent.webidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- 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/.
- */
-
-[Constructor(DOMString typeArg,
- optional BeforeAfterKeyboardEventInit eventInitDict),
- CheckPermissions="embed-apps before-after-keyboard-event",
- Pref="dom.beforeAfterKeyboardEvent.enabled"]
-interface BeforeAfterKeyboardEvent : KeyboardEvent
-{
-  // The valid value of embeddedCancelled is:
-  // - "mozbrowserbeforekeydown": null
-  // - "mozbrowserbeforekeyup": null
-  // - "mozbrowserafterkeydown": true/false
-  // - "mozbrowserafterkeyup": true/false
-  readonly attribute boolean? embeddedCancelled;
-};
-
-dictionary BeforeAfterKeyboardEventInit : KeyboardEventInit
-{
-  boolean? embeddedCancelled = null;
-};
--- a/dom/webidl/MozNFCTag.webidl
+++ b/dom/webidl/MozNFCTag.webidl
@@ -5,18 +5,16 @@
  * Part of this idl is from:
  * http://w3c.github.io/nfc/proposals/common/nfc.html#nfctag-interface
  *
  * Copyright © 2013 Deutsche Telekom, Inc.
  */
 
 enum NFCTechType {
   "NDEF",
-  "NDEF_WRITABLE",
-  "NDEF_FORMATABLE",
   "P2P",
   "NFC_A",
   "NFC_B",
   "NFC_F",
   "NFC_V",
   "NFC_ISO_DEP",
   "MIFARE_CLASSIC",
   "MIFARE_ULTRALIGHT",
--- a/dom/webidl/NfcOptions.webidl
+++ b/dom/webidl/NfcOptions.webidl
@@ -8,16 +8,17 @@ dictionary NfcCommandOptions
 
   long sessionId;
   DOMString requestId = "";
 
   long powerLevel;
 
   long techType;
 
+  boolean isP2P;
   sequence<MozNDEFRecordOptions> records;
 };
 
 dictionary NfcEventOptions
 {
   DOMString type = "";
 
   long status;
--- a/dom/webidl/USSDReceivedEvent.webidl
+++ b/dom/webidl/USSDReceivedEvent.webidl
@@ -1,19 +1,25 @@
 /* -*- 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/.
  */
 
-[Pref="dom.mobileconnection.enabled",
+[Pref="dom.telephony.enabled",
+ CheckPermissions="telephony mobileconnection",
+ AvailableIn="CertifiedApps",
  Constructor(DOMString type, optional USSDReceivedEventInit eventInitDict)]
 interface USSDReceivedEvent : Event
 {
+  readonly attribute unsigned long serviceId;
   readonly attribute DOMString? message;
-  readonly attribute boolean sessionEnded;
+  readonly attribute USSDSession? session;  // null if session is ended.
+  readonly attribute boolean sessionEnded;  // deprecated. Bug 1070831
 };
 
 dictionary USSDReceivedEventInit : EventInit
 {
+  unsigned long serviceId = 0;
   DOMString? message = null;
+  USSDSession? session = null;
   boolean sessionEnded = false;
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/USSDSession.webidl
@@ -0,0 +1,14 @@
+/* -*- 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/.
+ */
+
+[Pref="dom.telephony.enabled",
+ CheckPermissions="telephony",
+ AvailableIn="CertifiedApps",
+ Constructor(unsigned long serviceId)]
+interface USSDSession {
+  [Throws]
+  Promise<void> send(DOMString ussd);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -44,17 +44,16 @@ WEBIDL_FILES = [
     'AudioParam.webidl',
     'AudioProcessingEvent.webidl',
     'AudioStreamTrack.webidl',
     'AudioTrack.webidl',
     'AudioTrackList.webidl',
     'AutocompleteInfo.webidl',
     'BarProp.webidl',
     'BatteryManager.webidl',
-    'BeforeAfterKeyboardEvent.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BoxObject.webidl',
     'BrowserElementDictionaries.webidl',
     'CallsList.webidl',
     'CameraCapabilities.webidl',
     'CameraControl.webidl',
@@ -504,16 +503,17 @@ WEBIDL_FILES = [
     'UDPMessageEvent.webidl',
     'UDPSocket.webidl',
     'UIEvent.webidl',
     'UndoManager.webidl',
     'URL.webidl',
     'URLSearchParams.webidl',
     'URLUtils.webidl',
     'URLUtilsReadOnly.webidl',
+    'USSDSession.webidl',
     'ValidityState.webidl',
     'VideoPlaybackQuality.webidl',
     'VideoStreamTrack.webidl',
     'VideoTrack.webidl',
     'VideoTrackList.webidl',
     'VTTCue.webidl',
     'VTTRegion.webidl',
     'WaveShaperNode.webidl',
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -117,17 +117,21 @@ GetBitmapForSurface(SourceSurface* aSurf
   result.mBitmap.setInfo(info, surf->Stride());
 
   result.mBitmap.setPixels(surf->GetData());
   result.mTmpSurface = surf.forget();
   return result;
 }
 
 DrawTargetSkia::DrawTargetSkia()
-  : mTexture(0), mSnapshot(nullptr)
+  :
+#ifdef USE_SKIA_GPU
+ mTexture(0),
+#endif
+ mSnapshot(nullptr)
 {
 }
 
 DrawTargetSkia::~DrawTargetSkia()
 {
 }
 
 TemporaryRef<SourceSurface>
@@ -843,20 +847,21 @@ DrawTargetSkia::SetTransform(const Matri
   GfxMatrixToSkiaMatrix(aTransform, mat);
   mCanvas->setMatrix(mat);
   mTransform = aTransform;
 }
 
 void*
 DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType)
 {
+#ifdef USE_SKIA_GPU
   if (aType == NativeSurfaceType::OPENGL_TEXTURE) {
     return (void*)((uintptr_t)mTexture);
   }
-
+#endif
   return nullptr;
 }
 
 
 TemporaryRef<PathBuilder>
 DrawTargetSkia::CreatePathBuilder(FillRule aFillRule) const
 {
   return new PathBuilderSkia(aFillRule);
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -1151,41 +1151,55 @@ FilterNodeTransformSoftware::SourceRectF
 }
 
 TemporaryRef<DataSourceSurface>
 FilterNodeTransformSoftware::Render(const IntRect& aRect)
 {
   IntRect srcRect = SourceRectForOutputRect(aRect);
 
   RefPtr<DataSourceSurface> input =
-    GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect, NEED_COLOR_CHANNELS);
+    GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
 
   if (!input) {
     return nullptr;
   }
 
   Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
                      Matrix::Translation(-aRect.x, -aRect.y);
   if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
     return input.forget();
   }
 
+  RefPtr<DataSourceSurface> surf =
+    Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
+
+  if (!surf) {
+    return nullptr;
+  }
+
+  DataSourceSurface::MappedSurface mapping;
+  surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+
   RefPtr<DrawTarget> dt =
-    Factory::CreateDrawTarget(BackendType::CAIRO, aRect.Size(), input->GetFormat());
+    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                     mapping.mData,
+                                     surf->GetSize(),
+                                     mapping.mStride,
+                                     surf->GetFormat());
   if (!dt) {
     return nullptr;
   }
 
   Rect r(0, 0, srcRect.width, srcRect.height);
   dt->SetTransform(transform);
   dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter));
 
-  RefPtr<SourceSurface> result = dt->Snapshot();
-  RefPtr<DataSourceSurface> resultData = result->GetDataSurface();
-  return resultData.forget();
+  dt->Flush();
+  surf->Unmap();
+  return surf.forget();
 }
 
 void
 FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
   RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
 }
 
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -796,29 +796,29 @@ public:
     Float __33 = _12*_24*_41 - _14*_22*_41 +
                 _14*_21*_42 - _11*_24*_42 -
                 _12*_21*_44 + _11*_22*_44;
     return (__33 * det) < 0;
   }
 
   Matrix4x4 &NudgeToIntegersFixedEpsilon()
   {
+    NudgeToInteger(&_11);
+    NudgeToInteger(&_12);
+    NudgeToInteger(&_13);
+    NudgeToInteger(&_14);
+    NudgeToInteger(&_21);
+    NudgeToInteger(&_22);
+    NudgeToInteger(&_23);
+    NudgeToInteger(&_24);
+    NudgeToInteger(&_31);
+    NudgeToInteger(&_32);
+    NudgeToInteger(&_33);
+    NudgeToInteger(&_34);
     static const float error = 1e-5f;
-    NudgeToInteger(&_11, error);
-    NudgeToInteger(&_12, error);
-    NudgeToInteger(&_13, error);
-    NudgeToInteger(&_14, error);
-    NudgeToInteger(&_21, error);
-    NudgeToInteger(&_22, error);
-    NudgeToInteger(&_23, error);
-    NudgeToInteger(&_24, error);
-    NudgeToInteger(&_31, error);
-    NudgeToInteger(&_32, error);
-    NudgeToInteger(&_33, error);
-    NudgeToInteger(&_34, error);
     NudgeToInteger(&_41, error);
     NudgeToInteger(&_42, error);
     NudgeToInteger(&_43, error);
     NudgeToInteger(&_44, error);
     return *this;
   }
 
   Point4D TransposedVector(int aIndex) const
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -44,17 +44,17 @@
 #endif
 
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
 unsigned GLContext::sCurrentGLContextTLS = -1;
 #endif
 
 uint32_t GLContext::sDebugMode = 0;
 
 
 #define MAX_SYMBOL_LENGTH 128
 #define MAX_SYMBOL_NAMES 5
@@ -287,17 +287,17 @@ GLContext::GLContext(const SurfaceCaps& 
   : mInitialized(false),
     mIsOffscreen(isOffscreen),
     mContextLost(false),
     mVersion(0),
     mProfile(ContextProfile::Unknown),
     mVendor(GLVendor::Other),
     mRenderer(GLRenderer::Other),
     mHasRobustness(false),
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     mIsInLocalErrorCheck(false),
 #endif
     mSharedContext(sharedContext),
     mCaps(caps),
     mScreen(nullptr),
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
@@ -307,17 +307,17 @@ GLContext::GLContext(const SurfaceCaps& 
     mWorkAroundDriverBugs(true),
     mHeavyGLCallsSinceLastFlush(false)
 {
     mOwningThreadId = PlatformThread::CurrentId();
 }
 
 GLContext::~GLContext() {
     NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     if (mSharedContext) {
         GLContext *tip = mSharedContext;
         while (tip->mSharedContext)
             tip = tip->mSharedContext;
         tip->SharedContextDestroyed(this);
         tip->ReportOutstandingNames();
     } else {
         ReportOutstandingNames();
@@ -506,17 +506,17 @@ GLContext::InitWithPrefix(const char *pr
 
     mInitialized = LoadSymbols(&symbols[0], trygl, prefix);
     MakeCurrent();
     if (mInitialized) {
         unsigned int version = 0;
 
         ParseGLVersion(this, &version);
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
         printf_stderr("OpenGL version detected: %u\n", version);
         printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR));
         printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER));
 #endif
 
         if (version >= mVersion) {
             mVersion = version;
         }
@@ -626,32 +626,32 @@ GLContext::InitWithPrefix(const char *pr
             if (DoesStringMatch(glRendererString, rendererMatchStrings[i])) {
                 mRenderer = GLRenderer(i);
                 break;
             }
         }
     }
 
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     if (PR_GetEnv("MOZ_GL_DEBUG"))
         sDebugMode |= DebugEnabled;
 
     // enables extra verbose output, informing of the start and finish of every GL call.
     // useful e.g. to record information to investigate graphics system crashes/lockups
     if (PR_GetEnv("MOZ_GL_DEBUG_VERBOSE"))
         sDebugMode |= DebugTrace;
 
     // aborts on GL error. Can be useful to debug quicker code that is known not to generate any GL error in principle.
     if (PR_GetEnv("MOZ_GL_DEBUG_ABORT_ON_ERROR"))
         sDebugMode |= DebugAbortOnError;
 #endif
 
     if (mInitialized) {
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
         static bool firstRun = true;
         if (firstRun && DebugMode()) {
             const char *vendors[size_t(GLVendor::Other)] = {
                 "Intel",
                 "NVIDIA",
                 "ATI",
                 "Qualcomm"
             };
@@ -1603,17 +1603,17 @@ GLContext::DebugCallback(GLenum source,
 void
 GLContext::InitExtensions()
 {
     MakeCurrent();
     const char* extensions = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
     if (!extensions)
         return;
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     static bool firstRun = true;
 #else
     // Non-DEBUG, so never spew.
     const bool firstRun = false;
 #endif
 
     InitializeExtensionsBitSet(mAvailableExtensions, extensions, sExtensionNames, firstRun && DebugMode());
 
@@ -1654,17 +1654,17 @@ GLContext::InitExtensions()
         nsCocoaFeatures::OSXVersionMajor() == 10 &&
         nsCocoaFeatures::OSXVersionMinor() == 9 &&
         Renderer() == GLRenderer::IntelHD3000)
     {
         MarkExtensionUnsupported(EXT_texture_compression_s3tc);
     }
 #endif
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     firstRun = false;
 #endif
 }
 
 void
 GLContext::PlatformStartup()
 {
   RegisterStrongMemoryReporter(new GfxTexturesReporter());
@@ -1744,17 +1744,17 @@ GLContext::QueryPixelFormat()
 
     return format;
 }
 
 void
 GLContext::UpdatePixelFormat()
 {
     PixelBufferFormat format = QueryPixelFormat();
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     const SurfaceCaps& caps = Caps();
     MOZ_ASSERT(!caps.any, "Did you forget to DetermineCaps()?");
 
     MOZ_ASSERT(caps.color == !!format.red);
     MOZ_ASSERT(caps.color == !!format.green);
     MOZ_ASSERT(caps.color == !!format.blue);
 
     // These we either must have if they're requested, or
@@ -1968,27 +1968,27 @@ GLContext::AssembleOffscreenFBs(const GL
     }
 
     // We should be all resized.  Check for framebuffer completeness.
     GLenum status;
     bool isComplete = true;
 
     if (!IsFramebufferComplete(drawFB, &status)) {
         NS_WARNING("DrawFBO: Incomplete");
-  #ifdef DEBUG
+  #ifdef MOZ_GL_DEBUG
         if (DebugMode()) {
             printf_stderr("Framebuffer status: %X\n", status);
         }
   #endif
         isComplete = false;
     }
 
     if (!IsFramebufferComplete(readFB, &status)) {
         NS_WARNING("ReadFBO: Incomplete");
-  #ifdef DEBUG
+  #ifdef MOZ_GL_DEBUG
         if (DebugMode()) {
             printf_stderr("Framebuffer status: %X\n", status);
         }
   #endif
         isComplete = false;
     }
 
     if (drawFB_out) {
@@ -2102,17 +2102,17 @@ GLContext::MarkDestroyed()
         mTexGarbageBin->GLContextTeardown();
     } else {
         NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
     }
 
     mSymbols.Zero();
 }
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
 /* static */ void
 GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
 {
   int somethingOnTheStack;
   const void* someStackPtr = &somethingOnTheStack;
   const int page_bits = 12;
   intptr_t page = reinterpret_cast<uintptr_t>(ptr) >> page_bits;
   intptr_t someStackPage = reinterpret_cast<uintptr_t>(someStackPtr) >> page_bits;
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -21,16 +21,22 @@
 #ifdef WIN32
 #include <windows.h>
 #endif
 
 #ifdef GetClassName
 #undef GetClassName
 #endif
 
+// Define MOZ_GL_DEBUG unconditionally to enable GL debugging in opt
+// builds.
+#ifdef DEBUG
+#define MOZ_GL_DEBUG 1
+#endif
+
 #include "mozilla/UniquePtr.h"
 
 #include "GLDefs.h"
 #include "GLLibraryLoader.h"
 #include "nsISupportsImpl.h"
 #include "plstr.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
@@ -638,39 +644,39 @@ private:
 
             PushError(err);
         }
     }
 
     ////////////////////////////////////
     // Use this safer option.
 private:
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     bool mIsInLocalErrorCheck;
 #endif
 
 public:
     class ScopedLocalErrorCheck {
         GLContext* const mGL;
         bool mHasBeenChecked;
 
     public:
         explicit ScopedLocalErrorCheck(GLContext* gl)
             : mGL(gl)
             , mHasBeenChecked(false)
         {
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
             MOZ_ASSERT(!mGL->mIsInLocalErrorCheck);
             mGL->mIsInLocalErrorCheck = true;
 #endif
             mGL->GetAndPushAllErrors();
         }
 
         GLenum GetLocalError() {
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
             MOZ_ASSERT(mGL->mIsInLocalErrorCheck);
             mGL->mIsInLocalErrorCheck = false;
 #endif
 
             MOZ_ASSERT(!mHasBeenChecked);
             mHasBeenChecked = true;
 
             return mGL->GetAndClearUnpushedErrors();
@@ -699,17 +705,17 @@ private:
 
 // -----------------------------------------------------------------------------
 // MOZ_GL_DEBUG implementation
 private:
 
 #undef BEFORE_GL_CALL
 #undef AFTER_GL_CALL
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
 
 #ifndef MOZ_FUNCTION_NAME
 # ifdef __GNUC__
 #  define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
 # elif defined(_MSC_VER)
 #  define MOZ_FUNCTION_NAME __FUNCTION__
 # else
 #  define MOZ_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
@@ -787,29 +793,29 @@ private:
 
 #define TRACKING_CONTEXT(a)                         \
             do {                                    \
                 TrackingContext()->a;               \
             } while (0)
 
 #define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) AssertNotPassingStackBufferToTheGL(ptr)
 
-#else // ifdef DEBUG
+#else // ifdef MOZ_GL_DEBUG
 
 #ifdef MOZ_WIDGET_ANDROID
 // Record the name of the GL call for better hang stacks on Android.
 #define BEFORE_GL_CALL PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS)
 #else
 #define BEFORE_GL_CALL do { } while (0)
 #endif
 #define AFTER_GL_CALL do { } while (0)
 #define TRACKING_CONTEXT(a) do {} while (0)
 #define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) do {} while (0)
 
-#endif // ifdef DEBUG
+#endif // ifdef MOZ_GL_DEBUG
 
 #define ASSERT_SYMBOL_PRESENT(func) \
             do {\
                 MOZ_ASSERT(strstr(MOZ_FUNCTION_NAME, #func) != nullptr, "Mismatched symbol check.");\
                 if (MOZ_UNLIKELY(!mSymbols.func)) {\
                     printf_stderr("RUNTIME ASSERT: Uninitialized GL function: %s\n", #func);\
                     MOZ_CRASH();\
                 }\
@@ -3195,27 +3201,27 @@ public:
 // -----------------------------------------------------------------------------
 // Everything that isn't standard GL APIs
 protected:
     typedef gfx::SurfaceFormat SurfaceFormat;
 
     virtual bool MakeCurrentImpl(bool aForce) = 0;
 
 public:
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     static void StaticInit() {
         PR_NewThreadPrivateIndex(&sCurrentGLContextTLS, nullptr);
     }
 #endif
 
     bool MakeCurrent(bool aForce = false) {
         if (IsDestroyed()) {
             return false;
         }
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     PR_SetThreadPrivate(sCurrentGLContextTLS, this);
 
     // XXX this assertion is disabled because it's triggering on Mac;
     // we need to figure out why and reenable it.
 #if 0
         // IsOwningThreadCurrent is a bit of a misnomer;
         // the "owning thread" is the creation thread,
         // and the only thread that can own this.  We don't
@@ -3380,32 +3386,32 @@ public:
         DebugEnabled = 1 << 0,
         DebugTrace = 1 << 1,
         DebugAbortOnError = 1 << 2
     };
 
     static uint32_t sDebugMode;
 
     static uint32_t DebugMode() {
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
         return sDebugMode;
 #else
         return 0;
 #endif
     }
 
 protected:
     nsRefPtr<GLContext> mSharedContext;
 
     // The thread id which this context was created.
     PlatformThreadId mOwningThreadId;
 
     GLContextSymbols mSymbols;
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     // GLDebugMode will check that we don't send call
     // to a GLContext that isn't current on the current
     // thread.
     // Store the current context when binding to thread local
     // storage to support DebugMode on an arbitrary thread.
     static unsigned sCurrentGLContextTLS;
 #endif
 
@@ -3636,17 +3642,17 @@ public:
         mViewportRect[3] = height;
         BEFORE_GL_CALL;
         mSymbols.fViewport(x, y, width, height);
         AFTER_GL_CALL;
     }
 
 #undef ASSERT_SYMBOL_PRESENT
 
-#ifdef DEBUG
+#ifdef MOZ_GL_DEBUG
     void CreatedProgram(GLContext *aOrigin, GLuint aName);
     void CreatedShader(GLContext *aOrigin, GLuint aName);
     void CreatedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
     void CreatedQueries(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
     void CreatedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
     void CreatedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
     void CreatedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
     void DeletedProgram(GLContext *aOrigin, GLuint aName);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -2,33 +2,33 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 "Layers.h"
 #include <algorithm>                    // for max, min
+#include "apz/src/AsyncPanZoomController.h"
 #include "CompositableHost.h"           // for CompositableHost
 #include "ImageContainer.h"             // for ImageContainer, etc
 #include "ImageLayers.h"                // for ImageLayer
 #include "LayerSorter.h"                // for SortLayersBy3DZOrder
 #include "LayersLogging.h"              // for AppendToString
 #include "ReadbackLayer.h"              // for ReadbackLayer
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPrefs.h"
 #include "gfxUtils.h"                   // for gfxUtils, etc
 #include "gfx2DGlue.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/Telemetry.h"          // for Accumulate
 #include "mozilla/dom/AnimationPlayer.h" // for ComputedTimingFunction
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
-#include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
 #include "nsPrintfCString.h"            // for nsPrintfCString
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; 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/. */
 
 #include "APZCTreeManager.h"
+#include "AsyncPanZoomController.h"
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent, etc
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for Layer, etc
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
-#include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Preferences.h"        // for Preferences
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsThreadUtils.h"              // for NS_IsMainThread
@@ -55,16 +55,26 @@ struct APZCTreeManager::TreeBuildingStat
   const uint64_t mOriginatingLayersId;
   const APZPaintLogHelper mPaintLogger;
 
   // State that is updated as we perform the tree build
   nsTArray< nsRefPtr<AsyncPanZoomController> > mApzcsToDestroy;
   std::map<ScrollableLayerGuid, AsyncPanZoomController*> mApzcMap;
 };
 
+/*static*/ const LayerMargin
+APZCTreeManager::CalculatePendingDisplayPort(
+  const FrameMetrics& aFrameMetrics,
+  const ScreenPoint& aVelocity,
+  double aEstimatedPaintDuration)
+{
+  return AsyncPanZoomController::CalculatePendingDisplayPort(
+    aFrameMetrics, aVelocity, aEstimatedPaintDuration);
+}
+
 APZCTreeManager::APZCTreeManager()
     : mTreeLock("APZCTreeLock"),
       mInOverscrolledApzc(false),
       mRetainedTouchIdentifier(-1),
       mTouchCount(0),
       mApzcTreeLog("apzctree")
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -232,16 +232,26 @@ public:
   void ClearTree();
 
   /**
    * Tests if a screen point intersect an apz in the tree.
    */
   bool HitTestAPZC(const ScreenIntPoint& aPoint);
 
   /**
+   * See AsyncPanZoomController::CalculatePendingDisplayPort. This
+   * function simply delegates to that one, so that non-layers code
+   * never needs to include AsyncPanZoomController.h
+   */
+  static const LayerMargin CalculatePendingDisplayPort(
+    const FrameMetrics& aFrameMetrics,
+    const ScreenPoint& aVelocity,
+    double aEstimatedPaintDuration);
+
+  /**
    * Set the dpi value used by all AsyncPanZoomControllers.
    * DPI defaults to 72 if not set using SetDPI() at any point.
    */
   static void SetDPI(float aDpiValue) { sDPI = aDpiValue; }
 
   /**
    * Returns the current dpi value in use.
    */
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -4,23 +4,25 @@
  * 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 <math.h>                       // for fabsf, fabs, atan2
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include <sys/types.h>                  // for int32_t
 #include <algorithm>                    // for max, min
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
+#include "Axis.h"                       // for AxisX, AxisY, Axis, etc
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "InputBlockState.h"            // for InputBlockState, TouchBlockState
 #include "OverscrollHandoffState.h"     // for OverscrollHandoffState
+#include "TaskThrottler.h"              // for TaskThrottler
 #include "Units.h"                      // for CSSRect, CSSPoint, etc
 #include "UnitTransforms.h"             // for TransformTo
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
@@ -36,22 +38,20 @@
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
 #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
-#include "mozilla/layers/Axis.h"        // for AxisX, AxisY, Axis, etc
 #include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
 #include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
 #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
 #include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
-#include "mozilla/layers/TaskThrottler.h"  // for TaskThrottler
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/unused.h"             // for unused
 #include "mozilla/FloatingPoint.h"      // for FuzzyEquals*
 #include "nsAlgorithm.h"                // for clamped
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
@@ -1023,17 +1023,17 @@ nsEventStatus AsyncPanZoomController::Re
 
     // We want to cancel animations here as soon as possible (i.e. without waiting for
     // content responses) because a finger has gone down and we don't want to keep moving
     // the content under the finger. However, to prevent "future" touchstart events from
     // interfering with "past" animations (i.e. from a previous touch block that is still
     // being processed) we only do this animation-cancellation if there are no older
     // touch blocks still in the queue.
     if (block == CurrentTouchBlock()) {
-      if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+      if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
         // If we're already in a fast fling, then we want the touch event to stop the fling
         // and to disallow the touch event from being used as part of a fling.
         block->DisallowSingleTap();
       }
       block->GetOverscrollHandoffChain()->CancelAnimations();
     }
 
     if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) {
@@ -1771,17 +1771,17 @@ float AsyncPanZoomController::PanDistanc
   ToGlobalScreenCoordinates(&panVector, panStart);
   return NS_hypot(panVector.x, panVector.y);
 }
 
 ScreenPoint AsyncPanZoomController::PanStart() const {
   return ScreenPoint(mX.PanStart(), mY.PanStart());
 }
 
-const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
+const ScreenPoint AsyncPanZoomController::GetVelocityVector() const {
   return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
 }
 
 void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
   // Handling of cross sliding will need to be added in this method after touch-action released
   // enabled by default.
   if (CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
     if (mX.CanScrollNow() && mY.CanScrollNow()) {
@@ -2325,16 +2325,21 @@ bool AsyncPanZoomController::SnapBackIfO
   if (IsOverscrolled()) {
     APZC_LOG("%p is overscrolled, starting snap-back\n", this);
     StartSnapBack();
     return true;
   }
   return false;
 }
 
+bool AsyncPanZoomController::IsMovingFast() const {
+  ReentrantMonitorAutoEnter lock(mMonitor);
+  return (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold());
+}
+
 bool AsyncPanZoomController::IsPannable() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
   return mX.CanScroll() || mY.CanScroll();
 }
 
 int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
   nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
   return listener ? listener->GetLastTouchIdentifier() : -1;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -324,16 +324,22 @@ public:
   bool HasScrollgrab() const { return mFrameMetrics.GetHasScrollgrab(); }
 
   /**
    * Returns whether this APZC has room to be panned (in any direction).
    */
   bool IsPannable() const;
 
   /**
+   * Returns true if the APZC has a velocity greater than the stop-on-tap
+   * fling velocity threshold (which is pref-controlled).
+   */
+  bool IsMovingFast() const;
+
+  /**
    * Returns the identifier of the touch in the last touch event processed by
    * this APZC. This should only be called when the last touch event contained
    * only one touch.
    */
   int32_t GetLastTouchIdentifier() const;
 
   /**
    * Convert the vector |aVector|, rooted at the point |aAnchor|, from
@@ -486,17 +492,17 @@ protected:
    * Unlikely PanDistance(), this function returns a point in local screen
    * coordinates.
    */
   ScreenPoint PanStart() const;
 
   /**
    * Gets a vector of the velocities of each axis.
    */
-  const ScreenPoint GetVelocityVector();
+  const ScreenPoint GetVelocityVector() const;
 
   /**
    * Gets the first touch point from a MultiTouchInput.  This gets only
    * the first one and assumes the rest are either missing or not relevant.
    */
   ScreenPoint GetFirstTouchScreenPoint(const MultiTouchInput& aEvent);
 
   /**
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -315,17 +315,17 @@ CSSCoord Axis::ScaleWillOverscrollAmount
     return (originAfterScale - GetPageStart()) / zoom;
   }
   if (plus) {
     return (originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd()) / zoom;
   }
   return 0;
 }
 
-float Axis::GetVelocity() {
+float Axis::GetVelocity() const {
   return mAxisLocked ? 0 : mVelocity;
 }
 
 void Axis::SetVelocity(float aVelocity) {
   mVelocity = aVelocity;
 }
 
 ScreenCoord Axis::GetCompositionEnd() const {
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -146,17 +146,17 @@ public:
    */
   bool CanScrollNow() const;
 
   void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; }
 
   /**
    * Gets the raw velocity of this axis at this moment.
    */
-  float GetVelocity();
+  float GetVelocity() const;
 
   /**
    * Sets the raw velocity of this axis at this moment.
    * Intended to be called only when the axis "takes over" a velocity from
    * another APZC, in which case there are no touch points available to call
    * UpdateWithTouchAtDevicePoint. In other circumstances,
    * UpdateWithTouchAtDevicePoint should be used and the velocity calculated
    * there.
--- a/gfx/layers/apz/src/OverscrollHandoffState.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -142,11 +142,17 @@ OverscrollHandoffChain::CanBePanned(cons
 }
 
 bool
 OverscrollHandoffChain::HasOverscrolledApzc() const
 {
   return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
 }
 
+bool
+OverscrollHandoffChain::HasFastMovingApzc() const
+{
+  return AnyApzc(&AsyncPanZoomController::IsMovingFast);
+}
+
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -103,16 +103,20 @@ public:
   void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
 
   // Determine whether the given APZC, or any APZC further in the chain,
   // has room to be panned.
   bool CanBePanned(const AsyncPanZoomController* aApzc) const;
 
   // Determine whether any APZC along this handoff chain is overscrolled.
   bool HasOverscrolledApzc() const;
+
+  // Determine whether any APZC along this handoff chain is moving fast.
+  bool HasFastMovingApzc() const;
+
 private:
   std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
 
   typedef void (AsyncPanZoomController::*APZCMethod)();
   typedef bool (AsyncPanZoomController::*APZCPredicate)() const;
   void ForEachApzc(APZCMethod aMethod) const;
   bool AnyApzc(APZCPredicate aPredicate) const;
 };
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #include "MacIOSurfaceTextureHostBasic.h"
-#include "mozilla/layers/BasicCompositor.h"
 #include "mozilla/gfx/MacIOSurface.h"
 
 namespace mozilla {
 namespace layers {
 
 MacIOSurfaceTextureSourceBasic::MacIOSurfaceTextureSourceBasic(
                                 BasicCompositor* aCompositor,
                                 MacIOSurface* aSurface)
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
 #define MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
 
+#include "mozilla/layers/BasicCompositor.h"
 #include "mozilla/layers/TextureHostBasic.h"
 
 class MacIOSurface;
 
 namespace mozilla {
 namespace layers {
 
 class BasicCompositor;
@@ -36,17 +37,17 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
   virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) MOZ_OVERRIDE;
 
   virtual void DeallocateDeviceData() MOZ_OVERRIDE { }
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
 protected:
-  BasicCompositor* mCompositor;
+  RefPtr<BasicCompositor> mCompositor;
   RefPtr<MacIOSurface> mSurface;
   RefPtr<gfx::SourceSurface> mSourceSurface;
 };
 
 /**
  * A TextureHost for shared MacIOSurface
  *
  * Most of the logic actually happens in MacIOSurfaceTextureSourceBasic.
@@ -75,17 +76,17 @@ public:
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "MacIOSurfaceTextureHostBasic"; }
 #endif
 
 protected:
-  BasicCompositor* mCompositor;
+  RefPtr<BasicCompositor> mCompositor;
   RefPtr<MacIOSurfaceTextureSourceBasic> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 }
 }
 
 #endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_BASIC_H
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #include "X11TextureSourceBasic.h"
-#include "mozilla/layers/BasicCompositor.h"
 #include "gfxXlibSurface.h"
 #include "gfx2DGlue.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
--- a/gfx/layers/basic/X11TextureSourceBasic.h
+++ b/gfx/layers/basic/X11TextureSourceBasic.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #ifndef MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
 #define MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
 
+#include "mozilla/layers/BasicCompositor.h"
 #include "mozilla/layers/TextureHostBasic.h"
 #include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 namespace layers {
 
 class BasicCompositor;
 
@@ -32,17 +33,17 @@ public:
 
   virtual void DeallocateDeviceData() MOZ_OVERRIDE { }
 
   virtual void SetCompositor(Compositor* aCompositor);
 
   static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
 
 protected:
-  BasicCompositor* mCompositor;
+  RefPtr<BasicCompositor> mCompositor;
   RefPtr<gfxXlibSurface> mSurface;
   RefPtr<gfx::SourceSurface> mSourceSurface;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -210,16 +210,19 @@ TextureClient::GetIPDLActor()
 {
   return mActor;
 }
 
 #ifdef MOZ_WIDGET_GONK
 static bool
 DisableGralloc(SurfaceFormat aFormat, const gfx::IntSize& aSizeHint)
 {
+  if (gfxPrefs::DisableGralloc()) {
+    return true;
+  }
   if (aFormat == gfx::SurfaceFormat::A8) {
     return true;
   }
 
 #if ANDROID_VERSION <= 15
   // Adreno 200 has a problem of drawing gralloc buffer width less than 64 and
   // drawing gralloc buffer with a height 9px-16px.
   // See Bug 983971.
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -1,29 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=2 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include <stdint.h>                     // for uint32_t
+#include "apz/src/AsyncPanZoomController.h"
 #include "CompositorParent.h"           // for CompositorParent, etc
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "LayerManagerComposite.h"      // for LayerManagerComposite, etc
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "gfxPoint.h"                   // for gfxPoint, gfxSize
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 #include "mozilla/WidgetUtils.h"        // for ComputeTransformForRotation
 #include "mozilla/dom/AnimationPlayer.h" // for AnimationPlayer
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
-#include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "nsCSSPropList.h"
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsDeviceContext.h"            // for nsDeviceContext
 #include "nsDisplayList.h"              // for nsDisplayTransform, etc
 #include "nsMathUtils.h"                // for NS_round
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -11,16 +11,17 @@
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr, RefCounted, etc
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for Filter
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/Effects.h"     // for Texture Effect
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/LayersMessages.h"
 #include "mozilla/layers/TextureHost.h" // for TextureHost
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsRegion.h"                   // for nsIntRegion
@@ -306,17 +307,17 @@ public:
   virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) {
     return nullptr;
   }
 
 protected:
   TextureInfo mTextureInfo;
   uint64_t mAsyncID;
   uint64_t mCompositorID;
-  Compositor* mCompositor;
+  RefPtr<Compositor> mCompositor;
   Layer* mLayer;
   RefPtr<CompositableBackendSpecificData> mBackendData;
   uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
   bool mAttached;
   bool mKeepAttached;
 };
 
 class AutoLockCompositableHost MOZ_FINAL
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #include "ContainerLayerComposite.h"
 #include <algorithm>                    // for min
+#include "apz/src/AsyncPanZoomController.h"  // for AsyncPanZoomController
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for LayerRect, LayerPixel, etc
 #include "gfx2DGlue.h"                  // for ToMatrix4x4
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxUtils.h"                   // for gfxUtils, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/UniquePtr.h"          // for UniquePtr
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point, IntPoint
 #include "mozilla/gfx/Rect.h"           // for IntRect, Rect
 #include "mozilla/layers/Compositor.h"  // for Compositor, etc
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticFlags::CONTAINER
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/layers/TextureHost.h"  // for CompositingRenderTarget
-#include "mozilla/layers/AsyncPanZoomController.h"  // for AsyncPanZoomController
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_RELEASE
 #include "nsPoint.h"                    // for nsIntPoint
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -10,16 +10,17 @@
 #include <stdint.h>                     // for uint64_t, uint32_t, uint8_t
 #include "gfxTypes.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr, TemporaryRef, etc
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface
 #include "mozilla/gfx/Point.h"          // for IntSize, IntPoint
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
 #include "mozilla/layers/FenceUtils.h"  // for FenceHandle
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/UniquePtr.h"          // for UniquePtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
@@ -490,17 +491,17 @@ public:
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
 protected:
   bool Upload(nsIntRegion *aRegion = nullptr);
   bool MaybeUpload(nsIntRegion *aRegion = nullptr);
 
-  Compositor* mCompositor;
+  RefPtr<Compositor> mCompositor;
   RefPtr<DataTextureSource> mFirstSource;
   nsIntRegion mMaybeUpdatedRegion;
   gfx::IntSize mSize;
   // format of the data that is shared with the content process.
   gfx::SurfaceFormat mFormat;
   uint32_t mUpdateSerial;
   bool mLocked;
   bool mNeedsFullUpdate;
@@ -625,17 +626,17 @@ public:
   virtual const char* Name() { return "SharedSurfaceTextureHost"; }
 #endif
 
 protected:
   void EnsureTexSource();
 
   bool mIsLocked;
   gl::SharedSurface* const mSurf;
-  Compositor* mCompositor;
+  RefPtr<Compositor> mCompositor;
   RefPtr<TextureSource> mTexSource;
 };
 
 class MOZ_STACK_CLASS AutoLockTextureHost
 {
 public:
   explicit AutoLockTextureHost(TextureHost* aTexture)
     : mTexture(aTexture)
--- a/gfx/layers/composite/X11TextureHost.cpp
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -36,23 +36,23 @@ X11TextureHost::Lock()
   if (!mCompositor) {
     return false;
   }
 
   if (!mTextureSource) {
     switch (mCompositor->GetBackendType()) {
       case LayersBackend::LAYERS_BASIC:
         mTextureSource =
-          new X11TextureSourceBasic(static_cast<BasicCompositor*>(mCompositor),
+          new X11TextureSourceBasic(static_cast<BasicCompositor*>(mCompositor.get()),
                                     mSurface);
         break;
 #ifdef GL_PROVIDER_GLX
       case LayersBackend::LAYERS_OPENGL:
         mTextureSource =
-          new X11TextureSourceOGL(static_cast<CompositorOGL*>(mCompositor),
+          new X11TextureSourceOGL(static_cast<CompositorOGL*>(mCompositor.get()),
                                   mSurface);
         break;
 #endif
       default:
         return false;
     }
   }
 
--- a/gfx/layers/composite/X11TextureHost.h
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -40,17 +40,17 @@ public:
     return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
   }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "X11TextureHost"; }
 #endif
 
 protected:
-  Compositor* mCompositor;
+  RefPtr<Compositor> mCompositor;
   RefPtr<TextureSource> mTextureSource;
   RefPtr<gfxXlibSurface> mSurface;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_X11TEXTUREHOST__H
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -2,16 +2,17 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 "LayerTransactionParent.h"
 #include <vector>                       // for vector
+#include "apz/src/AsyncPanZoomController.h"
 #include "CompositableHost.h"           // for CompositableParent, Get, etc
 #include "ImageLayers.h"                // for ImageLayer
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "ShadowLayerParent.h"          // for ShadowLayerParent
 #include "CompositableTransactionParent.h"  // for EditReplyVector
 #include "ShadowLayersManager.h"        // for ShadowLayersManager
 #include "mozilla/gfx/BasePoint3D.h"    // for BasePoint3D
 #include "mozilla/layers/CanvasLayerComposite.h"
@@ -36,17 +37,16 @@
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsLayoutUtils.h"              // for nsLayoutUtils
 #include "nsMathUtils.h"                // for NS_round
 #include "nsPoint.h"                    // for nsPoint
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "GeckoProfiler.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/AsyncCompositionManager.h"
-#include "mozilla/layers/AsyncPanZoomController.h"
 
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 using mozilla::layout::RenderFrameParent;
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -108,20 +108,16 @@ EXPORTS.gfxipc += [
     'ipc/ShadowLayerUtils.h',
 ]
 
 EXPORTS.mozilla.layers += [
     'apz/public/GeckoContentController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZCTreeManager.h',
-    'apz/src/AsyncPanZoomController.h',
-    'apz/src/Axis.h',
-    'apz/src/GestureEventListener.h',
-    'apz/src/TaskThrottler.h',
     'apz/testutil/APZTestData.h',
     'apz/util/ActiveElementManager.h',
     'apz/util/APZCCallbackHelper.h',
     'AtomicRefCountedWithFinalize.h',
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.h
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -152,17 +152,22 @@ public:
 private:
   /**
    * Actually do the initialisation. Note that we leave our FBO bound, and so
    * calling this method is only suitable when about to use this render target.
    */
   void InitializeImpl();
 
   InitParams mInitParams;
-  CompositorOGL* mCompositor;
+  /**
+   * There is temporary a cycle between the compositor and the render target,
+   * each having a strong ref to the other. The compositor's reference to
+   * the target is always cleared at the end of a frame.
+   */
+  RefPtr<CompositorOGL> mCompositor;
   GLContext* mGL;
   GLuint mTextureHandle;
   GLuint mFBO;
 };
 
 }
 }
 
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/process.h"
 #include "GLContext.h"
 #include "gfx2DGlue.h"
 #include <ui/GraphicBuffer.h>
 #include "GrallocImages.h"  // for GrallocImage
 #include "mozilla/layers/GrallocTextureHost.h"
-#include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "EGLImageHelpers.h"
 #include "GLReadTexImageHelper.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace android;
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -2,16 +2,17 @@
 //  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_GRALLOCTEXTUREHOST_H
 #define MOZILLA_GFX_GRALLOCTEXTUREHOST_H
 #ifdef MOZ_WIDGET_GONK
 
+#include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/TextureHostOGL.h"
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
 #include <ui/GraphicBuffer.h>
 
 namespace mozilla {
 namespace layers {
 
 class GrallocTextureHostOGL;
@@ -70,17 +71,17 @@ public:
   {
     return mEGLImage;
   }
 
   bool Lock();
 
 protected:
   RefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
-  CompositorOGL* mCompositor;
+  RefPtr<CompositorOGL> mCompositor;
   GrallocTextureHostOGL* mTextureHost;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   GLuint mTexture;
   gfx::SurfaceFormat mFormat;
   bool mNeedsReset;
 };
 
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #include "MacIOSurfaceTextureHostOGL.h"
 #include "mozilla/gfx/MacIOSurface.h"
-#include "mozilla/layers/CompositorOGL.h"
 #include "GLContextCGL.h"
 
 namespace mozilla {
 namespace layers {
 
 MacIOSurfaceTextureHostOGL::MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
                                                        const SurfaceDescriptorMacIOSurface& aDescriptor)
   : TextureHost(aFlags)
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
 #define MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
 
+#include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/TextureHostOGL.h"
 
 class MacIOSurface;
 
 namespace mozilla {
 namespace layers {
 
 /**
@@ -44,17 +45,17 @@ public:
   // MacIOSurfaceTextureSourceOGL doesn't own any gl texture
   virtual void DeallocateDeviceData() {}
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   gl::GLContext* gl() const;
 
 protected:
-  CompositorOGL* mCompositor;
+  RefPtr<CompositorOGL> mCompositor;
   RefPtr<MacIOSurface> mSurface;
 };
 
 /**
  * A TextureHost for shared MacIOSurface
  *
  * Most of the logic actually happens in MacIOSurfaceTextureSourceOGL.
  */
@@ -87,17 +88,17 @@ public:
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() { return "MacIOSurfaceTextureHostOGL"; }
 #endif
 
 protected:
-  CompositorOGL* mCompositor;
+  RefPtr<CompositorOGL> mCompositor;
   RefPtr<MacIOSurfaceTextureSourceOGL> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 }
 }
 
 #endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -9,17 +9,16 @@
 #include "GLContext.h"                  // for GLContext, etc
 #include "GLLibraryEGL.h"               // for GLLibraryEGL
 #include "GLUploadHelpers.h"
 #include "GLReadTexImageHelper.h"
 #include "gfx2DGlue.h"                  // for ContentForFormat, etc
 #include "gfxReusableSurfaceWrapper.h"  // for gfxReusableSurfaceWrapper
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
-#include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #ifdef MOZ_WIDGET_GONK
 # include "GrallocImages.h"  // for GrallocImage
 # include "EGLImageHelpers.h"
 #endif
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include "mozilla/layers/GrallocTextureHost.h"
 #include "nsPoint.h"                    // for nsIntPoint
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -15,16 +15,17 @@
 #include "gfxTypes.h"
 #include "mozilla/GfxMessageUtils.h"    // for gfxContentType
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, IntPoint
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat, etc
+#include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsISupportsImpl.h"            // for TextureImage::Release, etc
@@ -391,17 +392,17 @@ public:
   virtual void DeallocateDeviceData() MOZ_OVERRIDE {}
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   gl::GLContext* gl() const;
 
 protected:
   const gfx::IntSize mSize;
-  CompositorOGL* mCompositor;
+  RefPtr<CompositorOGL> mCompositor;
   const GLuint mTex;
   const gfx::SurfaceFormat mFormat;
   const GLenum mTextureTarget;
 };