Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 21 Oct 2014 15:55:25 +0200
changeset 227784 f33aefd6cea229d25d0346cf07ab5e7fdbdd66bf
parent 227783 112be8f081b30f6b8863af84111aa8b520f6b83c (current diff)
parent 227664 fe1513fc09f6ccbd496664a76b00455e6a89594c (diff)
child 227785 a92bf7479a426020e8fdc4bbd132751c79e9a129
push id7326
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:58:42 +0000
treeherdermozilla-aurora@d3a3b2a0f2f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone36.0a1
Merge mozilla-central to mozilla-inbound
mobile/android/base/GeckoAppShell.java
widget/android/GeneratedJNIWrappers.cpp
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -132,16 +132,17 @@ const Panel = Class({
   setup: function setup(options) {
     let model = merge({
       defaultWidth: 320,
       defaultHeight: 240,
       focus: true,
       position: Object.freeze({}),
       contextMenu: false
     }, panelContract(options));
+    model.ready = false;
     models.set(this, model);
 
     if (model.contentStyle || model.contentStyleFile) {
       styles.set(this, Style({
         uri: model.contentStyleFile,
         source: model.contentStyle
       }));
     }
@@ -295,25 +296,40 @@ let hides = filter(panelEvents, ({type})
 let ready = filter(panelEvents, ({type, target}) =>
   getAttachEventType(modelFor(panelFor(target))) === type);
 
 // Styles should be always added as soon as possible, and doesn't makes them
 // depends on `contentScriptWhen`
 let start = filter(panelEvents, ({type}) => type === "document-element-inserted");
 
 // Forward panel show / hide events to panel's own event listeners.
-on(shows, "data", ({target}) => emit(panelFor(target), "show"));
+on(shows, "data", ({target}) => {
+  let panel = panelFor(target);
+  if (modelFor(panel).ready)
+    emit(panel, "show");
+});
 
-on(hides, "data", ({target}) => emit(panelFor(target), "hide"));
+on(hides, "data", ({target}) => {
+  let panel = panelFor(target);
+  if (modelFor(panel).ready)
+    emit(panel, "hide");
+});
 
 on(ready, "data", ({target}) => {
   let panel = panelFor(target);
   let window = domPanel.getContentDocument(target).defaultView;
 
   workerFor(panel).attach(window);
+
+  if (!modelFor(panel).ready) {
+    modelFor(panel).ready = true;
+
+    if (viewFor(panel).state == "open")
+      emit(panel, "show");
+  }
 });
 
 on(start, "data", ({target}) => {
   let panel = panelFor(target);
   let window = domPanel.getContentDocument(target).defaultView;
 
   attach(styleFor(panel), window);
 });
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -276,27 +276,38 @@ exports["test Resize Panel"] = function(
     browserWindow.focus();
   }
 };
 
 exports["test Hide Before Show"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let showCalled = false;
-  let panel = Panel({
+  let hideCalled = false;
+  let panel1 = Panel({
     onShow: function () {
       showCalled = true;
     },
     onHide: function () {
-      assert.ok(!showCalled, 'must not emit show if was hidden before');
-      done();
+      hideCalled = true;
     }
   });
-  panel.show();
-  panel.hide();
+  panel1.show();
+  panel1.hide();
+
+  let panel2 = Panel({
+    onShow: function () {
+      assert.ok(!showCalled, 'should not emit show');
+      assert.ok(!hideCalled, 'should not emit hide');
+      panel1.destroy();
+      panel2.destroy();
+      done();
+    },
+  });
+  panel2.show();
 };
 
 exports["test Several Show Hides"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let hideCalled = 0;
   let panel = Panel({
     contentURL: "about:buildconfig",
@@ -1274,16 +1285,57 @@ exports["test panel addon global object"
 
   yield wait(panel.port, "document-to-addon");
 
   assert.pass("Received an event from the document");
 
   loader.unload();
 }
 
+exports["test panel load doesn't show"] = function*(assert) {
+  let loader = Loader(module);
+
+  let showCount = 0;
+  let panel = loader.require("sdk/panel").Panel({
+    contentScript: "addEventListener('load', function(event) { self.postMessage('load'); });",
+    contentScriptWhen: "start",
+    contentURL: "data:text/html;charset=utf-8,",
+  });
+
+  let shown = defer();
+  let messaged = defer();
+
+  panel.once("show", function() {
+    shown.resolve();
+  });
+
+  panel.once("message", function() {
+    messaged.resolve();
+  });
+
+  panel.show();
+  yield all([shown.promise, messaged.promise]);
+  assert.ok(true, "Saw panel display");
+
+  panel.on("show", function() {
+    assert.fail("Should not have seen another show event")
+  });
+
+  messaged = defer();
+  panel.once("message", function() {
+    assert.ok(true, "Saw panel reload");
+    messaged.resolve();
+  });
+
+  panel.contentURL = "data:text/html;charset=utf-8,<html/>";
+
+  yield messaged.promise;
+  loader.unload();
+}
+
 if (isWindowPBSupported) {
   exports.testGetWindow = function(assert, done) {
     let activeWindow = getMostRecentBrowserWindow();
     open(null, { features: {
       toolbar: true,
       chrome: true,
       private: true
     } }).then(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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="a6cfdff6e9198a0f0aec5be024d26ecf2d36c614"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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"/>
@@ -128,17 +128,17 @@
   <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="05aa7b98d3f891b334031dc710d48d0d6b82ec1d"/>
-  <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="7731d63c809dbca4da408e1de0c1a044f0765e52"/>
+  <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="3c4f041e3e3dc676f2111caf20a186ec0467dbdb"/>
   <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"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7196881a0e9dd7bfbbcf0af64c8064e70f0fa094"/>
@@ -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="a6cfdff6e9198a0f0aec5be024d26ecf2d36c614"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="a6cfdff6e9198a0f0aec5be024d26ecf2d36c614"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
   <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": "162be54d45ece9196921eb2b342490ec2ab9e1a6", 
+    "revision": "ce8e42aa3688f56113d68bc82409a7fea055547b", 
     "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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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="a6cfdff6e9198a0f0aec5be024d26ecf2d36c614"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d872fc1462d367eb67833de6942c297d6c4dc874"/>
   <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="457a54fc3200b80e4f5e1cd3acaa062309230732"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3ec94f448bb5c1c9c264896685c6ef77ab718c87"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="73d68c9c91bc568ce7c888ac057b3f44bd1b2e79"/>
   <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/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -178,17 +178,17 @@ skip-if = e10s # Bug 924260 - "Window is
 skip-if = e10s # Bug 918634 - swapFrameLoaders not implemented for e10s
 [browser_bug479408.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
 [browser_bug481560.js]
 skip-if = e10s # Bug ????? - This bug attached an event listener directly to the content
 [browser_bug484315.js]
 skip-if = e10s
 [browser_bug491431.js]
-skip-if = buildapp == 'mulet' || e10s # Bug 918634 - swapFrameLoaders (and thus replaceTabWithWindow) not implemented for e10s
+skip-if = buildapp == 'mulet'
 [browser_bug495058.js]
 skip-if = e10s # Bug 918634 - swapFrameLoaders (and thus replaceTabWithWindow) not implemented for e10s
 [browser_bug517902.js]
 skip-if = e10s # Bug 866413 - PageInfo doesn't work in e10s
 [browser_bug519216.js]
 skip-if = e10s # Bug ?????? - some weird timing issue with progress listeners that fails intermittently
 [browser_bug520538.js]
 [browser_bug521216.js]
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -109,32 +109,32 @@ p {
   .btn-info:active {
     background-color: #006b9d;
     border: 1px solid #006b9d;
   }
 
 .btn-accept,
 .btn-success,
 .btn-accept + .btn-chevron {
-  background-color: #74bf43;
-  border: 1px solid #74bf43;
+  background-color: #5bc0a4;
+  border: 1px solid #5bc0a4;
 }
 
   .btn-accept:hover,
   .btn-success:hover,
   .btn-accept + .btn-chevron:hover {
-    background-color: #6cb23e;
-    border: 1px solid #6cb23e;
+    background-color: #47b396;
+    border: 1px solid #47b396;
   }
 
   .btn-accept:active,
   .btn-success:active,
   .btn-accept + .btn-chevron:active {
-    background-color: #64a43a;
-    border: 1px solid #64a43a;
+    background-color: #3aa689;
+    border: 1px solid #3aa689;
   }
 
 .btn-warning {
   background-color: #f0ad4e;
 }
 
 .btn-cancel,
 .btn-error,
@@ -148,27 +148,27 @@ p {
 
   .btn-cancel:hover,
   .btn-error:hover,
   .btn-decline:hover,
   .btn-hangup:hover,
   .btn-decline + .btn-chevron:hover,
   .btn-error + .btn-chevron:hover {
     background-color: #c53436;
-    border: 1px solid #c53436;
+    border-color: #c53436;
   }
 
   .btn-cancel:active,
   .btn-error:active,
   .btn-decline:active,
   .btn-hangup:active,
   .btn-decline + .btn-chevron:active,
   .btn-error + .btn-chevron:active {
     background-color: #ae2325;
-    border: 1px solid #ae2325;
+    border-color: #ae2325;
   }
 
 .btn-chevron {
   width: 26px;
   height: 26px;
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
 }
--- a/browser/components/loop/content/shared/css/contacts.css
+++ b/browser/components/loop/content/shared/css/contacts.css
@@ -131,23 +131,28 @@
   line-height: 16px;
 }
 
 .icons {
   cursor: pointer;
   display: none;
   margin-left: auto;
   padding: 12px 10px;
-  border-radius: 30px;
-  background: #7ed321;
+  border-radius: 2px;
+  background: #5bc0a4;
+  color: #fff;
   -moz-user-select: none;
 }
 
 .icons:hover {
-  background: #89e029;
+  background-color: #47b396;
+}
+
+.icons:hover:active {
+  background-color: #3aa689;
 }
 
 .icons i {
   margin: 0 5px;
   display: inline-block;
   background-position: center;
   background-repeat: no-repeat;
 }
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -107,17 +107,17 @@
 
 .fx-embedded-btn-audio-small,
 .fx-embedded-btn-video-small {
   width: 26px;
   height: 26px;
   border-left: 1px solid rgba(255,255,255,.4);
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
-  background-color: #74BF43;
+  background-color: #5bc0a4;
   background-position: center;
   background-size: 1rem;
   background-repeat: no-repeat;
   cursor: pointer;
 }
 
   .fx-embedded-btn-video-small:hover,
   .fx-embedded-btn-audio-small:hover {
--- a/browser/components/loop/content/shared/css/panel.css
+++ b/browser/components/loop/content/shared/css/panel.css
@@ -238,30 +238,30 @@ body {
 }
 
 .button:hover:active {
   background-color: #ccc;
   color: #111;
 }
 
 .button.button-accept {
-  background-color: #74bf43;
-  border-color: #74bf43;
+  background-color: #5bc0a4;
+  border-color: #5bc0a4;
   color: #fff;
 }
 
 .button.button-accept:hover {
-  background-color: #6cb23e;
-  border-color: #6cb23e;
+  background-color: #47b396;
+  border-color: #47b396;
   color: #fff;
 }
 
 .button.button-accept:hover:active {
-  background-color: #64a43a;
-  border-color: #64a43a;
+  background-color: #3aa689;
+  border-color: #3aa689;
   color: #fff;
 }
 
 /* Dropdown menu */
 
 .dropdown {
   position: relative;
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1903,16 +1903,20 @@ richlistitem[type~="action"][actiontype=
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
   -moz-margin-end: 4px;
 }
 
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
 }
 
+toolbar[brighttext] toolbarbutton.chevron:not(:hover):not([open="true"]) {
+  list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
+}
+
 toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 toolbarbutton.chevron > .toolbarbutton-text,
 toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
   display: none;
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -209,29 +209,37 @@ toolbarseparator {
 }
 
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/icons/chevron.png");
   margin: 1px 0 0;
   padding: 0;
 }
 
+toolbar[brighttext] toolbarbutton.chevron {
+  list-style-image: url("chrome://global/skin/icons/chevron-inverted.png");
+}
+
 toolbarbutton.chevron > .toolbarbutton-text {
   display: none;
 }
 
 toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 @media (min-resolution: 2dppx) {
   toolbarbutton.chevron {
     list-style-image: url("chrome://global/skin/icons/chevron@2x.png");
   }
 
+  toolbar[brighttext] toolbarbutton.chevron {
+    list-style-image: url("chrome://global/skin/icons/chevron-inverted@2x.png");
+  }
+
   toolbarbutton.chevron > .toolbarbutton-icon {
     width: 13px;
   }
 }
 
 /* ----- BOOKMARK BUTTONS ----- */
 
 toolbarbutton.bookmark-item:not(.subviewbutton):not(#bookmarks-menu-button),
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1970,16 +1970,20 @@ toolbarbutton[type="socialmark"] > .tool
   /* box-shadow instead of background-color to work around native styling */
   box-shadow: inset -5px 0 ThreeDShadow;
 }
 
 toolbarbutton.chevron {
   list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
 }
 
+toolbar[brighttext] toolbarbutton.chevron {
+  list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
+}
+
 toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 toolbarbutton.chevron > .toolbarbutton-text,
 toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
   display: none;
 }
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -64,16 +64,17 @@
 #include "nsNameSpaceManager.h"
 
 #include "nsThreadUtils.h"
 
 #include "nsIDOMChromeWindow.h"
 #include "nsInProcessTabChildGlobal.h"
 
 #include "Layers.h"
+#include "ClientLayerManager.h"
 
 #include "AppProcessChecker.h"
 #include "ContentParent.h"
 #include "TabParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
@@ -936,25 +937,121 @@ nsFrameLoader::Hide()
   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
   NS_ASSERTION(baseWin,
                "Found an nsIDocShell which doesn't implement nsIBaseWindow.");
   baseWin->SetVisibility(false);
   baseWin->SetParentWidget(nullptr);
 }
 
 nsresult
+nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
+                                         nsRefPtr<nsFrameLoader>& aFirstToSwap,
+                                         nsRefPtr<nsFrameLoader>& aSecondToSwap)
+{
+  Element* ourContent = mOwnerContent;
+  Element* otherContent = aOther->mOwnerContent;
+
+  if (!ourContent || !otherContent) {
+    // Can't handle this
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // Make sure there are no same-origin issues
+  bool equal;
+  nsresult rv =
+    ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
+  if (NS_FAILED(rv) || !equal) {
+    // Security problems loom.  Just bail on it all
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsIDocument* ourDoc = ourContent->GetCurrentDoc();
+  nsIDocument* otherDoc = otherContent->GetCurrentDoc();
+  if (!ourDoc || !otherDoc) {
+    // Again, how odd, given that we had docshells
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  nsIPresShell* ourShell = ourDoc->GetShell();
+  nsIPresShell* otherShell = otherDoc->GetShell();
+  if (!ourShell || !otherShell) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  if (mInSwap || aOther->mInSwap) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+  mInSwap = aOther->mInSwap = true;
+
+  nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
+  nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
+  if (!ourFrame || !otherFrame) {
+    mInSwap = aOther->mInSwap = false;
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
+  if (!ourFrameFrame) {
+    mInSwap = aOther->mInSwap = false;
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
+  if (NS_FAILED(rv)) {
+    mInSwap = aOther->mInSwap = false;
+    return rv;
+  }
+
+  SetOwnerContent(otherContent);
+  aOther->SetOwnerContent(ourContent);
+
+  mRemoteBrowser->SetOwnerElement(otherContent);
+  aOther->mRemoteBrowser->SetOwnerElement(ourContent);
+
+  nsRefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
+  nsRefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
+  // Swap and setup things in parent message managers.
+  if (mMessageManager) {
+    mMessageManager->SetCallback(aOther);
+  }
+  if (aOther->mMessageManager) {
+    aOther->mMessageManager->SetCallback(this);
+  }
+  mMessageManager.swap(aOther->mMessageManager);
+
+  aFirstToSwap.swap(aSecondToSwap);
+
+  ourFrameFrame->EndSwapDocShells(otherFrame);
+
+  ourDoc->FlushPendingNotifications(Flush_Layout);
+  otherDoc->FlushPendingNotifications(Flush_Layout);
+
+  mInSwap = aOther->mInSwap = false;
+  return NS_OK;
+}
+
+nsresult
 nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
                                    nsRefPtr<nsFrameLoader>& aFirstToSwap,
                                    nsRefPtr<nsFrameLoader>& aSecondToSwap)
 {
   NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
                   (aFirstToSwap == aOther && aSecondToSwap == this),
                   "Swapping some sort of random loaders?");
   NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
 
+  if (mRemoteFrame && aOther->mRemoteFrame) {
+    return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
+  }
+
+  if (mRemoteFrame || aOther->mRemoteFrame) {
+    NS_WARNING("Swapping remote and non-remote frames is not currently supported");
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
   Element* ourContent = mOwnerContent;
   Element* otherContent = aOther->mOwnerContent;
 
   if (!ourContent || !otherContent) {
     // Can't handle this
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -126,16 +126,20 @@ public:
 
   // The guts of an nsIFrameLoaderOwner::SwapFrameLoader implementation.  A
   // frame loader owner needs to call this, and pass in the two references to
   // nsRefPtrs for frame loaders that need to be swapped.
   nsresult SwapWithOtherLoader(nsFrameLoader* aOther,
                                nsRefPtr<nsFrameLoader>& aFirstToSwap,
                                nsRefPtr<nsFrameLoader>& aSecondToSwap);
 
+  nsresult SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
+                                     nsRefPtr<nsFrameLoader>& aFirstToSwap,
+                                     nsRefPtr<nsFrameLoader>& aSecondToSwap);
+
   // When IPC is enabled, destroy any associated child process.
   void DestroyChild();
 
   /**
    * Return the primary frame for our owning content, or null if it
    * can't be found.
    */
   nsIFrame* GetPrimaryFrameOfOwningContent() const
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -631,27 +631,23 @@ BluetoothHfpManager::HandleVoiceConnecti
   JS::Rooted<JS::Value> value(cx);
   voiceInfo->GetRelSignalStrength(&value);
   NS_ENSURE_TRUE_VOID(value.isNumber());
   uint8_t signal = ceil(value.toNumber() / 20.0);
   UpdateCIND(CINDType::SIGNAL, signal);
 
   /**
    * Possible return values for mode are:
-   * - null (unknown): set mNetworkSelectionMode to 0 (auto)
-   * - automatic: set mNetworkSelectionMode to 0 (auto)
-   * - manual: set mNetworkSelectionMode to 1 (manual)
+   * - -1 (unknown): set mNetworkSelectionMode to 0 (auto)
+   * - 0 (automatic): set mNetworkSelectionMode to 0 (auto)
+   * - 1 (manual): set mNetworkSelectionMode to 1 (manual)
    */
-  nsString mode;
-  connection->GetNetworkSelectionMode(mode);
-  if (mode.EqualsLiteral("manual")) {
-    mNetworkSelectionMode = 1;
-  } else {
-    mNetworkSelectionMode = 0;
-  }
+  int32_t mode;
+  connection->GetNetworkSelectionMode(&mode);
+  mNetworkSelectionMode = (mode == 1) ? 1 : 0;
 
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
   NS_ENSURE_TRUE_VOID(network);
   network->GetLongName(mOperatorName);
 
   // According to GSM 07.07, "<format> indicates if the format is alphanumeric
   // or numeric; long alphanumeric format can be upto 16 characters long and
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -631,27 +631,23 @@ BluetoothHfpManager::HandleVoiceConnecti
   JS::Rooted<JS::Value> value(cx);
   voiceInfo->GetRelSignalStrength(&value);
   NS_ENSURE_TRUE_VOID(value.isNumber());
   uint8_t signal = ceil(value.toNumber() / 20.0);
   UpdateCIND(CINDType::SIGNAL, signal);
 
   /**
    * Possible return values for mode are:
-   * - null (unknown): set mNetworkSelectionMode to 0 (auto)
-   * - automatic: set mNetworkSelectionMode to 0 (auto)
-   * - manual: set mNetworkSelectionMode to 1 (manual)
+   * - -1 (unknown): set mNetworkSelectionMode to 0 (auto)
+   * - 0 (automatic): set mNetworkSelectionMode to 0 (auto)
+   * - 1 (manual): set mNetworkSelectionMode to 1 (manual)
    */
-  nsString mode;
-  connection->GetNetworkSelectionMode(mode);
-  if (mode.EqualsLiteral("manual")) {
-    mNetworkSelectionMode = 1;
-  } else {
-    mNetworkSelectionMode = 0;
-  }
+  int32_t mode;
+  connection->GetNetworkSelectionMode(&mode);
+  mNetworkSelectionMode = (mode == 1) ? 1 : 0;
 
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
   NS_ENSURE_TRUE_VOID(network);
   network->GetLongName(mOperatorName);
 
   // According to GSM 07.07, "<format> indicates if the format is alphanumeric
   // or numeric; long alphanumeric format can be upto 16 characters long and
new file mode 100644
--- /dev/null
+++ b/dom/mobileconnection/Assertions.cpp
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MozMobileConnectionBinding.h"
+#include "nsIMobileConnectionService.h"
+
+namespace mozilla {
+namespace dom {
+
+#define ASSERT_NETWORK_SELECTION_MODE_EQUALITY(webidlState, xpidlState) \
+  static_assert(static_cast<int32_t>(MobileNetworkSelectionMode::webidlState) == nsIMobileConnection::xpidlState, \
+                "MobileNetworkSelectionMode::" #webidlState " should equal to nsIMobileConnection::" #xpidlState)
+
+ASSERT_NETWORK_SELECTION_MODE_EQUALITY(Automatic, NETWORK_SELECTION_MODE_AUTOMATIC);
+ASSERT_NETWORK_SELECTION_MODE_EQUALITY(Manual, NETWORK_SELECTION_MODE_MANUAL);
+
+#undef ASSERT_NETWORK_SELECTION_MODE_EQUALITY
+
+} // namespace dom
+} // namespace mozilla
+
--- a/dom/mobileconnection/MobileConnection.cpp
+++ b/dom/mobileconnection/MobileConnection.cpp
@@ -344,19 +344,22 @@ MobileConnection::GetNetworkSelectionMod
 {
   Nullable<MobileNetworkSelectionMode> retVal =
     Nullable<MobileNetworkSelectionMode>();
 
   if (!mMobileConnection) {
     return retVal;
   }
 
-  nsAutoString mode;
-  mMobileConnection->GetNetworkSelectionMode(mode);
-  CONVERT_STRING_TO_NULLABLE_ENUM(mode, MobileNetworkSelectionMode, retVal);
+  int32_t mode = nsIMobileConnection::NETWORK_SELECTION_MODE_UNKNOWN;
+  if (NS_SUCCEEDED(mMobileConnection->GetNetworkSelectionMode(&mode)) &&
+      mode != nsIMobileConnection::NETWORK_SELECTION_MODE_UNKNOWN) {
+    MOZ_ASSERT(mode < static_cast<int32_t>(MobileNetworkSelectionMode::EndGuard_));
+    retVal.SetValue(static_cast<MobileNetworkSelectionMode>(mode));
+  }
 
   return retVal;
 }
 
 Nullable<MobileRadioState>
 MobileConnection::GetRadioState() const
 {
   Nullable<MobileRadioState> retVal = Nullable<MobileRadioState>();
--- a/dom/mobileconnection/gonk/MobileConnectionService.js
+++ b/dom/mobileconnection/gonk/MobileConnectionService.js
@@ -231,17 +231,17 @@ MobileConnectionProvider.prototype = {
    * The networks that are currently trying to be selected (or "automatic").
    * This helps ensure that only one network per client is selected at a time.
    */
   _selectingNetwork: null,
 
   voice: null,
   data: null,
   iccId: null,
-  networkSelectionMode: null,
+  networkSelectionMode: Ci.nsIMobileConnection.NETWORK_SELECTION_MODE_UNKNOWN,
   radioState: null,
   lastKnownNetwork: null,
   lastKnownHomeNetwork: null,
   supportedNetworkTypes: null,
 
   /**
    * A utility function to dump debug message.
    */
--- a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
+++ b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl
@@ -229,17 +229,17 @@ interface nsIMobileConnectionService : n
 
 %{C++
 template<typename T> struct already_AddRefed;
 
 already_AddRefed<nsIMobileConnectionService>
 NS_CreateMobileConnectionService();
 %}
 
-[scriptable, uuid(1b76ccbf-dbc2-4b74-a62a-73ea91599afa)]
+[scriptable, uuid(bde83c1d-1335-43b6-8268-ec7e320167f0)]
 interface nsIMobileConnection : nsISupports
 {
   /*
    * ICC service class.
    */
   const long ICC_SERVICE_CLASS_NONE       = 0; // not available
   const long ICC_SERVICE_CLASS_VOICE      = (1 << 0);
   const long ICC_SERVICE_CLASS_DATA       = (1 << 1);
@@ -290,16 +290,23 @@ interface nsIMobileConnection : nsISuppo
    * Calling line identification restriction constants.
    *
    * @see 3GPP TS 27.007 7.7 Defined values.
    */
   const long CLIR_DEFAULT     = 0;
   const long CLIR_INVOCATION  = 1;
   const long CLIR_SUPPRESSION = 2;
 
+  /**
+   * Network selection mode.
+   */
+  const long NETWORK_SELECTION_MODE_UNKNOWN   = -1; // not available
+  const long NETWORK_SELECTION_MODE_AUTOMATIC = 0;
+  const long NETWORK_SELECTION_MODE_MANUAL    = 1;
+
   readonly attribute unsigned long serviceId;
 
   /**
    * Called when any one who is interested in receiving unsolicited messages
    * from this nsIMobileConnection instance.
    */
   void registerListener(in nsIMobileConnectionListener listener);
   void unregisterListener(in nsIMobileConnectionListener listener);
@@ -327,20 +334,20 @@ interface nsIMobileConnection : nsISuppo
   readonly attribute nsIMobileConnectionInfo data;
 
   /**
    * The integrated circuit card identifier of the SIM.
    */
   readonly attribute DOMString iccId;
 
   /**
-   * The selection mode of the voice and data networks. Possible values are
-   * 'automatic', 'manual', null (unknown).
+   * The selection mode of the voice and data networks. One of the
+   * nsIMobileConnection.NETWORK_SELECTION_MODE_* values.
    */
-  readonly attribute DOMString networkSelectionMode;
+  readonly attribute long networkSelectionMode;
 
   /**
    * Current radio state. Possible values are 'enabling', 'enabled',
    * 'disabling', 'disabled', null (unknown).
    */
   readonly attribute DOMString radioState;
 
   /**
--- a/dom/mobileconnection/ipc/MobileConnectionChild.cpp
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.cpp
@@ -141,19 +141,19 @@ MobileConnectionChild::GetLastKnownNetwo
 NS_IMETHODIMP
 MobileConnectionChild::GetLastKnownHomeNetwork(nsAString& aNetwork)
 {
   aNetwork = mLastHomeNetwork;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileConnectionChild::GetNetworkSelectionMode(nsAString& aMode)
+MobileConnectionChild::GetNetworkSelectionMode(int32_t* aMode)
 {
-  aMode = mNetworkSelectionMode;
+  *aMode = mNetworkSelectionMode;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileConnectionChild::GetNetworks(nsIMobileConnectionCallback* aCallback)
 {
   return SendRequest(GetNetworksRequest(), aCallback) ? NS_OK : NS_ERROR_FAILURE;
 }
@@ -507,19 +507,19 @@ bool
 MobileConnectionChild::RecvNotifyLastHomeNetworkChanged(const nsString& aNetwork)
 {
   mLastHomeNetwork.Assign(aNetwork);
 
   return true;
 }
 
 bool
-MobileConnectionChild::RecvNotifyNetworkSelectionModeChanged(const nsString& aMode)
+MobileConnectionChild::RecvNotifyNetworkSelectionModeChanged(const int32_t& aMode)
 {
-  mNetworkSelectionMode.Assign(aMode);
+  mNetworkSelectionMode = aMode;
 
   return true;
 }
 
 /******************************************************************************
  * MobileConnectionRequestChild
  ******************************************************************************/
 
--- a/dom/mobileconnection/ipc/MobileConnectionChild.h
+++ b/dom/mobileconnection/ipc/MobileConnectionChild.h
@@ -99,29 +99,29 @@ protected:
 
   virtual bool
   RecvNotifyLastNetworkChanged(const nsString& aNetwork) MOZ_OVERRIDE;
 
   virtual bool
   RecvNotifyLastHomeNetworkChanged(const nsString& aNetwork) MOZ_OVERRIDE;
 
   virtual bool
-  RecvNotifyNetworkSelectionModeChanged(const nsString& aMode) MOZ_OVERRIDE;
+  RecvNotifyNetworkSelectionModeChanged(const int32_t& aMode) MOZ_OVERRIDE;
 
 private:
   uint32_t mServiceId;
   bool mLive;
   nsCOMArray<nsIMobileConnectionListener> mListeners;
   nsRefPtr<MobileConnectionInfo> mVoice;
   nsRefPtr<MobileConnectionInfo> mData;
   nsString mIccId;
   nsString mRadioState;
   nsString mLastNetwork;
   nsString mLastHomeNetwork;
-  nsString mNetworkSelectionMode;
+  int32_t mNetworkSelectionMode;
   nsTArray<nsString> mSupportedNetworkTypes;
 };
 
 /******************************************************************************
  * PMobileConnectionRequestChild
  ******************************************************************************/
 
 /**
--- a/dom/mobileconnection/ipc/MobileConnectionParent.cpp
+++ b/dom/mobileconnection/ipc/MobileConnectionParent.cpp
@@ -124,28 +124,28 @@ MobileConnectionParent::DeallocPMobileCo
 }
 
 bool
 MobileConnectionParent::RecvInit(nsMobileConnectionInfo* aVoice,
                                  nsMobileConnectionInfo* aData,
                                  nsString* aLastKnownNetwork,
                                  nsString* aLastKnownHomeNetwork,
                                  nsString* aIccId,
-                                 nsString* aNetworkSelectionMode,
+                                 int32_t* aNetworkSelectionMode,
                                  nsString* aRadioState,
                                  nsTArray<nsString>* aSupportedNetworkTypes)
 {
   NS_ENSURE_TRUE(mMobileConnection, false);
 
   NS_ENSURE_SUCCESS(mMobileConnection->GetVoice(aVoice), false);
   NS_ENSURE_SUCCESS(mMobileConnection->GetData(aData), false);
   NS_ENSURE_SUCCESS(mMobileConnection->GetLastKnownNetwork(*aLastKnownNetwork), false);
   NS_ENSURE_SUCCESS(mMobileConnection->GetLastKnownHomeNetwork(*aLastKnownHomeNetwork), false);
   NS_ENSURE_SUCCESS(mMobileConnection->GetIccId(*aIccId), false);
-  NS_ENSURE_SUCCESS(mMobileConnection->GetNetworkSelectionMode(*aNetworkSelectionMode), false);
+  NS_ENSURE_SUCCESS(mMobileConnection->GetNetworkSelectionMode(aNetworkSelectionMode), false);
   NS_ENSURE_SUCCESS(mMobileConnection->GetRadioState(*aRadioState), false);
 
   char16_t** types = nullptr;
   uint32_t length = 0;
 
   nsresult rv = mMobileConnection->GetSupportedNetworkTypes(&types, &length);
   NS_ENSURE_SUCCESS(rv, false);
 
@@ -304,18 +304,18 @@ MobileConnectionParent::NotifyLastKnownH
 }
 
 NS_IMETHODIMP
 MobileConnectionParent::NotifyNetworkSelectionModeChanged()
 {
   NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE);
 
   nsresult rv;
-  nsAutoString mode;
-  rv = mMobileConnection->GetNetworkSelectionMode(mode);
+  int32_t mode;
+  rv = mMobileConnection->GetNetworkSelectionMode(&mode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return SendNotifyNetworkSelectionModeChanged(mode) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 /******************************************************************************
  * PMobileConnectionRequestParent
  ******************************************************************************/
--- a/dom/mobileconnection/ipc/MobileConnectionParent.h
+++ b/dom/mobileconnection/ipc/MobileConnectionParent.h
@@ -46,17 +46,17 @@ protected:
   AllocPMobileConnectionRequestParent(const MobileConnectionRequest& request) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPMobileConnectionRequestParent(PMobileConnectionRequestParent* aActor) MOZ_OVERRIDE;
 
   virtual bool
   RecvInit(nsMobileConnectionInfo* aVoice, nsMobileConnectionInfo* aData,
            nsString* aLastKnownNetwork, nsString* aLastKnownHomeNetwork,
-           nsString* aIccId, nsString* aNetworkSelectionMode,
+           nsString* aIccId, int32_t* aNetworkSelectionMode,
            nsString* aRadioState, nsTArray<nsString>* aSupportedNetworkTypes) MOZ_OVERRIDE;
 
 private:
   nsCOMPtr<nsIMobileConnection> mMobileConnection;
   bool mLive;
 };
 
 /******************************************************************************
--- a/dom/mobileconnection/ipc/PMobileConnection.ipdl
+++ b/dom/mobileconnection/ipc/PMobileConnection.ipdl
@@ -27,17 +27,17 @@ child:
                        uint16_t aServiceClass);
   NotifyEmergencyCbModeChanged(bool aActive, uint32_t aTimeoutMs);
   NotifyOtaStatusChanged(nsString aStatus);
   NotifyIccChanged(nsString aIccId);
   NotifyRadioStateChanged(nsString aRadioState);
   NotifyClirModeChanged(uint32_t aMode);
   NotifyLastNetworkChanged(nsString aNetwork);
   NotifyLastHomeNetworkChanged(nsString aNetwork);
-  NotifyNetworkSelectionModeChanged(nsString aMode);
+  NotifyNetworkSelectionModeChanged(int32_t aMode);
 
 parent:
   /**
    * Send when child no longer needs to use PMobileConnection.
    */
   __delete__();
 
   /**
@@ -46,17 +46,17 @@ parent:
   PMobileConnectionRequest(MobileConnectionRequest aRequest);
 
   /**
    * Sync call only be called once per child actor for initialization.
    */
   sync Init()
     returns (nsMobileConnectionInfo aVoice, nsMobileConnectionInfo aData,
              nsString aLastKnownNetwork, nsString aLastKnownHomeNetwork,
-             nsString aIccId, nsString aNetworkSelectionMode,
+             nsString aIccId, int32_t aNetworkSelectionMode,
              nsString aRadioState, nsString[] aSupportedNetworkTypes);
 };
 
 /**
  * MobileConnectionRequest
  */
 struct GetNetworksRequest
 {
--- a/dom/mobileconnection/moz.build
+++ b/dom/mobileconnection/moz.build
@@ -28,16 +28,17 @@ XPIDL_SOURCES += [
     'interfaces/nsIMobileCellInfo.idl',
     'interfaces/nsIMobileConnectionInfo.idl',
     'interfaces/nsIMobileConnectionService.idl',
     'interfaces/nsIMobileNetworkInfo.idl',
     'interfaces/nsINeighboringCellInfo.idl',
 ]
 
 UNIFIED_SOURCES += [
+    'Assertions.cpp',
     'DOMMMIError.cpp',
     'ipc/MobileConnectionChild.cpp',
     'ipc/MobileConnectionIPCService.cpp',
     'ipc/MobileConnectionParent.cpp',
     'MobileCallForwardingOptions.cpp',
     'MobileCellInfo.cpp',
     'MobileConnection.cpp',
     'MobileConnectionArray.cpp',
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2595,19 +2595,20 @@ GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECK
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK1_PUK] = CARD_PERSOSUBSTATE_RUIM_NETWORK1_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_NCK2_PUK] = CARD_PERSOSUBSTATE_RUIM_NETWORK2_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_HNCK_PUK] = CARD_PERSOSUBSTATE_RUIM_HRPD_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_CCK_PUK] = CARD_PERSOSUBSTATE_SIM_CORPORATE_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_SPCK_PUK] = CARD_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RCCK_PUK] = CARD_PERSOSUBSTATE_RUIM_CORPORATE_PUK;
 GECKO_PERSO_LOCK_TO_CARD_PERSO_LOCK[GECKO_CARDLOCK_RSPCK_PUK] = CARD_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK;
 
-this.GECKO_NETWORK_SELECTION_UNKNOWN   = null;
-this.GECKO_NETWORK_SELECTION_AUTOMATIC = "automatic";
-this.GECKO_NETWORK_SELECTION_MANUAL    = "manual";
+// See nsIMobileConnection::NETWORK_SELECTION_MODE_*
+this.GECKO_NETWORK_SELECTION_UNKNOWN   = -1;
+this.GECKO_NETWORK_SELECTION_AUTOMATIC = 0;
+this.GECKO_NETWORK_SELECTION_MANUAL    = 1;
 
 this.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN = null;
 this.GECKO_MOBILE_CONNECTION_STATE_NOTSEARCHING = "notSearching";
 this.GECKO_MOBILE_CONNECTION_STATE_SEARCHING = "searching";
 this.GECKO_MOBILE_CONNECTION_STATE_REGISTERED = "registered";
 this.GECKO_MOBILE_CONNECTION_STATE_DENIED = "denied";
 
 this.NETWORK_CREG_TO_GECKO_MOBILE_CONNECTION_STATE = {};
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -464,17 +464,17 @@ RilObject.prototype = {
      */
     this.aid = null;
 
     /**
      * Application type for apps in ICC.
      */
     this.appType = null;
 
-    this.networkSelectionMode = null;
+    this.networkSelectionMode = GECKO_NETWORK_SELECTION_UNKNOWN;
 
     this.voiceRegistrationState = {};
     this.dataRegistrationState = {};
 
     /**
      * List of strings identifying the network operator.
      */
     this.operator = null;
--- a/dom/webidl/PopupBoxObject.webidl
+++ b/dom/webidl/PopupBoxObject.webidl
@@ -6,18 +6,19 @@
 [Func="IsChromeOrXBL"]
 interface PopupBoxObject : BoxObject
 {
   /**
    *  This method is deprecated. Use openPopup or openPopupAtScreen instead.
    */
   void showPopup(Element? srcContent, Element popupContent,
                  long xpos, long ypos,
-                 DOMString popupType, DOMString anchorAlignment,
-                 DOMString popupAlignment);
+                 optional DOMString popupType = "",
+                 optional DOMString anchorAlignment = "",
+                 optional DOMString popupAlignment = "");
 
   /**
    *  Hide the popup if it is open. The cancel argument is used as a hint that
    *  the popup is being closed because it has been cancelled, rather than
    *  something being selected within the panel.
    *
    * @param cancel if true, then the popup is being cancelled.
    */
@@ -100,18 +101,19 @@ interface PopupBoxObject : BoxObject
    * @param position manner is which to anchor the popup to node
    * @param x horizontal offset
    * @param y vertical offset
    * @param isContextMenu true for context menus, false for other popups
    * @param attributesOverride true if popup node attributes override position
    * @param triggerEvent the event that triggered this popup (mouse click for example)
    */
   void openPopup(Element? anchorElement,
-                 DOMString position,
-                 long x, long y,
+                 optional DOMString position = "",
+                 long x,
+                 long y,
                  boolean isContextMenu,
                  boolean attributesOverride,
                  Event? triggerEvent);
 
   /**
    * Open the popup at a specific screen position specified by x and y. This
    * position may be adjusted if it would cause the popup to be off of the
    * screen. The x and y coordinates are measured in CSS pixels, and like all
@@ -155,17 +157,17 @@ interface PopupBoxObject : BoxObject
   DOMRect getOuterScreenRect();
 
   /**
    * Move an open popup to the given anchor position. The arguments have the same
    * meaning as the corresponding argument to openPopup. This method has no effect
    * on popups that are not open.
    */
   void moveToAnchor(Element? anchorElement,
-                    DOMString position,
+                    optional DOMString position = "",
                     long x, long y,
                     boolean attributesOverride);
 
   /** Returns the alignment position where the popup has appeared relative to its
    *  anchor node or point, accounting for any flipping that occurred.
    */
   readonly attribute DOMString alignmentPosition;
   readonly attribute long alignmentOffset;
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -2221,31 +2221,36 @@ public:
    * CONSTRUCTION PHASE ONLY
    * Connect this ref layer to its referent, temporarily.
    * ClearReferentLayer() must be called after composition.
    */
   void ConnectReferentLayer(Layer* aLayer)
   {
     MOZ_ASSERT(!mFirstChild && !mLastChild);
     MOZ_ASSERT(!aLayer->GetParent());
-    MOZ_ASSERT(aLayer->Manager() == Manager());
+    if (aLayer->Manager() != Manager()) {
+      // This can happen when e.g. rendering while dragging tabs
+      // between windows - aLayer's manager may be the manager for the
+      // old window's tab.  In that case, it will be changed before the
+      // next render (see SetLayerManager).  It is simply easier to
+      // ignore the rendering here than it is to pause it.
+      NS_WARNING("ConnectReferentLayer failed - Incorrect LayerManager");
+      return;
+    }
 
     mFirstChild = mLastChild = aLayer;
     aLayer->SetParent(this);
   }
 
   /**
    * DRAWING PHASE ONLY
    * |aLayer| is the same as the argument to ConnectReferentLayer().
    */
   void DetachReferentLayer(Layer* aLayer)
   {
-    MOZ_ASSERT(aLayer == mFirstChild && mFirstChild == mLastChild);
-    MOZ_ASSERT(aLayer->GetParent() == this);
-
     mFirstChild = mLastChild = nullptr;
     aLayer->SetParent(nullptr);
   }
 
   // These getters can be used anytime.
   virtual RefLayer* AsRefLayer() { return this; }
 
   virtual int64_t GetReferentId() { return mId; }
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -57,16 +57,26 @@ CanvasLayerComposite::SetCompositableHos
 }
 
 Layer*
 CanvasLayerComposite::GetLayer()
 {
   return this;
 }
 
+void
+CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+  LayerComposite::SetLayerManager(aManager);
+  mManager = aManager;
+  if (mImageHost) {
+    mImageHost->SetCompositor(mCompositor);
+  }
+}
+
 LayerRenderState
 CanvasLayerComposite::GetRenderState()
 {
   if (mDestroyed || !mImageHost || !mImageHost->IsAttached()) {
     return LayerRenderState();
   }
   return mImageHost->GetRenderState();
 }
--- a/gfx/layers/composite/CanvasLayerComposite.h
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -44,16 +44,18 @@ public:
 
   virtual bool SetCompositableHost(CompositableHost* aHost) MOZ_OVERRIDE;
 
   virtual void Disconnect() MOZ_OVERRIDE
   {
     Destroy();
   }
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE;
+
   virtual Layer* GetLayer() MOZ_OVERRIDE;
   virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
 
   virtual void CleanupResources() MOZ_OVERRIDE;
 
   virtual void GenEffectChain(EffectChain& aEffect) MOZ_OVERRIDE;
 
   CompositableHost* GetCompositableHost() MOZ_OVERRIDE;
--- a/gfx/layers/composite/ColorLayerComposite.h
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -37,16 +37,22 @@ protected:
     MOZ_COUNT_DTOR(ColorLayerComposite);
     Destroy();
   }
 
 public:
   // LayerComposite Implementation
   virtual Layer* GetLayer() MOZ_OVERRIDE { return this; }
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
+  {
+    LayerComposite::SetLayerManager(aManager);
+    mManager = aManager;
+  }
+
   virtual void Destroy() MOZ_OVERRIDE { mDestroyed = true; }
 
   virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
   virtual void CleanupResources() MOZ_OVERRIDE {};
 
   virtual void GenEffectChain(EffectChain& aEffect) MOZ_OVERRIDE;
 
   CompositableHost* GetCompositableHost() MOZ_OVERRIDE { return nullptr; }
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -57,16 +57,27 @@ public:
 
 protected:
   ~ContainerLayerComposite();
 
 public:
   // LayerComposite Implementation
   virtual Layer* GetLayer() MOZ_OVERRIDE { return this; }
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
+  {
+    LayerComposite::SetLayerManager(aManager);
+    mManager = aManager;
+
+    for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+      LayerComposite* child = l->AsLayerComposite();
+      child->SetLayerManager(aManager);
+    }
+  }
+
   virtual void Destroy() MOZ_OVERRIDE;
 
   LayerComposite* GetFirstChildComposite();
 
   virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
   virtual void Prepare(const RenderTargetIntRect& aClipRect) MOZ_OVERRIDE;
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) MOZ_OVERRIDE
--- a/gfx/layers/composite/ImageLayerComposite.h
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -9,24 +9,24 @@
 #include "GLTextureImage.h"             // for TextureImage
 #include "ImageLayers.h"                // for ImageLayer
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "nsISupportsImpl.h"            // for TextureImage::AddRef, etc
 #include "nscore.h"                     // for nsACString
+#include "CompositableHost.h"           // for CompositableHost
 
 struct nsIntPoint;
 struct nsIntRect;
 
 namespace mozilla {
 namespace layers {
 
-class CompositableHost;
 class ImageHost;
 class Layer;
 
 class ImageLayerComposite : public ImageLayer,
                             public LayerComposite
 {
   typedef gl::TextureImage TextureImage;
 
@@ -40,16 +40,25 @@ public:
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
   virtual void Disconnect() MOZ_OVERRIDE;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) MOZ_OVERRIDE;
 
   virtual Layer* GetLayer() MOZ_OVERRIDE;
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE
+  {
+    LayerComposite::SetLayerManager(aManager);
+    mManager = aManager;
+    if (mImageHost) {
+      mImageHost->SetCompositor(mCompositor);
+    }
+  }
+
   virtual void RenderLayer(const nsIntRect& aClipRect);
 
   virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) MOZ_OVERRIDE;
 
   virtual void CleanupResources() MOZ_OVERRIDE;
 
   CompositableHost* GetCompositableHost() MOZ_OVERRIDE;
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1130,16 +1130,23 @@ LayerManagerComposite::CanUseCanvasLayer
 void
 LayerManagerComposite::NotifyShadowTreeTransaction()
 {
   if (mFPS) {
     mFPS->NotifyShadowTreeTransaction();
   }
 }
 
+void
+LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+  mCompositeManager = aManager;
+  mCompositor = aManager->GetCompositor();
+}
+
 #ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
 
 /*static*/ bool
 LayerManagerComposite::SupportsDirectTexturing()
 {
   return false;
 }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -354,16 +354,18 @@ public:
 
   /* Do NOT call this from the generic LayerComposite destructor.  Only from the
    * concrete class destructor
    */
   virtual void Destroy();
 
   virtual Layer* GetLayer() = 0;
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager);
+
   /**
    * Perform a first pass over the layer tree to render all of the intermediate
    * surfaces that we can. This allows us to avoid framebuffer switches in the
    * middle of our render which is inefficient especially on mobile GPUs. This
    * must be called before RenderLayer.
    */
   virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
 
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -80,16 +80,26 @@ PaintedLayerComposite::Destroy()
 }
 
 Layer*
 PaintedLayerComposite::GetLayer()
 {
   return this;
 }
 
+void
+PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+{
+  LayerComposite::SetLayerManager(aManager);
+  mManager = aManager;
+  if (mBuffer) {
+    mBuffer->SetCompositor(mCompositor);
+  }
+}
+
 TiledLayerComposer*
 PaintedLayerComposite::GetTiledLayerComposer()
 {
   if (!mBuffer) {
     return nullptr;
   }
   MOZ_ASSERT(mBuffer->IsAttached());
   return mBuffer->AsTiledLayerComposer();
--- a/gfx/layers/composite/PaintedLayerComposite.h
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -48,16 +48,18 @@ public:
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
   CompositableHost* GetCompositableHost() MOZ_OVERRIDE;
 
   virtual void Destroy() MOZ_OVERRIDE;
 
   virtual Layer* GetLayer() MOZ_OVERRIDE;
 
+  virtual void SetLayerManager(LayerManagerComposite* aManager) MOZ_OVERRIDE;
+
   virtual TiledLayerComposer* GetTiledLayerComposer() MOZ_OVERRIDE;
 
   virtual void RenderLayer(const nsIntRect& aClipRect) MOZ_OVERRIDE;
 
   virtual void CleanupResources() MOZ_OVERRIDE;
 
   virtual void GenEffectChain(EffectChain& aEffect) MOZ_OVERRIDE;
 
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -366,16 +366,17 @@ BufferTextureHost::SetCompositor(Composi
   if (mCompositor == aCompositor) {
     return;
   }
   RefPtr<TextureSource> it = mFirstSource;
   while (it) {
     it->SetCompositor(aCompositor);
     it = it->GetNextSibling();
   }
+  mFirstSource = nullptr;
   mCompositor = aCompositor;
 }
 
 void
 BufferTextureHost::DeallocateDeviceData()
 {
   RefPtr<TextureSource> it = mFirstSource;
   while (it) {
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -210,16 +210,25 @@ public:
     return false;
   }
 
   const nsIntRegion& GetValidLowPrecisionRegion() const
   {
     return mLowPrecisionTiledBuffer.GetValidRegion();
   }
 
+  virtual void SetCompositor(Compositor* aCompositor)
+  {
+    CompositableHost::SetCompositor(aCompositor);
+    mTiledBuffer.SetCompositor(aCompositor);
+    mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+    mOldTiledBuffer.SetCompositor(aCompositor);
+    mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
+  }
+
   virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                    const SurfaceDescriptorTiles& aTiledDescriptor) MOZ_OVERRIDE;
 
   void Composite(EffectChain& aEffectChain,
                  float aOpacity,
                  const gfx::Matrix4x4& aTransform,
                  const gfx::Filter& aFilter,
                  const gfx::Rect& aClipRect,
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1034,16 +1034,29 @@ CompositorParent::RecvNotifyChildCreated
 
 void
 CompositorParent::NotifyChildCreated(const uint64_t& aChild)
 {
   sIndirectLayerTrees[aChild].mParent = this;
   sIndirectLayerTrees[aChild].mLayerManager = mLayerManager;
 }
 
+bool
+CompositorParent::RecvAdoptChild(const uint64_t& child)
+{
+  NotifyChildCreated(child);
+  if (sIndirectLayerTrees[child].mLayerTree) {
+    sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
+  }
+  if (sIndirectLayerTrees[child].mRoot) {
+    sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
+  }
+  return true;
+}
+
 /*static*/ uint64_t
 CompositorParent::AllocateLayerTreeId()
 {
   MOZ_ASSERT(CompositorLoop());
   MOZ_ASSERT(NS_IsMainThread());
   static uint64_t ids = 0;
   return ++ids;
 }
@@ -1174,16 +1187,17 @@ public:
 
   // FIXME/bug 774388: work out what shutdown protocol we need.
   virtual bool RecvRequestOverfill() MOZ_OVERRIDE { return true; }
   virtual bool RecvWillStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvPause() MOZ_OVERRIDE { return true; }
   virtual bool RecvResume() MOZ_OVERRIDE { return true; }
   virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
+  virtual bool RecvAdoptChild(const uint64_t& child) MOZ_OVERRIDE { return false; }
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const nsIntRect& aRect)
   { return true; }
   virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) { return true; }
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE { return true; }
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) MOZ_OVERRIDE  { return true; }
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -104,16 +104,17 @@ public:
                 mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
 
   virtual bool RecvRequestOverfill() MOZ_OVERRIDE;
   virtual bool RecvWillStop() MOZ_OVERRIDE;
   virtual bool RecvStop() MOZ_OVERRIDE;
   virtual bool RecvPause() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
   virtual bool RecvNotifyChildCreated(const uint64_t& child) MOZ_OVERRIDE;
+  virtual bool RecvAdoptChild(const uint64_t& child) MOZ_OVERRIDE;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const nsIntRect& aRect) MOZ_OVERRIDE;
   virtual bool RecvFlushRendering() MOZ_OVERRIDE;
 
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) MOZ_OVERRIDE;
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) MOZ_OVERRIDE;
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) MOZ_OVERRIDE;
 
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -68,16 +68,17 @@ parent:
   sync Stop();
 
   // Pause/resume the compositor. These are intended to be used on mobile, when
   // the compositor needs to pause/resume in lockstep with the application.
   sync Pause();
   sync Resume();
 
   async NotifyChildCreated(uint64_t id);
+  async AdoptChild(uint64_t id);
 
   // Make a snapshot of the content that would have been drawn to our
   // render target at the time this message is received.  If the size
   // or format of |inSnapshot| doesn't match our render target,
   // results are undefined.
   //
   // NB: this message will result in animations, transforms, effects,
   // and so forth being interpolated.  That's what we want to happen.
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -68,16 +68,19 @@ IsTempLayerManager(LayerManager* aManage
   return (mozilla::layers::LayersBackend::LAYERS_BASIC == aManager->GetBackendType() &&
           !static_cast<BasicLayerManager*>(aManager)->IsRetained());
 }
 
 already_AddRefed<LayerManager>
 GetFrom(nsFrameLoader* aFrameLoader)
 {
   nsIDocument* doc = aFrameLoader->GetOwnerDoc();
+  if (!doc) {
+    return nullptr;
+  }
   return nsContentUtils::LayerManagerForDocument(doc);
 }
 
 class RemoteContentController : public GeckoContentController {
 public:
   explicit RemoteContentController(RenderFrameParent* aRenderFrame)
     : mUILoop(MessageLoop::current())
     , mRenderFrame(aRenderFrame)
@@ -396,16 +399,24 @@ RenderFrameParent::BuildLayer(nsDisplayL
   return layer.forget();
 }
 
 void
 RenderFrameParent::OwnerContentChanged(nsIContent* aContent)
 {
   NS_ABORT_IF_FALSE(mFrameLoader->GetOwnerContent() == aContent,
                     "Don't build new map if owner is same!");
+
+  nsRefPtr<LayerManager> lm = GetFrom(mFrameLoader);
+  // Perhaps the document containing this frame currently has no presentation?
+  if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
+    ClientLayerManager *clientManager =
+      static_cast<ClientLayerManager*>(lm.get());
+    clientManager->GetRemoteRenderer()->SendAdoptChild(mLayersId);
+  }
 }
 
 nsEventStatus
 RenderFrameParent::NotifyInputEvent(WidgetInputEvent& aEvent,
                                     ScrollableLayerGuid* aOutTargetGuid)
 {
   if (GetApzcTreeManager()) {
     return GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutTargetGuid);
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -346,16 +346,17 @@
 #include ../services/manifests/HealthReportAndroidManifest_activities.xml.in
 #include ../services/manifests/SyncAndroidManifest_activities.xml.in
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 #include ../search/manifests/SearchAndroidManifest_activities.xml.in
 #endif
 
 #if MOZ_CRASHREPORTER
   <activity android:name="org.mozilla.gecko.CrashReporter"
+            android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
             android:label="@string/crash_reporter_title"
             android:icon="@drawable/crash_reporter"
             android:theme="@style/Gecko"
             android:exported="false"
             android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.reportCrash" />
           </intent-filter>
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -48,22 +48,24 @@ public class AppConstants {
         public static final boolean feature10Plus = MIN_SDK_VERSION >= 10 || (MAX_SDK_VERSION >= 10 && Build.VERSION.SDK_INT >= 10);
         public static final boolean feature11Plus = MIN_SDK_VERSION >= 11 || (MAX_SDK_VERSION >= 11 && Build.VERSION.SDK_INT >= 11);
         public static final boolean feature12Plus = MIN_SDK_VERSION >= 12 || (MAX_SDK_VERSION >= 12 && Build.VERSION.SDK_INT >= 12);
         public static final boolean feature14Plus = MIN_SDK_VERSION >= 14 || (MAX_SDK_VERSION >= 14 && Build.VERSION.SDK_INT >= 14);
         public static final boolean feature15Plus = MIN_SDK_VERSION >= 15 || (MAX_SDK_VERSION >= 15 && Build.VERSION.SDK_INT >= 15);
         public static final boolean feature16Plus = MIN_SDK_VERSION >= 16 || (MAX_SDK_VERSION >= 16 && Build.VERSION.SDK_INT >= 16);
         public static final boolean feature17Plus = MIN_SDK_VERSION >= 17 || (MAX_SDK_VERSION >= 17 && Build.VERSION.SDK_INT >= 17);
         public static final boolean feature19Plus = MIN_SDK_VERSION >= 19 || (MAX_SDK_VERSION >= 19 && Build.VERSION.SDK_INT >= 19);
+        public static final boolean feature21Plus = MIN_SDK_VERSION >= 21 || (MAX_SDK_VERSION >= 21 && Build.VERSION.SDK_INT >= 21);
 
         /*
          * If our MIN_SDK_VERSION is 14 or higher, we must be an ICS device.
          * If our MAX_SDK_VERSION is lower than ICS, we must not be an ICS device.
          * Otherwise, we need a range check.
          */
+        public static final boolean preLollipop = MAX_SDK_VERSION < 21 || (MIN_SDK_VERSION < 21 && Build.VERSION.SDK_INT < 21);
         public static final boolean preJBMR2 = MAX_SDK_VERSION < 18 || (MIN_SDK_VERSION < 18 && Build.VERSION.SDK_INT < 18);
         public static final boolean preJB = MAX_SDK_VERSION < 16 || (MIN_SDK_VERSION < 16 && Build.VERSION.SDK_INT < 16);
         public static final boolean preICS = MAX_SDK_VERSION < 14 || (MIN_SDK_VERSION < 14 && Build.VERSION.SDK_INT < 14);
         public static final boolean preHCMR2 = MAX_SDK_VERSION < 13 || (MIN_SDK_VERSION < 13 && Build.VERSION.SDK_INT < 13);
         public static final boolean preHCMR1 = MAX_SDK_VERSION < 12 || (MIN_SDK_VERSION < 12 && Build.VERSION.SDK_INT < 12);
         public static final boolean preHC = MAX_SDK_VERSION < 11 || (MIN_SDK_VERSION < 11 && Build.VERSION.SDK_INT < 11);
     }
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/CrashHandler.java
@@ -0,0 +1,434 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.UUID;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+
+class CrashHandler implements Thread.UncaughtExceptionHandler {
+
+    private static final String LOGTAG = "GeckoCrashHandler";
+    private static final Thread MAIN_THREAD = Thread.currentThread();
+    private static final String DEFAULT_SERVER_URL =
+        "https://crash-reports.mozilla.com/submit?id=%1$s&version=%2$s&buildid=%3$s";
+
+    // Context for getting device information
+    protected final Context appContext;
+    // Thread that this handler applies to, or null for a global handler
+    protected final Thread handlerThread;
+    protected final Thread.UncaughtExceptionHandler systemUncaughtHandler;
+
+    protected boolean crashing;
+    protected boolean unregistered;
+
+    /**
+     * Get the root exception from the 'cause' chain of an exception.
+     *
+     * @param exc An exception
+     * @return The root exception
+     */
+    public static Throwable getRootException(Throwable exc) {
+        for (Throwable cause = exc; cause != null; cause = cause.getCause()) {
+            exc = cause;
+        }
+        return exc;
+    }
+
+    /**
+     * Get the standard stack trace string of an exception.
+     *
+     * @param exc An exception
+     * @return The exception stack trace.
+     */
+    public static String getExceptionStackTrace(final Throwable exc) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        exc.printStackTrace(pw);
+        pw.flush();
+        return sw.toString();
+    }
+
+    /**
+     * Terminate the current process.
+     */
+    public static void terminateProcess() {
+        Process.killProcess(Process.myPid());
+    }
+
+    /**
+     * Create and register a CrashHandler for all threads and thread groups.
+     */
+    public CrashHandler() {
+        this((Context) null);
+    }
+
+    /**
+     * Create and register a CrashHandler for all threads and thread groups.
+     *
+     * @param appContext A Context for retrieving application information.
+     */
+    public CrashHandler(final Context appContext) {
+        this.appContext = appContext;
+        this.handlerThread = null;
+        this.systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * Create and register a CrashHandler for a particular thread.
+     *
+     * @param thread A thread to register the CrashHandler
+     */
+    public CrashHandler(final Thread thread) {
+        this(thread, null);
+    }
+
+    /**
+     * Create and register a CrashHandler for a particular thread.
+     *
+     * @param thread A thread to register the CrashHandler
+     * @param appContext A Context for retrieving application information.
+     */
+    public CrashHandler(final Thread thread, final Context appContext) {
+        this.appContext = appContext;
+        this.handlerThread = thread;
+        this.systemUncaughtHandler = thread.getUncaughtExceptionHandler();
+        thread.setUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * Unregister this CrashHandler for exception handling.
+     */
+    public void unregister() {
+        unregistered = true;
+
+        // Restore the previous handler if we are still the topmost handler.
+        // If not, we are part of a chain of handlers, and we cannot just restore the previous
+        // handler, because that would replace whatever handler that's above us in the chain.
+
+        if (handlerThread != null) {
+            if (handlerThread.getUncaughtExceptionHandler() == this) {
+                handlerThread.setUncaughtExceptionHandler(systemUncaughtHandler);
+            }
+        } else {
+            if (Thread.getDefaultUncaughtExceptionHandler() == this) {
+                Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
+            }
+        }
+    }
+
+    /**
+     * Record an exception stack in logs.
+     *
+     * @param thread The exception thread
+     * @param exc An exception
+     */
+    protected void logException(final Thread thread, final Throwable exc) {
+        try {
+            Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
+                          + thread.getId() + " (\"" + thread.getName() + "\")", exc);
+
+            if (MAIN_THREAD != thread) {
+                Log.e(LOGTAG, "Main thread (" + MAIN_THREAD.getId() + ") stack:");
+                for (StackTraceElement ste : MAIN_THREAD.getStackTrace()) {
+                    Log.e(LOGTAG, "    " + ste.toString());
+                }
+            }
+        } catch (final Throwable e) {
+            // If something throws here, we want to continue to report the exception,
+            // so we catch all exceptions and ignore them.
+        }
+    }
+
+    private static long getCrashTime() {
+        return System.currentTimeMillis() / 1000;
+    }
+
+    private static long getStartupTime() {
+        // Process start time is also the proc file modified time.
+        final long uptimeMins = (new File("/proc/self/cmdline")).lastModified();
+        if (uptimeMins == 0L) {
+            return getCrashTime();
+        }
+        return uptimeMins / 1000;
+    }
+
+    private static String getJavaPackageName() {
+        return CrashHandler.class.getPackage().getName();
+    }
+
+    protected String getAppPackageName() {
+        if (appContext != null) {
+            return appContext.getPackageName();
+        }
+
+        try {
+            // Package name is also the command line string in most cases.
+            final FileReader reader = new FileReader("/proc/self/cmdline");
+            final char[] buffer = new char[64];
+            try {
+                if (reader.read(buffer) > 0) {
+                    // cmdline is delimited by '\0', and we want the first token.
+                    final int nul = Arrays.asList(buffer).indexOf('\0');
+                    return (new String(buffer, 0, nul < 0 ? buffer.length : nul)).trim();
+                }
+            } finally {
+                reader.close();
+            }
+
+        } catch (final IOException e) {
+            Log.i(LOGTAG, "Error reading package name", e);
+        }
+
+        // Fallback to using CrashHandler's package name.
+        return getJavaPackageName();
+    }
+
+    /**
+     * Get the crash "extras" to be reported.
+     *
+     * @param thread The exception thread
+     * @param exc An exception
+     * @return "Extras" in the from of a Bundle
+     */
+    protected Bundle getCrashExtras(final Thread thread, final Throwable exc,
+                                    final Context appContext) {
+        final Bundle extras = new Bundle();
+        final String pkgName = getAppPackageName();
+
+        extras.putString("ProductName", pkgName);
+        extras.putLong("CrashTime", getCrashTime());
+        extras.putLong("StartupTime", getStartupTime());
+
+        if (appContext != null) {
+            final PackageManager pkgMgr = appContext.getPackageManager();
+            try {
+                final PackageInfo pkgInfo = pkgMgr.getPackageInfo(pkgName, 0);
+                extras.putString("Version", pkgInfo.versionName);
+                extras.putInt("BuildID", pkgInfo.versionCode);
+                extras.putLong("InstallTime", pkgInfo.firstInstallTime / 1000);
+            } catch (final PackageManager.NameNotFoundException e) {
+                Log.i(LOGTAG, "Error getting package info", e);
+            }
+        }
+
+        extras.putString("JavaStackTrace", getExceptionStackTrace(exc));
+        return extras;
+    }
+
+    /**
+     * Get the crash minidump content to be reported.
+     *
+     * @param thread The exception thread
+     * @param exc An exception
+     * @return Minidump content
+     */
+    protected byte[] getCrashDump(final Thread thread, final Throwable exc) {
+        return new byte[0]; // No minidump.
+    }
+
+    protected static String normalizeUrlString(final String str) {
+        if (str == null) {
+            return "";
+        }
+        return Uri.encode(str);
+    }
+
+    /**
+     * Get the server URL to send the crash report to.
+     *
+     * @param extras The crash extras Bundle
+     */
+    protected String getServerUrl(final Bundle extras) {
+        return String.format(DEFAULT_SERVER_URL,
+            normalizeUrlString(extras.getString("ProductID")),
+            normalizeUrlString(extras.getString("Version")),
+            normalizeUrlString(extras.getString("BuildID")));
+    }
+
+    /**
+     * Launch the crash reporter activity that sends the crash report to the server.
+     *
+     * @param dumpFile Path for the minidump file
+     * @param extraFile Path for the crash extra file
+     * @return Whether the crash reporter was successfully launched
+     */
+    protected boolean launchCrashReporter(final String dumpFile, final String extraFile) {
+        try {
+            final String javaPkg = getJavaPackageName();
+            final String pkg = getAppPackageName();
+            final String component = javaPkg + ".CrashReporter";
+            final String action = javaPkg + ".reportCrash";
+            final ProcessBuilder pb;
+
+            if (appContext != null) {
+                final Intent intent = new Intent(action);
+                intent.setComponent(new ComponentName(pkg, component));
+                intent.putExtra("minidumpPath", dumpFile);
+                appContext.startActivity(intent);
+                return true;
+            }
+
+            // Avoid AppConstants dependency for SDK version constants,
+            // because CrashHandler could be used outside of Fennec code.
+            if (Build.VERSION.SDK_INT < 17) {
+                pb = new ProcessBuilder(
+                    "/system/bin/am", "start",
+                    "-a", action,
+                    "-n", pkg + '/' + component,
+                    "--es", "minidumpPath", dumpFile);
+            } else {
+                pb = new ProcessBuilder(
+                    "/system/bin/am", "start",
+                    "--user", /* USER_CURRENT_OR_SELF */ "-3",
+                    "-a", action,
+                    "-n", pkg + '/' + component,
+                    "--es", "minidumpPath", dumpFile);
+            }
+
+            pb.start().waitFor();
+
+        } catch (final IOException e) {
+            Log.e(LOGTAG, "Error launching crash reporter", e);
+            return false;
+
+        } catch (final InterruptedException e) {
+            Log.i(LOGTAG, "Interrupted while waiting to launch crash reporter", e);
+            // Fall-through
+        }
+        return true;
+    }
+
+    /**
+     * Report an exception to Socorro.
+     *
+     * @param thread The exception thread
+     * @param exc An exception
+     * @return Whether the exception was successfully reported
+     */
+    protected boolean reportException(final Thread thread, final Throwable exc) {
+        final String id = UUID.randomUUID().toString();
+
+        // Use the cache directory under the app directory to store crash files.
+        final File dir;
+        if (appContext != null) {
+            dir = appContext.getCacheDir();
+        } else {
+            dir = new File("/data/data/" + getAppPackageName() + "/cache");
+        }
+
+        dir.mkdirs();
+        if (!dir.exists()) {
+            return false;
+        }
+
+        final File dmpFile = new File(dir, id + ".dmp");
+        final File extraFile = new File(dir, id + ".extra");
+
+        try {
+            // Write out minidump file as binary.
+
+            final byte[] minidump = getCrashDump(thread, exc);
+            final FileOutputStream dmpStream = new FileOutputStream(dmpFile);
+            try {
+                dmpStream.write(minidump);
+            } finally {
+                dmpStream.close();
+            }
+
+        } catch (final IOException e) {
+            Log.e(LOGTAG, "Error writing minidump file", e);
+            return false;
+        }
+
+        try {
+            // Write out crash extra file as text.
+
+            final Bundle extras = getCrashExtras(thread, exc, appContext);
+            final String url = getServerUrl(extras);
+            extras.putString("ServerURL", url);
+
+            final BufferedWriter extraWriter = new BufferedWriter(new FileWriter(extraFile));
+            try {
+                for (String key : extras.keySet()) {
+                    // Each extra line is in the format, key=value, with newlines escaped.
+                    extraWriter.write(key);
+                    extraWriter.write('=');
+                    extraWriter.write(String.valueOf(extras.get(key)).replace("\n", "\\n"));
+                    extraWriter.write('\n');
+                }
+            } finally {
+                extraWriter.close();
+            }
+
+        } catch (final IOException e) {
+            Log.e(LOGTAG, "Error writing extra file", e);
+            return false;
+        }
+
+        return launchCrashReporter(dmpFile.getAbsolutePath(), extraFile.getAbsolutePath());
+    }
+
+    /**
+     * Implements the default behavior for handling uncaught exceptions.
+     *
+     * @param thread The exception thread
+     * @param exc An uncaught exception
+     */
+    @Override
+    public void uncaughtException(Thread thread, Throwable exc) {
+        if (this.crashing) {
+            // Prevent possible infinite recusions.
+            return;
+        }
+
+        if (thread == null) {
+            // Gecko may pass in null for thread to denote the current thread.
+            thread = Thread.currentThread();
+        }
+
+        try {
+            if (!this.unregistered) {
+                // Only process crash ourselves if we have not been unregistered.
+
+                this.crashing = true;
+                exc = getRootException(exc);
+                logException(thread, exc);
+
+                if (reportException(thread, exc)) {
+                    // Reporting succeeded; we can terminate our process now.
+                    return;
+                }
+            }
+
+            if (systemUncaughtHandler != null) {
+                // Follow the chain of uncaught handlers.
+                systemUncaughtHandler.uncaughtException(thread, exc);
+            }
+        } finally {
+            terminateProcess();
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/DownloadsIntegration.java
@@ -0,0 +1,191 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.util.NativeEventListener;
+import org.mozilla.gecko.util.NativeJSObject;
+import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.MediaScannerConnection;
+import android.media.MediaScannerConnection.MediaScannerConnectionClient;
+import android.net.Uri;
+import android.text.TextUtils;
+
+public class DownloadsIntegration implements NativeEventListener
+{
+    private static final String LOGTAG = "GeckoDownloadsIntegration";
+
+    @SuppressWarnings("serial")
+    private static final List<String> UNKNOWN_MIME_TYPES = new ArrayList<String>(3) {{
+        add("unknown/unknown"); // This will be used as a default mime type for unknown files
+        add("application/unknown");
+        add("application/octet-stream"); // Github uses this for APK files
+    }};
+
+    private static final String DOWNLOAD_REMOVE = "Download:Remove";
+
+    private DownloadsIntegration() {
+        EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, DOWNLOAD_REMOVE);
+    }
+
+    private static DownloadsIntegration sInstance;
+
+    private static class Download {
+        final File file;
+        final long id;
+
+        final private static int UNKNOWN_ID = -1;
+
+        public Download(final String path) {
+            this(path, UNKNOWN_ID);
+        }
+
+        public Download(final String path, final long id) {
+            file = new File(path);
+            this.id = id;
+        }
+
+        public static Download fromJSON(final NativeJSObject obj) {
+            final String path = obj.getString("path");
+            return new Download(path);
+        }
+
+        public static Download fromCursor(final Cursor c) {
+            final String path = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
+            final long id = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID));
+            return new Download(path, id);
+        }
+
+        public boolean equals(final Download download) {
+            return file.equals(download.file);
+        }
+    }
+
+    public static void init() {
+        if (sInstance == null) {
+            sInstance = new DownloadsIntegration();
+        }
+    }
+
+    @Override
+    public void handleMessage(final String event, final NativeJSObject message,
+                              final EventCallback callback) {
+        if (DOWNLOAD_REMOVE.equals(event)) {
+            final Download d = Download.fromJSON(message);
+            removeDownload(d);
+        }
+    }
+
+    @WrapElementForJNI
+    public static void scanMedia(final String aFile, String aMimeType) {
+        String mimeType = aMimeType;
+        if (UNKNOWN_MIME_TYPES.contains(mimeType)) {
+            // If this is a generic undefined mimetype, erase it so that we can try to determine
+            // one from the file extension below.
+            mimeType = "";
+        }
+
+        // If the platform didn't give us a mimetype, try to guess one from the filename
+        if (TextUtils.isEmpty(mimeType)) {
+            final int extPosition = aFile.lastIndexOf(".");
+            if (extPosition > 0 && extPosition < aFile.length() - 1) {
+                mimeType = GeckoAppShell.getMimeTypeFromExtension(aFile.substring(extPosition+1));
+            }
+        }
+
+        // addCompletedDownload will throw if it received any null parameters. Use aMimeType or a default
+        // if we still don't have one.
+        if (TextUtils.isEmpty(mimeType)) {
+            if (TextUtils.isEmpty(aMimeType)) {
+                mimeType = UNKNOWN_MIME_TYPES.get(0);
+            } else {
+                mimeType = aMimeType;
+            }
+        }
+
+        if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
+            final File f = new File(aFile);
+            final DownloadManager dm = (DownloadManager) GeckoAppShell.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+            dm.addCompletedDownload(f.getName(),
+                    f.getName(),
+                    true, // Media scanner should scan this
+                    mimeType,
+                    f.getAbsolutePath(),
+                    Math.max(1, f.length()), // Some versions of Android require downloads to be at least length 1
+                    false); // Don't show a notification.
+        } else {
+            final Context context = GeckoAppShell.getContext();
+            final GeckoMediaScannerClient client = new GeckoMediaScannerClient(context, aFile, mimeType);
+            client.connect();
+        }
+    }
+
+    public static void removeDownload(final Download download) {
+        if (!AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
+            return;
+        }
+
+        final DownloadManager dm = (DownloadManager) GeckoAppShell.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+
+        Cursor c = null;
+        try {
+            c = dm.query((new DownloadManager.Query()).setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL));
+            if (c == null || !c.moveToFirst()) {
+                return;
+            }
+
+            do {
+                final Download d = Download.fromCursor(c);
+                // Try hard as we can to verify this download is the one we think it is
+                if (download.equals(d)) {
+                    dm.remove(d.id);
+                }
+            } while(c.moveToNext());
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private static final class GeckoMediaScannerClient implements MediaScannerConnectionClient {
+        private final String mFile;
+        private final String mMimeType;
+        private MediaScannerConnection mScanner;
+
+        public GeckoMediaScannerClient(Context context, String file, String mimeType) {
+            mFile = file;
+            mMimeType = mimeType;
+            mScanner = new MediaScannerConnection(context, this);
+        }
+
+        public void connect() {
+            mScanner.connect();
+        }
+
+        @Override
+        public void onMediaScannerConnected() {
+            mScanner.scanFile(mFile, mMimeType);
+        }
+
+        @Override
+        public void onScanCompleted(String path, Uri uri) {
+            if(path.equals(mFile)) {
+                mScanner.disconnect();
+                mScanner = null;
+            }
+        }
+    }
+}
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1120,18 +1120,16 @@ public abstract class GeckoApp
      * Called when the activity is first created.
      *
      * Here we initialize all of our profile settings, Firefox Health Report,
      * and other one-shot constructions.
      **/
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
-        GeckoAppShell.registerGlobalExceptionHandler();
-
         // Enable Android Strict Mode for developers' local builds (the "default" channel).
         if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) {
             enableStrictMode();
         }
 
         // The clock starts...now. Better hurry!
         mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
         mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -9,18 +9,16 @@ import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.net.Proxy;
 import java.net.URL;
 import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -55,17 +53,16 @@ import org.mozilla.gecko.util.NativeEven
 import org.mozilla.gecko.util.NativeJSContainer;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ProxySelector;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.DownloadManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -88,18 +85,16 @@ import android.graphics.drawable.BitmapD
 import android.graphics.drawable.Drawable;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
-import android.media.MediaScannerConnection;
-import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -127,30 +122,82 @@ import android.widget.Toast;
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
     private static final boolean LOGGING = false;
 
     // We have static members only.
     private GeckoAppShell() { }
 
-    private static Thread.UncaughtExceptionHandler systemUncaughtHandler;
     private static boolean restartScheduled;
     private static GeckoEditableListener editableListener;
 
+    private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
+        @Override
+        protected String getAppPackageName() {
+            return AppConstants.ANDROID_PACKAGE_NAME;
+        }
+
+        @Override
+        protected Bundle getCrashExtras(final Thread thread, final Throwable exc,
+                                        final Context appContext) {
+            final Bundle extras = super.getCrashExtras(
+                thread, exc, sContextGetter != null ? getContext() : null);
+
+            extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
+            extras.putString("ProductID", AppConstants.MOZ_APP_ID);
+            extras.putString("Version", AppConstants.MOZ_APP_VERSION);
+            extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
+            extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
+            extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
+            return extras;
+        }
+
+        @Override
+        public void uncaughtException(final Thread thread, final Throwable exc) {
+            if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoExited)) {
+                // We've called System.exit. All exceptions after this point are Android
+                // berating us for being nasty to it.
+                return;
+            }
+
+            super.uncaughtException(thread, exc);
+        }
+
+        @Override
+        public boolean reportException(final Thread thread, final Throwable exc) {
+            try {
+                if (exc instanceof OutOfMemoryError) {
+                    SharedPreferences prefs = getSharedPreferences();
+                    SharedPreferences.Editor editor = prefs.edit();
+                    editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true);
+
+                    // Synchronously write to disk so we know it's done before we
+                    // shutdown
+                    editor.commit();
+                }
+
+                reportJavaCrash(getExceptionStackTrace(exc));
+
+            } catch (final Throwable e) {
+            }
+
+            // reportJavaCrash should have caused us to hard crash. If we're still here,
+            // it probably means Gecko is not loaded, and we should do something else.
+            if (AppConstants.MOZ_CRASHREPORTER && AppConstants.MOZILLA_OFFICIAL) {
+                // Only use Java crash reporter if enabled on official build.
+                return super.reportException(thread, exc);
+            }
+            return false;
+        }
+    };
+
     private static final Queue<GeckoEvent> PENDING_EVENTS = new ConcurrentLinkedQueue<GeckoEvent>();
     private static final Map<String, String> ALERT_COOKIES = new ConcurrentHashMap<String, String>();
 
