Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 15 Jul 2014 16:17:15 +0200
changeset 216090 8a29d7e989007132f4350455b1051c4fbf1fb737
parent 216089 d6e0d60ed14af100e95a52c7cb77f20c290a1a56 (current diff)
parent 216015 835e22069c1a2bf00c4d9a57bac4299d185d8acc (diff)
child 216091 06a47123670eed7a7f7b74868db45a6f9e5fa838
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/devtools/shared/test/browser_graphs-11.js
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,18 +14,18 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <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="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,18 +12,18 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,19 +10,19 @@
   <!--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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <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="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,18 +14,18 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <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="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,18 +12,18 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "4a5a2ee1fd7ef549f4289bec42046a116b3251c0", 
+    "revision": "623b1441cb54a3105b8212589aaf2aa77d537aef", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,18 +12,18 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,18 +10,18 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <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"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="575fdbf046e966a5915b1f1e800e5d6ad0ea14c0"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,18 +12,18 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,18 +12,18 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
   <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="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
   <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="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -371,17 +371,17 @@
                 label="&viewPageInfoCmd.label;"
                 accesskey="&viewPageInfoCmd.accesskey;"
                 oncommand="gContextMenu.viewInfo();"/>
       <menuseparator id="spell-separator"/>
       <menuitem id="spell-check-enabled"
                 label="&spellCheckToggle.label;"
                 type="checkbox"
                 accesskey="&spellCheckToggle.accesskey;"
-                oncommand="InlineSpellCheckerUI.toggleEnabled();"/>
+                oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
       <menuitem id="spell-add-dictionaries-main"
                 label="&spellAddDictionaries.label;"
                 accesskey="&spellAddDictionaries.accesskey;"
                 oncommand="gContextMenu.addDictionaries();"/>
       <menu id="spell-dictionaries"
             label="&spellDictionaries.label;"
             accesskey="&spellDictionaries.accesskey;">
           <menupopup id="spell-dictionaries-menu">
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -21,17 +21,18 @@ support-files =
 [browser_graphs-04.js]
 [browser_graphs-05.js]
 [browser_graphs-06.js]
 [browser_graphs-07.js]
 [browser_graphs-08.js]
 [browser_graphs-09.js]
 [browser_graphs-10a.js]
 [browser_graphs-10b.js]
-[browser_graphs-11.js]
+[browser_graphs-11a.js]
+[browser_graphs-11b.js]
 [browser_graphs-12.js]
 [browser_graphs-13.js]
 [browser_graphs-14.js]
 [browser_layoutHelpers.js]
 [browser_layoutHelpers-getBoxQuads.js]
 [browser_num-l10n.js]
 [browser_observableobject.js]
 [browser_outputparser.js]
--- a/browser/devtools/shared/test/browser_graphs-02.js
+++ b/browser/devtools/shared/test/browser_graphs-02.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// Tests that graph widgets can properly add data and regions.
+// Tests that graph widgets can properly add data, regions and highlights.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
 let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
 let {DOMHelpers} = Cu.import("resource:///modules/devtools/DOMHelpers.jsm", {});
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 let {Hosts} = devtools.require("devtools/framework/toolbox-hosts");
 
@@ -17,23 +17,24 @@ let test = Task.async(function*() {
   finish();
 });
 
 function* performTest() {
   let [host, win, doc] = yield createHost();
   let graph = new LineGraphWidget(doc.body, "fps");
   yield graph.once("ready");
 
-  testGraph(graph);
+  testDataAndRegions(graph);
+  testHighlights(graph);
 
   graph.destroy();
   host.destroy();
 }
 