-    @SuppressWarnings("serial")
-    private static final List<String> UNKNOWN_MIME_TYPES = new ArrayList<String>(3) {{
-        add("unknown/unknown"); // This will be used as a default mime type for unknown files
-        add("application/unknown");
-        add("application/octet-stream"); // Github uses this for APK files
-    }};
-
     private static volatile boolean locationHighAccuracyEnabled;
 
     // Accessed by NotificationHelper. This should be encapsulated.
     /* package */ static NotificationClient notificationClient;
 
     // See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
     private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
 
@@ -211,37 +258,16 @@ public class GeckoAppShell
     }
     public static void removeObserver(String observerKey) {
         sendEventToGecko(GeckoEvent.createRemoveObserverEvent(observerKey));
     }
     public static native Message getNextMessageFromQueue(MessageQueue queue);
     public static native void onSurfaceTextureFrameAvailable(Object surfaceTexture, int id);
     public static native void dispatchMemoryPressure();
 
-    public static void registerGlobalExceptionHandler() {
-        if (systemUncaughtHandler == null) {
-            systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
-        }
-
-        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-            @Override
-            public void uncaughtException(Thread thread, Throwable e) {
-                handleUncaughtException(thread, e);
-            }
-        });
-    }
-
-    private static String getStackTraceString(Throwable e) {
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        e.printStackTrace(pw);
-        pw.flush();
-        return sw.toString();
-    }
-
     private static native void reportJavaCrash(String stackTrace);
 
     public static void notifyUriVisited(String uri) {
         sendEventToGecko(GeckoEvent.createVisitedEvent(uri));
     }
 
     public static native void processNextNativeEvent(boolean mayWait);
 
@@ -271,46 +297,16 @@ public class GeckoAppShell
         }
 
         @Override
         public void onFaviconLoaded(String pageUrl, String faviconURL, Bitmap favicon) {
             GeckoAppShell.createShortcut(title, url, favicon);
         }
     }
 
-    private static final class GeckoMediaScannerClient implements MediaScannerConnectionClient {
-        private final String mFile;
-        private final String mMimeType;
-        private MediaScannerConnection mScanner;
-
-        public static void startScan(Context context, String file, String mimeType) {
-            new GeckoMediaScannerClient(context, file, mimeType);
-        }
-
-        private GeckoMediaScannerClient(Context context, String file, String mimeType) {
-            mFile = file;
-            mMimeType = mimeType;
-            mScanner = new MediaScannerConnection(context, this);
-            mScanner.connect();
-        }
-
-        @Override
-        public void onMediaScannerConnected() {
-            mScanner.scanFile(mFile, mMimeType);
-        }
-
-        @Override
-        public void onScanCompleted(String path, Uri uri) {
-            if(path.equals(mFile)) {
-                mScanner.disconnect();
-                mScanner = null;
-            }
-        }
-    }
-
     private static LayerView sLayerView;
 
     public static void setLayerView(LayerView lv) {
         if (sLayerView == lv) {
             return;
         }
         sLayerView = lv;
         // Install new Gecko-to-Java editable listener.
@@ -450,67 +446,17 @@ public class GeckoAppShell
     public static native void notifyGeckoObservers(String subject, String data);
 
     /*
      *  The Gecko-side API: API methods that Gecko calls
      */
 
     @WrapElementForJNI(allowMultithread = true, noThrow = true)
     public static void handleUncaughtException(Thread thread, Throwable e) {
-        if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoExited)) {
-            // We've called System.exit. All exceptions after this point are Android
-            // berating us for being nasty to it.
-            return;
-        }
-
-        if (thread == null) {
-            thread = Thread.currentThread();
-        }
-        // If the uncaught exception was rethrown, walk the exception `cause` chain to find
-        // the original exception so Socorro can correctly collate related crash reports.
-        Throwable cause;
-        while ((cause = e.getCause()) != null) {
-            e = cause;
-        }
-
-        try {
-            Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
-                          + thread.getId() + " (\"" + thread.getName() + "\")", e);
-
-            Thread mainThread = ThreadUtils.getUiThread();
-            if (mainThread != null && thread != mainThread) {
-                Log.e(LOGTAG, "Main thread stack:");
-                for (StackTraceElement ste : mainThread.getStackTrace()) {
-                    Log.e(LOGTAG, ste.toString());
-                }
-            }
-
-            if (e instanceof OutOfMemoryError) {
-                SharedPreferences prefs = getSharedPreferences();
-                SharedPreferences.Editor editor = prefs.edit();
-                editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true);
-
-                // Synchronously write to disk so we know it's done before we
-                // shutdown
-                editor.commit();
-            }
-        } catch (final Throwable exc) {
-            // Report the Java crash below, even if we encounter an exception here.
-        }
-
-        try {
-            reportJavaCrash(getStackTraceString(e));
-        } finally {
-            // reportJavaCrash should have caused us to hard crash. If we're still here,
-            // it probably means Gecko is not loaded, and we should do something else.
-            // Bring up the app crashed dialog so we don't crash silently.
-            if (systemUncaughtHandler != null) {
-                systemUncaughtHandler.uncaughtException(thread, e);
-            }
-        }
+        CRASH_HANDLER.uncaughtException(thread, e);
     }
 
     @WrapElementForJNI
     public static void notifyIME(int type) {
         if (editableListener != null) {
             editableListener.notifyIME(type);
         }
     }