-function testGraph(graph) {
+function testDataAndRegions(graph) {
   let thrown1;
   try {
     graph.setRegions(TEST_REGIONS);
   } catch (e) {
     thrown1 = true;
   }
   ok(thrown1, "Setting regions before setting data shouldn't work.");
 
@@ -47,25 +48,39 @@ function testGraph(graph) {
     thrown2 = true;
   }
   ok(thrown2, "Setting regions twice shouldn't work.");
 
   ok(graph.hasData(), "The graph should now have the data source set.");
   ok(graph.hasRegions(), "The graph should now have the regions set.");
 
   is(graph.dataScaleX,
-     graph.width / (4180 - 112), // last & first tick in TEST_DATA
+     graph.width / 4180, // last & first tick in TEST_DATA
     "The data scale on the X axis is correct.");
 
   is(graph.dataScaleY,
      graph.height / 60 * 0.85, // max value in TEST_DATA * GRAPH_DAMPEN_VALUES
     "The data scale on the Y axis is correct.");
 
   for (let i = 0; i < TEST_REGIONS.length; i++) {
     let original = TEST_REGIONS[i];
     let normalized = graph._regions[i];
 
     is(original.start * graph.dataScaleX, normalized.start,
       "The region's start value was properly normalized.");
     is(original.end * graph.dataScaleX, normalized.end,
       "The region's end value was properly normalized.");
   }
 }
+
+function testHighlights(graph) {
+  graph.setMask(TEST_REGIONS);
+  ok(graph.hasMask(),
+    "The graph should now have the highlights set.");
+
+  graph.setMask([]);
+  ok(graph.hasMask(),
+    "The graph shouldn't have anything highlighted.");
+
+  graph.setMask(null);
+  ok(!graph.hasMask(),
+    "The graph should have everything highlighted.");
+}
rename from browser/devtools/shared/test/browser_graphs-11.js
rename to browser/devtools/shared/test/browser_graphs-11a.js
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/browser_graphs-11b.js
@@ -0,0 +1,132 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that bar graph's legend items handle mouseover/mouseout.
+
+let {BarGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
+let {DOMHelpers} = Cu.import("resource:///modules/devtools/DOMHelpers.jsm", {});
+let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
+let {Hosts} = devtools.require("devtools/framework/toolbox-hosts");
+
+const CATEGORIES = [
+  { color: "#46afe3", label: "Foo" },
+  { color: "#eb5368", label: "Bar" },
+  { color: "#70bf53", label: "Baz" }
+];
+
+let test = Task.async(function*() {
+  yield promiseTab("about:blank");
+  yield performTest();
+  gBrowser.removeCurrentTab();
+  finish();
+});
+
+function* performTest() {
+  let [host, win, doc] = yield createHost();
+  doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
+
+  let graph = new BarGraphWidget(doc.body, 1);
+  graph.fixedWidth = 200;
+  graph.fixedHeight = 100;
+
+  yield graph.once("ready");
+  yield testGraph(graph);
+
+  graph.destroy();
+  host.destroy();
+}
+
+function* testGraph(graph) {
+  graph.format = CATEGORIES;
+  graph.dataOffsetX = 1000;
+  graph.setData([{
+    delta: 1100, values: [0, 2, 3]
+  }, {
+    delta: 1200, values: [1, 0, 2]
+  }, {
+    delta: 1300, values: [2, 1, 0]
+  }, {
+    delta: 1400, values: [0, 3, 1]
+  }, {
+    delta: 1500, values: [3, 0, 2]
+  }, {
+    delta: 1600, values: [3, 2, 0]
+  }]);
+
+  is(graph._blocksBoundingRects.toSource(), "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
+    "The correct blocks bounding rects were calculated for the bar graph.");
+
+  let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
+  is(legendItems.length, 3,
+    "Three legend items should exist in the entire graph.");
+
+  yield testLegend(graph, 0, {
+    highlights: "[{type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}]",
+    selection: "({start:34.33333333333333, end:200})",
+    leftmost: "({type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100})",
+    rightmost: "({type:0, start:167.66666666666666, end:200, top:55, bottom:100})"
+  });
+  yield testLegend(graph, 1, {
+    highlights: "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
+    selection: "({start:0, end:200})",
+    leftmost: "({type:1, start:0, end:33.33333333333333, top:70, bottom:100})",
+    rightmost: "({type:1, start:167.66666666666666, end:200, top:24, bottom:54})"
+  });
+  yield testLegend(graph, 2, {
+    highlights: "[{type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}]",
+    selection: "({start:0, end:166.66666666666666})",
+    leftmost: "({type:2, start:0, end:33.33333333333333, top:24, bottom:69})",
+    rightmost: "({type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54})"
+  });
+}
+
+function* testLegend(graph, index, { highlights, selection, leftmost, rightmost }) {
+  // Hover.
+
+  let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
+  let colorBlock = legendItems[index].querySelector("[view=color]");
+
+  let debounced = graph.once("legend-hover");
+  graph._onLegendMouseOver({ target: colorBlock });
+  ok(!graph.hasMask(), "The graph shouldn't get highlights immediately.");
+
+  let [type, rects] = yield debounced;
+  ok(graph.hasMask(), "The graph should now have highlights.");
+
+  is(type, index,
+    "The legend item was correctly hovered.");
+  is(rects.toSource(), highlights,
+    "The legend item highlighted the correct regions.");
+
+  // Unhover.
+
+  let unhovered = graph.once("legend-unhover");
+  graph._onLegendMouseOut();
+  ok(!graph.hasMask(), "The graph shouldn't have highlights anymore.");
+
+  yield unhovered;
+  ok(true, "The 'legend-mouseout' event was emitted.");
+
+  // Select.
+
+  let selected = graph.once("legend-selection");
+  graph._onLegendMouseDown(mockEvent(colorBlock));
+  ok(graph.hasSelection(), "The graph should now have a selection.");
+  is(graph.getSelection().toSource(), selection, "The graph has a correct selection.");
+
+  let [left, right] = yield selected;
+  is(left.toSource(), leftmost, "The correct leftmost data block was found.");
+  is(right.toSource(), rightmost, "The correct rightmost data block was found.");
+
+  // Deselect.
+
+  graph.dropSelection();
+}
+
+function mockEvent(node) {
+  return {
+    target: node,
+    preventDefault: () => {},
+    stopPropagation: () => {}
+  };
+}
--- a/browser/devtools/shared/widgets/Graphs.jsm
+++ b/browser/devtools/shared/widgets/Graphs.jsm
@@ -71,16 +71,21 @@ const BAR_GRAPH_BACKGROUND_GRADIENT_END 
 
 const BAR_GRAPH_CLIPHEAD_LINE_COLOR = "#666";
 const BAR_GRAPH_SELECTION_LINE_COLOR = "#555";
 const BAR_GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(0,136,204,0.25)";
 const BAR_GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
 const BAR_GRAPH_REGION_BACKGROUND_COLOR = "transparent";
 const BAR_GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
 
+const BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
+const BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";
+
+const BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50; // ms
+
 /**
  * Small data primitives for all graphs.
  */
 this.GraphCursor = function() {}
 this.GraphSelection = function() {}
 this.GraphSelectionDragger = function() {}
 this.GraphSelectionResizer = function() {}
 
@@ -227,21 +232,27 @@ AbstractCanvasGraph.prototype = {
 
     let ownerWindow = this._parent.ownerDocument.defaultView;
     ownerWindow.removeEventListener("resize", this._onResize);
 
     this._window.cancelAnimationFrame(this._animationId);
     this._iframe.remove();
 
     this._data = null;
+    this._mask = null;
+    this._maskArgs = null;
     this._regions = null;
+
     this._cachedBackgroundImage = null;
     this._cachedGraphImage = null;
+    this._cachedMaskImage = null;
     this._renderTargets.clear();
     gCachedStripePattern.clear();
+
+    this.emit("destroyed");
   },
 
   /**
    * Rendering options. Subclasses should override these.
    */
   clipheadLineWidth: 1,
   clipheadLineColor: "transparent",
   selectionLineWidth: 1,
@@ -271,16 +282,25 @@ AbstractCanvasGraph.prototype = {
    * in `setData`. The graph image is not rebuilt on each frame, but
    * only when the data source changes.
    */
   buildGraphImage: function() {
     throw "This method needs to be implemented by inheriting classes.";
   },
 
   /**
+   * Optionally builds and caches a mask image for this graph, composited
+   * over the data image created via `buildGraphImage`. Inheriting classes
+   * may override this method.
+   */
+  buildMaskImage: function() {
+    return null;
+  },
+
+  /**
    * When setting the data source, the coordinates and values may be
    * stretched or squeezed on the X/Y axis, to fit into the available space.
    */
   dataScaleX: 1,
   dataScaleY: 1,
 
   /**
    * Sets the data source for this graph.
@@ -304,16 +324,29 @@ AbstractCanvasGraph.prototype = {
    *         A promise resolved once the data is set.
    */
   setDataWhenReady: Task.async(function*(data) {
     yield this.ready();
     this.setData(data);
   }),
 
   /**
+   * Adds a mask to this graph.
+   *
+   * @param any mask, options
+   *        See `buildMaskImage` in inheriting classes for the required args.
+   */
+  setMask: function(mask, ...options) {
+    this._mask = mask;
+    this._maskArgs = [mask, ...options];
+    this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
+    this._shouldRedraw = true;
+  },
+
+  /**
    * Adds regions to this graph.
    *
    * See the "Language" section in the constructor documentation
    * for details about what "regions" represent.
    *
    * @param array regions
    *        A list of { start, end } values.
    */
@@ -336,16 +369,24 @@ AbstractCanvasGraph.prototype = {
    * Gets whether or not this graph has a data source.
    * @return boolean
    */
   hasData: function() {
     return !!this._data;
   },
 
   /**
+   * Gets whether or not this graph has any mask applied.
+   * @return boolean
+   */
+  hasMask: function() {
+    return !!this._mask;
+  },
+
+  /**
    * Gets whether or not this graph has any regions.
    * @return boolean
    */
   hasRegions: function() {
     return !!this._regions;
   },
 
   /**
@@ -577,16 +618,19 @@ AbstractCanvasGraph.prototype = {
     this._iframe.setAttribute("height", bounds.height);
     this._width = this._canvas.width = bounds.width * this._pixelRatio;
     this._height = this._canvas.height = bounds.height * this._pixelRatio;
 
     if (this.hasData()) {
       this._cachedBackgroundImage = this.buildBackgroundImage();
       this._cachedGraphImage = this.buildGraphImage();
     }
+    if (this.hasMask()) {
+      this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
+    }
     if (this.hasRegions()) {
       this._bakeRegions(this._regions, this._cachedGraphImage);
     }
 
     this._shouldRedraw = true;
     this.emit("refresh");
   },
 
@@ -643,21 +687,31 @@ AbstractCanvasGraph.prototype = {
    */
   _drawWidget: function() {
     if (!this._shouldRedraw) {
       return;
     }
     let ctx = this._ctx;
     ctx.clearRect(0, 0, this._width, this._height);
 
+    if (this._cachedGraphImage) {
+      ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
+    }
+    if (this._cachedMaskImage) {
+      ctx.globalCompositeOperation = "destination-out";
+      ctx.drawImage(this._cachedMaskImage, 0, 0, this._width, this._height);
+    }
     if (this._cachedBackgroundImage) {
+      ctx.globalCompositeOperation = "destination-over";
       ctx.drawImage(this._cachedBackgroundImage, 0, 0, this._width, this._height);
     }
-    if (this._cachedGraphImage) {
-      ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
+
+    // Revert to the original global composition operation.
+    if (this._cachedMaskImage || this._cachedBackgroundImage) {
+      ctx.globalCompositeOperation = "source-over";
     }
 
     if (this.hasCursor()) {
       this._drawCliphead();
     }
     if (this.hasSelection() || this.hasSelectionInProgress()) {
       this._drawSelection();
     }
@@ -1108,23 +1162,28 @@ LineGraphWidget.prototype = Heritage.ext
   clipheadLineColor: LINE_GRAPH_CLIPHEAD_LINE_COLOR,
   selectionLineColor: LINE_GRAPH_SELECTION_LINE_COLOR,
   selectionBackgroundColor: LINE_GRAPH_SELECTION_BACKGROUND_COLOR,
   selectionStripesColor: LINE_GRAPH_SELECTION_STRIPES_COLOR,
   regionBackgroundColor: LINE_GRAPH_REGION_BACKGROUND_COLOR,
   regionStripesColor: LINE_GRAPH_REGION_STRIPES_COLOR,
 
   /**
+   * Optionally offsets the `delta` in the data source by this scalar.
+   */
+  dataOffsetX: 0,
+
+  /**
    * Points that are too close too each other in the graph will not be rendered.
    * This scalar specifies the required minimum squared distance between points.
    */
   minDistanceBetweenPoints: LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS,
 
   /**
-   * Renders the graph on a canvas.
+   * Renders the graph's data source.
    * @see AbstractCanvasGraph.prototype.buildGraphImage
    */
   buildGraphImage: function() {
     let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
     let width = this._width;
     let height = this._height;
 
     let totalTicks = this._data.length;
@@ -1135,17 +1194,17 @@ LineGraphWidget.prototype = Heritage.ext
     let sumValues = 0;
 
     for (let { delta, value } of this._data) {
       maxValue = Math.max(value, maxValue);
       minValue = Math.min(value, minValue);
       sumValues += value;
     }
 
-    let dataScaleX = this.dataScaleX = width / (lastTick - firstTick);
+    let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
     let dataScaleY = this.dataScaleY = height / maxValue * LINE_GRAPH_DAMPEN_VALUES;
 
     /**
      * Calculates the squared distance between two 2D points.
      */
     function distSquared(x0, y0, x1, y1) {
       let xs = x1 - x0;
       let ys = y1 - y0;
@@ -1161,17 +1220,17 @@ LineGraphWidget.prototype = Heritage.ext
     ctx.strokeStyle = LINE_GRAPH_STROKE_COLOR;
     ctx.lineWidth = LINE_GRAPH_STROKE_WIDTH * this._pixelRatio;
     ctx.beginPath();
 
     let prevX = 0;
     let prevY = 0;
 
     for (let { delta, value } of this._data) {
-      let currX = (delta - firstTick) * dataScaleX;
+      let currX = (delta - this.dataOffsetX) * dataScaleX;
       let currY = height - value * dataScaleY;
 
       if (delta == firstTick) {
         ctx.moveTo(-LINE_GRAPH_STROKE_WIDTH, height);
         ctx.lineTo(-LINE_GRAPH_STROKE_WIDTH, currY);
       }
 
       let distance = distSquared(prevX, prevY, currX, currY);
@@ -1346,19 +1405,34 @@ LineGraphWidget.prototype = Heritage.ext
  * represents a "block" inside that "bar", plotted at the "delta" position.
  *
  * @param nsIDOMNode parent
  *        The parent node holding the graph.
  */
 this.BarGraphWidget = function(parent, ...args) {
   AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);
 
+  // Populated with [node, event, listener] entries which need to be removed
+  // when this graph is being destroyed.
+  this.outstandingEventListeners = [];
+
   this.once("ready", () => {
+    this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
+    this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
+    this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
+    this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
     this._createLegend();
   });
+
+  this.once("destroyed", () => {
+    for (let [node, event, listener] of this.outstandingEventListeners) {
+      node.removeEventListener(event, listener);
+    }
+    this.outstandingEventListeners = null;
+  });
 }
 
 BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
   clipheadLineColor: BAR_GRAPH_CLIPHEAD_LINE_COLOR,
   selectionLineColor: BAR_GRAPH_SELECTION_LINE_COLOR,
   selectionBackgroundColor: BAR_GRAPH_SELECTION_BACKGROUND_COLOR,
   selectionStripesColor: BAR_GRAPH_SELECTION_STRIPES_COLOR,
   regionBackgroundColor: BAR_GRAPH_REGION_BACKGROUND_COLOR,
@@ -1366,16 +1440,21 @@ BarGraphWidget.prototype = Heritage.exte
 
   /**
    * List of colors used to fill each block inside every bar, also
    * corresponding to labels displayed in this graph's legend.
    */
   format: null,
 
   /**
+   * Optionally offsets the `delta` in the data source by this scalar.
+   */
+  dataOffsetX: 0,
+
+  /**
    * Bars that are too close too each other in the graph will be combined.
    * This scalar specifies the required minimum width of each bar.
    */
   minBarsWidth: BAR_GRAPH_MIN_BARS_WIDTH,
 
   /**
    * Blocks in a bar that are too thin inside the bar will not be rendered.
    * This scalar specifies the required minimum height of each block.
@@ -1396,141 +1475,222 @@ BarGraphWidget.prototype = Heritage.exte
     gradient.addColorStop(1, BAR_GRAPH_BACKGROUND_GRADIENT_END);
     ctx.fillStyle = gradient;
     ctx.fillRect(0, 0, width, height);
 
     return canvas;
   },
 
   /**
-   * Renders the graph on a canvas.
+   * Renders the graph's data source.
    * @see AbstractCanvasGraph.prototype.buildGraphImage
    */
   buildGraphImage: function() {
     if (!this.format || !this.format.length) {
       throw "The graph format traits are mandatory to style the data source.";
     }
     let { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
     let width = this._width;
     let height = this._height;
 
     let totalTypes = this.format.length;
     let totalTicks = this._data.length;
-    let firstTick = this._data[0].delta;
     let lastTick = this._data[totalTicks - 1].delta;
 
     let minBarsWidth = this.minBarsWidth * this._pixelRatio;
     let minBlocksHeight = this.minBlocksHeight * this._pixelRatio;
 
-    let dataScaleX = this.dataScaleX = width / (lastTick - firstTick);
+    let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
     let dataScaleY = this.dataScaleY = height / this._calcMaxHeight({
       data: this._data,
       dataScaleX: dataScaleX,
-      dataOffsetX: firstTick,
       minBarsWidth: minBarsWidth
     }) * BAR_GRAPH_DAMPEN_VALUES;
 
     // Draw the graph.
 
     // Iterate over the blocks, then the bars, to draw all rectangles of
     // the same color in a single pass. See the @constructor for more
     // information about the data source, and how a "bar" contains "blocks".
 
+    this._blocksBoundingRects = [];
     let prevHeight = [];
     let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
     let unscaledMarginTop = BAR_GRAPH_BARS_MARGIN_TOP;
 
     for (let type = 0; type < totalTypes; type++) {
       ctx.fillStyle = this.format[type].color || "#000";
       ctx.beginPath();
 
-      let prevLeft = 0;
+      let prevRight = 0;
       let skippedCount = 0;
       let skippedHeight = 0;
 
       for (let tick = 0; tick < totalTicks; tick++) {
         let delta = this._data[tick].delta;
         let value = this._data[tick].values[type] || 0;
-        let blockLeft = (delta - firstTick) * dataScaleX;
+        let blockRight = (delta - this.dataOffsetX) * dataScaleX;
         let blockHeight = value * dataScaleY;
 
-        let blockWidth = blockLeft - prevLeft;
+        let blockWidth = blockRight - prevRight;
         if (blockWidth < minBarsWidth) {
           skippedCount++;
           skippedHeight += blockHeight;
           continue;
         }
 
         let averageHeight = (blockHeight + skippedHeight) / (skippedCount + 1);
         if (averageHeight >= minBlocksHeight) {
           let bottom = height - ~~prevHeight[tick];
-          ctx.moveTo(prevLeft, bottom);
-          ctx.lineTo(prevLeft, bottom - averageHeight);
-          ctx.lineTo(blockLeft, bottom - averageHeight);
-          ctx.lineTo(blockLeft, bottom);
+          ctx.moveTo(prevRight, bottom);
+          ctx.lineTo(prevRight, bottom - averageHeight);
+          ctx.lineTo(blockRight, bottom - averageHeight);
+          ctx.lineTo(blockRight, bottom);
+
+          // Remember this block's type and location.
+          this._blocksBoundingRects.push({
+            type: type,
+            start: prevRight,
+            end: blockRight,
+            top: bottom - averageHeight,
+            bottom: bottom
+          });
 
           if (prevHeight[tick] === undefined) {
             prevHeight[tick] = averageHeight + unscaledMarginTop;
           } else {
             prevHeight[tick] += averageHeight + unscaledMarginTop;
           }
         }
 
-        prevLeft += blockWidth + scaledMarginEnd;
+        prevRight += blockWidth + scaledMarginEnd;
         skippedHeight = 0;
         skippedCount = 0;
       }
 
       ctx.fill();
     }
 
+    // The blocks bounding rects isn't guaranteed to be sorted ascending by
+    // block location on the X axis. This should be the case, for better
+    // cache cohesion and a faster `buildMaskImage`.
+    this._blocksBoundingRects.sort((a, b) => a.start > b.start ? 1 : -1);
+
     // Update the legend.
 
     while (this._legendNode.hasChildNodes()) {
       this._legendNode.firstChild.remove();
     }
     for (let { color, label } of this.format) {
       this._createLegendItem(color, label);
     }
 
     return canvas;
   },
 
   /**
+   * Renders the graph's mask.
+   * Fades in only the parts of the graph that are inside the specified areas.
+   *
+   * @param array highlights
+   *        A list of { start, end } values. Optionally, each object
+   *        in the list may also specify { top, bottom } pixel values if the
+   *        highlighting shouldn't span across the full height of the graph.
+   * @param boolean inPixels
+   *        Set this to true if the { start, end } values in the highlights
+   *        list are pixel values, and not values from the data source.
+   * @param function unpack [optional]
+   *        @see AbstractCanvasGraph.prototype.getMappedSelection
+   */
+  buildMaskImage: function(highlights, inPixels = false, unpack = e => e.delta) {
+    // A null `highlights` array is used to clear the mask. An empty array
+    // will mask the entire graph.
+    if (!highlights) {
+      return null;
+    }
+
+    // Get a render target for the highlights. It will be overlaid on top of
+    // the existing graph, masking the areas that aren't highlighted.
+
+    let { canvas, ctx } = this._getNamedCanvas("graph-highlights");
+    let width = this._width;
+    let height = this._height;
+
+    // Draw the background mask.
+
+    let pattern = AbstractCanvasGraph.getStripePattern({
+      ownerDocument: this._document,
+      backgroundColor: BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
+      stripesColor: BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES
+    });
+    ctx.fillStyle = pattern;
+    ctx.fillRect(0, 0, width, height);
+
+    // Clear highlighted areas.
+
+    let totalTicks = this._data.length;
+    let firstTick = unpack(this._data[0]);
+    let lastTick = unpack(this._data[totalTicks - 1]);
+
+    for (let { start, end, top, bottom } of highlights) {
+      if (!inPixels) {
+        start = map(start, firstTick, lastTick, 0, width);
+        end = map(end, firstTick, lastTick, 0, width);
+      }
+      let firstSnap = findFirst(this._blocksBoundingRects, e => e.start >= start);
+      let lastSnap = findLast(this._blocksBoundingRects, e => e.start >= start && e.end <= end);
+
+      let x1 = firstSnap ? firstSnap.start : start;
+      let x2 = lastSnap ? lastSnap.end : firstSnap ? firstSnap.end : end;
+      let y1 = top || 0;
+      let y2 = bottom || height;
+      ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
+    }
+
+    return canvas;
+  },
+
+  /**
+   * A list storing the bounding rectangle for each drawn block in the graph.
+   * Created whenever `buildGraphImage` is invoked.
+   */
+  _blocksBoundingRects: null,
+
+  /**
    * Calculates the height of the tallest bar that would eventially be rendered
    * in this graph.
    *
    * Bars that are too close too each other in the graph will be combined.
    * @see `minBarsWidth`
    *
    * @return number
    *         The tallest bar height in this graph.
    */
-  _calcMaxHeight: function({ data, dataScaleX, dataOffsetX, minBarsWidth }) {
+  _calcMaxHeight: function({ data, dataScaleX, minBarsWidth }) {
     let maxHeight = 0;
-    let prevLeft = 0;
+    let prevRight = 0;
     let skippedCount = 0;
     let skippedHeight = 0;
     let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
 
     for (let { delta, values } of data) {
-      let barLeft = (delta - dataOffsetX) * dataScaleX;
+      let barRight = (delta - this.dataOffsetX) * dataScaleX;
       let barHeight = values.reduce((a, b) => a + b, 0);
 
-      let barWidth = barLeft - prevLeft;
+      let barWidth = barRight - prevRight;
       if (barWidth < minBarsWidth) {
         skippedCount++;
         skippedHeight += barHeight;
         continue;
       }
 
       let averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
       maxHeight = Math.max(averageHeight, maxHeight);
 
-      prevLeft += barWidth + scaledMarginEnd;
+      prevRight += barWidth + scaledMarginEnd;
       skippedHeight = 0;
       skippedCount = 0;
     }
 
     return maxHeight;
   },
 
   /**
@@ -1546,25 +1706,94 @@ BarGraphWidget.prototype = Heritage.exte
    * Creates a legend item when constructing this graph.
    */
   _createLegendItem: function(color, label) {
     let itemNode = this._document.createElementNS(HTML_NS, "div");
     itemNode.className = "bar-graph-widget-legend-item";
 
     let colorNode = this._document.createElementNS(HTML_NS, "span");
     colorNode.setAttribute("view", "color");
+    colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
     colorNode.style.backgroundColor = color;
+    colorNode.addEventListener("mouseover", this._onLegendMouseOver);
+    colorNode.addEventListener("mouseout", this._onLegendMouseOut);
+    colorNode.addEventListener("mousedown", this._onLegendMouseDown);
+    colorNode.addEventListener("mouseup", this._onLegendMouseUp);
+
+    this.outstandingEventListeners.push([colorNode, "mouseover", this._onLegendMouseOver]);
+    this.outstandingEventListeners.push([colorNode, "mouseout", this._onLegendMouseOut]);
+    this.outstandingEventListeners.push([colorNode, "mousedown", this._onLegendMouseDown]);
+    this.outstandingEventListeners.push([colorNode, "mouseup", this._onLegendMouseUp]);
 
     let labelNode = this._document.createElementNS(HTML_NS, "span");
     labelNode.setAttribute("view", "label");
     labelNode.textContent = label;
 
     itemNode.appendChild(colorNode);
     itemNode.appendChild(labelNode);
     this._legendNode.appendChild(itemNode);
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is hovered.
+   */
+  _onLegendMouseOver: function(e) {
+    setNamedTimeout("bar-graph-debounce", BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE, () => {
+      let type = e.target.dataset.index;
+      let rects = this._blocksBoundingRects.filter(e => e.type == type);
+
+      this._originalHighlights = this._mask;
+      this._hasCustomHighlights = true;
+      this.setMask(rects, true);
+
+      this.emit("legend-hover", [type, rects]);
+    });
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is unhovered.
+   */
+  _onLegendMouseOut: function() {
+    clearNamedTimeout("bar-graph-debounce");
+
+    if (this._hasCustomHighlights) {
+      this.setMask(this._originalHighlights);
+      this._hasCustomHighlights = false;
+      this._originalHighlights = null;
+    }
+
+    this.emit("legend-unhover");
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is pressed.
+   */
+  _onLegendMouseDown: function(e) {
+    e.preventDefault();
+    e.stopPropagation();
+
+    let type = e.target.dataset.index;
+    let rects = this._blocksBoundingRects.filter(e => e.type == type);
+    let leftmost = rects[0];
+    let rightmost = rects[rects.length - 1];
+    if (!leftmost || !rightmost) {
+      this.dropSelection();
+    } else {
+      this.setSelection({ start: leftmost.start, end: rightmost.end });
+    }
+
+    this.emit("legend-selection", [leftmost, rightmost]);
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is released.
+   */
+  _onLegendMouseUp: function(e) {
+    e.preventDefault();
+    e.stopPropagation();
   }
 });
 
 // Helper functions.
 
 /**
  * Creates an iframe element with the provided source URL, appends it to
  * the specified node and invokes the callback once the content is loaded.
@@ -1689,12 +1918,40 @@ this.CanvasGraphUtils = {
     graph2.on("deselecting", () => {
       graph1.dropSelection();
     });
   }
 };
 
 /**
  * Maps a value from one range to another.
+ * @param number value, istart, istop, ostart, ostop
+ * @return number
  */
 function map(value, istart, istop, ostart, ostop) {
   return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
 }
+
+/**
+ * Finds the first element in an array that validates a predicate.
+ * @param array
+ * @param function predicate
+ * @return number
+ */
+function findFirst(array, predicate) {
+  for (let i = 0, len = array.length; i < len; i++) {
+    let element = array[i];
+    if (predicate(element)) return element;
+  }
+}
+
+/**
+ * Finds the last element in an array that validates a predicate.
+ * @param array
+ * @param function predicate
+ * @return number
+ */
+function findLast(array, predicate) {
+  for (let i = array.length - 1; i >= 0; i--) {
+    let element = array[i];
+    if (predicate(element)) return element;
+  }
+}
--- a/browser/themes/shared/devtools/widgets.inc.css
+++ b/browser/themes/shared/devtools/widgets.inc.css
@@ -921,34 +921,31 @@
 .graph-widget-canvas[input=hovering-selection-contents] {
   cursor: grab;
 }
 
 .graph-widget-canvas[input=dragging-selection-contents] {
   cursor: grabbing;
 }
 
-.graph-widget-canvas ~ * {
-  pointer-events: none;
-}
-
 /* Line graph widget */
 
 .line-graph-widget-canvas {
   background: #0088cc;
 }
 
 .line-graph-widget-gutter {
   position: absolute;
   background: rgba(255,255,255,0.75);
   width: 10px;
   height: 100%;
   top: 0;
   left: 0;
   border-right: 1px solid rgba(255,255,255,0.25);
+  pointer-events: none;
 }
 
 .line-graph-widget-gutter-line {
   position: absolute;
   width: 100%;
   border-top: 1px solid;
   transform: translateY(-1px);
 }
@@ -971,16 +968,17 @@
   box-shadow: 0 2px 1px rgba(0,0,0,0.1);
   border-radius: 2px;
   line-height: 15px;
   -moz-padding-start: 6px;
   -moz-padding-end: 6px;
   transform: translateY(-50%);
   font-size: 80%;
   z-index: 1;
+  pointer-events: none;
 }
 
 .line-graph-widget-tooltip::before {
   content: "";
   position: absolute;
   border-top: 3px solid transparent;
   border-bottom: 3px solid transparent;
   top: calc(50% - 3px);
@@ -1048,16 +1046,17 @@
 }
 
 .bar-graph-widget-legend {
   position: absolute;
   top: 4px;
   left: 8px;
   color: #292e33;
   font-size: 80%;
+  pointer-events: none;
 }
 
 .bar-graph-widget-legend-item {
   float: left;
   -moz-margin-end: 8px;
 }
 
 .bar-graph-widget-legend-item > [view="color"],
@@ -1067,16 +1066,18 @@
 
 .bar-graph-widget-legend-item > [view="color"] {
   display: inline-block;
   width: 8px;
   height: 8px;
   border: 1px solid #fff;
   border-radius: 1px;
   -moz-margin-end: 4px;
+  pointer-events: all;
+  cursor: pointer;
 }
 
 .bar-graph-widget-legend-item > [view="label"] {
   text-shadow: 1px  0px rgba(255,255,255,0.8),
               -1px  0px rgba(255,255,255,0.8),
                0px -1px rgba(255,255,255,0.8),
                0px  1px rgba(255,255,255,0.8);
 }
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -101,16 +101,18 @@
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #if defined(XP_LINUX)
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
+#include "mozilla/dom/FeatureList.h"
+
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static uint32_t sDoNotTrackValue = 1;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
@@ -1529,16 +1531,27 @@ Navigator::GetFeature(const nsAString& a
   nsAutoCString feature = NS_ConvertUTF16toUTF8(aName);
   for (uint32_t i = 0; i < MOZ_ARRAY_LENGTH(manifestFeatures); i++) {
     if (feature.Equals(manifestFeatures[i])) {
       p->MaybeResolve(true);
       return p.forget();
     }
   }
 
+  NS_NAMED_LITERAL_STRING(apiWindowPrefix, "api.window.");
+  if (StringBeginsWith(aName, apiWindowPrefix)) {
+    const nsAString& featureName = Substring(aName, apiWindowPrefix.Length(), aName.Length()-apiWindowPrefix.Length());
+    if (IsFeatureDetectible(featureName)) {
+      p->MaybeResolve(true);
+    } else {
+      p->MaybeResolve(JS::UndefinedHandleValue);
+    }
+    return p.forget();
+  }
+
   // resolve with <undefined> because the feature name is not supported
   p->MaybeResolve(JS::UndefinedHandleValue);
 
   return p.forget();
 }
 
 
 PowerManager*
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13662,16 +13662,46 @@ class GlobalGenRoots():
         curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
 
         # Add include guards.
         curr = CGIncludeGuard('UnionConversions', curr)
 
         # Done.
         return curr
 
+    @staticmethod
+    def FeatureList(config):
+        things = set()
+        for d in config.getDescriptors():
+            if not d.interface.isExternal() and d.featureDetectibleThings is not None:
+                things.update(d.featureDetectibleThings)
+        things = CGList((CGGeneric(declare='"%s",' % t) for t in sorted(things)), joiner="\n")
+        things.append(CGGeneric(declare="nullptr"))
+        things = CGWrapper(CGIndenter(things), pre="static const char* const FeatureList[] = {\n",
+                                               post="\n};\n")
+
+        helper = CGWrapper(CGIndenter(things), pre="bool IsFeatureDetectible(const nsAString& aFeature) {\n",
+                                               post=dedent("""
+              const char* const* feature = FeatureList;
+              while (*feature) {
+                if (aFeature.EqualsASCII(*feature)) {
+                  return true;
+                }
+                ++feature;
+              }
+
+              return false;
+            }
+        """))
+
+        curr = CGNamespace.build(['mozilla', 'dom'], helper)
+        curr = CGHeaders([], [], [], [], ["nsString.h"], [], 'FeatureList', curr)
+        curr = CGIncludeGuard('FeatureList', curr)
+
+        return curr
 
 # Code generator for simple events
 class CGEventGetter(CGNativeMember):
     def __init__(self, descriptor, attr):
         ea = descriptor.getExtendedAttributes(attr, getter=True)
         if not attr.type.isSequence():
             ea.append('resultNotAddRefed')
         CGNativeMember.__init__(self, descriptor, attr,
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -440,16 +440,28 @@ class Descriptor(DescriptorProvider):
 
             self.checkPermissionsIndex = addPermissions(self.interface)
             self.checkPermissionsIndicesForMembers = dict()
             for m in self.interface.members:
                 permissionsIndex = addPermissions(m)
                 if permissionsIndex is not None:
                     self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex
 
+            self.featureDetectibleThings = set()
+            if self.interface.getExtendedAttribute("FeatureDetectible") is not None:
+                if self.interface.getNavigatorProperty():
+                    self.featureDetectibleThings.add("Navigator.%s" % self.interface.getNavigatorProperty())
+                else:
+                    assert(self.interface.ctor() is not None)
+                    self.featureDetectibleThings.add(self.interface.identifier.name)
+
+            for m in self.interface.members:
+                if m.getExtendedAttribute("FeatureDetectible") is not None:
+                    self.featureDetectibleThings.add("%s.%s" % (self.interface.identifier.name, m.identifier.name))
+
         # Build the prototype chain.
         self.prototypeChain = []
         parent = interface
         while parent:
             self.prototypeChain.insert(0, parent.identifier.name)
             parent = parent.parent
         config.maxProtoChainLength = max(config.maxProtoChainLength,
                                          len(self.prototypeChain))
--- a/dom/bindings/mozwebidlcodegen/__init__.py
+++ b/dom/bindings/mozwebidlcodegen/__init__.py
@@ -122,16 +122,17 @@ class WebIDLCodegenManager(LoggingMixin)
     """Manages all code generation around WebIDL.
 
     To facilitate testing, this object is meant to be generic and reusable.
     Paths, etc should be parameters and not hardcoded.
     """
 
     # Global parser derived declaration files.
     GLOBAL_DECLARE_FILES = {
+        'FeatureList.h',
         'GeneratedAtomList.h',
         'PrototypeList.h',
         'RegisterBindings.h',
         'UnionConversions.h',
         'UnionTypes.h',
     }
 
     # Global parser derived definition files.
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -563,16 +563,26 @@ class IDLInterface(IDLObjectWithScope):
         if parent and isinstance(parent, IDLExternalInterface):
             raise WebIDLError("%s inherits from %s which does not have "
                               "a definition" %
                               (self.identifier.name,
                                self.parent.identifier.name),
                               [self.location])
         assert not parent or isinstance(parent, IDLInterface)
 
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if self.getExtendedAttribute("NoInterfaceObject"):
+                raise WebIDLError("[FeatureDetectible] not allowed on interface "
+                                  " with [NoInterfaceObject]",
+                                  [self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                  "in combination with [Pref]",
+                                  [self.location])
+
         self.parent = parent
 
         assert iter(self.members)
 
         if self.parent:
             self.parent.finish(scope)
 
             self.parent._hasChildInterfaces = True
@@ -1036,17 +1046,18 @@ class IDLInterface(IDLObjectWithScope):
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
-                  identifier == "LegacyEventInit"):
+                  identifier == "LegacyEventInit" or
+                  identifier == "FeatureDetectible"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif (identifier == "Pref" or
                   identifier == "JSImplementation" or
                   identifier == "HeaderFile" or
                   identifier == "NavigatorProperty" or
@@ -2885,16 +2896,28 @@ class IDLAttribute(IDLInterfaceMember):
         if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
             raise WebIDLError("An attribute with [PutForwards] must have an "
                               "interface type as its type", [self.location])
 
         if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
             raise WebIDLError("An attribute with [SameObject] must have an "
                               "interface type as its type", [self.location])
 
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[%s] is only allowed in combination with [Func], "
+                                  "[AvailableIn] or [CheckPermissions]" % identifier,
+                                  [attr.location, self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                  "in combination with [Pref]",
+                                  [self.location])
+
     def validate(self):
         if ((self.getExtendedAttribute("Cached") or
              self.getExtendedAttribute("StoreInSlot")) and
             not self.getExtendedAttribute("Constant") and
             not self.getExtendedAttribute("Pure")):
             raise WebIDLError("Cached attributes and attributes stored in "
                               "slots must be constant or pure, since the "
                               "getter won't always be called.",
@@ -3003,16 +3026,23 @@ class IDLAttribute(IDLInterfaceMember):
             if self.isStatic():
                 raise WebIDLError("[%s] is only allowed on non-static "
                                   "attributes" % identifier,
                                   [attr.location, self.location])
             if self.getExtendedAttribute("LenientThis"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
                                   "with [%s]" % identifier,
                                   [attr.location, self.location])
+        elif identifier == "FeatureDetectible":
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[%s] is only allowed in combination with [Func], "
+                                  "[AvailableIn] or [CheckPermissions]" % identifier,
+                                  [attr.location, self.location])
         elif (identifier == "Pref" or
               identifier == "SetterThrows" or
               identifier == "Pure" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "SameObject" or
               identifier == "Constant" or
@@ -3418,16 +3448,28 @@ class IDLMethod(IDLInterfaceMember, IDLS
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
 
     def finish(self, scope):
+        if self.getExtendedAttribute("FeatureDetectible"):
+            if not (self.getExtendedAttribute("Func") or
+                    self.getExtendedAttribute("AvailableIn") or
+                    self.getExtendedAttribute("CheckPermissions")):
+                raise WebIDLError("[FeatureDetectible] is only allowed in combination "
+                                  "with [Func], [AvailableIn] or [CheckPermissions]",
+                                  [self.location])
+            if self.getExtendedAttribute("Pref"):
+                raise WebIDLError("[FeatureDetectible] must not be specified "
+                                      "in combination with [Pref]",
+                                      [self.location])
+
         overloadWithPromiseReturnType = None
         overloadWithoutPromiseReturnType = None
         for overload in self._overloads:
             variadicArgument = None
 
             arguments = overload.arguments
             for (idx, argument) in enumerate(arguments):
                 if not argument.isComplete():
@@ -3588,17 +3630,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
                 raise WebIDLError("[LenientFloat] used on a non-void method",
                                   [attr.location, self.location])
             if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
                 raise WebIDLError("[LenientFloat] used on an operation with no "
                                   "restricted float type arguments",
                                   [attr.location, self.location])
         elif (identifier == "Pure" or
               identifier == "CrossOriginCallable" or
-              identifier == "WebGLHandlesContextLoss"):
+              identifier == "WebGLHandlesContextLoss" or
+              identifier == "FeatureDetectible"):
             # Known no-argument attributes.
             if not attr.noArguments():
                 raise WebIDLError("[%s] must take no arguments" % identifier,
                                   [attr.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "Pref" or
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -147,16 +147,17 @@
 #include "nsIPrincipal.h"
 #include "nsDeviceStorage.h"
 #include "AudioChannelService.h"
 #include "JavaScriptChild.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/RemoteSpellCheckEngineChild.h"
 
 using namespace base;
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobilemessage;
@@ -1049,16 +1050,30 @@ ContentChild::DeallocPBrowserChild(PBrow
 }
 
 PBlobChild*
 ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
 {
     return nsIContentChild::AllocPBlobChild(aParams);
 }
 
+mozilla::PRemoteSpellcheckEngineChild *
+ContentChild::AllocPRemoteSpellcheckEngineChild()
+{
+    NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChilf should never be called");
+    return nullptr;
+}
+
+bool
+ContentChild::DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild *child)
+{
+    delete child;
+    return true;
+}
+
 bool
 ContentChild::DeallocPBlobChild(PBlobChild* aActor)
 {
     return nsIContentChild::DeallocPBlobChild(aActor);
 }
 
 PBlobChild*
 ContentChild::SendPBlobConstructor(PBlobChild* aActor,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -21,16 +21,17 @@
 
 struct ChromePackage;
 class nsIDOMBlob;
 class nsIObserver;
 struct ResourceMapping;
 struct OverrideMapping;
 
 namespace mozilla {
+class RemoteSpellcheckEngineChild;
 
 namespace ipc {
 class OptionalURIParams;
 class URIParams;
 }// namespace ipc
 
 namespace jsipc {
 class JavaScriptChild;
@@ -238,16 +239,18 @@ public:
 
     virtual bool RecvRegisterChrome(const InfallibleTArray<ChromePackage>& packages,
                                     const InfallibleTArray<ResourceMapping>& resources,
                                     const InfallibleTArray<OverrideMapping>& overrides,
                                     const nsCString& locale) MOZ_OVERRIDE;
 
     virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() MOZ_OVERRIDE;
     virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) MOZ_OVERRIDE;
+    virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() MOZ_OVERRIDE;
+    virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) MOZ_OVERRIDE;
 
     virtual bool RecvSetOffline(const bool& offline) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerNotify() MOZ_OVERRIDE;
 
     virtual bool RecvNotifyVisited(const URIParams& aURI) MOZ_OVERRIDE;
     // auto remove when alertfinished is received.
     nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -153,16 +153,18 @@ using namespace mozilla::system;
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothParent.h"
 #include "BluetoothService.h"
 #endif
 
 #include "JavaScriptParent.h"
 
+#include "mozilla/RemoteSpellCheckEngineParent.h"
+
 #ifdef MOZ_B2G_FM
 #include "mozilla/dom/FMRadioParent.h"
 #endif
 
 #include "Crypto.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesisParent.h"
@@ -2690,16 +2692,30 @@ ContentParent::AllocPBlobParent(const Bl
 
 bool
 ContentParent::DeallocPBlobParent(PBlobParent* aActor)
 {
     delete aActor;
     return true;
 }
 
+mozilla::PRemoteSpellcheckEngineParent *
+ContentParent::AllocPRemoteSpellcheckEngineParent()
+{
+    mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent();
+    return parent;
+}
+
+bool
+ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent)
+{
+    delete parent;
+    return true;
+}
+
 void
 ContentParent::KillHard()
 {
     // On Windows, calling KillHard multiple times causes problems - the
     // process handle becomes invalid on the first call, causing a second call
     // to crash our process - more details in bug 890840.
     if (mCalledKillHard) {
         return;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -31,16 +31,17 @@ class mozIApplication;
 class nsConsoleService;
 class nsICycleCollectorLogSink;
 class nsIDOMBlob;
 class nsIDumpGCAndCCLogsCallback;
 class nsIMemoryReporter;
 class ParentIdleListener;
 
 namespace mozilla {
+class PRemoteSpellcheckEngineParent;
 
 namespace ipc {
 class OptionalURIParams;
 class URIParams;
 class TestShellParent;
 } // namespace ipc
 
 namespace jsipc {
@@ -247,16 +248,17 @@ public:
     }
 
     virtual PJavaScriptParent*
     AllocPJavaScriptParent() MOZ_OVERRIDE;
     virtual bool
     RecvPJavaScriptConstructor(PJavaScriptParent* aActor) MOZ_OVERRIDE {
         return PContentParent::RecvPJavaScriptConstructor(aActor);
     }
+    virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() MOZ_OVERRIDE;
 
     virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
                                            const nsString& aPageURL,
                                            const bool& aIsAudio,
                                            const bool& aIsVideo) MOZ_OVERRIDE;
 
     bool CycleCollectWithLogs(bool aDumpAllTraces,
                               nsICycleCollectorLogSink* aSink,
@@ -390,16 +392,17 @@ private:
 
     virtual bool RecvGetProcessAttributes(uint64_t* aId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
 
     virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
 
+    virtual bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) MOZ_OVERRIDE;
     virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
                                                 const uint32_t& aChromeFlags,
                                                 const uint64_t& aId,
                                                 const bool& aIsForApp,
                                                 const bool& aIsForBrowser) MOZ_OVERRIDE;
     virtual bool DeallocPBrowserParent(PBrowserParent* frame) MOZ_OVERRIDE;
 
     virtual PDeviceStorageRequestParent*
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -26,16 +26,17 @@ include protocol PNecko;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
 include protocol PJavaScript;
+include protocol PRemoteSpellcheckEngine;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
 include ProtocolTypes;
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
@@ -306,16 +307,17 @@ intr protocol PContent
     manages PNecko;
     manages PScreenManager;
     manages PSms;
     manages PSpeechSynthesis;
     manages PStorage;
     manages PTelephony;
     manages PTestShell;
     manages PJavaScript;
+    manages PRemoteSpellcheckEngine;
 
 both:
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
     // TabChild::BrowserFrameProvideWindow (which happens when the child's
     // content calls window.open()), and the parent creates the PBrowser as part
@@ -459,16 +461,17 @@ parent:
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority)
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     intr BridgeToChildProcess(uint64_t id);
 
     async PJavaScript();
 
+    sync PRemoteSpellcheckEngine();
     PDeviceStorageRequest(DeviceStorageParams params);
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
     sync GetRandomValues(uint32_t length)
         returns (uint8_t[] randomValues);
--- a/editor/composer/nsEditorSpellCheck.cpp
+++ b/editor/composer/nsEditorSpellCheck.cpp
@@ -37,16 +37,17 @@
 #include "nsIVariant.h"                 // for nsIWritableVariant, etc
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING, etc
 #include "nsMemory.h"                   // for nsMemory
 #include "nsReadableUtils.h"            // for ToNewUnicode, EmptyString, etc
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsAutoString, nsString, etc
 #include "nsStringFwd.h"                // for nsAFlatString
 #include "nsStyleUtil.h"                // for nsStyleUtil
+#include "nsXULAppAPI.h"                // for XRE_GetProcessType
 
 using namespace mozilla;
 
 class UpdateDictionnaryHolder {
   private:
     nsEditorSpellCheck* mSpellCheck;
   public:
     UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
@@ -700,18 +701,26 @@ nsEditorSpellCheck::UpdateCurrentDiction
 
   DictionaryFetcher* fetcher = new DictionaryFetcher(this, aCallback,
                                                      mDictionaryFetcherGroup);
   rootContent->GetLang(fetcher->mRootContentLang);
   nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
   NS_ENSURE_STATE(doc);
   doc->GetContentLanguage(fetcher->mRootDocContentLang);
 
-  rv = fetcher->Fetch(mEditor);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    // Content prefs don't work in E10S (Bug 1027898) pretend that we
+    // didn't have any & trigger the asynchrous completion.
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethodWithArg<uint16_t>(fetcher, &DictionaryFetcher::HandleCompletion, 0);
+    NS_DispatchToMainThread(runnable);
+  } else {
+    rv = fetcher->Fetch(mEditor);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
 {
   nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/PRemoteSpellcheckEngine.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PContent;
+
+namespace mozilla {
+
+sync protocol PRemoteSpellcheckEngine {
+  manager PContent;
+
+parent:
+  __delete__();
+
+  sync CheckForMisspelling(nsString aWord) returns (bool isMisspelled);
+
+  sync SetDictionary(nsString aDictionary) returns (bool success);
+};
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/RemoteSpellCheckEngineChild.cpp
@@ -0,0 +1,21 @@
+/* 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 "RemoteSpellCheckEngineChild.h"
+
+namespace mozilla {
+RemoteSpellcheckEngineChild::RemoteSpellcheckEngineChild(mozSpellChecker *aOwner)
+  :mOwner(aOwner)
+{
+}
+
+RemoteSpellcheckEngineChild::~RemoteSpellcheckEngineChild()
+{
+  // null out the owner's SpellcheckEngineChild to prevent state corruption
+  // during shutdown
+  mOwner->DeleteRemoteEngine();
+
+}
+
+} //namespace mozilla
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/RemoteSpellCheckEngineChild.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RemoteSpellcheckEngineChild_h_
+#define RemoteSpellcheckEngineChild_h_
+
+#include "mozilla/PRemoteSpellcheckEngineChild.h"
+#include "mozSpellChecker.h"
+
+class mozSpellChecker;
+
+namespace mozilla {
+class RemoteSpellcheckEngineChild : public mozilla::PRemoteSpellcheckEngineChild
+{
+public:
+  RemoteSpellcheckEngineChild(mozSpellChecker *aOwner);
+  ~RemoteSpellcheckEngineChild();
+
+private:
+  mozSpellChecker *mOwner;
+};
+
+} //namespace mozilla
+
+#endif // RemoteSpellcheckEngineChild_h_
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/RemoteSpellCheckEngineParent.cpp
@@ -0,0 +1,49 @@
+/* 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 "RemoteSpellCheckEngineParent.h"
+#include "mozISpellCheckingEngine.h"
+#include "nsServiceManagerUtils.h"
+
+#define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
+
+namespace mozilla {
+
+RemoteSpellcheckEngineParent::RemoteSpellcheckEngineParent()
+{
+  mEngine = do_GetService(DEFAULT_SPELL_CHECKER);
+}
+
+RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
+{
+}
+
+bool
+RemoteSpellcheckEngineParent::RecvSetDictionary(
+  const nsString& aDictionary,
+  bool* success)
+{
+  nsresult rv = mEngine->SetDictionary(aDictionary.get());
+  *success = NS_SUCCEEDED(rv);
+  return true;
+}
+
+bool
+RemoteSpellcheckEngineParent::RecvCheckForMisspelling(
+  const nsString& aWord,
+  bool* isMisspelled)
+{
+  bool isCorrect = false;
+  mEngine->Check(aWord.get(), &isCorrect);
+  *isMisspelled = !isCorrect;
+  return true;
+}
+
+void
+RemoteSpellcheckEngineParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/RemoteSpellCheckEngineParent.h
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef RemoteSpellcheckEngineParent_h_
+#define RemoteSpellcheckEngineParent_h_
+
+#include "mozISpellCheckingEngine.h"
+#include "mozilla/PRemoteSpellcheckEngineParent.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+
+class RemoteSpellcheckEngineParent : public mozilla::PRemoteSpellcheckEngineParent {
+
+public:
+  RemoteSpellcheckEngineParent();
+
+  ~RemoteSpellcheckEngineParent();
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy);
+
+  bool RecvSetDictionary(const nsString& aDictionary, bool* success);
+
+  bool RecvCheckForMisspelling( const nsString& aWord, bool* isMisspelled);
+
+private:
+  nsCOMPtr<mozISpellCheckingEngine> mEngine;
+};
+
+}
+#endif
--- a/extensions/spellcheck/hunspell/src/moz.build
+++ b/extensions/spellcheck/hunspell/src/moz.build
@@ -1,17 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-UNIFIED_SOURCES += [
+SOURCES += [
     'mozHunspell.cpp',
     'mozHunspellDirProvider.cpp',
+    'RemoteSpellCheckEngineChild.cpp',
+    'RemoteSpellCheckEngineParent.cpp',
 ]
 
 if not CONFIG['MOZ_NATIVE_HUNSPELL']:
     SOURCES += [
         'affentry.cxx',
         'affixmgr.cxx',
         'csutil.cxx',
         'dictmgr.cxx',
@@ -35,8 +37,19 @@ LOCAL_INCLUDES += [
 
 LOCAL_INCLUDES += [
     '/editor/libeditor/base',
 ]
 
 # Suppress warnings in third-party code.
 if CONFIG['CLANG_CXX']:
     CXXFLAGS += ['-Wno-unused-private-field']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+IPDL_SOURCES = [
+    'PRemoteSpellcheckEngine.ipdl',
+]
+
+EXPORTS.mozilla += [
+     'RemoteSpellCheckEngineChild.h',
+     'RemoteSpellCheckEngineParent.h',
+]
--- a/extensions/spellcheck/src/moz.build
+++ b/extensions/spellcheck/src/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-UNIFIED_SOURCES += [
+include('/ipc/chromium/chromium-config.mozbuild')
+SOURCES += [
     'mozEnglishWordUtils.cpp',
     'mozGenericWordUtils.cpp',
     'mozInlineSpellChecker.cpp',
     'mozInlineSpellWordUtil.cpp',
     'mozPersonalDictionary.cpp',
     'mozSpellChecker.cpp',
     'mozSpellCheckerFactory.cpp',
     'mozSpellI18NManager.cpp',
@@ -19,10 +20,13 @@ LIBRARY_NAME = 'spellchecker'
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../hunspell/src',
     '/content/base/src',
     '/editor/libeditor/base',
 ]
+EXPORTS.mozilla += [
+     'mozSpellChecker.h',
+]
 
 FAIL_ON_WARNINGS = True
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -5,16 +5,23 @@
 
 #include "mozSpellChecker.h"
 #include "nsIServiceManager.h"
 #include "mozISpellI18NManager.h"
 #include "nsIStringEnumerator.h"
 #include "nsICategoryManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISimpleEnumerator.h"
+#include "mozilla/PRemoteSpellcheckEngineChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::dom::ContentChild;
+using mozilla::PRemoteSpellcheckEngineChild;
+using mozilla::RemoteSpellcheckEngineChild;
 
 #define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozSpellChecker)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozSpellChecker)
 
 NS_INTERFACE_MAP_BEGIN(mozSpellChecker)
   NS_INTERFACE_MAP_ENTRY(nsISpellChecker)
@@ -33,24 +40,34 @@ mozSpellChecker::mozSpellChecker()
 mozSpellChecker::~mozSpellChecker()
 {
   if(mPersonalDictionary){
     //    mPersonalDictionary->Save();
     mPersonalDictionary->EndSession();
   }
   mSpellCheckingEngine = nullptr;
   mPersonalDictionary = nullptr;
+
+  if(XRE_GetProcessType() == GeckoProcessType_Content) {
+    mEngine->Send__delete__(mEngine);
+  }
 }
 
 nsresult 
 mozSpellChecker::Init()
 {
   mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
   
   mSpellCheckingEngine = nullptr;
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
+    MOZ_ASSERT(contentChild);
+    mEngine = new RemoteSpellcheckEngineChild(this);
+    contentChild->SendPRemoteSpellcheckEngineConstructor(mEngine);
+  }
 
   return NS_OK;
 } 
 
 NS_IMETHODIMP 
 mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, bool aFromStartofDoc)
 {
   mTsDoc = aDoc;
@@ -103,19 +120,26 @@ mozSpellChecker::NextMisspelledWord(nsAS
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray<nsString> *aSuggestions)
 {
   nsresult result;
   bool correct;
-  if(!mSpellCheckingEngine)
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    nsString wordwrapped = nsString(aWord);
+    bool rv = mEngine->SendCheckForMisspelling(wordwrapped, aIsMisspelled);
+    return rv ? NS_OK : NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if(!mSpellCheckingEngine) {
     return NS_ERROR_NULL_POINTER;
-
+  }
   *aIsMisspelled = false;
   result = mSpellCheckingEngine->Check(PromiseFlatString(aWord).get(), &correct);
   NS_ENSURE_SUCCESS(result, result);
   if(!correct){
     if(aSuggestions){
       uint32_t count,i;
       char16_t **words;
       
@@ -327,16 +351,23 @@ mozSpellChecker::GetCurrentDictionary(ns
   mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
   aDictionary = dictname;
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
 {
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    nsString wrappedDict = nsString(aDictionary);
+    bool isSuccess;
+    mEngine->SendSetDictionary(wrappedDict, &isSuccess);
+    return isSuccess ? NS_OK : NS_ERROR_NOT_AVAILABLE;
+  }
+
   // Calls to mozISpellCheckingEngine::SetDictionary might destroy us
   nsRefPtr<mozSpellChecker> kungFuDeathGrip = this;
 
   mSpellCheckingEngine = nullptr;
 
   if (aDictionary.IsEmpty()) {
     return NS_OK;
   }
@@ -523,8 +554,12 @@ mozSpellChecker::GetEngineList(nsCOMArra
     // Fail if not succeeded to load HunSpell. Ignore errors
     // for external spellcheck engines.
     return rv;
   }
   aSpellCheckingEngines->AppendObject(engine);
 
   return NS_OK;
 }
+
+void mozSpellChecker::DeleteRemoteEngine() {
+  mEngine = nullptr;
+}
\ No newline at end of file
--- a/extensions/spellcheck/src/mozSpellChecker.h
+++ b/extensions/spellcheck/src/mozSpellChecker.h
@@ -12,16 +12,22 @@
 #include "nsString.h"
 #include "nsITextServicesDocument.h"
 #include "mozIPersonalDictionary.h"
 #include "mozISpellCheckingEngine.h"
 #include "nsClassHashtable.h"
 #include "nsTArray.h"
 #include "mozISpellI18NUtil.h"
 #include "nsCycleCollectionParticipant.h"
+#include "RemoteSpellCheckEngineChild.h"
+
+namespace mozilla {
+class PRemoteSpellcheckEngineChild;
+class RemoteSpellcheckEngineChild;
+}
 
 class mozSpellChecker : public nsISpellChecker
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(mozSpellChecker)
 
   mozSpellChecker();
@@ -38,26 +44,29 @@ public:
   NS_IMETHOD AddWordToPersonalDictionary(const nsAString &aWord);
   NS_IMETHOD RemoveWordFromPersonalDictionary(const nsAString &aWord);
   NS_IMETHOD GetPersonalDictionary(nsTArray<nsString> *aWordList);
 
   NS_IMETHOD GetDictionaryList(nsTArray<nsString> *aDictionaryList);
   NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary);
   NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary);
   NS_IMETHOD CheckCurrentDictionary();
+  void DeleteRemoteEngine();
 
 protected:
   virtual ~mozSpellChecker();
 
   nsCOMPtr<mozISpellI18NUtil> mConverter;
   nsCOMPtr<nsITextServicesDocument> mTsDoc;
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
 
   nsCOMPtr<mozISpellCheckingEngine>  mSpellCheckingEngine;
   bool mFromStart;
 
   nsresult SetupDoc(int32_t *outBlockOffset);
 
   nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *outBlockIndex);
 
   nsresult GetEngineList(nsCOMArray<mozISpellCheckingEngine> *aDictionaryList);
+
+  mozilla::PRemoteSpellcheckEngineChild *mEngine;
 };
 #endif // mozSpellChecker_h__
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1305,16 +1305,22 @@ public class BrowserApp extends GeckoApp
 
         } else if ("Settings:Show".equals(event)) {
             final String resource =
                     message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null);
             final Intent settingsIntent = new Intent(this, GeckoPreferences.class);
             GeckoPreferences.setResourceToOpen(settingsIntent, resource);
             startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES);
 
+            // Don't use a transition to settings if we're on a device where that
+            // would look bad.
+            if (HardwareUtils.IS_KINDLE_DEVICE) {
+                overridePendingTransition(0, 0);
+            }
+
         } else if ("Telemetry:Gather".equals(event)) {
             Telemetry.HistogramAdd("PLACES_PAGES_COUNT",
                     BrowserDB.getCount(getContentResolver(), "history"));
             Telemetry.HistogramAdd("PLACES_BOOKMARKS_COUNT",
                     BrowserDB.getCount(getContentResolver(), "bookmarks"));
             Telemetry.HistogramAdd("FENNEC_FAVICONS_COUNT",
                     BrowserDB.getCount(getContentResolver(), "favicons"));
             Telemetry.HistogramAdd("FENNEC_THUMBNAILS_COUNT",
@@ -1599,16 +1605,19 @@ public class BrowserApp extends GeckoApp
     private void openUrlAndStopEditing(String url, String searchEngine) {
         openUrlAndStopEditing(url, searchEngine, false);
     }
 
     private void openUrlAndStopEditing(String url, String searchEngine, boolean newTab) {
         int flags = Tabs.LOADURL_NONE;
         if (newTab) {
             flags |= Tabs.LOADURL_NEW_TAB;
+            if (Tabs.getInstance().getSelectedTab().isPrivate()) {
+                flags |= Tabs.LOADURL_PRIVATE;
+            }
         }
 
         Tabs.getInstance().loadUrl(url, searchEngine, -1, flags);
 
         mBrowserToolbar.cancelEdit();
     }
 
     private boolean isHomePagerVisible() {
--- a/mobile/android/base/home/RecentTabsPanel.java
+++ b/mobile/android/base/home/RecentTabsPanel.java
@@ -29,18 +29,16 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.Loader;
-import android.support.v4.widget.CursorAdapter;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -247,19 +245,17 @@ public class RecentTabsPanel extends Hom
 
         // Only modify mClosedTabs on the UI thread
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mClosedTabs = closedTabs;
 
                 // Reload the cursor to show recently closed tabs.
-                if (mClosedTabs.length > 0 && canLoad()) {
-                    getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
-                }
+                getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
             }
         });
     }
 
     private void openTabsWithType(int type) {
         final Cursor c = mAdapter.getCursor();
         if (c == null || !c.moveToFirst()) {
             return;
@@ -305,18 +301,18 @@ public class RecentTabsPanel extends Hom
             if (closedTabs != null && closedTabs.length > 0) {
                 // How many closed tabs are actually displayed.
                 int visibleClosedTabs = 0;
 
                 final int length = closedTabs.length;
                 for (int i = 0; i < length; i++) {
                     final String url = closedTabs[i].url;
 
-                    // Don't show recent tabs for about:home.
-                    if (!AboutPages.isAboutHome(url)) {
+                    // Don't show recent tabs for about:home or about:privatebrowsing.
+                    if (!AboutPages.isTitlelessAboutPage(url)) {
                         // If this is the first closed tab we're adding, add a header for the section.
                         if (visibleClosedTabs == 0) {
                             addRow(c, null, context.getString(R.string.home_closed_tabs_title), RecentTabs.TYPE_HEADER);
                         }
                         addRow(c, url, closedTabs[i].title, RecentTabs.TYPE_CLOSED);
                         visibleClosedTabs++;
                     }
                 }
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -28,16 +28,17 @@ import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.TelemetryContract.Method;
 import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
 import org.mozilla.gecko.home.HomePanelPicker;
 import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.FloatingHintEditText;
 
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.Fragment;
@@ -81,16 +82,21 @@ extends PreferenceActivity
 implements
 GeckoActivityStatus,
 GeckoEventListener,
 OnPreferenceChangeListener,
 OnSharedPreferenceChangeListener
 {
     private static final String LOGTAG = "GeckoPreferences";
 
+    // We have a white background, which makes transitions on
+    // some devices look bad. Don't use transitions on those
+    // devices.
+    private static final boolean NO_TRANSITIONS = HardwareUtils.IS_KINDLE_DEVICE;
+
     private static final String NON_PREF_PREFIX = "android.not_a_preference.";
     public static final String INTENT_EXTRA_RESOURCES = "resource";
     public static String PREFS_HEALTHREPORT_UPLOAD_ENABLED = NON_PREF_PREFIX + "healthreport.uploadEnabled";
 
     private static boolean sIsCharEncodingEnabled = false;
     private boolean mInitialized = false;
     private int mPrefsRequestId = 0;
     private PanelsPreferenceCategory mPanelsPreferenceCategory;
@@ -128,16 +134,29 @@ OnSharedPreferenceChangeListener
     public static final int RESULT_CODE_LOCALE_DID_CHANGE = 7;
 
     /**
      * Track the last locale so we know whether to redisplay.
      */
     private Locale lastLocale = Locale.getDefault();
     private boolean localeSwitchingIsEnabled;
 
+    private void startActivityForResultChoosingTransition(final Intent intent, final int requestCode) {
+        startActivityForResult(intent, requestCode);
+        if (NO_TRANSITIONS) {
+            overridePendingTransition(0, 0);
+        }
+    }
+
+    private void finishChoosingTransition() {
+        finish();
+        if (NO_TRANSITIONS) {
+            overridePendingTransition(0, 0);
+        }
+    }
     private void updateActionBarTitle(int title) {
         if (Build.VERSION.SDK_INT >= 14) {
             final String newTitle = getString(title);
             if (newTitle != null) {
                 Log.v(LOGTAG, "Setting action bar title to " + newTitle);
 
                 final ActionBar actionBar = getActionBar();
                 if (actionBar != null) {
@@ -245,20 +264,20 @@ OnSharedPreferenceChangeListener
         }
 
         // Cause the current fragment to redisplay, the hard way.
         // This avoids nonsense with trying to reach inside fragments and force them
         // to redisplay themselves.
         // We also don't need to update the title.
         final Intent intent = (Intent) getIntent().clone();
         intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-        startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
+        startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
 
         setResult(RESULT_CODE_LOCALE_DID_CHANGE);
-        finish();
+        finishChoosingTransition();
     }
 
     private void checkLocale() {
         final Locale currentLocale = Locale.getDefault();
         Log.v(LOGTAG, "Checking locale: " + currentLocale + " vs " + lastLocale);
         if (currentLocale.equals(lastLocale)) {
             return;
         }
@@ -436,16 +455,25 @@ OnSharedPreferenceChangeListener
         mInitialized = true;
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
             PreferenceScreen screen = getPreferenceScreen();
             mPrefsRequestId = setupPreferences(screen);
         }
     }
 
     @Override
+    public void onBackPressed() {
+        super.onBackPressed();
+
+        if (NO_TRANSITIONS) {
+            overridePendingTransition(0, 0);
+        }
+    }
+
+    @Override
     protected void onDestroy() {
         super.onDestroy();
         EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
             "Sanitize:Finished");
         if (mPrefsRequestId > 0) {
             PrefsHelper.removeObserver(mPrefsRequestId);
         }
     }
@@ -489,30 +517,33 @@ OnSharedPreferenceChangeListener
     @Override
     public void startActivity(Intent intent) {
         // For settings, we want to be able to pass results up the chain
         // of preference screens so Settings can behave as a single unit.
         // Specifically, when we open a link, we want to back out of all
         // the settings screens.
         // We need to start nested PreferenceScreens withStartActivityForResult().
         // Android doesn't let us do that (see Preference.onClick), so we're overriding here.
-        startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
+        startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
     }
 
     @Override
     public void startWithFragment(String fragmentName, Bundle args,
             Fragment resultTo, int resultRequestCode, int titleRes, int shortTitleRes) {
         Log.v(LOGTAG, "Starting with fragment: " + fragmentName + ", title " + titleRes);
 
         // Overriding because we want to use startActivityForResult for Fragment intents.
         Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes);
         if (resultTo == null) {
-            startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
+            startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
         } else {
             resultTo.startActivityForResult(intent, resultRequestCode);
+            if (NO_TRANSITIONS) {
+                overridePendingTransition(0, 0);
+            }
         }
     }
 
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         // We might have just returned from a settings activity that allows us
         // to switch locales, so reflect any change that occurred.
         checkLocale();
@@ -520,17 +551,17 @@ OnSharedPreferenceChangeListener
         switch (requestCode) {
           case REQUEST_CODE_PREF_SCREEN:
               switch (resultCode) {
               case RESULT_CODE_EXIT_SETTINGS:
                   updateActionBarTitle(R.string.settings_title);
 
                   // Pass this result up to the parent activity.
                   setResult(RESULT_CODE_EXIT_SETTINGS);
-                  finish();
+                  finishChoosingTransition();
                   break;
               }
               break;
 
           case HomePanelPicker.REQUEST_CODE_ADD_PANEL:
               switch (resultCode) {
                   case Activity.RESULT_OK:
                       // Panel installed, refresh panels list.
@@ -685,17 +716,17 @@ OnSharedPreferenceChangeListener
                             return true;
                         }
                     });
                 } else if (PREFS_HOME_ADD_PANEL.equals(key)) {
                     pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                         @Override
                         public boolean onPreferenceClick(Preference preference) {
                             Intent dialogIntent = new Intent(GeckoPreferences.this, HomePanelPicker.class);
-                            startActivityForResult(dialogIntent, HomePanelPicker.REQUEST_CODE_ADD_PANEL);
+                            startActivityForResultChoosingTransition(dialogIntent, HomePanelPicker.REQUEST_CODE_ADD_PANEL);
                             return true;
                         }
                     });
                 }
 
                 // Some Preference UI elements are not actually preferences,
                 // but they require a key to work correctly. For example,
                 // "Clear private data" requires a key for its state to be
@@ -735,17 +766,17 @@ OnSharedPreferenceChangeListener
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         int itemId = item.getItemId();
         switch (itemId) {
             case android.R.id.home:
-                finish();
+                finishChoosingTransition();
                 return true;
         }
 
         // Generated R.id.* apparently aren't constant expressions, so they can't be switched.
         if (itemId == R.id.restore_defaults) {
             restoreDefaultSearchEngines();
             return true;
        }
@@ -1002,17 +1033,17 @@ OnSharedPreferenceChangeListener
 
         if (preference instanceof ListPreference) {
             // We need to find the entry for the new value
             int newIndex = ((ListPreference) preference).findIndexOfValue((String) newValue);
             CharSequence newEntry = ((ListPreference) preference).getEntries()[newIndex];
             ((ListPreference) preference).setSummary(newEntry);
         } else if (preference instanceof LinkPreference) {
             setResult(RESULT_CODE_EXIT_SETTINGS);
-            finish();
+            finishChoosingTransition();
         } else if (preference instanceof FontSizePreference) {
             final FontSizePreference fontSizePref = (FontSizePreference) preference;
             fontSizePref.setSummary(fontSizePref.getSavedFontSizeName());
         }
 
         return true;
     }
 
--- a/mobile/android/base/preferences/SyncPreference.java
+++ b/mobile/android/base/preferences/SyncPreference.java
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.preferences;
 
 import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
+import org.mozilla.gecko.util.HardwareUtils;
 
 import android.content.Context;
 import android.content.Intent;
 import android.preference.Preference;
 import android.util.AttributeSet;
 
 class SyncPreference extends Preference {
     private static final boolean DEFAULT_TO_FXA = true;
@@ -34,16 +35,21 @@ class SyncPreference extends Preference 
         }
         Intent intent = new Intent(mContext, SetupSyncActivity.class);
         mContext.startActivity(intent);
     }
 
     private void launchFxASetup() {
         Intent intent = new Intent(mContext, FxAccountGetStartedActivity.class);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        if (HardwareUtils.IS_KINDLE_DEVICE) {
+            intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+        }
+
         mContext.startActivity(intent);
     }
 
     @Override
     protected void onClick() {
         // If we're not defaulting to FxA, just do what we've always done.
         if (!DEFAULT_TO_FXA) {
             openSync11Settings();
--- a/mobile/android/base/resources/layout/private_tabs_panel.xml
+++ b/mobile/android/base/resources/layout/private_tabs_panel.xml
@@ -1,50 +1,55 @@
 <?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/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
-    <org.mozilla.gecko.tabspanel.TabsTray android:id="@+id/private_tabs_tray"
-                                          style="@style/TabsList"
-                                          android:layout_width="match_parent"
-                                          android:layout_height="match_parent"
-                                          android:choiceMode="singleChoice"
-                                          gecko:tabs="tabs_private"/>
+   <FrameLayout android:layout_width="match_parent"
+                android:layout_height="match_parent">
 
-    <LinearLayout android:id="@+id/private_tabs_empty"
-                  style="@style/TabsPanelFrame.PrivateTabs"
-                  android:layout_width="match_parent"
-                  android:layout_height="match_parent">
+        <org.mozilla.gecko.tabspanel.TabsTray android:id="@+id/private_tabs_tray"
+                                              style="@style/TabsList"
+                                              android:layout_width="match_parent"
+                                              android:layout_height="match_parent"
+                                              android:choiceMode="singleChoice"
+                                              gecko:tabs="tabs_private"/>
+
+        <LinearLayout android:id="@+id/private_tabs_empty"
+                      style="@style/TabsPanelFrame.PrivateTabs"
+                      android:layout_width="match_parent"
+                      android:layout_height="match_parent">
+
+            <LinearLayout style="@style/TabsPanelSection.PrivateTabs.Header"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content">
 
-        <LinearLayout style="@style/TabsPanelSection.PrivateTabs.Header"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content">
+                <TextView style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content"
+                          android:text="@string/private_browsing_title"/>
+
+                <TextView style="@style/TabsPanelItem.TextAppearance"
+                          android:layout_width="match_parent"
+                          android:layout_height="wrap_content"
+                          android:text="@string/private_tabs_panel_empty_desc"/>
 
-            <TextView style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content"
-                      android:text="@string/private_browsing_title"/>
+            </LinearLayout>
+
+            <LinearLayout style="@style/TabsPanelSection.PrivateTabs"
+                          android:layout_width="match_parent"
+                          android:layout_height="match_parent">
 
-            <TextView style="@style/TabsPanelItem.TextAppearance"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content"
-                      android:text="@string/private_tabs_panel_empty_desc"/>
+                <TextView android:id="@+id/private_tabs_learn_more"
+                          style="@style/TabsPanelItem.TextAppearance.Linkified.LearnMore"
+                          android:layout_width="match_parent"
+                          android:text="@string/private_tabs_panel_learn_more"/>
+
+            </LinearLayout>
 
         </LinearLayout>
 
-        <LinearLayout style="@style/TabsPanelSection.PrivateTabs"
-                      android:layout_width="match_parent"
-                      android:layout_height="match_parent">
-
-            <TextView android:id="@+id/private_tabs_learn_more"
-                      style="@style/TabsPanelItem.TextAppearance.Linkified.LearnMore"
-                      android:layout_width="match_parent"
-                      android:text="@string/private_tabs_panel_learn_more"/>
-
-        </LinearLayout>
-
-    </LinearLayout>
+    </FrameLayout>
 
 </merge>
--- a/mobile/android/base/resources/values-land/styles.xml
+++ b/mobile/android/base/resources/values-land/styles.xml
@@ -28,17 +28,17 @@
         <item name="android:paddingTop">0dp</item>
     </style>
 
     <style name="TabsPanelSection" parent="TabsPanelSectionBase">
         <item name="android:layout_weight">1</item>
     </style>
 
     <style name="TabsPanelSection.PrivateTabs.Header">
-        <item name="android:paddingTop">18dp</item>
+        <item name="android:paddingTop">10dp</item>
     </style>
 
     <style name="TabsPanelItem">
         <item name="android:layout_marginBottom">20dp</item>
         <item name="android:layout_gravity">left</item>
         <item name="android:gravity">left</item>
     </style>
 
--- a/mobile/android/base/resources/values-large-land-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-land-v11/styles.xml
@@ -83,17 +83,17 @@
 
     <style name="TabsPanelItem.Button" parent="TabsPanelItem.ButtonBase">
         <item name="android:paddingTop">12dp</item>
         <item name="android:paddingBottom">12dp</item>
         <item name="android:paddingLeft">6dp</item>
         <item name="android:paddingRight">6dp</item>
         <item name="android:layout_marginLeft">12dp</item>
         <item name="android:layout_marginRight">12dp</item>
-        <item name="android:textSize">16dp</item>
+        <item name="android:textSize">16sp</item>
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Header.FXAccounts">
         <item name="android:visibility">gone</item>
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Header.PrivateTabs">
         <item name="android:visibility">visible</item>
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -469,18 +469,18 @@
     </style>
 
     <style name="TabsPanelFrame.PrivateTabs">
         <!-- We set values on tablet. -->
     </style>
 
     <style name="TabsPanelSectionBase">
         <item name="android:orientation">vertical</item>
-        <item name="android:layout_marginLeft">16dp</item>
-        <item name="android:layout_marginRight">16dp</item>
+        <item name="android:layout_marginLeft">40dp</item>
+        <item name="android:layout_marginRight">40dp</item>
     </style>
 
     <style name="TabsPanelSection" parent="TabsPanelSectionBase">
         <!-- We set values in landscape. -->
     </style>
 
     <style name="TabsPanelSection.PrivateTabs">
         <!-- We set values on tablet. -->
@@ -498,36 +498,36 @@
 
     <style name="TabsPanelItem" parent="TabsPanelItemBase">
         <!-- We set values in landscape. -->
     </style>
 
     <style name="TabsPanelItem.ButtonBase">
         <item name="android:background">@drawable/remote_tabs_setup_button_background</item>
         <item name="android:textColor">#FFFEFF</item>
-        <item name="android:textSize">20sp</item>
+        <item name="android:textSize">16sp</item>
         <item name="android:gravity">center</item>
     </style>
 
     <style name="TabsPanelItem.Button" parent="TabsPanelItem.ButtonBase">
         <item name="android:paddingTop">18dp</item>
         <item name="android:paddingBottom">18dp</item>
         <item name="android:paddingLeft">9dp</item>
         <item name="android:paddingRight">9dp</item>
-        <item name="android:layout_marginLeft">24dp</item>
-        <item name="android:layout_marginRight">24dp</item>
     </style>
 
     <style name="TabsPanelItem.TextAppearance">
         <item name="android:textColor">#C0C9D0</item>
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineSpacingMultiplier">1.35</item>
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Header">
-        <item name="android:textSize">20sp</item>
+        <item name="android:textSize">18sp</item>
+        <item name="android:layout_marginBottom">16dp</item>
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Header.FXAccounts">
         <!-- We change these values on landscape. -->
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Header.PrivateTabs">
         <!-- We change these values on landscape and tablet. -->
--- a/mobile/android/base/tabspanel/PrivateTabsPanel.java
+++ b/mobile/android/base/tabspanel/PrivateTabsPanel.java
@@ -12,24 +12,25 @@ import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.tabspanel.TabsPanel.CloseAllPanelView;
 
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
+import android.widget.ScrollView;
 
 /**
  * A container that wraps the private tabs {@link android.widget.AdapterView} and empty
  * {@link android.view.View} to manage both of their visibility states by changing the visibility of
  * this container as calling {@link android.widget.AdapterView#setVisibility} does not affect the
  * empty View's visibility.
  */
-class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
+class PrivateTabsPanel extends ScrollView implements CloseAllPanelView {
     private TabsPanel tabsPanel;
     private TabsTray tabsTray;
 
     public PrivateTabsPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         LayoutInflater.from(context).inflate(R.layout.private_tabs_panel, this);
         tabsTray = (TabsTray) findViewById(R.id.private_tabs_tray);
--- a/mobile/android/base/tabspanel/RemoteTabsList.java
+++ b/mobile/android/base/tabspanel/RemoteTabsList.java
@@ -37,17 +37,17 @@ class RemoteTabsList extends ExpandableL
 
     private final Context context;
     private TabsPanel tabsPanel;
 
     private ArrayList <HashMap <String, String>> clients;
     private ArrayList <ArrayList <HashMap <String, String>>> tabsList;
 
     // A list of the clients that are currently expanded.
-    private List<String> expandedClientList = new ArrayList<String>();
+    private List<String> expandedClientList;
 
     // The client that previously had an item selected is used to restore the scroll position.
     private String clientScrollPosition;
 
     public RemoteTabsList(Context context, AttributeSet attrs) {
         super(context, attrs);
         this.context = context;
 
@@ -138,27 +138,36 @@ class RemoteTabsList extends ExpandableL
                                                    R.layout.remote_tabs_group,
                                                    CLIENT_KEY,
                                                    CLIENT_RESOURCE,
                                                    tabsList,
                                                    R.layout.remote_tabs_child,
                                                    TAB_KEY,
                                                    TAB_RESOURCE));
 
-        // Expand the client groups, and restore the previous scroll position.
+        // Either set the initial UI state, or restore it.
         List<String> newExpandedClientList = new ArrayList<String>();
         for (int i = 0; i < clients.size(); i++) {
             final String clientGuid = clients.get(i).get("guid");
-            if (expandedClientList.contains(clientGuid)) {
+
+            if (expandedClientList == null) {
+                // On initial entry we expand all clients by default.
                 newExpandedClientList.add(clientGuid);
                 expandGroup(i);
-            }
+            } else {
+                // On subsequent entries, we expand clients based on their previous UI state.
+                if (expandedClientList.contains(clientGuid)) {
+                    newExpandedClientList.add(clientGuid);
+                    expandGroup(i);
+                }
 
-            if (clientGuid.equals(clientScrollPosition)) {
-                setSelectedGroup(i);
+                // Additionally we reset the UI scroll position.
+                if (clientGuid.equals(clientScrollPosition)) {
+                    setSelectedGroup(i);
+                }
             }
         }
         expandedClientList = newExpandedClientList;
     }
 
     /**
      * Return a relative "Last synced" time span for the given tab record.
      *
--- a/mobile/android/base/util/HardwareUtils.java
+++ b/mobile/android/base/util/HardwareUtils.java
@@ -25,16 +25,20 @@ public final class HardwareUtils {
     // xpcom/base/nsMemoryImpl.cpp) and should only be used in cases
     // where we can't depend on Gecko to be up and running e.g. show/hide
     // reading list capabilities in HomePager.
     private static final int LOW_MEMORY_THRESHOLD_MB = 384;
 
     // Number of bytes of /proc/meminfo to read in one go.
     private static final int MEMINFO_BUFFER_SIZE_BYTES = 256;
 
+    private static final boolean IS_AMAZON_DEVICE = Build.MANUFACTURER.equalsIgnoreCase("Amazon");
+    public static final boolean IS_KINDLE_DEVICE = IS_AMAZON_DEVICE &&
+                                                   (Build.MODEL.equals("Kindle Fire") ||
+                                                    Build.MODEL.startsWith("KF"));
     private static volatile int sTotalRAM = -1;
 
     private static volatile boolean sInited;
 
     // These are all set once, during init.
     private static volatile boolean sIsLargeTablet;
     private static volatile boolean sIsSmallTablet;
     private static volatile boolean sIsTelevision;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1015,19 +1015,16 @@ var BrowserApp = {
       this.selectedTab = null;
 
     let tabIndex = this._tabs.indexOf(aTab);
 
     let evt = document.createEvent("UIEvents");
     evt.initUIEvent("TabClose", true, false, window, tabIndex);
     aTab.browser.dispatchEvent(evt);
 
-    aTab.destroy();
-    this._tabs.splice(tabIndex, 1);
-
     if (aShowUndoToast) {
       // Get a title for the undo close toast. Fall back to the URL if there is no title.
       let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
       let closedTabData = ss.getClosedTabs(window)[0];
 
       let message;
       let title = closedTabData.entries[closedTabData.index - 1].title;
 
@@ -1038,21 +1035,24 @@ var BrowserApp = {
       }
 
       NativeWindow.toast.show(message, "short", {
         button: {
           icon: "drawable://undo_button_icon",
           label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
           callback: function() {
             UITelemetry.addEvent("undo.1", "toast", null, "closetab");
-            ss.undoCloseTab(window, 0);
+            ss.undoCloseTab(window, closedTabData);
           }
         }
       });
     }
+
+    aTab.destroy();
+    this._tabs.splice(tabIndex, 1);
   },
 
   // Use this method to select a tab from JS. This method sends a message
   // to Java to select the tab in the Java UI (we'll get a Tab:Selected message
   // back from Java when that happens).
   selectTab: function selectTab(aTab) {
     if (!aTab) {
       Cu.reportError("Error trying to select tab (tab doesn't exist)");
--- a/mobile/android/components/SessionStore.idl
+++ b/mobile/android/components/SessionStore.idl
@@ -9,17 +9,17 @@ interface nsIDOMNode;
 
 /**
  * nsISessionStore keeps track of the current browsing state.
  *
  * The nsISessionStore API operates mostly on browser windows and the browser
  * tabs contained in them.
  */
 
-[scriptable, uuid(91eca9cf-6741-4c8f-a3a0-2e957240894d)]
+[scriptable, uuid(5497d9a1-c378-47a9-9488-4c47a644131a)]
 interface nsISessionStore : nsISupports
 {
   /**
    * Get the current browsing state.
    * @returns a JSON string representing the session state.
    */
   AString getBrowserState();
 
@@ -33,20 +33,20 @@ interface nsISessionStore : nsISupports
    *
    * @param aWindow is the browser window for which to get closed tab data
    * @returns a JS array of closed tabs.
    */
   jsval getClosedTabs(in nsIDOMWindow aWindow);
 
   /**
    * @param aWindow is the browser window to reopen a closed tab in.
-   * @param aIndex  is the index of the tab to be restored (FIFO ordered).
+   * @param aCloseTabData is the data of the tab to be restored.
    * @returns a reference to the reopened tab.
    */
-  nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
+  nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in jsval aCloseTabData);
 
   /**
    * @param aWindow is the browser window associated with the closed tab.
    * @param aIndex  is the index of the closed tab to be removed (FIFO ordered).
    */
   nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
 
   /**
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -13,16 +13,17 @@ Cu.import("resource://gre/modules/Servic
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
   "@mozilla.org/xre/app-info;1", "nsICrashReporter");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "sendMessageToJava", "resource://gre/modules/Messaging.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 function dump(a) {
   Services.console.logStringMessage(a);
 }
 
 // -----------------------------------------------------------------------
 // Session Store
 // -----------------------------------------------------------------------
@@ -79,16 +80,17 @@ SessionStore.prototype = {
         observerService.addObserver(this, "final-ui-startup", true);
         observerService.addObserver(this, "domwindowopened", true);
         observerService.addObserver(this, "domwindowclosed", true);
         observerService.addObserver(this, "browser:purge-session-history", true);
         observerService.addObserver(this, "Session:Restore", true);
         observerService.addObserver(this, "application-background", true);
         observerService.addObserver(this, "ClosedTabs:StartNotifications", true);
         observerService.addObserver(this, "ClosedTabs:StopNotifications", true);
+        observerService.addObserver(this, "last-pb-context-exited", true);
         break;
       case "final-ui-startup":
         observerService.removeObserver(this, "final-ui-startup");
         this.init();
         break;
       case "domwindowopened": {
         let window = aSubject;
         window.addEventListener("load", function() {
@@ -164,16 +166,23 @@ SessionStore.prototype = {
         break;
       case "ClosedTabs:StartNotifications":
         this._notifyClosedTabs = true;
         this._sendClosedTabsToJava(Services.wm.getMostRecentWindow("navigator:browser"));
         break;
       case "ClosedTabs:StopNotifications":
         this._notifyClosedTabs = false;
         break;
+      case "last-pb-context-exited":
+        // Clear private closed tab data when we leave private browsing.
+        for (let [, window] in Iterator(this._windows)) {
+          window.closedTabs = window.closedTabs.filter(tab => !tab.isPrivate);
+        }
+        this._lastClosedTabIndex = -1;
+        break;
     }
   },
 
   handleEvent: function ss_handleEvent(aEvent) {
     let window = aEvent.currentTarget.ownerDocument.defaultView;
     switch (aEvent.type) {
       case "TabOpen": {
         let browser = aEvent.target;
@@ -360,16 +369,23 @@ SessionStore.prototype = {
         this._restoreHistory(data, aBrowser.sessionHistory);
 
       delete aBrowser.__SS_restore;
       aBrowser.removeAttribute("pending");
     }
 
     this.saveStateDelayed();
     this._updateCrashReportURL(aWindow);
+
+    // If the selected tab has changed while listening for closed tab
+    // notifications, we may have switched between different private browsing
+    // modes.
+    if (this._notifyClosedTabs) {
+      this._sendClosedTabsToJava(aWindow);
+    }
   },
 
   saveStateDelayed: function ss_saveStateDelayed() {
     if (!this._saveTimer) {
       // Interval until the next disk operation is allowed
       let minimalDelay = this._lastSaveTime + this._interval - Date.now();
 
       // If we have to wait, set a timer, otherwise saveState directly
@@ -844,46 +860,46 @@ SessionStore.prototype = {
 
   getClosedTabs: function ss_getClosedTabs(aWindow) {
     if (!aWindow.__SSID)
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
 
     return this._windows[aWindow.__SSID].closedTabs;
   },
 
-  undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
+  undoCloseTab: function ss_undoCloseTab(aWindow, aCloseTabData) {
     if (!aWindow.__SSID)
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
 
     let closedTabs = this._windows[aWindow.__SSID].closedTabs;
     if (!closedTabs)
       return null;
 
-    // default to the most-recently closed tab
-    aIndex = aIndex || 0;
-    if (!(aIndex in closedTabs))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-
-    // fetch the data of closed tab, while removing it from the array
-    let closedTab = closedTabs.splice(aIndex, 1).shift();
+    // If the tab data is in the closedTabs array, remove it.
+    closedTabs.find(function (tabData, i) {
+      if (tabData == aCloseTabData) {
+        closedTabs.splice(i, 1);
+        return true;
+      }
+    });
 
     // create a new tab and bring to front
     let params = {
       selected: true,
-      isPrivate: closedTab.isPrivate,
-      desktopMode: closedTab.desktopMode,
+      isPrivate: aCloseTabData.isPrivate,
+      desktopMode: aCloseTabData.desktopMode,
       tabIndex: this._lastClosedTabIndex
     };
-    let tab = aWindow.BrowserApp.addTab(closedTab.entries[closedTab.index - 1].url, params);
-    this._restoreHistory(closedTab, tab.browser.sessionHistory);
+    let tab = aWindow.BrowserApp.addTab(aCloseTabData.entries[aCloseTabData.index - 1].url, params);
+    this._restoreHistory(aCloseTabData, tab.browser.sessionHistory);
 
     this._lastClosedTabIndex = -1;
 
     // Put back the extra data
-    tab.browser.__SS_extdata = closedTab.extData;
+    tab.browser.__SS_extdata = aCloseTabData.extData;
 
     if (this._notifyClosedTabs) {
       this._sendClosedTabsToJava(aWindow);
     }
 
     return tab.browser;
   },
 
@@ -910,19 +926,20 @@ SessionStore.prototype = {
     }
   },
 
   _sendClosedTabsToJava: function ss_sendClosedTabsToJava(aWindow) {
     if (!aWindow.__SSID)
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
 
     let closedTabs = this._windows[aWindow.__SSID].closedTabs;
+    let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aWindow.BrowserApp.selectedBrowser.contentWindow);
 
     let tabs = closedTabs
-      .filter(tab => !tab.isPrivate)
+      .filter(tab => tab.isPrivate == isPrivate)
       .map(function (tab) {
         // Get the url and title for the last entry in the session history.
         let lastEntry = tab.entries[tab.entries.length - 1];
         return {
           url: lastEntry.url,
           title: lastEntry.title || ""
         };
       });
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -536,17 +536,17 @@
         <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
         <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
         <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
         <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
         <xul:menuseparator/>
         <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
         <xul:menuseparator anonid="spell-check-separator"/>
         <xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
-                      oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
+                      oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled(window);"/>
         <xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
           <xul:menupopup anonid="spell-dictionaries-menu"
                          onpopupshowing="event.stopPropagation();"
                          onpopuphiding="event.stopPropagation();"/>
         </xul:menu>
       </xul:menupopup>
     </content>
 
--- a/toolkit/devtools/server/protocol.js
+++ b/toolkit/devtools/server/protocol.js
@@ -1102,19 +1102,17 @@ let Front = Class({
       let msg = "Unexpected packet " + this.actorID + ", " + JSON.stringify(packet);
       let err = Error(msg);
       console.error(err);
       throw err;
     }
 
     let deferred = this._requests.shift();
     if (packet.error) {
-      let message = (packet.error == "unknownError" && packet.message) ?
-                    packet.message : packet.error;
-      deferred.reject(message);
+      deferred.reject(packet.error);
     } else {
       deferred.resolve(packet);
     }
   }
 });
 exports.Front = Front;
 
 /**
--- a/toolkit/modules/InlineSpellChecker.jsm
+++ b/toolkit/modules/InlineSpellChecker.jsm
@@ -186,17 +186,17 @@ InlineSpellChecker.prototype = {
       var item = menu.ownerDocument.createElement("menuitem");
       item.setAttribute("id", "spell-check-dictionary-" + sortedList[i].id);
       item.setAttribute("label", sortedList[i].label);
       item.setAttribute("type", "radio");
       this.mDictionaryItems.push(item);
       if (curlang == sortedList[i].id) {
         item.setAttribute("checked", "true");
       } else {
-        var callback = function(me, val) { return function(evt) { me.selectDictionary(val); } };
+        var callback = function(me, val) { return function(evt) { me.selectDictionary(val, me.menu.ownerDocument.defaultView); } };
         item.addEventListener("command", callback(this, i), true);
       }
       if (insertBefore)
         menu.insertBefore(item, insertBefore);
       else
         menu.appendChild(item);
     }
     return list.length;
@@ -267,18 +267,30 @@ InlineSpellChecker.prototype = {
   {
     for (var i = 0; i < this.mDictionaryItems.length; i ++) {
       this.mDictionaryMenu.removeChild(this.mDictionaryItems[i]);
     }
     this.mDictionaryItems = [];
   },
 
   // callback for selecting a dictionary
-  selectDictionary: function(index)
+  selectDictionary: function(index, aWindow)
   {
+    // Avoid a crash in multiprocess until Bug 1030451 lands
+    // Remove aWindow parameter at that time
+    const Ci = Components.interfaces;
+    let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                                  getInterface(Ci.nsIWebNavigation).
+                                  QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+                                  QueryInterface(Ci.nsIInterfaceRequestor).
+                                  getInterface(Ci.nsIXULWindow).chromeFlags;
+    let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
+    if (chromeFlags & chromeRemoteWindow) {
+      return;
+    }
     if (! this.mInlineSpellChecker || index < 0 || index >= this.mDictionaryNames.length)
       return;
     var spellchecker = this.mInlineSpellChecker.spellChecker;
     spellchecker.SetCurrentDictionary(this.mDictionaryNames[index]);
     this.mInlineSpellChecker.spellCheckRange(null); // causes recheck
   },
 
   // callback for selecting a suggesteed replacement
@@ -288,18 +300,30 @@ InlineSpellChecker.prototype = {
       return;
     if (index < 0 || index >= this.mSpellSuggestions.length)
       return;
     this.mInlineSpellChecker.replaceWord(this.mWordNode, this.mWordOffset,
                                          this.mSpellSuggestions[index]);
   },
 
   // callback for enabling or disabling spellchecking
-  toggleEnabled: function()
+  toggleEnabled: function(aWindow)
   {
+    // Avoid a crash in multiprocess until Bug 1030451 lands
+    // Remove aWindow parameter at that time
+    const Ci = Components.interfaces;
+    let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+                                  getInterface(Ci.nsIWebNavigation).
+                                  QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
+                                  QueryInterface(Ci.nsIInterfaceRequestor).
+                                  getInterface(Ci.nsIXULWindow).chromeFlags;
+    let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
+    if (chromeFlags & chromeRemoteWindow) {
+      return;
+    }
     this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
   },
 
   // callback for adding the current misspelling to the user-defined dictionary
   addToDictionary: function()
   {
     // Prevent the undo stack from growing over the max depth
     if (this.mAddedWordStack.length == MAX_UNDO_STACK_DEPTH)