@@ -1795,59 +1741,16 @@ public class GeckoAppShell
                 String file = split[nameColumn];
                 if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(file) && file.startsWith(filter))
                     Log.d(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file);
             }
             in.close();
         } catch (Exception e) { }
     }
 
-    @WrapElementForJNI
-    public static void scanMedia(final String aFile, String aMimeType) {
-        String mimeType = aMimeType;
-        if (UNKNOWN_MIME_TYPES.contains(mimeType)) {
-            // If this is a generic undefined mimetype, erase it so that we can try to determine
-            // one from the file extension below.
-            mimeType = "";
-        }
-
-        // If the platform didn't give us a mimetype, try to guess one from the filename
-        if (TextUtils.isEmpty(mimeType)) {
-            int extPosition = aFile.lastIndexOf(".");
-            if (extPosition > 0 && extPosition < aFile.length() - 1) {
-                mimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
-            }
-        }
-
-        // addCompletedDownload will throw if it received any null parameters. Use aMimeType or a default
-        // if we still don't have one.
-        if (TextUtils.isEmpty(mimeType)) {
-            if (TextUtils.isEmpty(aMimeType)) {
-                mimeType = UNKNOWN_MIME_TYPES.get(0);
-            } else {
-                mimeType = aMimeType;
-            }
-        }
-
-        if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
-            final File f = new File(aFile);
-            final DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
-            dm.addCompletedDownload(f.getName(),
-                                    f.getName(),
-                                    true, // Media scanner should scan this
-                                    mimeType,
-                                    f.getAbsolutePath(),
-                                    Math.max(1, f.length()), // Some versions of Android require downloads to be at least length 1
-                                    false); // Don't show a notification.
-        } else {
-            Context context = getContext();
-            GeckoMediaScannerClient.startScan(context, aFile, mimeType);
-        }
-    }
-
     @WrapElementForJNI(stubName = "GetIconForExtensionWrapper")
     public static byte[] getIconForExtension(String aExt, int iconSize) {
         try {
             if (iconSize <= 0)
                 iconSize = 16;
 
             if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.')
                 aExt = aExt.substring(1);
@@ -1869,17 +1772,17 @@ public class GeckoAppShell
             return buf.array();
         }
         catch (Exception e) {
             Log.w(LOGTAG, "getIconForExtension failed.",  e);
             return null;
         }
     }
 
-    private static String getMimeTypeFromExtension(String ext) {
+    public static String getMimeTypeFromExtension(String ext) {
         final MimeTypeMap mtm = MimeTypeMap.getSingleton();
         return mtm.getMimeTypeFromExtension(ext);
     }
     
     private static Drawable getDrawableForExtension(PackageManager pm, String aExt) {
         Intent intent = new Intent(Intent.ACTION_VIEW);
         final String mimeType = getMimeTypeFromExtension(aExt);
         if (mimeType != null && mimeType.length() > 0)
--- a/mobile/android/base/GeckoApplication.java
+++ b/mobile/android/base/GeckoApplication.java
@@ -117,16 +117,17 @@ public class GeckoApplication extends Ap
 
     @Override
     public void onCreate() {
         final Context context = getApplicationContext();
         HardwareUtils.init(context);
         Clipboard.init(context);
         FilePicker.init(context);
         GeckoLoader.loadMozGlue(context);
+        DownloadsIntegration.init();
         HomePanelsManager.getInstance().init(context);
 
         // This getInstance call will force initialization of the NotificationHelper, but does nothing with the result
         NotificationHelper.getInstance(context).init();
         super.onCreate();
     }
 
     public boolean isApplicationInBackground() {
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -473,16 +473,17 @@ public class Tab {
 
     public void addToReadingList() {
         if (!mReaderEnabled)
             return;
 
         JSONObject json = new JSONObject();
         try {
             json.put("tabID", String.valueOf(getId()));
+            json.put("url", getURL());
         } catch (JSONException e) {
             Log.e(LOGTAG, "JSON error - failing to add to reading list", e);
             return;
         }
 
         GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Add", json.toString());
         GeckoAppShell.sendEventToGecko(e);
     }
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -105,17 +105,19 @@ public class Tabs implements GeckoEventL
             "DOMContentLoaded",
             "DOMTitleChanged",
             "Link:Favicon",
             "Link:Feed",
             "Link:OpenSearch",
             "DesktopMode:Changed",
             "Tab:ViewportMetadata",
             "Tab:StreamStart",
-            "Tab:StreamStop");
+            "Tab:StreamStop",
+            "Reader:Click",
+            "Reader:LongClick");
 
     }
 
     public synchronized void attachToContext(Context context) {
         final Context appContext = context.getApplicationContext();
         if (mAppContext == appContext) {
             return;
         }
@@ -520,16 +522,20 @@ public class Tabs implements GeckoEventL
                 tab.setIsRTL(message.getBoolean("isRTL"));
                 notifyListeners(tab, TabEvents.VIEWPORT_CHANGE);
             } else if (event.equals("Tab:StreamStart")) {
                 tab.setRecording(true);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
             } else if (event.equals("Tab:StreamStop")) {
                 tab.setRecording(false);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
+            } else if (event.equals("Reader:Click")) {
+                tab.toggleReaderMode();
+            } else if (event.equals("Reader:LongClick")) {
+                tab.addToReadingList();
             }
 
         } catch (Exception e) {
             Log.w(LOGTAG, "handleMessage threw for " + event, e);
         }
     }
 
     /**
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -127,16 +127,17 @@ gbjar.sources += [
     'ANRReporter.java',
     'AppNotificationClient.java',
     'Assert.java',
     'BaseGeckoInterface.java',
     'BrowserApp.java',
     'BrowserLocaleManager.java',
     'ContactService.java',
     'ContextGetter.java',
+    'CrashHandler.java',
     'CustomEditText.java',
     'DataReportingNotification.java',
     'db/AbstractPerProfileDatabaseProvider.java',
     'db/AbstractTransactionalProvider.java',
     'db/BaseTable.java',
     'db/BrowserContract.java',
     'db/BrowserDatabaseHelper.java',
     'db/BrowserDB.java',
@@ -157,16 +158,17 @@ gbjar.sources += [
     'db/TabsProvider.java',
     'db/TopSitesCursorWrapper.java',
     'db/URLMetadata.java',
     'db/URLMetadataTable.java',
     'distribution/Distribution.java',
     'distribution/ReferrerDescriptor.java',
     'distribution/ReferrerReceiver.java',
     'DoorHangerPopup.java',
+    'DownloadsIntegration.java',
     'DynamicToolbar.java',
     'EditBookmarkDialog.java',
     'EventDispatcher.java',
     'favicons/cache/FaviconCache.java',
     'favicons/cache/FaviconCacheElement.java',
     'favicons/cache/FaviconsForURL.java',
     'favicons/decoders/FaviconDecoder.java',
     'favicons/decoders/ICODecoder.java',
--- a/mobile/android/base/mozglue/GeckoLoader.java.in
+++ b/mobile/android/base/mozglue/GeckoLoader.java.in
@@ -260,16 +260,21 @@ public final class GeckoLoader {
             sNSSLibsLoaded = true;
         }
 
         loadMozGlue(context);
         loadLibsSetup(context);
         loadNSSLibsNative(apkName);
     }
 
+    @SuppressWarnings("deprecation")
+    private static final String getCPUABI() {
+        return android.os.Build.CPU_ABI;
+    }
+
     /**
      * Copy a library out of our APK.
      *
      * @param context a Context.
      * @param lib the name of the library; e.g., "mozglue".
      * @param outDir the output directory for the .so. No trailing slash.
      * @return true on success, false on failure.
      */
@@ -286,17 +291,18 @@ public final class GeckoLoader {
         File outDirFile = new File(outDir);
         if (!outDirFile.isDirectory()) {
             if (!outDirFile.mkdirs()) {
                 Log.e(LOGTAG, "Couldn't create " + outDir);
                 return false;
             }
         }
 
-        final String abi = android.os.Build.CPU_ABI;
+        final String abi = getCPUABI();
+
         try {
             final ZipFile zipFile = new ZipFile(new File(apkPath));
             try {
                 final String libPath = "lib/" + abi + "/lib" + lib + ".so";
                 final ZipEntry entry = zipFile.getEntry(libPath);
                 if (entry == null) {
                     Log.w(LOGTAG, libPath + " not found in APK " + apkPath);
                     return false;
@@ -348,17 +354,17 @@ public final class GeckoLoader {
         }
     }
 
     private static String getLoadDiagnostics(final Context context, final String lib) {
         final StringBuilder message = new StringBuilder("LOAD ");
         message.append(lib);
 
         // These might differ. If so, we know why the library won't load!
-        message.append(": ABI: " + MOZ_APP_ABI + ", " + android.os.Build.CPU_ABI);
+        message.append(": ABI: " + MOZ_APP_ABI + ", " + getCPUABI());
         message.append(": Data: " + context.getApplicationInfo().dataDir);
         try {
             final boolean appLibExists = new File("/data/app-lib/" + ANDROID_PACKAGE_NAME + "/lib" + lib + ".so").exists();
             final boolean dataDataExists = new File("/data/data/" + ANDROID_PACKAGE_NAME + "/lib/lib" + lib + ".so").exists();
             message.append(", ax=" + appLibExists);
             message.append(", ddx=" + dataDataExists);
         } catch (Throwable e) {
             message.append(": ax/ddx fail, ");
--- a/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml
+++ b/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml
@@ -70,17 +70,17 @@
                         android:layout_alignLeft="@id/tabs"
                         android:layout_alignRight="@id/tabs"
                         android:layout_alignTop="@id/tabs"
                         android:layout_alignBottom="@id/tabs"
                         android:layout_marginTop="18dp"
                         android:layout_marginBottom="18dp"
                         android:layout_marginLeft="16dp"
                         android:layout_marginRight="16dp"
-                        gecko:layout="@layout/new_tablet_tabs_counter"/>
+                        android:layout="@layout/new_tablet_tabs_counter"/>
 
     <org.mozilla.gecko.widget.ThemedImageButton
             android:id="@+id/menu"
             style="@style/UrlBar.ImageButton.NewTablet"
             android:layout_alignParentRight="true"
             android:layout_marginRight="6dp"
             android:contentDescription="@string/menu"
             android:background="@drawable/new_tablet_action_bar_button"
--- a/mobile/android/base/resources/layout-large-land-v11/tabs_panel_sidebar.xml
+++ b/mobile/android/base/resources/layout-large-land-v11/tabs_panel_sidebar.xml
@@ -10,17 +10,16 @@
                     android:layout_width="match_parent"
                     android:layout_height="@dimen/browser_toolbar_height">
 
         <view class="org.mozilla.gecko.tabs.TabsPanel$TabsPanelToolbar"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="@color/background_tabs">
 
-
             <org.mozilla.gecko.widget.IconTabWidget android:id="@+id/tab_widget"
                                                     android:layout_width="wrap_content"
                                                     android:layout_height="match_parent"
                                                     android:tabStripEnabled="false"
                                                     android:divider="@drawable/tab_indicator_divider"
                                                     android:layout="@layout/tabs_panel_indicator"/>
 
 
--- a/mobile/android/base/resources/values/attrs.xml
+++ b/mobile/android/base/resources/values/attrs.xml
@@ -1,15 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
-<resources>
-
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- Theme level attributes -->
     <declare-styleable name="GeckoTheme">
 
         <!-- Style for GeckoMenu ListView -->
         <attr name="geckoMenuListViewStyle" format="reference"/>
 
         <!-- Style for MenuItemActionBar -->
         <attr name="menuItemActionBarStyle" format="reference"/>
@@ -101,17 +100,17 @@
     <declare-styleable name="TabsLayout">
         <attr name="tabs">
             <flag name="tabs_normal" value="0x00" />
             <flag name="tabs_private" value ="0x01" />
         </attr>
     </declare-styleable>
 
     <declare-styleable name="TabCounter">
-        <attr name="layout" format="reference"/>
+        <attr name="android:layout"/>
     </declare-styleable>
 
     <declare-styleable name="TextSelectionHandle">
         <attr name="handleType">
             <flag name="start" value="0x01" />
             <flag name="middle" value="0x02" />
             <flag name="end" value="0x03" />
         </attr>
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -7,17 +7,16 @@ package org.mozilla.gecko.toolbar;
 
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.BrowserApp;
-import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.LightweightTheme;
 import org.mozilla.gecko.NewTabletUI;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
@@ -26,17 +25,16 @@ import org.mozilla.gecko.animation.Prope
 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.MenuPopup;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
 import org.mozilla.gecko.util.Clipboard;
-import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.widget.ThemedImageButton;
 import org.mozilla.gecko.widget.ThemedImageView;
 import org.mozilla.gecko.widget.ThemedRelativeLayout;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -71,18 +69,17 @@ import android.widget.PopupWindow;
 * displays the current state for the selected tab. In edit state, it shows
 * a text entry for searching bookmarks/history. {@code BrowserToolbar}
 * provides public API to enter, cancel, and commit the edit state as well
 * as a set of listeners to allow {@code BrowserToolbar} users to react
 * to state changes accordingly.
 */
 public abstract class BrowserToolbar extends ThemedRelativeLayout
                                      implements Tabs.OnTabsChangedListener,
-                                                GeckoMenu.ActionItemBarPresenter,
-                                                GeckoEventListener {
+                                                GeckoMenu.ActionItemBarPresenter {
     private static final String LOGTAG = "GeckoToolbar";
 
     public interface OnActivateListener {
         public void onActivate();
     }
 
     public interface OnCommitListener {
         public void onCommit();
@@ -182,20 +179,16 @@ public abstract class BrowserToolbar ext
             LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this);
         } else {
             LayoutInflater.from(context).inflate(R.layout.new_tablet_browser_toolbar, this);
         }
 
         Tabs.registerOnTabsChangedListener(this);
         isSwitchingTabs = true;
 
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
-            "Reader:Click",
-            "Reader:LongClick");
-
         urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout);
         urlBarEntry = findViewById(R.id.url_bar_entry);
         urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
 
         tabsButton = (ThemedImageButton) findViewById(R.id.tabs);
         tabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
         if (Versions.feature11Plus) {
             tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
@@ -845,20 +838,16 @@ public abstract class BrowserToolbar ext
     }
 
     public View getDoorHangerAnchor() {
         return urlDisplayLayout.getDoorHangerAnchor();
     }
 
     public void onDestroy() {
         Tabs.unregisterOnTabsChangedListener(this);
-
-        EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
-            "Reader:Click",
-            "Reader:LongClick");
     }
 
     public boolean openOptionsMenu() {
         if (!hasSoftMenuButton) {
             return false;
         }
 
         // Initialize the popup.
@@ -891,32 +880,16 @@ public abstract class BrowserToolbar ext
         if (menuPopup != null && menuPopup.isShowing()) {
             menuPopup.dismiss();
         }
 
         return true;
     }
 
     @Override
-    public void handleMessage(String event, JSONObject message) {
-        Log.d(LOGTAG, "handleMessage: " + event);
-        if (event.equals("Reader:Click")) {
-            Tab tab = Tabs.getInstance().getSelectedTab();
-            if (tab != null) {
-                tab.toggleReaderMode();
-            }
-        } else if (event.equals("Reader:LongClick")) {
-            Tab tab = Tabs.getInstance().getSelectedTab();
-            if (tab != null) {
-                tab.addToReadingList();
-            }
-        }
-    }
-
-    @Override
     public void onLightweightThemeChanged() {
         Drawable drawable = theme.getDrawable(this);
         if (drawable == null)
             return;
 
         StateListDrawable stateList = new StateListDrawable();
         stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.background_private));
         stateList.addState(EMPTY_STATE_SET, drawable);
--- a/mobile/android/base/toolbar/TabCounter.java
+++ b/mobile/android/base/toolbar/TabCounter.java
@@ -43,17 +43,17 @@ public class TabCounter extends ThemedTe
         FADE_IN,
         FADE_OUT
     }
 
     public TabCounter(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabCounter);
-        mLayoutId = a.getResourceId(R.styleable.TabCounter_layout, R.layout.tabs_counter);
+        mLayoutId = a.getResourceId(R.styleable.TabCounter_android_layout, R.layout.tabs_counter);
         a.recycle();
 
         mInflater = LayoutInflater.from(context);
 
         mFlipInForward = createAnimation(-90, 0, FadeMode.FADE_IN, -1 * Z_DISTANCE, false);
         mFlipInBackward = createAnimation(90, 0, FadeMode.FADE_IN, Z_DISTANCE, false);
         mFlipOutForward = createAnimation(0, -90, FadeMode.FADE_OUT, -1 * Z_DISTANCE, true);
         mFlipOutBackward = createAnimation(0, 90, FadeMode.FADE_OUT, Z_DISTANCE, true);
new file mode 100644
--- /dev/null
+++ b/mobile/android/chrome/content/Reader.js
@@ -0,0 +1,560 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+let Reader = {
+  // Version of the cache database schema
+  DB_VERSION: 1,
+
+  DEBUG: 0,
+
+  READER_ADD_SUCCESS: 0,
+  READER_ADD_FAILED: 1,
+  READER_ADD_DUPLICATE: 2,
+
+  // Don't try to parse the page if it has too many elements (for memory and
+  // performance reasons)
+  MAX_ELEMS_TO_PARSE: 3000,
+
+  _requests: {},
+
+  get isEnabledForParseOnLoad() {
+    delete this.isEnabledForParseOnLoad;
+
+    // Listen for future pref changes.
+    Services.prefs.addObserver("reader.parse-on-load.", this, false);
+
+    return this.isEnabledForParseOnLoad = this.getStateForParseOnLoad();
+  },
+
+  pageAction: {
+    readerModeCallback: function(tabID) {
+      Messaging.sendRequest({
+        type: "Reader:Click",
+        tabID: tabID
+      });
+    },
+
+    readerModeActiveCallback: function(tabID) {
+      Messaging.sendRequest({
+        type: "Reader:LongClick",
+        tabID: tabID
+      });
+
+      UITelemetry.addEvent("save.1", "pageaction", null, "reader");
+    },
+  },
+
+  updatePageAction: function(tab) {
+    if (this.pageAction.id) {
+      PageActions.remove(this.pageAction.id);
+      delete this.pageAction.id;
+    }
+
+    if (tab.readerActive) {
+      this.pageAction.id = PageActions.add({
+        title: Strings.browser.GetStringFromName("readerMode.exit"),
+        icon: "drawable://reader_active",
+        clickCallback: () => this.pageAction.readerModeCallback(tab.id),
+        important: true
+      });
+
+      // Only start a reader session if the viewer is in the foreground. We do
+      // not track background reader viewers.
+      UITelemetry.startSession("reader.1", null);
+      return;
+    }
+
+    // Only stop a reader session if the foreground viewer is not visible.
+    UITelemetry.stopSession("reader.1", "", null);
+
+    if (tab.readerEnabled) {
+      this.pageAction.id = PageActions.add({
+        title: Strings.browser.GetStringFromName("readerMode.enter"),
+        icon: "drawable://reader",
+        clickCallback: () => this.pageAction.readerModeCallback(tab.id),
+        longClickCallback: () => this.pageAction.readerModeActiveCallback(tab.id),
+        important: true
+      });
+    }
+  },
+
+  observe: function(aMessage, aTopic, aData) {
+    switch(aTopic) {
+      case "Reader:Add": {
+        let args = JSON.parse(aData);
+        if ('fromAboutReader' in args) {
+          // Ignore adds initiated from aboutReader menu banner
+          break;
+        }
+
+        let tabID = null;
+        let url, urlWithoutRef;
+
+        if ('tabID' in args) {
+          tabID = args.tabID;
+
+          let tab = BrowserApp.getTabForId(tabID);
+          let currentURI = tab.browser.currentURI;
+
+          url = currentURI.spec;
+          urlWithoutRef = currentURI.specIgnoringRef;
+        } else if ('url' in args) {
+          let uri = Services.io.newURI(args.url, null, null);
+          url = uri.spec;
+          urlWithoutRef = uri.specIgnoringRef;
+        } else {
+          throw new Error("Reader:Add requires a tabID or an URL as argument");
+        }
+
+        let sendResult = function(result, article) {
+          article = article || {};
+          this.log("Reader:Add success=" + result + ", url=" + url + ", title=" + article.title + ", excerpt=" + article.excerpt);
+
+          Messaging.sendRequest({
+            type: "Reader:Added",
+            result: result,
+            title: truncate(article.title, MAX_TITLE_LENGTH),
+            url: truncate(url, MAX_URI_LENGTH),
+            length: article.length,
+            excerpt: article.excerpt
+          });
+        }.bind(this);
+
+        let handleArticle = function(article) {
+          if (!article) {
+            sendResult(this.READER_ADD_FAILED, null);
+            return;
+          }
+
+          this.storeArticleInCache(article, function(success) {
+            let result = (success ? this.READER_ADD_SUCCESS : this.READER_ADD_FAILED);
+            sendResult(result, article);
+          }.bind(this));
+        }.bind(this);
+
+        this.getArticleFromCache(urlWithoutRef, function (article) {
+          // If the article is already in reading list, bail
+          if (article) {
+            sendResult(this.READER_ADD_DUPLICATE, null);
+            return;
+          }
+
+          if (tabID != null) {
+            this.getArticleForTab(tabID, urlWithoutRef, handleArticle);
+          } else {
+            this.parseDocumentFromURL(urlWithoutRef, handleArticle);
+          }
+        }.bind(this));
+        break;
+      }
+
+      case "Reader:Remove": {
+        let args = JSON.parse(aData);
+
+        if (!("url" in args)) {
+          throw new Error("Reader:Remove requires URL as an argument");
+        }
+
+        this.removeArticleFromCache(args.url, function(success) {
+          this.log("Reader:Remove success=" + success + ", url=" + args.url);
+          if (success && args.notify) {
+            Messaging.sendRequest({
+              type: "Reader:Removed",
+              url: args.url
+            });
+          }
+        }.bind(this));
+        break;
+      }
+
+      case "nsPref:changed": {
+        if (aData.startsWith("reader.parse-on-load.")) {
+          this.isEnabledForParseOnLoad = this.getStateForParseOnLoad();
+        }
+        break;
+      }
+    }
+  },
+
+  getStateForParseOnLoad: function Reader_getStateForParseOnLoad() {
+    let isEnabled = Services.prefs.getBoolPref("reader.parse-on-load.enabled");
+    let isForceEnabled = Services.prefs.getBoolPref("reader.parse-on-load.force-enabled");
+    // For low-memory devices, don't allow reader mode since it takes up a lot of memory.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=792603 for details.
+    return isForceEnabled || (isEnabled && !BrowserApp.isOnLowMemoryPlatform);
+  },
+
+  parseDocumentFromURL: function Reader_parseDocumentFromURL(url, callback) {
+    // If there's an on-going request for the same URL, simply append one
+    // more callback to it to be called when the request is done.
+    if (url in this._requests) {
+      let request = this._requests[url];
+      request.callbacks.push(callback);
+      return;
+    }
+
+    let request = { url: url, callbacks: [callback] };
+    this._requests[url] = request;
+
+    try {
+      this.log("parseDocumentFromURL: " + url);
+
+      // First, try to find a cached parsed article in the DB
+      this.getArticleFromCache(url, function(article) {
+        if (article) {
+          this.log("Page found in cache, return article immediately");
+          this._runCallbacksAndFinish(request, article);
+          return;
+        }
+
+        if (!this._requests) {
+          this.log("Reader has been destroyed, abort");
+          return;
+        }
+
+        // Article hasn't been found in the cache DB, we need to
+        // download the page and parse the article out of it.
+        this._downloadAndParseDocument(url, request);
+      }.bind(this));
+    } catch (e) {
+      this.log("Error parsing document from URL: " + e);
+      this._runCallbacksAndFinish(request, null);
+    }
+  },
+
+  getArticleForTab: function Reader_getArticleForTab(tabId, url, callback) {
+    let tab = BrowserApp.getTabForId(tabId);
+    if (tab) {
+      let article = tab.savedArticle;
+      if (article && article.url == url) {
+        this.log("Saved article found in tab");
+        callback(article);
+        return;
+      }
+    }
+
+    this.parseDocumentFromURL(url, callback);
+  },
+
+  parseDocumentFromTab: function(tabId, callback) {
+    try {
+      this.log("parseDocumentFromTab: " + tabId);
+
+      let tab = BrowserApp.getTabForId(tabId);
+      let url = tab.browser.contentWindow.location.href;
+      let uri = Services.io.newURI(url, null, null);
+
+      if (!this._shouldCheckUri(uri)) {
+        callback(null);
+        return;
+      }
+
+      // First, try to find a cached parsed article in the DB
+      this.getArticleFromCache(url, function(article) {
+        if (article) {
+          this.log("Page found in cache, return article immediately");
+          callback(article);
+          return;
+        }
+
+        let doc = tab.browser.contentWindow.document;
+        this._readerParse(uri, doc, function (article) {
+          if (!article) {
+            this.log("Failed to parse page");
+            callback(null);
+            return;
+          }
+
+          callback(article);
+        }.bind(this));
+      }.bind(this));
+    } catch (e) {
+      this.log("Error parsing document from tab: " + e);
+      callback(null);
+    }
+  },
+
+  getArticleFromCache: function Reader_getArticleFromCache(url, callback) {
+    this._getCacheDB(function(cacheDB) {
+      if (!cacheDB) {
+        callback(false);
+        return;
+      }
+
+      let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
+      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
+
+      let request = articles.get(url);
+
+      request.onerror = function(event) {
+        this.log("Error getting article from the cache DB: " + url);
+        callback(null);
+      }.bind(this);
+
+      request.onsuccess = function(event) {
+        this.log("Got article from the cache DB: " + event.target.result);
+        callback(event.target.result);
+      }.bind(this);
+    }.bind(this));
+  },
+
+  storeArticleInCache: function Reader_storeArticleInCache(article, callback) {
+    this._getCacheDB(function(cacheDB) {
+      if (!cacheDB) {
+        callback(false);
+        return;
+      }
+
+      let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
+      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
+
+      let request = articles.add(article);
+
+      request.onerror = function(event) {
+        this.log("Error storing article in the cache DB: " + article.url);
+        callback(false);
+      }.bind(this);
+
+      request.onsuccess = function(event) {
+        this.log("Stored article in the cache DB: " + article.url);
+        callback(true);
+      }.bind(this);
+    }.bind(this));
+  },
+
+  removeArticleFromCache: function Reader_removeArticleFromCache(url, callback) {
+    this._getCacheDB(function(cacheDB) {
+      if (!cacheDB) {
+        callback(false);
+        return;
+      }
+
+      let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
+      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
+
+      let request = articles.delete(url);
+
+      request.onerror = function(event) {
+        this.log("Error removing article from the cache DB: " + url);
+        callback(false);
+      }.bind(this);
+
+      request.onsuccess = function(event) {
+        this.log("Removed article from the cache DB: " + url);
+        callback(true);
+      }.bind(this);
+    }.bind(this));
+  },
+
+  uninit: function Reader_uninit() {
+    Services.prefs.removeObserver("reader.parse-on-load.", this);
+
+    Services.obs.removeObserver(this, "Reader:Add");
+    Services.obs.removeObserver(this, "Reader:Remove");
+
+    let requests = this._requests;
+    for (let url in requests) {
+      let request = requests[url];
+      if (request.browser) {
+        let browser = request.browser;
+        browser.parentNode.removeChild(browser);
+      }
+    }
+    delete this._requests;
+
+    if (this._cacheDB) {
+      this._cacheDB.close();
+      delete this._cacheDB;
+    }
+  },
+
+  log: function(msg) {
+    if (this.DEBUG)
+      dump("Reader: " + msg);
+  },
+
+  _shouldCheckUri: function Reader_shouldCheckUri(uri) {
+    if ((uri.prePath + "/") === uri.spec) {
+      this.log("Not parsing home page: " + uri.spec);
+      return false;
+    }
+
+    if (!(uri.schemeIs("http") || uri.schemeIs("https") || uri.schemeIs("file"))) {
+      this.log("Not parsing URI scheme: " + uri.scheme);
+      return false;
+    }
+
+    return true;
+  },
+
+  _readerParse: function Reader_readerParse(uri, doc, callback) {
+    let numTags = doc.getElementsByTagName("*").length;
+    if (numTags > this.MAX_ELEMS_TO_PARSE) {
+      this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
+      callback(null);
+      return;
+    }
+
+    let worker = new ChromeWorker("readerWorker.js");
+    worker.onmessage = function (evt) {
+      let article = evt.data;
+
+      // Append URL to the article data. specIgnoringRef will ignore any hash
+      // in the URL.
+      if (article) {
+        article.url = uri.specIgnoringRef;
+        let flags = Ci.nsIDocumentEncoder.OutputSelectionOnly | Ci.nsIDocumentEncoder.OutputAbsoluteLinks;
+        article.title = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils)
+                                                        .convertToPlainText(article.title, flags, 0);
+      }
+
+      callback(article);
+    };
+
+    try {
+      worker.postMessage({
+        uri: {
+          spec: uri.spec,
+          host: uri.host,
+          prePath: uri.prePath,
+          scheme: uri.scheme,
+          pathBase: Services.io.newURI(".", null, uri).spec
+        },
+        doc: new XMLSerializer().serializeToString(doc)
+      });
+    } catch (e) {
+      dump("Reader: could not build Readability arguments: " + e);
+      callback(null);
+    }
+  },
+
+  _runCallbacksAndFinish: function Reader_runCallbacksAndFinish(request, result) {
+    delete this._requests[request.url];
+
+    request.callbacks.forEach(function(callback) {
+      callback(result);
+    });
+  },
+
+  _downloadDocument: function Reader_downloadDocument(url, callback) {
+    // We want to parse those arbitrary pages safely, outside the privileged
+    // context of chrome. We create a hidden browser element to fetch the
+    // loaded page's document object then discard the browser element.
+
+    let browser = document.createElement("browser");
+    browser.setAttribute("type", "content");
+    browser.setAttribute("collapsed", "true");
+    browser.setAttribute("disablehistory", "true");
+
+    document.documentElement.appendChild(browser);
+    browser.stop();
+
+    browser.webNavigation.allowAuth = false;
+    browser.webNavigation.allowImages = false;
+    browser.webNavigation.allowJavascript = false;
+    browser.webNavigation.allowMetaRedirects = true;
+    browser.webNavigation.allowPlugins = false;
+
+    browser.addEventListener("DOMContentLoaded", function (event) {
+      let doc = event.originalTarget;
+
+      // ignore on frames and other documents
+      if (doc != browser.contentDocument)
+        return;
+
+      this.log("Done loading: " + doc);
+      if (doc.location.href == "about:blank") {
+        callback(null);
+
+        // Request has finished with error, remove browser element
+        browser.parentNode.removeChild(browser);
+        return;
+      }
+
+      callback(doc);
+    }.bind(this));
+
+    browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
+                             null, null, null);
+
+    return browser;
+  },
+
+  _downloadAndParseDocument: function Reader_downloadAndParseDocument(url, request) {
+    try {
+      this.log("Needs to fetch page, creating request: " + url);
+
+      request.browser = this._downloadDocument(url, function(doc) {
+        this.log("Finished loading page: " + doc);
+
+        if (!doc) {
+          this.log("Error loading page");
+          this._runCallbacksAndFinish(request, null);
+          return;
+        }
+
+        this.log("Parsing response with Readability");
+
+        let uri = Services.io.newURI(url, null, null);
+        this._readerParse(uri, doc, function (article) {
+          // Delete reference to the browser element as we've finished parsing.
+          let browser = request.browser;
+          if (browser) {
+            browser.parentNode.removeChild(browser);
+            delete request.browser;
+          }
+
+          if (!article) {
+            this.log("Failed to parse page");
+            this._runCallbacksAndFinish(request, null);
+            return;
+          }
+
+          this.log("Parsing has been successful");
+
+          this._runCallbacksAndFinish(request, article);
+        }.bind(this));
+      }.bind(this));
+    } catch (e) {
+      this.log("Error downloading and parsing document: " + e);
+      this._runCallbacksAndFinish(request, null);
+    }
+  },
+
+  _getCacheDB: function Reader_getCacheDB(callback) {
+    if (this._cacheDB) {
+      callback(this._cacheDB);
+      return;
+    }
+
+    let request = window.indexedDB.open("about:reader", this.DB_VERSION);
+
+    request.onerror = function(event) {
+      this.log("Error connecting to the cache DB");
+      this._cacheDB = null;
+      callback(null);
+    }.bind(this);
+
+    request.onsuccess = function(event) {
+      this.log("Successfully connected to the cache DB");
+      this._cacheDB = event.target.result;
+      callback(this._cacheDB);
+    }.bind(this);
+
+    request.onupgradeneeded = function(event) {
+      this.log("Database schema upgrade from " +
+           event.oldVersion + " to " + event.newVersion);
+
+      let cacheDB = event.target.result;
+
+      // Create the articles object store
+      this.log("Creating articles object store");
+      cacheDB.createObjectStore("articles", { keyPath: "url" });
+
+      this.log("Database upgrade done: " + this.DB_VERSION);
+    }.bind(this);
+  }
+};
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -97,22 +97,16 @@ var SelectionHandler = {
           this._updateCacheForSelection();
         }
         if (this._activeType != this.TYPE_NONE) {
           this._positionHandlesOnChange();
         }
         break;
       }
 
-      // Update caret position on keyboard activity
-      case "TextSelection:UpdateCaretPos":
-        // Generated by IME close, autoCorrection / styling
-        this._positionHandles();
-        break;
-
       case "Gesture:SingleTap": {
         if (this._activeType == this.TYPE_CURSOR) {
           // attachCaret() is called in the "Gesture:SingleTap" handler in BrowserEventHandler
           // We're guaranteed to call this first, because this observer was added last
           this._deactivate();
         }
         break;
       }
@@ -707,17 +701,16 @@ var SelectionHandler = {
     // Ensure it isn't disabled, isn't handled by Android native dialog, and is editable text element
     if (aElement.disabled || InputWidgetHelper.hasInputWidget(aElement) || !this.isElementEditableText(aElement)) {
       return false;
     }
 
     this._initTargetInfo(aElement, this.TYPE_CURSOR);
 
     // Caret-specific observer/listeners
-    Services.obs.addObserver(this, "TextSelection:UpdateCaretPos", false);
     BrowserApp.deck.addEventListener("keyup", this, false);
     BrowserApp.deck.addEventListener("compositionupdate", this, false);
     BrowserApp.deck.addEventListener("compositionend", this, false);
 
     this._activeType = this.TYPE_CURSOR;
 
     // Determine position and show caret, open actionbar
     this._positionHandles();
@@ -1009,17 +1002,16 @@ var SelectionHandler = {
     this._stopDraggingHandles();
     // Hide handle/caret, close actionbar
     Messaging.sendRequest({ type: "TextSelection:HideHandles" });
 
     this._removeObservers();
 
     // Only observed for caret positioning
     if (this._activeType == this.TYPE_CURSOR) {
-      Services.obs.removeObserver(this, "TextSelection:UpdateCaretPos");
       BrowserApp.deck.removeEventListener("keyup", this);
       BrowserApp.deck.removeEventListener("compositionupdate", this);
       BrowserApp.deck.removeEventListener("compositionend", this);
     }
 
     this._contentWindow = null;
     this._targetElement = null;
     this._isRTL = false;
--- a/mobile/android/chrome/content/aboutDownloads.js
+++ b/mobile/android/chrome/content/aboutDownloads.js
@@ -6,16 +6,18 @@ let Ci = Components.interfaces, Cc = Com
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
+                                  "resource://gre/modules/Messaging.jsm");
 
 let gStrings = Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties");
 
 let downloadTemplate =
 "<li downloadGUID='{guid}' class='list-item' role='button' state='{state}' contextmenu='downloadmenu'>" +
   "<img class='icon' src='{icon}'/>" +
   "<div class='details'>" +
      "<div class='row'>" +
@@ -477,16 +479,21 @@ let Downloads = {
         OS.File.remove(aDownload.targetFile.path).then(null, function onError(reason) {
           if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) {
             this.logError("removeDownload() " + reason, aDownload);
           }
         }.bind(this));
       }
 
       aDownload.remove();
+      Messaging.sendRequest({
+        type: "Download:Remove",
+        path: aDownload.targetFile.path,
+      });
+
     }.bind(this));
   },
 
   removeAll: function dl_removeAll() {
     let title = gStrings.GetStringFromName("downloadAction.deleteAll");
     let messageForm = gStrings.GetStringFromName("downloadMessage.deleteAll");
     let elements = document.body.querySelectorAll("li[state='" + this._dlmgr.DOWNLOAD_FINISHED + "']," +
                                                "li[state='" + this._dlmgr.DOWNLOAD_CANCELED + "']," +
--- a/mobile/android/chrome/content/aboutReader.js
+++ b/mobile/android/chrome/content/aboutReader.js
@@ -186,16 +186,17 @@ AboutReader.prototype = {
       case "Reader:FaviconReturn": {
         let args = JSON.parse(aData);
         this._loadFavicon(args.url, args.faviconUrl);
         Services.obs.removeObserver(this, "Reader:FaviconReturn");
         break;
       }
 
       case "Reader:Add": {
+        // Page can be added by long-press pageAction, or by tap on banner icon.
         let args = JSON.parse(aData);
         if (args.url == this._article.url) {
           if (this._isReadingListItem != 1) {
             this._isReadingListItem = 1;
             this._updateToggleButton();
           }
         }
         break;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -132,16 +132,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   ["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
   ["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
   ["PermissionsHelper", ["Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
   ["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
   ["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
   ["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
   ["Notifications", ["Notification:Event"], "chrome://browser/content/Notifications.jsm"],
   ["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
+  ["Reader", ["Reader:Add", "Reader:Remove"], "chrome://browser/content/Reader.js"],
 ].forEach(function (aScript) {
   let [name, notifications, script] = aScript;
   XPCOMUtils.defineLazyGetter(window, name, function() {
     let sandbox = {};
     Services.scriptloader.loadSubScript(script, sandbox);
     return sandbox[name];
   });
   let observer = (s, t, d) => {
@@ -421,17 +422,16 @@ var BrowserApp = {
     HealthReportStatusListener.init();
     XPInstallObserver.init();
     CharacterEncoding.init();
     ActivityObserver.init();
     // TODO: replace with Android implementation of WebappOSUtils.isLaunchable.
     Cu.import("resource://gre/modules/Webapps.jsm");
     DOMApplicationRegistry.allAppsLaunchable = true;
     RemoteDebugger.init();
-    Reader.init();
     UserAgentOverrides.init();
     DesktopUserAgent.init();
     CastingApps.init();
     Distribution.init();
     Tabs.init();
 #ifdef ACCESSIBILITY
     AccessFu.attach(window);
 #endif
@@ -7355,572 +7355,16 @@ var RemoteDebugger = {
 
 var Telemetry = {
   addData: function addData(aHistogramId, aValue) {
     let histogram = Services.telemetry.getHistogramById(aHistogramId);
     histogram.add(aValue);
   },
 };
 
-let Reader = {
-  // Version of the cache database schema
-  DB_VERSION: 1,
-
-  DEBUG: 0,
-
-  READER_ADD_SUCCESS: 0,
-  READER_ADD_FAILED: 1,
-  READER_ADD_DUPLICATE: 2,
-
-  // Don't try to parse the page if it has too many elements (for memory and
-  // performance reasons)
-  MAX_ELEMS_TO_PARSE: 3000,
-
-  isEnabledForParseOnLoad: false,
-
-  init: function Reader_init() {
-    this.log("Init()");
-    this._requests = {};
-
-    this.isEnabledForParseOnLoad = this.getStateForParseOnLoad();
-
-    Services.obs.addObserver(this, "Reader:Add", false);
-    Services.obs.addObserver(this, "Reader:Remove", false);
-
-    Services.prefs.addObserver("reader.parse-on-load.", this, false);
-  },
-
-  pageAction: {
-    readerModeCallback: function(){
-      Messaging.sendRequest({
-        type: "Reader:Click",
-      });
-    },
-
-    readerModeActiveCallback: function(){
-      Messaging.sendRequest({
-        type: "Reader:LongClick",
-      });
-
-      UITelemetry.addEvent("save.1", "pageaction", null, "reader");
-    },
-  },
-
-  updatePageAction: function(tab) {
-    if (this.pageAction.id) {
-      PageActions.remove(this.pageAction.id);
-      delete this.pageAction.id;
-    }
-
-    if (tab.readerActive) {
-      this.pageAction.id = PageActions.add({
-        title: Strings.browser.GetStringFromName("readerMode.exit"),
-        icon: "drawable://reader_active",
-        clickCallback: this.pageAction.readerModeCallback,
-        important: true
-      });
-
-      // Only start a reader session if the viewer is in the foreground. We do
-      // not track background reader viewers.
-      UITelemetry.startSession("reader.1", null);
-      return;
-    }
-
-    // Only stop a reader session if the foreground viewer is not visible.
-    UITelemetry.stopSession("reader.1", "", null);
-
-    if (tab.readerEnabled) {
-      this.pageAction.id = PageActions.add({
-        title: Strings.browser.GetStringFromName("readerMode.enter"),
-        icon: "drawable://reader",
-        clickCallback:this.pageAction.readerModeCallback,
-        longClickCallback: this.pageAction.readerModeActiveCallback,
-        important: true
-      });
-    }
-  },
-
-  observe: function(aMessage, aTopic, aData) {
-    switch(aTopic) {
-      case "Reader:Add": {
-        let args = JSON.parse(aData);
-        if ('fromAboutReader' in args) {
-          // Ignore adds initiated from aboutReader menu banner
-          break;
-        }
-
-        let tabID = null;
-        let url, urlWithoutRef;
-
-        if ('tabID' in args) {
-          tabID = args.tabID;
-
-          let tab = BrowserApp.getTabForId(tabID);
-          let currentURI = tab.browser.currentURI;
-
-          url = currentURI.spec;
-          urlWithoutRef = currentURI.specIgnoringRef;
-        } else if ('url' in args) {
-          let uri = Services.io.newURI(args.url, null, null);
-          url = uri.spec;
-          urlWithoutRef = uri.specIgnoringRef;
-        } else {
-          throw new Error("Reader:Add requires a tabID or an URL as argument");
-        }
-
-        let sendResult = function(result, article) {
-          article = article || {};
-          this.log("Reader:Add success=" + result + ", url=" + url + ", title=" + article.title + ", excerpt=" + article.excerpt);
-
-          Messaging.sendRequest({
-            type: "Reader:Added",
-            result: result,
-            title: truncate(article.title, MAX_TITLE_LENGTH),
-            url: truncate(url, MAX_URI_LENGTH),
-            length: article.length,
-            excerpt: article.excerpt
-          });
-        }.bind(this);
-
-        let handleArticle = function(article) {
-          if (!article) {
-            sendResult(this.READER_ADD_FAILED, null);
-            return;
-          }
-
-          this.storeArticleInCache(article, function(success) {
-            let result = (success ? this.READER_ADD_SUCCESS : this.READER_ADD_FAILED);
-            sendResult(result, article);
-          }.bind(this));
-        }.bind(this);
-
-        this.getArticleFromCache(urlWithoutRef, function (article) {
-          // If the article is already in reading list, bail
-          if (article) {
-            sendResult(this.READER_ADD_DUPLICATE, null);
-            return;
-          }
-
-          if (tabID != null) {
-            this.getArticleForTab(tabID, urlWithoutRef, handleArticle);
-          } else {
-            this.parseDocumentFromURL(urlWithoutRef, handleArticle);
-          }
-        }.bind(this));
-        break;
-      }
-
-      case "Reader:Remove": {
-        let args = JSON.parse(aData);
-
-        if (!("url" in args)) {
-          throw new Error("Reader:Remove requires URL as an argument");
-        }
-
-        this.removeArticleFromCache(args.url, function(success) {
-          this.log("Reader:Remove success=" + success + ", url=" + args.url);
-          if (success && args.notify) {
-            Messaging.sendRequest({
-              type: "Reader:Removed",
-              url: args.url
-            });
-          }
-        }.bind(this));
-        break;
-      }
-
-      case "nsPref:changed": {
-        if (aData.startsWith("reader.parse-on-load.")) {
-          this.isEnabledForParseOnLoad = this.getStateForParseOnLoad();
-        }
-        break;
-      }
-    }
-  },
-
-  getStateForParseOnLoad: function Reader_getStateForParseOnLoad() {
-    let isEnabled = Services.prefs.getBoolPref("reader.parse-on-load.enabled");
-    let isForceEnabled = Services.prefs.getBoolPref("reader.parse-on-load.force-enabled");
-    // For low-memory devices, don't allow reader mode since it takes up a lot of memory.
-    // See https://bugzilla.mozilla.org/show_bug.cgi?id=792603 for details.
-    return isForceEnabled || (isEnabled && !BrowserApp.isOnLowMemoryPlatform);
-  },
-
-  parseDocumentFromURL: function Reader_parseDocumentFromURL(url, callback) {
-    // If there's an on-going request for the same URL, simply append one
-    // more callback to it to be called when the request is done.
-    if (url in this._requests) {
-      let request = this._requests[url];
-      request.callbacks.push(callback);
-      return;
-    }
-
-    let request = { url: url, callbacks: [callback] };
-    this._requests[url] = request;
-
-    try {
-      this.log("parseDocumentFromURL: " + url);
-
-      // First, try to find a cached parsed article in the DB
-      this.getArticleFromCache(url, function(article) {
-        if (article) {
-          this.log("Page found in cache, return article immediately");
-          this._runCallbacksAndFinish(request, article);
-          return;
-        }
-
-        if (!this._requests) {
-          this.log("Reader has been destroyed, abort");
-          return;
-        }
-
-        // Article hasn't been found in the cache DB, we need to
-        // download the page and parse the article out of it.
-        this._downloadAndParseDocument(url, request);
-      }.bind(this));
-    } catch (e) {
-      this.log("Error parsing document from URL: " + e);
-      this._runCallbacksAndFinish(request, null);
-    }
-  },
-
-  getArticleForTab: function Reader_getArticleForTab(tabId, url, callback) {
-    let tab = BrowserApp.getTabForId(tabId);
-    if (tab) {
-      let article = tab.savedArticle;
-      if (article && article.url == url) {
-        this.log("Saved article found in tab");
-        callback(article);
-        return;
-      }
-    }
-
-    this.parseDocumentFromURL(url, callback);
-  },
-
-  parseDocumentFromTab: function(tabId, callback) {
-    try {
-      this.log("parseDocumentFromTab: " + tabId);
-
-      let tab = BrowserApp.getTabForId(tabId);
-      let url = tab.browser.contentWindow.location.href;
-      let uri = Services.io.newURI(url, null, null);
-
-      if (!this._shouldCheckUri(uri)) {
-        callback(null);
-        return;
-      }
-
-      // First, try to find a cached parsed article in the DB
-      this.getArticleFromCache(url, function(article) {
-        if (article) {
-          this.log("Page found in cache, return article immediately");
-          callback(article);
-          return;
-        }
-
-        let doc = tab.browser.contentWindow.document;
-        this._readerParse(uri, doc, function (article) {
-          if (!article) {
-            this.log("Failed to parse page");
-            callback(null);
-            return;
-          }
-
-          callback(article);
-        }.bind(this));
-      }.bind(this));
-    } catch (e) {
-      this.log("Error parsing document from tab: " + e);
-      callback(null);
-    }
-  },
-
-  getArticleFromCache: function Reader_getArticleFromCache(url, callback) {
-    this._getCacheDB(function(cacheDB) {
-      if (!cacheDB) {
-        callback(false);
-        return;
-      }
-
-      let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
-      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
-
-      let request = articles.get(url);
-
-      request.onerror = function(event) {
-        this.log("Error getting article from the cache DB: " + url);
-        callback(null);
-      }.bind(this);
-
-      request.onsuccess = function(event) {
-        this.log("Got article from the cache DB: " + event.target.result);
-        callback(event.target.result);
-      }.bind(this);
-    }.bind(this));
-  },
-
-  storeArticleInCache: function Reader_storeArticleInCache(article, callback) {
-    this._getCacheDB(function(cacheDB) {
-      if (!cacheDB) {
-        callback(false);
-        return;
-      }
-
-      let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
-      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
-
-      let request = articles.add(article);
-
-      request.onerror = function(event) {
-        this.log("Error storing article in the cache DB: " + article.url);
-        callback(false);
-      }.bind(this);
-
-      request.onsuccess = function(event) {
-        this.log("Stored article in the cache DB: " + article.url);
-        callback(true);
-      }.bind(this);
-    }.bind(this));
-  },
-
-  removeArticleFromCache: function Reader_removeArticleFromCache(url, callback) {
-    this._getCacheDB(function(cacheDB) {
-      if (!cacheDB) {
-        callback(false);
-        return;
-      }
-
-      let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
-      let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
-
-      let request = articles.delete(url);
-
-      request.onerror = function(event) {
-        this.log("Error removing article from the cache DB: " + url);
-        callback(false);
-      }.bind(this);
-
-      request.onsuccess = function(event) {
-        this.log("Removed article from the cache DB: " + url);
-        callback(true);
-      }.bind(this);
-    }.bind(this));
-  },
-
-  uninit: function Reader_uninit() {
-    Services.prefs.removeObserver("reader.parse-on-load.", this);
-
-    Services.obs.removeObserver(this, "Reader:Add");
-    Services.obs.removeObserver(this, "Reader:Remove");
-
-    let requests = this._requests;
-    for (let url in requests) {
-      let request = requests[url];
-      if (request.browser) {
-        let browser = request.browser;
-        browser.parentNode.removeChild(browser);
-      }
-    }
-    delete this._requests;
-
-    if (this._cacheDB) {
-      this._cacheDB.close();
-      delete this._cacheDB;
-    }
-  },
-
-  log: function(msg) {
-    if (this.DEBUG)
-      dump("Reader: " + msg);
-  },
-
-  _shouldCheckUri: function Reader_shouldCheckUri(uri) {
-    if ((uri.prePath + "/") === uri.spec) {
-      this.log("Not parsing home page: " + uri.spec);
-      return false;
-    }
-
-    if (!(uri.schemeIs("http") || uri.schemeIs("https") || uri.schemeIs("file"))) {
-      this.log("Not parsing URI scheme: " + uri.scheme);
-      return false;
-    }
-
-    return true;
-  },
-
-  _readerParse: function Reader_readerParse(uri, doc, callback) {
-    let numTags = doc.getElementsByTagName("*").length;
-    if (numTags > this.MAX_ELEMS_TO_PARSE) {
-      this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
-      callback(null);
-      return;
-    }
-
-    let worker = new ChromeWorker("readerWorker.js");
-    worker.onmessage = function (evt) {
-      let article = evt.data;
-
-      // Append URL to the article data. specIgnoringRef will ignore any hash
-      // in the URL.
-      if (article) {
-        article.url = uri.specIgnoringRef;
-        let flags = Ci.nsIDocumentEncoder.OutputSelectionOnly | Ci.nsIDocumentEncoder.OutputAbsoluteLinks;
-        article.title = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils)
-                                                        .convertToPlainText(article.title, flags, 0);
-      }
-
-      callback(article);
-    };
-
-    try {
-      worker.postMessage({
-        uri: {
-          spec: uri.spec,
-          host: uri.host,
-          prePath: uri.prePath,
-          scheme: uri.scheme,
-          pathBase: Services.io.newURI(".", null, uri).spec
-        },
-        doc: new XMLSerializer().serializeToString(doc)
-      });
-    } catch (e) {
-      dump("Reader: could not build Readability arguments: " + e);
-      callback(null);
-    }
-  },
-
-  _runCallbacksAndFinish: function Reader_runCallbacksAndFinish(request, result) {
-    delete this._requests[request.url];
-
-    request.callbacks.forEach(function(callback) {
-      callback(result);
-    });
-  },
-
-  _downloadDocument: function Reader_downloadDocument(url, callback) {
-    // We want to parse those arbitrary pages safely, outside the privileged
-    // context of chrome. We create a hidden browser element to fetch the
-    // loaded page's document object then discard the browser element.
-
-    let browser = document.createElement("browser");
-    browser.setAttribute("type", "content");
-    browser.setAttribute("collapsed", "true");
-    browser.setAttribute("disablehistory", "true");
-
-    document.documentElement.appendChild(browser);
-    browser.stop();
-
-    browser.webNavigation.allowAuth = false;
-    browser.webNavigation.allowImages = false;
-    browser.webNavigation.allowJavascript = false;
-    browser.webNavigation.allowMetaRedirects = true;
-    browser.webNavigation.allowPlugins = false;
-
-    browser.addEventListener("DOMContentLoaded", function (event) {
-      let doc = event.originalTarget;
-
-      // ignore on frames and other documents
-      if (doc != browser.contentDocument)
-        return;
-
-      this.log("Done loading: " + doc);
-      if (doc.location.href == "about:blank") {
-        callback(null);
-
-        // Request has finished with error, remove browser element
-        browser.parentNode.removeChild(browser);
-        return;
-      }
-
-      callback(doc);
-    }.bind(this));
-
-    browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
-                             null, null, null);
-
-    return browser;
-  },
-
-  _downloadAndParseDocument: function Reader_downloadAndParseDocument(url, request) {
-    try {
-      this.log("Needs to fetch page, creating request: " + url);
-
-      request.browser = this._downloadDocument(url, function(doc) {
-        this.log("Finished loading page: " + doc);
-
-        if (!doc) {
-          this.log("Error loading page");
-          this._runCallbacksAndFinish(request, null);
-          return;
-        }
-
-        this.log("Parsing response with Readability");
-
-        let uri = Services.io.newURI(url, null, null);
-        this._readerParse(uri, doc, function (article) {
-          // Delete reference to the browser element as we've finished parsing.
-          let browser = request.browser;
-          if (browser) {
-            browser.parentNode.removeChild(browser);
-            delete request.browser;
-          }
-
-          if (!article) {
-            this.log("Failed to parse page");
-            this._runCallbacksAndFinish(request, null);
-            return;
-          }
-
-          this.log("Parsing has been successful");
-
-          this._runCallbacksAndFinish(request, article);
-        }.bind(this));
-      }.bind(this));
-    } catch (e) {
-      this.log("Error downloading and parsing document: " + e);
-      this._runCallbacksAndFinish(request, null);
-    }
-  },
-
-  _getCacheDB: function Reader_getCacheDB(callback) {
-    if (this._cacheDB) {
-      callback(this._cacheDB);
-      return;
-    }
-
-    let request = window.indexedDB.open("about:reader", this.DB_VERSION);
-
-    request.onerror = function(event) {
-      this.log("Error connecting to the cache DB");
-      this._cacheDB = null;
-      callback(null);
-    }.bind(this);
-
-    request.onsuccess = function(event) {
-      this.log("Successfully connected to the cache DB");
-      this._cacheDB = event.target.result;
-      callback(this._cacheDB);
-    }.bind(this);
-
-    request.onupgradeneeded = function(event) {
-      this.log("Database schema upgrade from " +
-           event.oldVersion + " to " + event.newVersion);
-
-      let cacheDB = event.target.result;
-
-      // Create the articles object store
-      this.log("Creating articles object store");
-      cacheDB.createObjectStore("articles", { keyPath: "url" });
-
-      this.log("Database upgrade done: " + this.DB_VERSION);
-    }.bind(this);
-  }
-};
-
 var ExternalApps = {
   _contextMenuId: null,
 
   // extend _getLink to pickup html5 media links.
   _getMediaLink: function(aElement) {
     let uri = NativeWindow.contextmenus._getLink(aElement);
     if (uri == null && aElement.nodeType == Ci.nsIDOMNode.ELEMENT_NODE && (aElement instanceof Ci.nsIDOMHTMLMediaElement)) {
       try {
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -17,16 +17,17 @@ chrome.jar:
   content/aboutDownloads.js            (content/aboutDownloads.js)
   content/aboutFeedback.xhtml          (content/aboutFeedback.xhtml)
   content/aboutFeedback.js             (content/aboutFeedback.js)
   content/aboutPrivateBrowsing.xhtml   (content/aboutPrivateBrowsing.xhtml)
   content/aboutPrivateBrowsing.js      (content/aboutPrivateBrowsing.js)
   content/aboutReader.html             (content/aboutReader.html)
   content/aboutReader.js               (content/aboutReader.js)
   content/Readability.js               (content/Readability.js)
+  content/Reader.js                    (content/Reader.js)
   content/JSDOMParser.js               (content/JSDOMParser.js)
   content/readerWorker.js              (content/readerWorker.js)
   content/aboutHome.xhtml              (content/aboutHome.xhtml)
   content/aboutRights.xhtml            (content/aboutRights.xhtml)
 * content/aboutApps.xhtml              (content/aboutApps.xhtml)
 * content/aboutApps.js                 (content/aboutApps.js)
   content/blockedSite.xhtml            (content/blockedSite.xhtml)
   content/languages.properties         (content/languages.properties)
--- a/python/mozbuild/mozbuild/backend/templates/android_eclipse/project.properties
+++ b/python/mozbuild/mozbuild/backend/templates/android_eclipse/project.properties
@@ -4,11 +4,11 @@
 #
 # This file must be checked in Version Control Systems.
 #
 # To customize properties used by the Ant build system edit
 # "ant.properties", and override values to adapt the script to your
 # project structure.
 
 # Project target.
-target=android-@ANDROID_TARGET_SDK@
+target=android-L
 @IDE_PROJECT_LIBRARY_SETTING@
 @IDE_PROJECT_LIBRARY_REFERENCES@
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -2768,40 +2768,43 @@ nsDownload::SetState(DownloadState aStat
 
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
         // On Windows and Gtk, add the download to the system's "recent documents"
         // list, with a pref to disable.
         {
           bool addToRecentDocs = true;
           if (pref)
             pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
-
+#ifdef MOZ_WIDGET_ANDROID
+          if (addToRecentDocs) {
+            nsCOMPtr<nsIMIMEInfo> mimeInfo;
+            nsAutoCString contentType;
+            GetMIMEInfo(getter_AddRefs(mimeInfo));
+
+            if (mimeInfo)
+              mimeInfo->GetMIMEType(contentType);
+
+            mozilla::widget::android::DownloadsIntegration::ScanMedia(path, NS_ConvertUTF8toUTF16(contentType));
+          }
+#else
           if (addToRecentDocs && !mPrivate) {
 #ifdef XP_WIN
             ::SHAddToRecentDocs(SHARD_PATHW, path.get());
 #elif defined(MOZ_WIDGET_GTK)
             GtkRecentManager* manager = gtk_recent_manager_get_default();
 
             gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
                                            nullptr, nullptr);
             if (uri) {
               gtk_recent_manager_add_item(manager, uri);
               g_free(uri);
             }
-#elif defined(MOZ_WIDGET_ANDROID)
-            nsCOMPtr<nsIMIMEInfo> mimeInfo;
-            nsAutoCString contentType;
-            GetMIMEInfo(getter_AddRefs(mimeInfo));
-
-            if (mimeInfo)
-              mimeInfo->GetMIMEType(contentType);
-
-            mozilla::widget::android::GeckoAppShell::ScanMedia(path, NS_ConvertUTF8toUTF16(contentType));
 #endif
           }
+#endif
 #ifdef MOZ_ENABLE_GIO
           // Use GIO to store the source URI for later display in the file manager.
           GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
           nsCString source_uri;
           mSource->GetSpec(source_uri);
           GFileInfo *file_info = g_file_info_new();
           g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
           g_file_set_attributes_async(gio_file,
@@ -2809,16 +2812,17 @@ nsDownload::SetState(DownloadState aStat
                                       G_FILE_QUERY_INFO_NONE,
                                       G_PRIORITY_DEFAULT,
                                       nullptr, gio_set_metadata_done, nullptr);
           g_object_unref(file_info);
           g_object_unref(gio_file);
 #endif
         }
 #endif
+
 #ifdef XP_MACOSX
         // On OS X, make the downloads stack bounce.
         CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
                                                  NS_ConvertUTF16toUTF8(path).get(),
                                                  kCFStringEncodingUTF8);
         CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
         ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
                                                observedObject, nullptr, TRUE);
--- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
+++ b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
@@ -77,32 +77,36 @@ nsresult DownloadPlatform::DownloadDone(
 
   nsAutoString path;
   if (aTarget && NS_SUCCEEDED(aTarget->GetPath(path))) {
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
     // On Windows and Gtk, add the download to the system's "recent documents"
     // list, with a pref to disable.
     {
       bool addToRecentDocs = Preferences::GetBool(PREF_BDM_ADDTORECENTDOCS);
+#ifdef MOZ_WIDGET_ANDROID
+      if (addToRecentDocs) {
+        mozilla::widget::android::DownloadsIntegration::ScanMedia(path, NS_ConvertUTF8toUTF16(aContentType));
+      }
+#else
       if (addToRecentDocs && !aIsPrivate) {
 #ifdef XP_WIN
         ::SHAddToRecentDocs(SHARD_PATHW, path.get());
 #elif defined(MOZ_WIDGET_GTK)
         GtkRecentManager* manager = gtk_recent_manager_get_default();
 
         gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
                                        nullptr, nullptr);
         if (uri) {
           gtk_recent_manager_add_item(manager, uri);
           g_free(uri);
         }
-#elif MOZ_WIDGET_ANDROID
-        mozilla::widget::android::GeckoAppShell::ScanMedia(path, NS_ConvertUTF8toUTF16(aContentType));
 #endif
       }
+#endif
 #ifdef MOZ_ENABLE_GIO
       // Use GIO to store the source URI for later display in the file manager.
       GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
       nsCString source_uri;
       aSource->GetSpec(source_uri);
       GFileInfo *file_info = g_file_info_new();
       g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
       g_file_set_attributes_async(gio_file,
@@ -110,16 +114,17 @@ nsresult DownloadPlatform::DownloadDone(
                                   G_FILE_QUERY_INFO_NONE,
                                   G_PRIORITY_DEFAULT,
                                   nullptr, gio_set_metadata_done, nullptr);
       g_object_unref(file_info);
       g_object_unref(gio_file);
 #endif
     }
 #endif
+
 #ifdef XP_MACOSX
     // On OS X, make the downloads stack bounce.
     CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
                                              NS_ConvertUTF16toUTF8(path).get(),
                                              kCFStringEncodingUTF8);
     CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
     ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
                                            observedObject, nullptr, TRUE);
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1118,18 +1118,22 @@
           aOtherBrowser.detachFormFill();
 
           for each (var field in fieldsToSwap) {
             this[field] = otherFieldValues[field];
             aOtherBrowser[field] = ourFieldValues[field];
           }
 
           // Re-attach the docShells to the form fill controller.
-          this.attachFormFill();
-          aOtherBrowser.attachFormFill();
+          if (!this.isRemoteBrowser) {
+            this.attachFormFill();
+          }
+          if (!aOtherBrowser.isRemoteBrowser) {
+            aOtherBrowser.attachFormFill();
+          }
 
           // Null the current nsITypeAheadFind instances so that they're
           // lazily re-created on access. We need to do this because they
           // might have attached the wrong docShell.
           this._fastFind = aOtherBrowser._fastFind = null;
         ]]>
         </body>
       </method>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8ad164baaf7d205f16d7d7742d2fdd09caf71efc
GIT binary patch
literal 247
zc$@+E00{qyP)<h;3K|Lk000e1NJLTq000dD000RH1ONa4LvwE70002KNkl<Z2wP(~
zrTzcM|L_05Z&8A=b}BM3GJt_tZsh+@K=>`q2*z3^2a)7uFo>%F3Vi(k&DK4(62e+2
z4U%MH;A1e%to#4||GWQRjNLNoA*|WrAbm^>0_`dP-~NB||GlGmTPlPVsR5QcVe|jP
z|JVQDm?lmLhO!bEWPlRK%z#F`{QufEr#uS6vdd=BVc=pos0+0A)&Eysh3O#>mUjVz
xE(15n8Db2!43P{$3<eBJ40d3aE&~rp0s#4Hc?k2!<jDX4002ovPDHLkV1m-aYbO8z
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4327a1a4570208a46553b392fc840c649cfbbbc3
GIT binary patch
literal 481
zc$@*@0UrK|P)<h;3K|Lk000e1NJLTq000^Q000sQ1ONa4?+^wk0004~Nkl<ZcmZvZ
z&8reo9EM-|m`T(qOmUHuq)22fEH_pdwQ3VqBLyykySe|vrGk(a5fagVQxO$q8aNuE
z(MTB<>5OBIAEnFF&nX$tVSE;Hp7%VWBOoCR1#t%jAv$|G;T4=~Y`JFe3P&J$IU&NS
zuXP$14omYLT?u(OiI1|ox7!^yV^d<r7T;gsIC7%nGZ}Fdq<hYV2{C1VhIO2S$~&XR
zXA&J%f93NxE<1}GuJg{4DES|BdNkX_gt8jW<0kKnw@|Rx&3s7V`oa5pkLF*5&3=D*
zPFaa}{ild+W-<~eMjzAoa|ON6d(V&&UH=IV0GnA_*`aCtAG`Y!hdo?mGxBA!R+k3L
zVO(vYh?wZ0BJ3t|?xdyBQyMu~O&xb|>BJeq-Nr@t>&dBOR)bAwGKtZ99d~gpdSRpq
z?lP41W0%6oag*6J8-)2mM;U4$Z?yzhaQ`VOk}zxT-1tayF_1Uvg1dtFI+6Ds9lWO5
zWJALE)CDDb@;}~ij!S^xeDzzbf1g-w@1hy$sU|os0rulGwot}HJVFJxaFymmqT`dl
Xk4DNQT~lE;00000NkvXXu0mjfX1>!~
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -92,17 +92,19 @@ toolkit.jar:
   skin/classic/global/dirListing/remote.png                          (dirListing/remote.png)
   skin/classic/global/dirListing/up.png                              (dirListing/up.png)
   skin/classic/global/icons/autocomplete-dropmarker.png              (icons/autocomplete-dropmarker.png)
   skin/classic/global/icons/autocomplete-search.svg                  (icons/autocomplete-search.svg)
   skin/classic/global/icons/autoscroll.png                           (icons/autoscroll.png)
   skin/classic/global/icons/blacklist_favicon.png                    (icons/blacklist_favicon.png)
   skin/classic/global/icons/blacklist_64.png                         (icons/blacklist_64.png)
   skin/classic/global/icons/chevron.png                              (icons/chevron.png)
+  skin/classic/global/icons/chevron-inverted.png                     (icons/chevron-inverted.png)
   skin/classic/global/icons/chevron@2x.png                           (icons/chevron@2x.png)
+  skin/classic/global/icons/chevron-inverted@2x.png                  (icons/chevron-inverted@2x.png)
   skin/classic/global/icons/checkbox.png                             (icons/checkbox.png)
   skin/classic/global/icons/checkbox@2x.png                          (icons/checkbox@2x.png)
   skin/classic/global/icons/close.png                                (icons/close.png)
   skin/classic/global/icons/close@2x.png                             (icons/close@2x.png)
   skin/classic/global/icons/find-arrows.png                          (icons/find-arrows.png)
   skin/classic/global/icons/glyph-dropdown.png                       (icons/glyph-dropdown.png)
   skin/classic/global/icons/glyph-dropdown@2x.png                    (icons/glyph-dropdown@2x.png)
   skin/classic/global/icons/information-16.png                       (icons/information-16.png)
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -188,16 +188,17 @@ toolkit.jar:
         skin/classic/global/radio/radio-check.gif                (radio/radio-check.gif)
         skin/classic/global/radio/radio-check-dis.gif            (radio/radio-check-dis.gif)
         skin/classic/global/scrollbar/slider.gif                 (scrollbar/slider.gif)
         skin/classic/global/splitter/grip-bottom.gif             (splitter/grip-bottom.gif)
         skin/classic/global/splitter/grip-top.gif                (splitter/grip-top.gif)
         skin/classic/global/splitter/grip-left.gif               (splitter/grip-left.gif)
         skin/classic/global/splitter/grip-right.gif              (splitter/grip-right.gif)
         skin/classic/global/toolbar/chevron.gif                  (toolbar/chevron.gif)
+        skin/classic/global/toolbar/chevron-inverted.png         (toolbar/chevron-inverted.png)
         skin/classic/global/toolbar/spring.png                   (toolbar/spring.png)
         skin/classic/global/tree/columnpicker.gif                (tree/columnpicker.gif)
         skin/classic/global/tree/sort-asc.png                    (tree/sort-asc.png)
         skin/classic/global/tree/sort-dsc.png                    (tree/sort-dsc.png)
         skin/classic/global/tree/sort-asc-classic.png            (tree/sort-asc-classic.png)
         skin/classic/global/tree/sort-dsc-classic.png            (tree/sort-dsc-classic.png)
         skin/classic/global/tree/twisty-clsd.png                 (tree/twisty-clsd.png)
         skin/classic/global/tree/twisty-open.png                 (tree/twisty-open.png)
@@ -384,16 +385,17 @@ toolkit.jar:
         skin/classic/aero/global/radio/radio-check.gif                   (radio/radio-check.gif)
         skin/classic/aero/global/radio/radio-check-dis.gif               (radio/radio-check-dis.gif)
         skin/classic/aero/global/scrollbar/slider.gif                    (scrollbar/slider.gif)
         skin/classic/aero/global/splitter/grip-bottom.gif                (splitter/grip-bottom.gif)
         skin/classic/aero/global/splitter/grip-top.gif                   (splitter/grip-top.gif)
         skin/classic/aero/global/splitter/grip-left.gif                  (splitter/grip-left.gif)
         skin/classic/aero/global/splitter/grip-right.gif                 (splitter/grip-right.gif)
         skin/classic/aero/global/toolbar/chevron.gif                     (toolbar/chevron.gif)
+        skin/classic/aero/global/toolbar/chevron-inverted.png            (toolbar/chevron-inverted.png)
         skin/classic/aero/global/toolbar/spring.png                      (toolbar/spring-aero.png)
         skin/classic/aero/global/tree/columnpicker.gif                   (tree/columnpicker.gif)
         skin/classic/aero/global/tree/sort-asc.png                       (tree/sort-asc-aero.png)
         skin/classic/aero/global/tree/sort-dsc.png                       (tree/sort-dsc-aero.png)
         skin/classic/aero/global/tree/sort-asc-classic.png               (tree/sort-asc-classic.png)
         skin/classic/aero/global/tree/sort-dsc-classic.png               (tree/sort-dsc-classic.png)
         skin/classic/aero/global/tree/twisty-clsd.png                    (tree/twisty-clsd-aero.png)
         skin/classic/aero/global/tree/twisty-clsd-rtl.png                (tree/twisty-clsd-rtl-aero.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..861e41a8121442012833210ea9eea68effb57813
GIT binary patch
literal 85
zc%17D@N?(olHy`uVBq!ia0vp^96-#<!3HENCaw(yQZk+{jv*Y^lYjjBd)-Ij-+2bs
j7d2~|mS`)*3vn=TYkIysq$7C}sE)zY)z4*}Q$iB}S#1}r
--- a/tools/profiler/EHABIStackWalk.cpp
+++ b/tools/profiler/EHABIStackWalk.cpp
@@ -37,20 +37,47 @@
 #include <stdint.h>
 #include <vector>
 #include <string>
 
 #ifndef PT_ARM_EXIDX
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
+// Bug 1082817: ICS B2G has a buggy linker that doesn't always ensure
+// that the EXIDX is sorted by address, as the spec requires.  So in
+// that case we build and sort an array of pointers into the index,
+// and binary-search that; otherwise, we search the index in place
+// (avoiding the time and space overhead of the indirection).
+#if defined(ANDROID_VERSION) && ANDROID_VERSION < 16
+#define HAVE_UNSORTED_EXIDX
+#endif
 
 namespace mozilla {
 
-struct EHEntry;
+struct PRel31 {
+  uint32_t mBits;
+  bool topBit() const { return mBits & 0x80000000; }
+  uint32_t value() const { return mBits & 0x7fffffff; }
+  int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
+  const void *compute() const {
+    return reinterpret_cast<const char *>(this) + offset();
+  }
+private:
+  PRel31(const PRel31 &copied) MOZ_DELETE;
+  PRel31() MOZ_DELETE;
+};
+
+struct EHEntry {
+  PRel31 startPC;
+  PRel31 exidx;
+private:
+  EHEntry(const EHEntry &copied) MOZ_DELETE;
+  EHEntry() MOZ_DELETE;
+};
 
 class EHState {
   // Note that any core register can be used as a "frame pointer" to
   // influence the unwinding process, so this must track all of them.
   uint32_t mRegs[16];
 public:
   bool unwind(const EHEntry *aEntry, const void *stackBase);
   uint32_t &operator[](int i) { return mRegs[i]; }
@@ -59,36 +86,56 @@ public:
 };
 
 enum {
   R_SP = 13,
   R_LR = 14,
   R_PC = 15
 };
 
+#ifdef HAVE_UNSORTED_EXIDX
 class EHEntryHandle {
   const EHEntry *mValue;
 public:
   EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { }
   const EHEntry *value() const { return mValue; }
 };
 
+bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
+  return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
+}
+#endif
+
 class EHTable {
   uint32_t mStartPC;
   uint32_t mEndPC;
   uint32_t mLoadOffset;
+#ifdef HAVE_UNSORTED_EXIDX
   // In principle we should be able to binary-search the index section in
   // place, but the ICS toolchain's linker is noncompliant and produces
   // indices that aren't entirely sorted (e.g., libc).  So we have this:
   std::vector<EHEntryHandle> mEntries;
+  typedef std::vector<EHEntryHandle>::const_iterator EntryIterator;
+  EntryIterator entriesBegin() const { return mEntries.begin(); }
+  EntryIterator entriesEnd() const { return mEntries.end(); }
+  static const EHEntry* entryGet(EntryIterator aEntry) {
+    return aEntry->value();
+  }
+#else
+  typedef const EHEntry *EntryIterator;
+  EntryIterator mEntriesBegin, mEntriesEnd;
+  EntryIterator entriesBegin() const { return mEntriesBegin; }
+  EntryIterator entriesEnd() const { return mEntriesEnd; }
+  static const EHEntry* entryGet(EntryIterator aEntry) { return aEntry; }
+#endif
   std::string mName;
 public:
   EHTable(const void *aELF, size_t aSize, const std::string &aName);
   const EHEntry *lookup(uint32_t aPC) const;
-  bool isValid() const { return mEntries.size() > 0; }
+  bool isValid() const { return entriesEnd() != entriesBegin(); }
   const std::string &name() const { return mName; }
   uint32_t startPC() const { return mStartPC; }
   uint32_t endPC() const { return mEndPC; }
   uint32_t loadOffset() const { return mLoadOffset; }
 };
 
 class EHAddrSpace {
   std::vector<uint32_t> mStarts;
@@ -134,38 +181,16 @@ size_t EHABIStackWalk(const mcontext_t &
     if (!state.unwind(entry, stackBase))
       break;
   }
   
   return count;
 }
 
 
-struct PRel31 {
-  uint32_t mBits;
-  bool topBit() const { return mBits & 0x80000000; }
-  uint32_t value() const { return mBits & 0x7fffffff; }
-  int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
-  const void *compute() const {
-    return reinterpret_cast<const char *>(this) + offset();
-  }
-private:
-  PRel31(const PRel31 &copied) MOZ_DELETE;
-  PRel31() MOZ_DELETE;
-};
-
-struct EHEntry {
-  PRel31 startPC;
-  PRel31 exidx;
-private:
-  EHEntry(const EHEntry &copied) MOZ_DELETE;
-  EHEntry() MOZ_DELETE;
-};
-
-
 class EHInterp {
 public:
   // Note that stackLimit is exclusive and stackBase is inclusive
   // (i.e, stackLimit < SP <= stackBase), following the convention
   // set by the AAPCS spec.
   EHInterp(EHState &aState, const EHEntry *aEntry,
            uint32_t aStackLimit, uint32_t aStackBase)
     : mState(aState),
@@ -472,54 +497,60 @@ const EHTable *EHAddrSpace::lookup(uint3
                  - mStarts.begin()) - 1;
 
   if (i < 0 || aPC >= mTables[i].endPC())
     return 0;
   return &mTables[i];
 }
 
 
-bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
-  return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
-}
-
 const EHEntry *EHTable::lookup(uint32_t aPC) const {
   MOZ_ASSERT(aPC >= mStartPC);
   if (aPC >= mEndPC)
     return nullptr;
 
-  std::vector<EHEntryHandle>::const_iterator begin = mEntries.begin();
-  std::vector<EHEntryHandle>::const_iterator end = mEntries.end();
+  EntryIterator begin = entriesBegin();
+  EntryIterator end = entriesEnd();
   MOZ_ASSERT(begin < end);
-  if (aPC < reinterpret_cast<uint32_t>(begin->value()->startPC.compute()))
+  if (aPC < reinterpret_cast<uint32_t>(entryGet(begin)->startPC.compute()))
     return nullptr;
 
   while (end - begin > 1) {
-    std::vector<EHEntryHandle>::const_iterator mid = begin + (end - begin) / 2;
-    if (aPC < reinterpret_cast<uint32_t>(mid->value()->startPC.compute()))
+#ifdef EHABI_UNWIND_MORE_ASSERTS
+    if (entryGet(end - 1)->startPC.compute()
+        < entryGet(begin)->startPC.compute()) {
+      MOZ_CRASH("unsorted exidx");
+    }
+#endif
+    EntryIterator mid = begin + (end - begin) / 2;
+    if (aPC < reinterpret_cast<uint32_t>(entryGet(mid)->startPC.compute()))
       end = mid;
     else
       begin = mid;
   }
-  return begin->value();
+  return entryGet(begin);
 }
 
 
 #if MOZ_LITTLE_ENDIAN
 static const unsigned char hostEndian = ELFDATA2LSB;
 #elif MOZ_BIG_ENDIAN
 static const unsigned char hostEndian = ELFDATA2MSB;
 #else
 #error "No endian?"
 #endif
 
-// Async signal unsafe.  (Note use of std::vector::reserve.)
+// Async signal unsafe: std::vector::reserve, std::string copy ctor.
 EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
   : mStartPC(~0), // largest uint32_t
     mEndPC(0),
+#ifndef HAVE_UNSORTED_EXIDX
+    mEntriesBegin(nullptr),
+    mEntriesEnd(nullptr),
+#endif
     mName(aName)
 {
   const uint32_t base = reinterpret_cast<uint32_t>(aELF);
 
   if (aSize < sizeof(Elf32_Ehdr))
     return;
 
   const Elf32_Ehdr &file = *(reinterpret_cast<Elf32_Ehdr *>(base));
@@ -563,20 +594,25 @@ EHTable::EHTable(const void *aELF, size_
   mEndPC += mLoadOffset;
 
   // Create a sorted index of the index to work around linker bugs.
   const EHEntry *startTable =
     reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr);
   const EHEntry *endTable =
     reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr
                                     + exidxHdr->p_memsz);
+#ifdef HAVE_UNSORTED_EXIDX
   mEntries.reserve(endTable - startTable);
   for (const EHEntry *i = startTable; i < endTable; ++i)
     mEntries.push_back(i);
   std::sort(mEntries.begin(), mEntries.end());
+#else
+  mEntriesBegin = startTable;
+  mEntriesEnd = endTable;
+#endif
 }
 
 
 mozilla::Atomic<const EHAddrSpace*> EHAddrSpace::sCurrent(nullptr);
 
 // Async signal safe; can fail if Update() hasn't returned yet.
 const EHAddrSpace *EHAddrSpace::Get() {
   return sCurrent;
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -6,16 +6,46 @@
 #include "GeneratedJNIWrappers.h"
 #include "AndroidBridgeUtilities.h"
 #include "nsXPCOMStrings.h"
 #include "AndroidBridge.h"
 
 namespace mozilla {
 namespace widget {
 namespace android {
+jclass DownloadsIntegration::mDownloadsIntegrationClass = 0;
+jmethodID DownloadsIntegration::jScanMedia = 0;
+void DownloadsIntegration::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mDownloadsIntegrationClass = getClassGlobalRef("org/mozilla/gecko/DownloadsIntegration");
+    jScanMedia = getStaticMethod("scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V");
+}
+
+DownloadsIntegration* DownloadsIntegration::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    DownloadsIntegration* ret = new DownloadsIntegration(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+void DownloadsIntegration::ScanMedia(const nsAString& a0, const nsAString& a1) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+    jstring j1 = AndroidBridge::NewJavaString(env, a1);
+
+    env->CallStaticVoidMethod(mDownloadsIntegrationClass, jScanMedia, j0, j1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
 jclass GeckoAppShell::mGeckoAppShellClass = 0;
 jmethodID GeckoAppShell::jAcknowledgeEvent = 0;
 jmethodID GeckoAppShell::jAddPluginViewWrapper = 0;
 jmethodID GeckoAppShell::jAlertsProgressListener_OnProgress = 0;
 jmethodID GeckoAppShell::jCancelVibrate = 0;
 jmethodID GeckoAppShell::jCheckURIVisited = 0;
 jmethodID GeckoAppShell::jClearMessageList = 0;
 jmethodID GeckoAppShell::jCloseCamera = 0;
@@ -75,17 +105,16 @@ jmethodID GeckoAppShell::jNotifyIMEConte
 jmethodID GeckoAppShell::jNotifyWakeLockChanged = 0;
 jmethodID GeckoAppShell::jNotifyXreExit = 0;
 jmethodID GeckoAppShell::jOpenUriExternal = 0;
 jmethodID GeckoAppShell::jPerformHapticFeedback = 0;
 jmethodID GeckoAppShell::jPumpMessageLoop = 0;
 jmethodID GeckoAppShell::jRegisterSurfaceTextureFrameListener = 0;
 jmethodID GeckoAppShell::jRemovePluginView = 0;
 jmethodID GeckoAppShell::jRequestUiThreadCallback = 0;
-jmethodID GeckoAppShell::jScanMedia = 0;
 jmethodID GeckoAppShell::jScheduleRestart = 0;
 jmethodID GeckoAppShell::jSendMessageWrapper = 0;
 jmethodID GeckoAppShell::jSetFullScreen = 0;
 jmethodID GeckoAppShell::jSetKeepScreenOn = 0;
 jmethodID GeckoAppShell::jSetURITitle = 0;
 jmethodID GeckoAppShell::jShowAlertNotificationWrapper = 0;
 jmethodID GeckoAppShell::jShowInputMethodPicker = 0;
 jmethodID GeckoAppShell::jStartMonitoringGamepad = 0;
@@ -162,17 +191,16 @@ void GeckoAppShell::InitStubs(JNIEnv *jE
     jNotifyWakeLockChanged = getStaticMethod("notifyWakeLockChanged", "(Ljava/lang/String;Ljava/lang/String;)V");
     jNotifyXreExit = getStaticMethod("onXreExit", "()V");
     jOpenUriExternal = getStaticMethod("openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
     jPerformHapticFeedback = getStaticMethod("performHapticFeedback", "(Z)V");
     jPumpMessageLoop = getStaticMethod("pumpMessageLoop", "()Z");
     jRegisterSurfaceTextureFrameListener = getStaticMethod("registerSurfaceTextureFrameListener", "(Ljava/lang/Object;I)V");
     jRemovePluginView = getStaticMethod("removePluginView", "(Landroid/view/View;Z)V");
     jRequestUiThreadCallback = getStaticMethod("requestUiThreadCallback", "(J)V");
-    jScanMedia = getStaticMethod("scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V");
     jScheduleRestart = getStaticMethod("scheduleRestart", "()V");
     jSendMessageWrapper = getStaticMethod("sendMessage", "(Ljava/lang/String;Ljava/lang/String;I)V");
     jSetFullScreen = getStaticMethod("setFullScreen", "(Z)V");
     jSetKeepScreenOn = getStaticMethod("setKeepScreenOn", "(Z)V");
     jSetURITitle = getStaticMethod("setUriTitle", "(Ljava/lang/String;Ljava/lang/String;)V");
     jShowAlertNotificationWrapper = getStaticMethod("showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     jShowInputMethodPicker = getStaticMethod("showInputMethodPicker", "()V");
     jStartMonitoringGamepad = getStaticMethod("startMonitoringGamepad", "()V");
@@ -1118,31 +1146,16 @@ void GeckoAppShell::RequestUiThreadCallb
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jRequestUiThreadCallback, a0);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
-void GeckoAppShell::ScanMedia(const nsAString& a0, const nsAString& a1) {
-    JNIEnv *env = AndroidBridge::GetJNIEnv();
-    if (env->PushLocalFrame(2) != 0) {
-        AndroidBridge::HandleUncaughtException(env);
-        MOZ_CRASH("Exception should have caused crash.");
-    }
-
-    jstring j0 = AndroidBridge::NewJavaString(env, a0);
-    jstring j1 = AndroidBridge::NewJavaString(env, a1);
-
-    env->CallStaticVoidMethod(mGeckoAppShellClass, jScanMedia, j0, j1);
-    AndroidBridge::HandleUncaughtException(env);
-    env->PopLocalFrame(nullptr);
-}
-
 void GeckoAppShell::ScheduleRestart() {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(0) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jScheduleRestart);
@@ -2473,16 +2486,17 @@ void Clipboard::SetClipboardText(const n
     jstring j0 = AndroidBridge::NewJavaString(env, a0);
 
     env->CallStaticVoidMethod(mClipboardClass, jSetClipboardText, j0);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
 void InitStubs(JNIEnv *jEnv) {
+    DownloadsIntegration::InitStubs(jEnv);
     GeckoAppShell::InitStubs(jEnv);
     GeckoJavaSampler::InitStubs(jEnv);
     RestrictedProfiles::InitStubs(jEnv);
     SurfaceBits::InitStubs(jEnv);
     ThumbnailHelper::InitStubs(jEnv);
     DisplayPortMetrics::InitStubs(jEnv);
     GLController::InitStubs(jEnv);
     GeckoLayerClient::InitStubs(jEnv);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -9,16 +9,28 @@
 #include "nsXPCOMStrings.h"
 #include "AndroidJavaWrappers.h"
 
 namespace mozilla {
 namespace widget {
 namespace android {
 void InitStubs(JNIEnv *jEnv);
 
+class DownloadsIntegration : public AutoGlobalWrappedJavaObject {
+public:
+    static void InitStubs(JNIEnv *jEnv);
+    static DownloadsIntegration* Wrap(jobject obj);
+    DownloadsIntegration(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
+    static void ScanMedia(const nsAString& a0, const nsAString& a1);
+    DownloadsIntegration() : AutoGlobalWrappedJavaObject() {};
+protected:
+    static jclass mDownloadsIntegrationClass;
+    static jmethodID jScanMedia;
+};
+
 class GeckoAppShell : public AutoGlobalWrappedJavaObject {
 public:
     static void InitStubs(JNIEnv *jEnv);
     static GeckoAppShell* Wrap(jobject obj);
     GeckoAppShell(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
     static void AcknowledgeEvent();
     static void AddPluginViewWrapper(jobject a0, jfloat a1, jfloat a2, jfloat a3, jfloat a4, bool a5);
     static void AlertsProgressListener_OnProgress(const nsAString& a0, int64_t a1, int64_t a2, const nsAString& a3);
@@ -82,17 +94,16 @@ public:
     static void NotifyWakeLockChanged(const nsAString& a0, const nsAString& a1);
     static void NotifyXreExit();
     static bool OpenUriExternal(const nsAString& a0, const nsAString& a1, const nsAString& a2 = EmptyString(), const nsAString& a3 = EmptyString(), const nsAString& a4 = EmptyString(), const nsAString& a5 = EmptyString());
     static void PerformHapticFeedback(bool a0);
     static bool PumpMessageLoop();
     static void RegisterSurfaceTextureFrameListener(jobject a0, int32_t a1);
     static void RemovePluginView(jobject a0, bool a1);
     static void RequestUiThreadCallback(int64_t a0);
-    static void ScanMedia(const nsAString& a0, const nsAString& a1);
     static void ScheduleRestart();
     static void SendMessageWrapper(const nsAString& a0, const nsAString& a1, int32_t a2);
     static void SetFullScreen(bool a0);
     static void SetKeepScreenOn(bool a0);
     static void SetURITitle(const nsAString& a0, const nsAString& a1);
     static void ShowAlertNotificationWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3, const nsAString& a4);
     static void ShowInputMethodPicker();
     static void StartMonitoringGamepad();
@@ -168,17 +179,16 @@ protected:
     static jmethodID jNotifyWakeLockChanged;
     static jmethodID jNotifyXreExit;
     static jmethodID jOpenUriExternal;
     static jmethodID jPerformHapticFeedback;
     static jmethodID jPumpMessageLoop;
     static jmethodID jRegisterSurfaceTextureFrameListener;
     static jmethodID jRemovePluginView;
     static jmethodID jRequestUiThreadCallback;
-    static jmethodID jScanMedia;
     static jmethodID jScheduleRestart;
     static jmethodID jSendMessageWrapper;
     static jmethodID jSetFullScreen;
     static jmethodID jSetKeepScreenOn;
     static jmethodID jSetURITitle;
     static jmethodID jShowAlertNotificationWrapper;
     static jmethodID jShowInputMethodPicker;
     static jmethodID jStartMonitoringGamepad;
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1906,23 +1906,16 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
             }
 
             selEvent.mOffset = std::min(start, end);
             selEvent.mLength = std::max(start, end) - selEvent.mOffset;
             selEvent.mReversed = start > end;
             selEvent.mExpandToClusterBoundary = false;
 
             DispatchEvent(&selEvent);
-
-            // Notify SelectionHandler of final caret position
-            // Required after IME hide via 'Back' button
-            AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
-                NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
-                NS_LITERAL_CSTRING(""));
-            nsAppShell::gAppShell->PostEvent(broadcastEvent);
         }
         break;
     case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
         {
             TextRange range;
             range.mStartOffset = ae->Start();
             range.mEndOffset = ae->End();
             range.mRangeType = ae->RangeType();
@@ -2006,24 +1999,16 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
 #ifdef DEBUG_ANDROID_IME
             const NS_ConvertUTF16toUTF8 data(event.mData);
             const char* text = data.get();
             ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u",
                     text, event.mData.Length(), event.mRanges->Length());
 #endif // DEBUG_ANDROID_IME
 
             DispatchEvent(&event);
-
-            // Notify SelectionHandler of final caret position
-            // Required in cases of keyboards providing autoCorrections
-            AndroidGeckoEvent* broadcastEvent =
-                AndroidGeckoEvent::MakeBroadcastEvent(
-                    NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
-                    NS_LITERAL_CSTRING(""));
-            nsAppShell::gAppShell->PostEvent(broadcastEvent);
         }
         break;
 
     case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
         {
             /*
              *  Remove any previous composition.  This is only used for
              *    visual indication and does not affect the text content.