Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 22 Mar 2015 14:20:40 -0700
changeset 263842 467b5a5b5f71c55b1f78699bac900fff85f17470
parent 263841 e0f47fcda5cde133697378cf7a2753a2d9538053 (current diff)
parent 263809 e730012260a437b2aa0a018f99f70de682d58f66 (diff)
child 263843 9ff253e22f9b4fd17bfeb96c3d8c9cf445b2037e
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i
mobile/android/base/tests/reader_mode_pages/addons.mozilla.org/en-US/firefox/index.html
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "c8aecdd05ddc11013945923b8d5fa4df077e56a0", 
+        "git_revision": "9b6f3024e4d0e62dd057231f4b14abe1782932ab", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "78b760e2859cb7504f864aa96a5b98748ae1bbb6", 
+    "revision": "4c2751f5fc24ffad381aae2c50b160cba0f33d36", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c8aecdd05ddc11013945923b8d5fa4df077e56a0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1874,13 +1874,13 @@ pref("dom.ipc.reportProcessHangs", false
 pref("dom.ipc.reportProcessHangs", true);
 #endif
 
 #ifndef NIGHTLY_BUILD
 // Disable reader mode by default.
 pref("reader.parse-on-load.enabled", false);
 #endif
 
-// Disable ReadingList browser UI by default.
-pref("browser.readinglist.enabled", false);
+// Enable ReadingList browser UI by default.
+pref("browser.readinglist.enabled", true);
 pref("browser.readinglist.sidebarEverOpened", false);
 // Enable the readinglist engine by default.
 pref("readinglist.scheduler.enabled", true);
--- a/browser/base/content/browser-loop.js
+++ b/browser/base/content/browser-loop.js
@@ -357,17 +357,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
      * - {Integer} windowId  The new windowId for the browser.
      *
      * @param {Function} listener The listener to receive information on when the
      *                            windowId changes.
      */
     addBrowserSharingListener: function(listener) {
       if (!this._tabChangeListeners) {
         this._tabChangeListeners = new Set();
-        gBrowser.addEventListener("select", this);
+        gBrowser.tabContainer.addEventListener("TabSelect", this);
       }
 
       this._tabChangeListeners.add(listener);
       this._maybeShowBrowserSharingInfoBar();
 
       // Get the first window Id for the listener.
       listener(null, gBrowser.selectedBrowser.outerWindowID);
     },
@@ -383,17 +383,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
       }
 
       if (this._tabChangeListeners.has(listener)) {
         this._tabChangeListeners.delete(listener);
       }
 
       if (!this._tabChangeListeners.size) {
         this._hideBrowserSharingInfoBar();
-        gBrowser.removeEventListener("select", this);
+        gBrowser.tabContainer.removeEventListener("TabSelect", this);
         delete this._tabChangeListeners;
       }
     },
 
     /**
      * Helper function to fetch a localized string via the MozLoopService API.
      * It's currently inconveniently wrapped inside a string of stringified JSON.
      *
@@ -479,24 +479,24 @@ XPCOMUtils.defineLazyModuleGetter(this, 
       return removed;
     },
 
     /**
      * Handles events from gBrowser.
      */
     handleEvent: function(event) {
       // We only should get "select" events.
-      if (event.type != "select") {
+      if (event.type != "TabSelect") {
         return;
       }
 
       let wasVisible = false;
       // Hide the infobar from the previous tab.
-      if (event.fromTab) {
-        wasVisible = this._hideBrowserSharingInfoBar(false, event.fromTab.linkedBrowser);
+      if (event.detail.previousTabfromTab) {
+        wasVisible = this._hideBrowserSharingInfoBar(false, event.detail.previousTab.linkedBrowser);
       }
 
       // We've changed the tab, so get the new window id.
       for (let listener of this._tabChangeListeners) {
         try {
           listener(null, gBrowser.selectedBrowser.outerWindowID);
         } catch (ex) {
           Cu.reportError("Tab switch caused an error: " + ex.message);
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -301,16 +301,19 @@
 #else
                 <menuitem id="fullScreenItem"
                           accesskey="&fullScreenCmd.accesskey;"
                           label="&fullScreenCmd.label;"
                           key="key_fullScreen"
                           type="checkbox"
                           observes="View:FullScreen"/>
 #endif
+                <menuitem id="menu_readerModeItem"
+                          observes="View:ReaderView"
+                          hidden="true"/>
                 <menuitem id="menu_showAllTabs"
                           hidden="true"
                           accesskey="&showAllTabsCmd.accesskey;"
                           label="&showAllTabsCmd.label;"
                           command="Browser:ShowAllTabs"
                           key="key_showAllTabs"/>
                 <menuseparator hidden="true" id="documentDirection-separator"/>
                 <menuitem id="documentDirection-swap"
--- a/browser/base/content/browser-readinglist.js
+++ b/browser/base/content/browser-readinglist.js
@@ -53,16 +53,21 @@ let ReadingListUI = {
    */
   uninit() {
     Preferences.ignore("browser.readinglist.enabled", this.updateUI, this);
 
     const mm = window.messageManager;
     for (let msg of this.MESSAGES) {
       mm.removeMessageListener(msg, this);
     }
+
+    if (this.listenerRegistered) {
+      ReadingList.removeListener(this);
+      this.listenerRegistered = false;
+    }
   },
 
   /**
    * Whether the ReadingList feature is enabled or not.
    * @type {boolean}
    */
   get enabled() {
     return Preferences.get("browser.readinglist.enabled", false);
@@ -86,17 +91,17 @@ let ReadingListUI = {
       // This is a no-op if we're already registered.
       ReadingList.addListener(this);
       this.listenerRegistered = true;
     } else {
       if (this.listenerRegistered) {
         // This is safe to call if we're not currently registered, but we don't
         // want to forcibly load the normally lazy-loaded module on startup.
         ReadingList.removeListener(this);
-        this.listenerRegistered = true;
+        this.listenerRegistered = false;
       }
 
       this.hideSidebar();
     }
 
     document.getElementById(READINGLIST_COMMAND_ID).setAttribute("hidden", !enabled);
   },
 
@@ -246,16 +251,22 @@ let ReadingListUI = {
     if (!uri) {
       this.toolbarButton.setAttribute("hidden", true);
       if (this.isSidebarOpen)
         document.getElementById("sidebar").contentWindow.postMessage(msg, "*");
       return;
     }
 
     let isInList = yield ReadingList.hasItemForURL(uri);
+
+    if (window.closed) {
+      // Skip updating the UI if the window was closed since our hasItemForURL call.
+      return;
+    }
+
     if (this.isSidebarOpen) {
       if (isInList)
         msg.url = typeof uri == "string" ? uri : uri.spec;
       document.getElementById("sidebar").contentWindow.postMessage(msg, "*");
     }
     this.setToolbarButtonState(isInList);
   }),
 
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -36,16 +36,17 @@
     <command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/>
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(window.gBrowser.selectedBrowser.contentDocumentAsCPOW);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
+    <command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
     <command id="cmd_find"
              oncommand="gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
              observes="isImage"/>
     <command id="cmd_findPrevious"
              oncommand="gFindBar.onFindAgainCommand(true);"
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -822,25 +822,24 @@
                 <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
               </box>
               <hbox id="urlbar-icons">
                 <image id="page-report-button"
                        class="urlbar-icon"
                        hidden="true"
                        tooltiptext="&pageReportIcon.tooltip;"
                        onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
-                <toolbarbutton id="readinglist-addremove-button"
-                               class="tabbable urlbar-icon"
-                               hidden="true"
-                               oncommand="ReadingListUI.togglePageByBrowser(gBrowser.selectedBrowser);"/>
-                <toolbarbutton id="reader-mode-button"
-                               class="tabbable"
-                               hidden="true"
-                               onclick="ReaderParent.handleReaderButtonEvent(event);"
-                               onkeypress="ReaderParent.handleReaderButtonEvent(event);"/>
+                <image id="readinglist-addremove-button"
+                       class="urlbar-icon"
+                       hidden="true"
+                       onclick="ReadingListUI.togglePageByBrowser(gBrowser.selectedBrowser);"/>
+                <image id="reader-mode-button"
+                       class="urlbar-icon"
+                       hidden="true"
+                       onclick="ReaderParent.toggleReaderMode(event);"/>
               </hbox>
               <toolbarbutton id="urlbar-go-button"
                              class="chromeclass-toolbar-additional"
                              onclick="gURLBar.handleCommand(event);"
                              tooltiptext="&goEndCap.tooltip;"/>
               <toolbarbutton id="urlbar-reload-button"
                              class="chromeclass-toolbar-additional"
                              command="Browser:ReloadOrDuplicate"
--- a/browser/base/content/test/general/browser_offlineQuotaNotification.js
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -46,18 +46,30 @@ function checkInContentPreferences(win) 
   is(tab, "networkTab", "Network tab is selected");
   // all good, we are done.
   win.close();
   finish();
 }
 
 function test() {
   waitForExplicitFinish();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+
+  Services.prefs.setBoolPref("offline-apps.allow_by_default", false);
+
+  // Open a new tab.
+  gBrowser.selectedTab = gBrowser.addTab(URL);
+  registerCleanupFunction(() => gBrowser.removeCurrentTab());
+
+
+  Promise.all([
+    // Wait for a notification that asks whether to allow offline storage.
+    promiseNotification(),
+    // Wait for the tab to load.
+    BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser)
+  ]).then(() => {
     gBrowser.selectedBrowser.contentWindow.applicationCache.oncached = function() {
       executeSoon(function() {
         // We got cached - now we should have provoked the quota warning.
         let notification = PopupNotifications.getNotification('offline-app-usage');
         ok(notification, "have offline-app-usage notification");
         // select the default action - this should cause the preferences
         // window to open - which we track either via a window watcher (for
         // the window-based prefs) or via an "Initialized" event (for
@@ -82,13 +94,19 @@ function test() {
         }
       });
     };
     Services.prefs.setIntPref("offline-apps.quota.warn", 1);
 
     // Click the notification panel's "Allow" button.  This should kick
     // off updates which will call our oncached handler above.
     PopupNotifications.panel.firstElementChild.button.click();
-  }, true);
+  });
+}
 
-  Services.prefs.setBoolPref("offline-apps.allow_by_default", false);
-  gBrowser.contentWindow.location = URL;
+function promiseNotification() {
+  return new Promise(resolve => {
+    PopupNotifications.panel.addEventListener("popupshown", function onShown() {
+      PopupNotifications.panel.removeEventListener("popupshown", onShown);
+      resolve();
+    });
+  });
 }
--- a/browser/base/content/test/general/browser_readerMode.js
+++ b/browser/base/content/test/general/browser_readerMode.js
@@ -60,16 +60,21 @@ add_task(function* () {
   // After we click ReadingList button, status should be "open".
   listButton.click();
   yield promiseWaitForCondition(() => listButton.classList.contains("on"));
   ok(listButton.classList.contains("on"),
     "List button should now indicate SideBar-ReadingList open.");
   ok(ReadingListUI.isSidebarOpen,
     "The ReadingListUI should now indicate SideBar-ReadingList open.");
 
+  // Now close the sidebar.
+  listButton.click();
+  yield promiseWaitForCondition(() => !listButton.classList.contains("on"));
+  ok(!ReadingListUI.isSidebarOpen, "The sidebar should be closed.");
+
   readerButton.click();
   yield promiseTabLoadEvent(tab);
   is(gBrowser.selectedBrowser.currentURI.spec, url, "Original page loaded after clicking active reader mode button");
 
   // Load a new tab that is NOT reader-able.
   let newTab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(newTab, "about:robots");
   yield promiseWaitForCondition(() => readerButton.hidden);
--- a/browser/modules/ReaderParent.jsm
+++ b/browser/modules/ReaderParent.jsm
@@ -110,36 +110,36 @@ let ReaderParent = {
 
   updateReaderButton: function(browser) {
     let win = browser.ownerDocument.defaultView;
     if (browser != win.gBrowser.selectedBrowser) {
       return;
     }
 
     let button = win.document.getElementById("reader-mode-button");
+    let command = win.document.getElementById("View:ReaderView");
     if (browser.currentURI.spec.startsWith("about:reader")) {
       button.setAttribute("readeractive", true);
       button.hidden = false;
-      button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerView.close"));
+      let closeText = gStringBundle.GetStringFromName("readerView.close");
+      button.setAttribute("tooltiptext", closeText);
+      command.setAttribute("label", closeText);
+      command.setAttribute("hidden", false);
     } else {
       button.removeAttribute("readeractive");
-      button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerView.enter"));
       button.hidden = !browser.isArticle;
+      let enterText = gStringBundle.GetStringFromName("readerView.enter");
+      button.setAttribute("tooltiptext", enterText);
+      command.setAttribute("label", enterText);
+      command.setAttribute("hidden", !browser.isArticle);
     }
+    command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.accesskey"));
   },
 
-  handleReaderButtonEvent: function(event) {
-    event.stopPropagation();
-
-    if ((event.type == "click" && event.button != 0) ||
-        (event.type == "keypress" && event.charCode != Ci.nsIDOMKeyEvent.DOM_VK_SPACE &&
-         event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_RETURN)) {
-      return; // Left click, space or enter only
-    }
-
+  toggleReaderMode: function(event) {
     let win = event.target.ownerDocument.defaultView;
     let browser = win.gBrowser.selectedBrowser;
     let url = browser.currentURI.spec;
 
     if (url.startsWith("about:reader")) {
       let originalURL = this._getOriginalUrl(url);
       if (!originalURL) {
         Cu.reportError("Error finding original URL for about:reader URL: " + url);
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1625,40 +1625,31 @@ richlistitem[type~="action"][actiontype=
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
 %include ../shared/readinglist/readinglist.inc.css
 
-#readinglist-addremove-button {
-  padding: 0 2px;
-}
-
 /* Reader mode button */
 
 #reader-mode-button {
-  -moz-appearance: none;
-  padding: 0;
-  list-style-image: url("chrome://browser/skin/reader-mode-16.png");
+  list-style-image: url("chrome://browser/skin/readerMode.svg");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#reader-mode-button > .toolbarbutton-icon {
-  width: 16px;
-}
-
-#reader-mode-button:focus {
-  outline: 1px dotted;
+#reader-mode-button:hover,
+#reader-mode-button[readeractive]:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #reader-mode-button:hover:active,
 #reader-mode-button[readeractive] {
-   -moz-image-region: rect(0, 32px, 16px, 16px);
+  -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* social share panel */
 
 .social-share-frame {
   border-top: 1px solid #f8f8f8;
   width: 756px;
   height: 150px;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -85,17 +85,17 @@ browser.jar:
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/session-restore.svg                  (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                      (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/welcome-back.svg                     (../shared/incontent-icons/welcome-back.svg)
-  skin/classic/browser/reader-mode-16.png             (../shared/reader/reader-mode-16.png)
+  skin/classic/browser/readerMode.svg                       (../shared/reader/readerMode.svg)
   skin/classic/browser/readinglist/icons.svg          (../shared/readinglist/icons.svg)
   skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg)
 * skin/classic/browser/readinglist/sidebar.css        (readinglist/sidebar.css)
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/webRTC-sharingDevice-16.png    (../shared/webrtc/webRTC-sharingDevice-16.png)
   skin/classic/browser/webRTC-shareMicrophone-16.png
   skin/classic/browser/webRTC-shareMicrophone-64.png
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2524,56 +2524,30 @@ richlistitem[type~="action"][actiontype=
   #page-report-button:hover:active,
   #page-report-button[open="true"] {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 }
 
 %include ../shared/readinglist/readinglist.inc.css
 
-#readinglist-addremove-button {
-  padding: 3px;
-  -moz-padding-start: 2px;
-  -moz-padding-end: 1px;
-}
-
 /* Reader mode button */
 
 #reader-mode-button {
-  -moz-appearance: none;
-  padding: 0;
-  list-style-image: url("chrome://browser/skin/reader-mode-16.png");
+  list-style-image: url("chrome://browser/skin/readerMode.svg");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#reader-mode-button > .toolbarbutton-icon {
-  width: 16px;
-}
-
-#reader-mode-button:focus {
-  @hudButtonFocused@
-}
-
-#reader-mode-button:hover:active,
+#reader-mode-button:hover:active {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
 #reader-mode-button[readeractive] {
-   -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-@media (min-resolution: 2dppx) {
-  #reader-mode-button {
-    list-style-image: url("chrome://browser/skin/reader-mode-16@2x.png");
-    -moz-image-region: rect(0, 32px, 32px, 0);
-  }
-
-  #reader-mode-button:hover:active,
-  #reader-mode-button[readeractive] {
-    -moz-image-region: rect(0, 64px, 32px, 32px);
-  }
-}
-
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
 
 /* social share panel */
 .social-share-frame {
   border-top: 1px solid #f8f8f8;
   min-width: 756px;
   height: 150px;
   /* we resize our panels dynamically, make it look nice */
 }
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -135,18 +135,17 @@ browser.jar:
   skin/classic/browser/urlbar-history-dropmarker@2x.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-arrow@2x.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/urlbar-popup-blocked@2x.png
   skin/classic/browser/session-restore.svg            (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/welcome-back.svg               (../shared/incontent-icons/welcome-back.svg)
-  skin/classic/browser/reader-mode-16.png             (../shared/reader/reader-mode-16.png)
-  skin/classic/browser/reader-mode-16@2x.png          (../shared/reader/reader-mode-16@2x.png)
+  skin/classic/browser/readerMode.svg                 (../shared/reader/readerMode.svg)
   skin/classic/browser/readinglist/icons.svg          (../shared/readinglist/icons.svg)
   skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg)
 * skin/classic/browser/readinglist/sidebar.css        (readinglist/sidebar.css)
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-16@2x.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/webRTC-shareDevice-64@2x.png
   skin/classic/browser/webRTC-sharingDevice-16.png    (../shared/webrtc/webRTC-sharingDevice-16.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/reader/readerMode.svg
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0"
+ y="0"
+ width="48"
+ height="16"
+ viewBox="0 0 48 16">
+<defs>
+  <path id="glyphShape-readerMode-book" d="M5.5,5h-2C3.2,5,3,5.2,3,5.5S3.2,6,3.5,6h2 C5.8,6,6,5.8,6,5.5S5.8,5,5.5,5z M5.5,7h-2C3.2,7,3,7.2,3,7.5S3.2,8,3.5,8h2C5.8,8,6,7.8,6,7.5S5.8,7,5.5,7z M5.5,9h-2 C3.2,9,3,9.2,3,9.5S3.2,10,3.5,10h2C5.8,10,6,9.8,6,9.5S5.8,9,5.5,9z M15.4,2c0,0-3.1,0-4.4,0S8.1,2.5,8,4.3C7.9,2.5,6.3,2,5,2 S0.6,2,0.6,2C0.3,2,0,2.3,0,2.7v9.6C0,12.6,0.3,13,0.6,13c0,0,2.6,0,4.4,0c1.6,0,2.8,1,3,2.3C8.2,14,9.4,13,11,13 c1.8,0,4.4,0,4.4,0c0.4,0,0.6-0.4,0.6-0.8V2.7C16,2.3,15.7,2,15.4,2z M14,11L14,11c-0.2,0-1.6,0-3,0c-1.6,0-2.9,0.8-3,2.2 C7.9,11.8,6.6,11,5,11c-1.4,0-2.8,0-3,0l0,0l0,0V4c0,0,2.7,0,3.5,0C6.6,4,8,5.5,8,6.8C8,5.5,9.4,4,10.5,4C11.3,4,14,4,14,4V11 L14,11z"/>
+  <linearGradient id="gradient-state-default" x1="0%" y1="0%" x2="0" y2="100%">
+    <stop stop-color="#989898" offset="0%"/>
+    <stop stop-color="#808080" offset="100%"/>
+  </linearGradient>
+  <linearGradient id="gradient-state-hover" x1="0%" y1="0%" x2="0" y2="100%">
+    <stop stop-color="#24aef4" offset="0%"/>
+    <stop stop-color="#177bdb" offset="100%"/>
+  </linearGradient>
+  <linearGradient id="gradient-state-pressed" x1="0%" y1="0%" x2="0" y2="100%">
+    <stop stop-color="#ff9300" offset="0%"/>
+    <stop stop-color="#ff5500" offset="100%"/>
+  </linearGradient>
+  <style type="text/css">
+    .icon-state-default { fill: url(#gradient-state-default); }
+    .icon-state-hover   { fill: url(#gradient-state-hover); }
+    .icon-state-pressed { fill: url(#gradient-state-pressed); }
+  </style>
+</defs>
+<use xlink:href="#glyphShape-readerMode-book" class="icon-state-default"/>
+<use xlink:href="#glyphShape-readerMode-book" class="icon-state-hover"    transform="translate(16)"/>
+<use xlink:href="#glyphShape-readerMode-book" class="icon-state-pressed"  transform="translate(32)"/>
+</svg>
--- a/browser/themes/shared/readinglist/readinglist.inc.css
+++ b/browser/themes/shared/readinglist/readinglist.inc.css
@@ -1,27 +1,17 @@
 /* Reading List button */
 
 #urlbar:not([focused]):not(:hover) #readinglist-addremove-button {
   display: none;
 }
 
 #readinglist-addremove-button {
-  -moz-appearance: none;
-  border: none;
   list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage");
-}
-
-#readinglist-addremove-button:hover {
-  border: none;
-}
-
-#readinglist-addremove-button > .toolbarbutton-icon {
-  width: 14px;
-  height: 14px
+  -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 #readinglist-addremove-button:hover {
   list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage-hover");
 }
 
 #readinglist-addremove-button:active {
   list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage-active");
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1573,40 +1573,31 @@ richlistitem[type~="action"][actiontype=
 
 #page-report-button:hover:active,
 #page-report-button[open="true"] {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 %include ../shared/readinglist/readinglist.inc.css
 
-#readinglist-addremove-button {
-  padding: 0 2px;
-}
-
 /* Reader mode button */
 
 #reader-mode-button {
-  -moz-appearance: none;
-  padding: 0;
-  list-style-image: url("chrome://browser/skin/reader-mode-16.png");
+  list-style-image: url("chrome://browser/skin/readerMode.svg");
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#reader-mode-button > .toolbarbutton-icon {
-  width: 16px;
-}
-
-#reader-mode-button:focus {
-  outline: 1px dotted;
+#reader-mode-button:hover,
+#reader-mode-button[readeractive]:hover {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #reader-mode-button:hover:active,
 #reader-mode-button[readeractive] {
-   -moz-image-region: rect(0, 32px, 16px, 16px);
+  -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* social share panel */
 
 .social-share-frame {
   min-width: 756px;
   height: 150px;
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -104,17 +104,17 @@ browser.jar:
         skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
         skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
         skin/classic/browser/urlbar-arrow.png
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
         skin/classic/browser/session-restore.svg                     (../shared/incontent-icons/session-restore.svg)
         skin/classic/browser/tab-crashed.svg                         (../shared/incontent-icons/tab-crashed.svg)
         skin/classic/browser/welcome-back.svg                        (../shared/incontent-icons/welcome-back.svg)
-        skin/classic/browser/reader-mode-16.png                      (../shared/reader/reader-mode-16.png)
+        skin/classic/browser/readerMode.svg                          (../shared/reader/readerMode.svg)
         skin/classic/browser/readinglist/icons.svg                   (../shared/readinglist/icons.svg)
         skin/classic/browser/readinglist/readinglist-icon.svg        (../shared/readinglist/readinglist-icon.svg)
 *       skin/classic/browser/readinglist/sidebar.css                 (readinglist/sidebar.css)
         skin/classic/browser/notification-pluginNormal.png           (../shared/plugins/notification-pluginNormal.png)
         skin/classic/browser/notification-pluginAlert.png            (../shared/plugins/notification-pluginAlert.png)
         skin/classic/browser/notification-pluginBlocked.png          (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/browser/webRTC-shareDevice-16.png
         skin/classic/browser/webRTC-shareDevice-64.png
@@ -576,17 +576,17 @@ browser.jar:
         skin/classic/aero/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
         skin/classic/aero/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
         skin/classic/aero/browser/urlbar-arrow.png
         skin/classic/aero/browser/urlbar-popup-blocked.png
         skin/classic/aero/browser/urlbar-history-dropmarker.png
         skin/classic/aero/browser/session-restore.svg               (../shared/incontent-icons/session-restore.svg)
         skin/classic/aero/browser/tab-crashed.svg                   (../shared/incontent-icons/tab-crashed.svg)
         skin/classic/aero/browser/welcome-back.svg                  (../shared/incontent-icons/welcome-back.svg)
-        skin/classic/aero/browser/reader-mode-16.png                (../shared/reader/reader-mode-16.png)
+        skin/classic/aero/browser/readerMode.svg                    (../shared/reader/readerMode.svg)
         skin/classic/aero/browser/readinglist/icons.svg             (../shared/readinglist/icons.svg)
         skin/classic/aero/browser/readinglist/readinglist-icon.svg  (../shared/readinglist/readinglist-icon.svg)
 *       skin/classic/aero/browser/readinglist/sidebar.css           (readinglist/sidebar.css)
         skin/classic/aero/browser/notification-pluginNormal.png     (../shared/plugins/notification-pluginNormal.png)
         skin/classic/aero/browser/notification-pluginAlert.png      (../shared/plugins/notification-pluginAlert.png)
         skin/classic/aero/browser/notification-pluginBlocked.png    (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/aero/browser/webRTC-shareDevice-16.png
         skin/classic/aero/browser/webRTC-shareDevice-64.png
--- a/configure.in
+++ b/configure.in
@@ -6300,17 +6300,17 @@ dnl Installer
 dnl ========================================================
 dnl Abort Windows build if the required major version and
 dnl minimum minor version of Unicode NSIS isn't in the path
 dnl (unless in case of cross compiling, for which Unicode
 dnl is not yet sufficient).
 if test "$OS_ARCH" = "WINNT"; then
     MIN_NSIS_MAJOR_VER=2
     MIN_NSIS_MINOR_VER=46
-    MOZ_PATH_PROGS(MAKENSISU, $MAKENSISU makensisu-3.0a2.exe makensisu-2.46.exe makensis)
+    MOZ_PATH_PROGS(MAKENSISU, $MAKENSISU makensis-3.0b1.exe makensisu-3.0a2.exe makensisu-2.46.exe makensis)
     if test -n "$MAKENSISU" -a "$MAKENSISU" != ":"; then
       AC_MSG_RESULT([yes])
       MAKENSISU_VER=`"$MAKENSISU" -version 2>/dev/null`
       changequote(,)
       MAKENSISU_PARSED_VER=`echo "$MAKENSISU_VER" | sed -e '/-Unicode/!s/.*//g' -e 's/^v\([0-9]\+\.[0-9]\+\).*\-Unicode$/\1/g'`
       changequote([,])
       if test "$MAKENSISU_PARSED_VER" = ""; then
           changequote(,)
deleted file mode 100644
--- a/mobile/android/base/tests/reader_mode_pages/addons.mozilla.org/en-US/firefox/index.html
+++ /dev/null
@@ -1,320 +0,0 @@
-<!DOCTYPE html>
-<html lang="en-US" dir="ltr">
-  <head>
-          <style>
-        html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}.js-hidden{display:none}i{font-style:italic;color:#838382}.item details .vitals:after,header:after,header nav:after,section:after,.menu:after,.grouped_ratings:after,.persona-confirm:after,.persona-slider .confirm-buttons:after{content:".";display:block;clear:both;height:0;visibility:hidden}.locked #page{display:none}.moz-menu .tab{background:#33589f}footer:before,.tabs{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAYAAAASC7TOAAAAEElEQVQIHWP8//8/Aw7wHwBR6AP+G53o6QAAAABJRU5ErkJggg==),-moz-linear-gradient(#fff, #dcedfd);background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAYAAAASC7TOAAAAEElEQVQIHWP8//8/Aw7wHwBR6AP+G53o6QAAAABJRU5ErkJggg==),-webkit-gradient(linear,left top,left bottom,from(#fff),to( #dcedfd))}#page{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAYAAAASC7TOAAAAEElEQVQIHWP8//8/Aw7wHwBR6AP+G53o6QAAAABJRU5ErkJggg==),-moz-linear-gradient( #dcedfd 0,#fff 200px);background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAYAAAASC7TOAAAAEElEQVQIHWP8//8/Aw7wHwBR6AP+G53o6QAAAABJRU5ErkJggg==),-webkit-gradient(linear,left top,left 200,from( #dcedfd),to(#fff));margin-bottom:30px;min-height:430px}#sort-menu .label span:after,.moz-menu .tab a:after,.menu a.expando:after{content:"\00a0";display:block;position:absolute;right:8px;width:0;height:0;-moz-transition:.3s -moz-transform ease;-webkit-transition:.3s -webkit-transform ease;-moz-transform-origin:50% 25%;-webkit-transform-origin:50% 25%;top:16px;border:5px solid transparent;border-top-color:#000;pointer-events:none}html,body{height:100%}table{width:100%}table tr{border:1px solid #ddd}table th,table td{padding:4px 8px 4px 4px;vertical-align:top}.html-rtl table th,.html-rtl table td{padding:4px 4px 4px 8px}table th{background:#f4f4f4;color:#666;text-align:right;font-weight:700;width:50%}.html-rtl table th{text-align:left}#content{width:100%;overflow:hidden;position:relative;padding:8px 0 16px}.paginator{display:-moz-box;display:-webkit-box;text-align:center;color:#ccc;font-size:1.1em;width:100%;height:40px}.paginator .page{color:#666;margin:0 2%;-moz-box-flex:1;-webkit-box-flex:1}.paginator a{padding:14px}.paginator .next a:after{content:' \00bb'}.paginator .prev a:before{content:' \00ab'}#sort-menu{-moz-box-align:center;padding:0 14px}#sort-menu ul{height:0;position:relative;top:-1px;overflow:hidden;-moz-transition:.5s height ease;-webkit-transition:.5s height ease;border:0 solid transparent;border-width:1px 0}#sort-menu.expand ul{border-color:#a9badb;height:254px;margin:0 -14px}.purchases #sort-menu.expand ul{height:100px}#sort-menu li{border-top:1px solid #dde4ef}#sort-menu li a{display:block;height:26px;line-height:26px;padding:12px 14px}#sort-menu li:first-child{border-top:0}#sort-menu .label{color:#888}#sort-menu .label span{position:relative;display:inline-block;z-index:10;background:transparent;color:#447bc4;text-align:center;white-space:nowrap;border:1px solid transparent;border-width:1px 1px 0 1px;border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0;margin-left:4px;padding:11px 22px 11px 6px;-moz-transition:.3s background ease}#sort-menu.expand .label span{background:#fff;border-color:#a9badb;border-bottom:1px solid white;box-shadow:0 -1px 1px #dde4ef}#sort-menu .label span:after{top:16px;border:5px solid transparent;border-top-color:#447BC4}#sort-menu.expand .label span:after{-moz-transform:rotate(180deg);-webkit-transform:rotate(180deg)}.tabs{border-bottom:1px solid #a2bbdc;height:41px;font:16px Georgia,serif;padding:14px 14px 0;box-shadow:0 -1px 1px rgba(0,0,0,.1) inset}.tabs ul{display:-webkit-box;display:-moz-box;margin:0;width:100%;z-index:1}.tabs ul li{-moz-box-flex:1;-webkit-box-flex:1;border:0;-webkit-transition:.3s background ease;-moz-transition:.3s background ease}.tabs a:-moz-focusring{outline:0;border:0}.tabs ul li a{display:block;white-space:nowrap;text-align:center;color:#447bc4;padding:11px 8px 11px 6px}.tabs ul li.selected{background:#fff;box-shadow:0 -1px 1px rgba(0,0,0,.1);border:1px solid #a2bbdc;border-width:1px 1px 0 1px;border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0}.tabs ul li.selected a{color:#444;padding:11px 8px 11px 6px}.tab-pane{display:none}.tab-pane.selected{display:block}.slider{white-space:nowrap;position:relative;left:0;-moz-transition-duration:.3s;-moz-transition-property:left,right;-webkit-transition-duration:.3s;-webkit-transition-property:left,right}.html-rtl .slider{left:auto;right:0}.slider-mask{width:100%;overflow:hidden}.slider .tab-pane{vertical-align:top;width:100%;display:inline-block}#lightbox{background:rgba(0,0,0,.9);position:absolute;width:100%;height:101%;display:none;z-index:9000;color:#fff;text-align:center;opacity:0;overflow:hidden;pointer-events:none;-moz-transition:.5s opacity ease;-webkit-transition:.5s opacity ease}#lightbox .close{display:block;position:absolute;z-index:100;font-family:sans-serif;color:#fff;top:14px;right:14px;line-height:12px;text-align:center;-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px;width:12px;height:12px;padding:12px;border:1px solid white}#lightbox.show{pointer-events:auto;opacity:1}#lightbox section{position:relative;width:100%;height:100%}#lightbox .content{height:70%;display:block;position:relative;text-align:center}#lightbox .controls{height:25%;min-height:100px;position:relative;padding:5% 40px 0}#lightbox .control.prev,#lightbox .control.next{color:#fff;left:0;-moz-transition:.3s opacity ease}.html-rtl #lightbox .control.prev{left:auto;right:0}#lightbox .control.next{left:auto;right:0}.html-rtl #lightbox .control.next{right:auto;left:0}#lightbox .control.disabled{opacity:0}#lightbox .caption{font-size:18px}#lightbox img{max-width:95%;max-height:100%;-moz-transition:.5s opacity ease;-webkit-transition:.5s opacity ease;opacity:0;position:absolute}td .versions li a{display:block;margin:0 0 8px}td .versions li:last-child a{margin:0}.author,.author a{font-family:"Droid Sans",Helvetica,Arial,sans-serif;color:#888;font-size:12px;line-height:12px}.personas-grid h2{border-bottom:#ccc 1px solid}#persona-lists{padding-bottom:104px}#persona-header{margin:0 14px}#persona-header hgroup{padding-left:0}#persona-header h2{margin:0}#persona-header h3{color:#444;font-size:18px;margin-bottom:14px}#persona-header h2{font-style:normal;text-transform:uppercase}#persona h3,#persona .author{display:inline-block;font-size:14px}#persona h3{color:#444;font-family:"Droid Sans",Helvetica,sans-serif;font-weight:700}#persona .author,#persona .persona-large{margin-bottom:8px}#persona .badges{margin-top:0}#persona .badges li{margin:8px 0 0}ul.license{position:relative;top:3px}ul.license li{display:block;float:left;list-style:none;margin-right:2px}.html-rtl ul.license li{float:right;margin:0 0 0 2px}ul.license li.text{font-size:90%;line-height:15px;margin-left:4px}.html-rtl ul.license li.text{margin:0 4px 0 0}ul.license li.icon{background:url(../../media/img/zamboni/licenses.png%3Ffa83f55) no-repeat top left;height:15px;width:15px}ul.license li.cc-attrib{background-position:0 0}ul.license li.cc-noderiv{background-position:0 -65px}ul.license li.cc-noncom{background-position:0 -130px}ul.license li.cc-share{background-position:0 -195px}ul.license li.copyr{background-position:0 -260px}.persona-preview [data-browsertheme]{display:block;position:relative}.persona-large [data-browsertheme],.persona-large p{border-radius:6px}#listing-featured .persona-previewer:last-child .persona-slider{border-radius:0 0 6px 6px}.persona-large{max-width:680px}.persona-large [data-browsertheme]{background:transparent no-repeat right top;border-bottom:1px solid rgba(0,0,0,.4);display:table;height:64px;width:100%}.persona-large p{background-image:url(../../media/img/zamboni/mobile/loading-white.png%3F2816057);background-repeat:no-repeat;background-position:50% 50%;-moz-background-size:auto 32px;-wekbkit-background-size:auto 32px;background-size:auto 32px;color:#fff;display:none;font:18px Georgia,serif;pointer-events:none;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.5);vertical-align:middle}.persona-hover [data-browsertheme] p{background-color:rgba(0,0,0,.4);display:table-cell}.persona-previewing p,.persona-installed p{background:0}#persona .confirm-buttons,.persona-slider .badges{display:none}.persona-previewer .confirm-buttons .add{float:left;width:-moz-calc(50% - 63px)}.persona-previewer .confirm-buttons .cancel{float:right;width:-moz-calc(50% - 39px)}.persona-previewer .persona-installed p:before{background:url(../../media/img/zamboni/mobile/checkmark.png%3F521c6c8) no-repeat top left;content:" ";display:inline-block;margin:0 3px -3px 0;position:relative;top:3px;height:25px;width:25px}li.persona-previewer{padding:10px}.persona-slider{background-color:#ccc;height:0;overflow:hidden;padding:0 10px;position:relative;top:11px;left:-10px;width:100%;-moz-transition:.5s height ease;-webkit-transition:.5s height ease}.persona-slider.expand{border-top:1px solid #999}.persona-slider .more{clear:both;color:#447bc4;display:block;font-weight:700;line-height:1;padding-top:10px;text-align:center}.persona-slider .confirm-buttons{margin-top:10px}.version h3{font-size:20px;color:#000}.version time{display:block;font-family:"Droid Sans",Helvetica,Arial,sans-serif;margin:10px 0}.version blockquote{color:#666;font-family:Georgia,serif;overflow:hidden}.version .meta{font-style:italic;color:#666}.version .meta h4{margin-top:12px;font-family:"Droid Sans",Helvetica,Arial,sans-serif;font-weight:700;color:#666}.message{-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;margin:14px;padding:14px}.message h3{font-family:"Droid Sans",Helvetica,sans-serif;font-weight:700;margin-bottom:14px}.message.warning{background:#fff0a6}.apps-error-msg{display:none;padding:1em 0}#content .apps-error-msg h2{font-style:normal}#content .apps-error-msg p{margin-bottom:1em;margin-left:1em}.install-wrapper{margin:10px 0 4px;padding-top:8px;text-align:center;font-weight:700}.infobox .install-wrapper,#persona .persona-confirm{border-top:2px solid #fff;box-shadow:0 -1px #ccc}#persona .persona-confirm{padding-top:8px}.persona-confirm .install-wrapper{margin:0;padding-top:0}#persona .persona-confirm .install-wrapper{border-top-width:0;box-shadow:none}button{cursor:pointer;margin:0}button,.button,a.button{-moz-transition:-moz-box-shadow .3s ease 0s;background-color:#669BE1;border:0;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;box-shadow:0 3px rgba(0,0,0,.1),0 -4px rgba(0,0,0,.1) inset;-moz-box-shadow:0 3px rgba(0,0,0,.1),0 -4px rgba(0,0,0,.1) inset;-webkit-box-shadow:0 3px rgba(0,0,0,.1),0 -4px rgba(0,0,0,.1) inset;color:#fff;font:bold 20px/1 "Trebuchet MS",sans-serif;padding:12px 16px 14px;position:relative;text-align:center;display:block;text-decoration:none}a.button:link{color:#fff}button:active,.button:active,.button.selected{top:2px;-moz-box-shadow:0 1px rgba(0,0,0,.1),0 -4px rgba(0,0,0,.1) inset,0 0 100px rgba(255,255,255,.2) inset}.button.small{font-size:13px;font-weight:400;padding:6px 12px;padding-bottom:9px;-moz-box-shadow:0 2px rgba(0,0,0,.1),0 -3px rgba(0,0,0,.1) inset}.button.small:hover,.button.small:focus,.button.small:active,.button.small.selected{-moz-box-shadow:0 2px rgba(0,0,0,.1),0 -3px rgba(0,0,0,.1) inset,0 0 100px rgba(255,255,255,.2) inset}.button.small:active,.button.small.selected{top:2px;-moz-box-shadow:0 0 rgba(0,0,0,.1),0 -3px rgba(0,0,0,.1) inset,0 0 100px rgba(255,255,255,.2) inset}.button.preview,.button.affirmative,.button.add{background-color:#84C63C;color:#fff}.button.premium{background:#da6}.button.negative{background-color:#84C63C;color:#fff}.button.cancel{background-color:#b25951}.button.add{padding-left:40px}.button.add:before{content:" ";display:block;width:24px;height:24px;position:absolute;left:14px;top:10px;background:url(../../media/img/zamboni/mobile/install.svg%3Fad1ef3d) no-repeat top left}.button.warning{background-attachment:scroll,scroll;background-clip:border-box,border-box;background-color:transparent;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGlJREFUeNqc0cEJACEMRFFNARaw/RexFe1dG3AjCBJJYDL/kNtjDqn9lRLUnlrixjd5qVdo6WNQOhiXN05Jg7PyYEJuzMmFaRn+GZG6KrQMlxHpY1A6GJc3TkmDs/JgQm7MyYVpqf0CDABVcj3T2ITzOAAAAABJRU5ErkJggg==);background-origin:padding-box,padding-box;background-position:0 0,0 0;background-repeat:repeat,repeat;background-size:auto auto,auto auto;text-shadow:0 -1px 0 rgba(255,255,255,.5);top:0}.button.warning,.button.warning:link{color:#333}.button.add.warning:before{background:url(../../media/img/zamboni/mobile/install.svg%3Fad1ef3d) no-repeat 0 -50px}.button.disabled,.button.waiting{background-color:#d1d4d7;color:#fff;-moz-box-shadow:0 3px rgba(0,0,0,.05),0 -4px rgba(0,0,0,.05) inset;top:0}.button.add.disabled:before{background:url(../../media/img/zamboni/mobile/install.svg%3Fad1ef3d) no-repeat top left}.badges{margin-top:8px}.badges a,.badges li{display:block;color:#000}#content .badges li{border:1px solid #ddd;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;margin-bottom:8px;font-size:12px;line-height:20px;height:20px;background:#fff}#content .badges .error{background-color:#edd4d2;border-color:#ac9a98}#content .badges .warning{background-color:#fef9d7;border-color:#bebaa1}#content .badges .warning span{cursor:pointer}.install .privacy-policy{display:block;margin-top:12px;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;height:27px;font-size:.9em;color:#000;line-height:25px;border:1px solid #ccc;background-color:#e6e6e6}.badges li span{color:#447bc4}.lite-msg{display:none;font-size:.9em}#eula{display:block;position:absolute;top:0;left:0;width:100%;min-height:100%;background:#333;display:none}#eula-content{-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;margin:14px;padding:14px 0 70px 0;background:#fff}#eula-text{padding:0 14px;font-family:Georgia,serif;line-height:1.2em}#eula-body{margin-top:14px;color:#666}#eula-body ul{list-style:disc outside none;margin-left:20px}#eula-body ol{list-style:decimal outside none;margin-left:20px}#eula-menu{position:fixed;bottom:-1px;left:14px;width:-moz-calc(100% - 28px);background:#fff}#eula:after{position:absolute;content:"";bottom:0;height:20px;width:-moz-calc(100% - 28px);z-index:1000;border-width:14px;border-style:solid;pointer-events:none;border-color:transparent #333 #333;-moz-border-radius:14px;-webkit-border-radius:14px;border-radius:14px}#eula-menu div{margin-bottom:14px;overflow:hidden;position:relative;background:#fff;-moz-border-radius:0 0 6px 6px;-webkit-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}#eula-menu .button{margin:8px;display:block;float:left;width:-moz-calc(50% - 48px)}.stars{text-indent:-5000px;background-image:url(../../media/img/icons/stars.png%3F29bed4f);background-position:0 50%;background-repeat:no-repeat;width:63px;display:block}.reviews .stars{float:left}.html-rtl .stars{float:right}.stars-4{background-position:-13px 50%}.stars-3{background-position:-26px 50%}.stars-2{background-position:-39px 50%}.stars-1{background-position:-52px 50%}.stars-0{background-position:-65px 50%}.grouped_ratings{margin:16px 0}.grouped_ratings .stars-4{background-position:13px 50%}.grouped_ratings .stars-3{background-position:26px 50%}.grouped_ratings .stars-2{background-position:39px 50%}.grouped_ratings .stars-1{background-position:52px 50%}.grouped_ratings .stars-0{background-position:65px 50%}.grouped_ratings li{height:12px;margin-bottom:8px;clear:both}.grouped_ratings .rating_bar{float:left;height:12px;margin-top:4px;background:#eee;position:relative;overflow:visible;width:120px}.num_ratings{color:#888;width:1px;position:absolute;right:-6px;top:0;line-height:1em;font-size:.9em}.grouped_ratings .rating_bar span.bar{display:block;height:100%;background:#888;height:12px}.review p{max-height:6.2em;overflow:hidden}.html-rtl .grouped_ratings .rating_bar{border-left:0;float:right}.html-rtl .grouped_ratings .rating_bar span.bar{float:right}.html-rtl .grouped_ratings .rating_bar span.num_ratings{right:auto;left:-4px}.review .old-version{font-size:.8em;font-style:italic;line-height:1em;color:#888;margin-top:4px}.review{margin-bottom:1.5em}.carousel .control,#lightbox .control{display:block;position:absolute;top:0;opacity:.9;width:45px;height:95%;line-height:100px;font-size:48px;text-align:center;color:#447BC4;text-shadow:0 0 5px #fff;-moz-transition:.2s color ease;-webkit-transition:.2s color ease}.carousel .control.disabled{color:#ccc}.carousel .control.prev,.html-rtl .carousel .control.next{background-image:-moz-linear-gradient(left,#fff 20%,rgba(255,255,255,0));right:auto;left:0}.carousel .control.next,.html-rtl .carousel .control.prev{background-image:-moz-linear-gradient(left,rgba(255,255,255,0),#fff 80%);left:auto;right:0}.carousel{position:relative;padding:14px 40px;overflow:hidden;background-color:#fff;border-bottom:2px solid #dcedfd;margin-bottom:20px}.carousel ul{height:78px;white-space:nowrap;position:relative;left:0;-moz-transition-duration:.5s;-moz-transition-property:left,right;-webkit-transition-duration:.5s;-webkit-transition-property:left,right}.carousel li{display:inline-block;width:50%;text-align:center}.carousel li img{border:1px solid #afbed0;max-height:75px;max-width:100px;border-radius:6px;-moz-box-shadow:0 0 4px rgba(0,0,0,.2);-webkit-box-shadow:0 0 4px rgba(0,0,0,.2)}.browserid-divider{text-align:center;margin:2em 1em 0;border-bottom:1px dashed #ccc}.browserid-divider span{background-color:#FFF;font-style:italic;padding:0 15px;position:relative;top:.4em}#browserid{margin-bottom:2em;text-align:center}#browserid .button{margin:auto;display:inline-block}a.expando:-moz-focusring{border:0;outline:0}a.expando{color:#447BC4}.menu a.expando{position:relative;display:block;padding:10px 20px;float:right;color:#447BC4}.html-rtl .menu a.expando{float:left}.menu a.expando:after{border-top-color:#447BC4;top:15px}.menu a.expando.expand:after{-moz-transform:rotate(180deg);-webkit-transform:rotate(180deg)}.expando-managed{overflow:hidden;height:0;-moz-transition:.5s height ease;-webkit-transition:.5s height ease}.readmore{padding:10px 20px;color:#447BC4;text-align:right;display:none}body{font-family:Georgia,serif}a:link{color:#447bc4;text-decoration:none}.num_ratings,.review .old-version,.install-wrapper,table,table a:link{font-family:"Helvetica Neue",Arial,sans-serif}#full h1,#content h2,#eula h2{font-style:italic;margin:0 14px 14px;font-size:1.4em}#content #full{line-height:1.3}#content #full h1{margin:0}#content #full p{margin:1em 0 0}#content #full>p{margin:1em}#content h3{font-size:1.2em;padding-bottom:2px}#full p,.copy p{color:#666}p strong{font-weight:700}#content details,#content p,#content .copy,#content .menu{margin:0 14px}.copy{line-height:1.2em}#full h1,#content h2{padding-bottom:4px;border-bottom:1px solid #ccc;box-shadow:0 1px #fff}#content .error h2{border:0;box-shadow:none;padding:0}.htruncate{white-space:nowrap;overflow:hidden}.req,.errorlist,.error{color:#c00000}.form-mobile,#full form{padding:1em}.form-mobile.form-mobile label,#full form.form-mobile label,.form-mobile.form-mobile .errorlist,#full form.form-mobile .errorlist{font-family:"Helvetica Neue",Arial,sans-serif}.form-mobile label,#full form label{color:#666;display:block;padding-bottom:6px}.form-mobile label.check,#full form label.check{margin-bottom:1em}.form-mobile .errorlist,#full form .errorlist{font-weight:700;margin:0 0 1em}.form-mobile .errorlist+p input,#full form .errorlist+p input,.form-mobile .errorlist+p textarea,#full form .errorlist+p textarea{border-color:red;background-color:#FFEBEB}.form-mobile input[type=checkbox],#full form input[type=checkbox]{margin-bottom:.5em}.form-mobile input[type=text],#full form input[type=text],.form-mobile input[type=password],#full form input[type=password],.form-mobile select,#full form select,.form-mobile textarea,#full form textarea{margin-bottom:1em;font-size:1.3em}.form-mobile input[type=text]+.errorlist,#full form input[type=text]+.errorlist,.form-mobile input[type=password]+.errorlist,#full form input[type=password]+.errorlist,.form-mobile select+.errorlist,#full form select+.errorlist,.form-mobile textarea+.errorlist,#full form textarea+.errorlist{margin-top:-.5em}.form-mobile textarea,#full form textarea,.form-mobile input[type=text],#full form input[type=text],.form-mobile input[type=password],#full form input[type=password]{border:1px solid #ccc;padding:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%}.form-mobile .stars,#full form .stars{background-image:url(../../media/img/icons/stars-big.png%3F19d1207)}.form-mobile .stars label,#full form .stars label{height:40px}.form-mobile .stars-0,#full form .stars-0{background-position:-195px 50%}.form-mobile .stars-1,#full form .stars-1{background-position:-156px 50%}.form-mobile .stars-2,#full form .stars-2{background-position:-117px 50%}.form-mobile .stars-3,#full form .stars-3{background-position:-78px 50%}.form-mobile .stars-4,#full form .stars-4{background-position:-39px 50%}.form-mobile .stars-5,#full form .stars-5{background-position:0 50%}.form-mobile .ratingwidget,#full form .ratingwidget{display:block;float:none;width:195px;margin-bottom:1em;height:40px}.form-mobile .ratingwidget input,#full form .ratingwidget input{display:none}.form-mobile .ratingwidget label,#full form .ratingwidget label{text-indent:-5000px;display:block;float:left;width:39px}#content .notification-box{padding:1em;margin:1em;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}#content .notification-box h2{margin:0}#content .notification-box.error{background-color:#FFD5D5}.browserid-login.loading-submit:after{background-image:url(../../media/img/zamboni/loading-white.gif%3F005dbc7);content:"";display:block;height:16px;margin-left:10px;position:absolute;right:-32px;top:12px;width:16px}#home-header,.mini-header{margin:0 14px}.moz-menu{-moz-transition:.5s top ease;-webkit-transition:.5s top ease;transition:.5s top ease;position:relative;top:0;z-index:100}.moz-menu .tab{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;box-shadow:0 -3px rgba(0,0,0,.2) inset;-moz-box-shadow:0 -3px rgba(0,0,0,.2) inset;-webkit-box-shadow:0 -3px rgba(0,0,0,.2) inset}.moz-menu .tab a{background:url(../../media/img/zamboni/global/wordmark.png%3F71db942) no-repeat 8px 6px;display:block;height:30px;line-height:22px;text-align:right;color:#fff;font-style:italic;padding:8px 25px}.moz-menu .tab a:after{top:18px;border:6px solid transparent;border-top-color:#fff}.mini-header .moz-menu .tab{width:104px;float:right;overflow:hidden;position:relative}.mini-header .moz-menu .tab a{text-indent:-9999px;text-align:left}.html-rtl .mini-header .moz-menu .tab{float:left}.html-rtl .mini-header .moz-menu .tab a{text-indent:9999px}.moz-menu .menu-items{position:absolute;width:100%;left:-14px;right:-14px;bottom:46px}.moz-menu .menu-items li{border-bottom:1px solid #294976;border-top:1px solid #537ab4;background-color:#3a67af;height:50px;width:100%;padding:0 14px}.moz-menu .menu-items a{font:bold 17px/26px "Trebuchet MS",sans-serif;color:#fff;display:block;height:26px;padding:12px 0;text-transform:uppercase}.mini-header .moz-menu .menu-items{bottom:0}.moz-menu.expand{top:312px}.moz-menu.expand .tab a:after{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}header hgroup{margin:8px 0;position:relative;padding-left:80px}.html-rtl header hgroup{padding-left:0;padding-right:80px}.html-rtl .mini-header hgroup{padding-right:0}header nav ul{float:left;padding:14px 0}header nav ul li{float:left;line-height:14px}header nav ul a{color:#447bc4;display:block}header nav #auth-nav{float:right}.html-rtl header nav ul,.html-rtl header nav li{float:right}.html-rtl header nav #auth-nav{float:left}header #auth-nav li{border-left:1px solid #ccc;margin-left:7px;padding-left:7px}header #auth-nav li.user{font-weight:700}.html-ltr header #auth-nav li:first-child,.html-rtl header #auth-nav li:last-child{border-left:0;margin-left:0;padding-left:0}.mini-header .site-title{padding:4px 120px 0 0}.mini-header .site-title a{font-size:28px;line-height:50px}.html-rtl .mini-header .site-title{padding:4px 0 0 120px}#home-header hgroup{min-height:75px;margin-bottom:8px}#home-header h1{padding-top:4px}#home-header h2{margin:4px 0;color:#666}#home-header .site-title img{position:absolute;left:0;top:0}.mini-header hgroup{margin-top:0;padding:2px 0 0}.html-rtl #home-header .site-title img{left:auto;right:0}.site-title a{color:#000;font:36px/32px Georgia,serif;text-decoration:none;text-transform:uppercase}.site-title span{display:block}.mini-header .site-title img{float:left;height:50px;margin-right:6px}.html-rtl .mini-header .site-title img{float:right;margin:0 0 0 6px}#home-header #search{margin:0}#learnmore-msg{-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:1em;box-shadow:0 0 3px #000;-moz-box-shadow:0 0 3px #000;-webkit-box-shadow:0 0 3px #000;-moz-transition:.3s opacity ease;-webkit-transition:.3s opacity ease;transition:.3s opacity ease;background:#444;color:#fff;font:14px/1.3 "Helvetica Neue",Arial,sans-serif;margin:12px 14px 0;max-width:260px;opacity:0;padding:14px;pointer-events:none;position:absolute;left:0;z-index:100}#learnmore-msg.show{opacity:1;pointer-events:auto}#learnmore-msg:after{content:"\00a0";display:block;position:absolute;z-index:100;top:-28px;left:94px;width:0;height:0;border:15px solid transparent;border-bottom-color:#444;pointer-events:none}#learnmore{color:#447bc4;padding:4px 0}.get-fx-message{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;border:1px solid #e3873b;background:#fcefb7 url(../../media/img/zamboni/icons/alert-32x32.png%3Fee16f6d) 11px center no-repeat;clear:both;display:none;font-size:105%;margin-top:12px;line-height:1.1em;padding:12px 12px 10px 56px}#search{position:relative;margin-bottom:14px;width:100%}#search input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;background:url(../../media/img/zamboni/mobile/search.svg%3F6f451df) no-repeat 10px center #fff;border:1px solid #ddd;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;display:block;font-size:18px;margin-right:8px;padding:10px 10px 10px 40px;width:94%;width:-moz-calc(100% - 68px);width:-webkit-calc(100% - 68px);width:-o-calc(100% - 68px);width:calc(100% - 68px)}#search button{box-shadow:0 -3px rgba(0,0,0,.1) inset;-moz-box-shadow:0 -3px rgba(0,0,0,.1) inset;-webkit-box-shadow:0 -3px rgba(0,0,0,.1) inset;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;background-color:#83c53c;display:block;font-size:18px;padding:8px 0;position:absolute;top:0;right:0;width:56px}.html-rtl #search input[type=search]{background-position:99% 50%;margin:0 0 0 8px;padding:10px 40px 10px 10px}.html-rtl #search button{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1);right:auto;left:0}.noInlineSVG #search button{background:#83c53c url(../../media/img/zamboni/mobile/arrow.png%3F1ae5de1) center no-repeat}#content.search .item .install-wrapper{clear:both;margin:0 10px;padding:0}#content.search .item .badges{margin:0}#content.search .item .buttons,#content.search .item .privacy-policy{display:none}.no-results{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;border:1px solid #ddd;background-color:#f4f4f4;color:#999;font:bold 18px Georgia,serif;text-align:center;margin:14px 14px 1em;padding:14px}.listview,.infobox{margin:14px 14px 1em;border-radius:6px;-moz-border-radius:6px;-webkit-border-radius:6px;border:1px solid #ddd;background-color:#f4f4f4;display:block}.infobox{padding:10px}.listview>li{border-top:1px solid #ccc;font-size:1em;white-space:normal}.listview>li:first-child{border:0}.listview>li,a.listview{position:relative}.listview>li>a,.listview>li>.info>a,a.listview{color:#444;font-family:Georgia,serif;font-size:1.1em;display:block;overflow:hidden;text-decoration:none;padding:10px}.html-rtl .listview>li>a,.html-rtl .listview>li>.info>a,.html-rtl a.listview{padding-left:34px}.listview .item>a,.listview>li>.info>a,.listview div.item{padding:10px;font-size:1em}.listview li.item>a,.listview>li>.info>a{padding:10px 10px 0}.listview>li>a:before,.listview>li>.info>a:before,a.listview:before{content:" ";display:block;width:10px;height:100%;position:absolute;right:10px;top:0;background:url(../../media/img/zamboni/mobile/arrow.svg%3F8638b4c) no-repeat center 12px}.html-rtl .listview>li>a:before,.html-rtl .listview>li>.info>a:before,.html-rtl a.listview:before{right:auto;left:10px;-moz-transform:scalex(-1)}.icon{float:left;margin-right:8px}.html-rtl .icon{float:right;margin-left:8px;margin-right:0}li.item{line-height:1.3em;overflow:hidden;padding-left:42px}li.item .icon{float:none;margin:0;position:absolute;left:10px}li.item h3{font-size:1.1em}li.item .desc{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#666;display:block}li.item details{font-size:.9em;margin:0 0 7px!important;padding:0 10px}li.item .vital{color:#093;padding-left:.5em}li.item .vital:before{color:#aaa;content:"\B7";padding-right:.25em}li.item .vital:first-child{padding:0}li.item .vital:first-child:before{display:none}li.item .vital.premium{color:#d16b00}li.item .stars,li.item i,li.item .vital{display:block;float:left}li.item .info,li.item .action{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}li.item .info{float:left;width:80%}li.item .action{float:right;padding:14px 34px 14px 14px;text-align:right;width:20%}li.item .action,li.item .contributions{color:#999;font-size:13px;line-height:15px}li.item.refunded{opacity:.6}li.item .contributions{clear:both;margin-top:.5em}li.item .contributions .supportable:after{content:'\00B7';padding:0 .25em}.html-rtl li.item{padding:0 42px 0 0}.html-rtl li.item .icon{left:auto;right:10px}.html-rtl li.item .stars,.html-rtl li.item i,.html-rtl li.item .vital{float:right}.html-rtl li.item .vital{padding:0 .5em 0 0}.html-rtl li.item .vital:first-child{padding:0}.html-rtl li.item .price:before{padding:0 0 0 .25em}.html-rtl li.item .info{float:right}.html-rtl li.item .action{float:left;margin:0;padding:14px 14px 14px 34px;text-align:left}footer{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;box-shadow:0 2px rgba(0,0,0,.1) inset;-moz-box-shadow:0 2px rgba(0,0,0,.1) inset;-webkit-box-shadow:0 2px rgba(0,0,0,.1) inset;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAYAAAASC7TOAAAAFElEQVQIHWO0DZ3xnwELYOXgYwAAOHwCRjqu6SgAAAAASUVORK5CYII=);color:#fff;font:1em "Helvetica Neue",Arial,sans-serif;padding:14px;position:relative;z-index:-1}footer:before{box-shadow:0 -2px rgba(0,0,0,.1) inset;-moz-box-shadow:0 -2px rgba(0,0,0,.1) inset;-webkit-box-shadow:0 -2px rgba(0,0,0,.1) inset;content:"";display:block;height:30px;position:absolute;top:-30px;left:0;width:100%}footer.sticky{position:absolute;bottom:0;z-index:1;width:100%}footer select{display:block;height:30px;margin-top:4px;padding-top:4px;width:100%}footer a.desktop-link{background:#5f92ce;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;color:#fff;display:block;font-family:"Helvetica Neue",Arial,sans-serif;font-size:150%;font-weight:700;line-height:2;margin:1em 0;text-align:center;text-transform:uppercase}#footer-links,#footer-links a{color:#5f92ce;font-size:14px;padding:4px;text-align:center}
-      </style>
-        <script></script>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, user-scalable=no, maximum-scale=1">
-    <title>Add-ons for Firefox</title>
-
-    <link rel="shortcut icon" type="image/x-icon"
-          href="../../../addons.cdn.mozilla.net/media/img/favicon.ico%3Fb=4ddb6c0">
-
-    
-        <link title="Firefox Add-ons"
-          rel="search" type="application/opensearchdescription+xml"
-          href="https://addons.mozilla.org/en-US/firefox/opensearch.xml" />
-
-    
-      </head>
-  <body class="html-ltr firefox lang-en-US "
-        data-app="firefox"
-        data-appname="Firefox"
-        data-appid="1"
-        data-min-beta-version="3.7"
-        data-anonymous="true"
-        data-readonly="false"
-        data-media-url="https://addons.cdn.mozilla.net/media/"
-                        >
-
-    <div id="page">
-        <header id="home-header">
-    <div class="moz-menu">
-  <ul class="menu-items">
-    <li><a href="http://www.mozilla.com/mobile/">Mozilla Firefox</a></li>
-    <li><a href="http://www.mozilla.com/firefox/features/">Features</a></li>
-    <li><a href="http://mozilla.com/firefox/">Desktop</a></li>
-    <li><a href="http://addons.mozilla.org/">Add-Ons</a></li>
-    <li><a href="http://support.mozilla.com/">Support</a></li>
-    <li><a href="http://mozilla.org/">Visit Mozilla</a></li>
-  </ul>
-  <div class="tab"><a href="index.html#">menu</a></div>
-</div>          <div class="get-fx-message">
-        You need Firefox to install add-ons. <a href="http://mozilla.com/mobile">Learn More&nbsp;&raquo;</a>
-      </div>
-        <hgroup>
-      <h1 class="site-title">
-                <a href="index.html"
-          title="Return to the Firefox Add-ons homepage">
-          <img alt="Firefox" src="../../../addons.cdn.mozilla.net/media/img/zamboni/app_icons/firefox.png%3Fb=4ddb6c0">
-          Firefox Add-ons
-        </a>
-      </h1>
-      <h2>Easy ways to personalize.</h2>
-      <a href="index.html#" id="learnmore">Learn More&raquo;</a>
-      <div id="learnmore-msg">
-        Add-ons are applications that let you personalize Firefox with extra functionality and style. Whether you mistype the name of a website or can't read a busy page, there's an add-on to improve your on-the-go browsing.      </div>
-    </hgroup>
-      <form id="search" action="https://addons.mozilla.org/en-US/firefox/search/">
-  <input name="q" type="search" value=""
-         placeholder="search for add-ons">
-        <input type="hidden" name="appver" id="id_appver" />
-    <input type="hidden" name="platform" id="id_platform" />
-    <button type="submit" value="">
-    <svg width="22" height="23" xmlns="http://www.w3.org/2000/svg" version="1.1"><polygon fill="#ffffff" points="22,12 11,23 8,20 14,14 0,14 0,10 14,10 8,4 11,1"/></svg>
-  </button>
-</form>  </header>
-
-      <section id="content" class="">
-          <nav class="tabs" data-manages="addon-lists">
-    <ul>
-      <li class="selected"><a href="index.html#listing-featured">Featured</a></li>
-      <li><a href="index.html#listing-popular">Popular</a></li>
-      <li><a href="index.html#listing-categories">Categories</a></li>
-    </ul>
-  </nav>
-  <div id="addon-lists" class="slider">
-    <div id="listing-featured" class="tab-pane selected">
-      <ul id="list-featured" class="listview">
-                <li class="item">
-    <div class="info">
-                                <a href="https://addons.mozilla.org/en-US/firefox/addon/autopager/?src=homepagebrowse">
-        <img class="icon" width="32" height="32" src="../../../addons.cdn.mozilla.net/img/uploads/addon_icons/4/4925-32.png%3Fmodified=1347927624">
-        <h3>AutoPager</h3>
-        <span class="desc">AutoPager automatically loads next pages when you reach the end of a page. It works on a ton of sites,Google,Yahoo.. It works well with most other add-ons adblock plus, WOT and most of the greasemonke...</span>
-      </a>
-      <details>
-        <div class="vitals">
-                                      <span class="stars stars-4" title="Rated 4 out of 5 stars">Rated 4 out of 5 stars</span>
-
-                                  
-                      <span class="vital adu">
-                                              <strong>225,651</strong> users
-                          </span>
-                                      </div>
-      </details>
-    </div>
-          </li>
-        <li class="item">
-    <div class="info">
-                                <a href="https://addons.mozilla.org/en-US/firefox/addon/simple-timer/?src=homepagebrowse">
-        <img class="icon" width="32" height="32" src="../../../addons.cdn.mozilla.net/img/uploads/addon_icons/9/9675-32.png%3Fmodified=1330563742">
-        <h3>Simple Timer</h3>
-        <span class="desc">A timer/clock residing in the statusbar/toolbar.</span>
-      </a>
-      <details>
-        <div class="vitals">
-                                      <span class="stars stars-4" title="Rated 4 out of 5 stars">Rated 4 out of 5 stars</span>
-
-                                  
-                      <span class="vital adu">
-                                              <strong>10,531</strong> users
-                          </span>
-                                      </div>
-      </details>
-    </div>
-          </li>
-
-        <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/?sort=featured">
-          View all Featured add-ons</a></li>
-      </ul>
-    </div><div id="listing-popular" class="tab-pane">
-      <ul id="list-popular" class="listview">
-                <li class="item">
-    <div class="info">
-                                <a href="https://addons.mozilla.org/en-US/firefox/addon/adblock-plus/?src=homepagebrowse">
-        <img class="icon" width="32" height="32" src="../../../addons.cdn.mozilla.net/img/uploads/addon_icons/1/1865-32.png%3Fmodified=1346769870">
-        <h3>Adblock Plus</h3>
-        <span class="desc">Annoyed by adverts? Troubled by tracking? Bothered by banners? Install Adblock Plus now to regain control of the internet and change the way that you view the web. A short video overview is available ...</span>
-      </a>
-      <details>
-        <div class="vitals">
-                                      <span class="stars stars-5" title="Rated 5 out of 5 stars">Rated 5 out of 5 stars</span>
-
-                                  
-                      <span class="vital downloads">
-                                              <strong>935,408</strong> weekly downloads
-                          </span>
-                                      </div>
-      </details>
-    </div>
-          </li>
-        <li class="item">
-    <div class="info">
-                                <a href="https://addons.mozilla.org/en-US/firefox/addon/noscript/?src=homepagebrowse">
-        <img class="icon" width="32" height="32" src="../../../addons.cdn.mozilla.net/img/uploads/addon_icons/0/722-32.png%3Fmodified=1343588643">
-        <h3>NoScript</h3>
-        <span class="desc">The best security you can get in a web browser! Allow active content to run only from sites you trust, and protect yourself against XSS and Clickjacking attacks.</span>
-      </a>
-      <details>
-        <div class="vitals">
-                                      <span class="stars stars-5" title="Rated 5 out of 5 stars">Rated 5 out of 5 stars</span>
-
-                                  
-                      <span class="vital downloads">
-                                              <strong>117,491</strong> weekly downloads
-                          </span>
-                                      </div>
-      </details>
-    </div>
-          </li>
-        <li class="item">
-    <div class="info">
-                                <a href="https://addons.mozilla.org/en-US/firefox/addon/easy-youtube-video-downl-10137/?src=homepagebrowse">
-        <img class="icon" width="32" height="32" src="../../../addons.cdn.mozilla.net/img/uploads/addon_icons/10/10137-32.png%3Fmodified=1348505777">
-        <h3>Easy YouTube Video Downloader</h3>
-        <span class="desc">Fastest, easiest &amp; most reliable free YouTube downloader. Highest sound quality available with M4A, MP3, MP4, AAC, FLV and HD formats! Single click, non-intrusive, direct download button works directl...</span>
-      </a>
-      <details>
-        <div class="vitals">
-                                      <span class="stars stars-4" title="Rated 4 out of 5 stars">Rated 4 out of 5 stars</span>
-
-                                  
-                      <span class="vital downloads">
-                                              <strong>321,726</strong> weekly downloads
-                          </span>
-                                      </div>
-      </details>
-    </div>
-          </li>
-
-        <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/?sort=popular">
-          View all Popular add-ons</a></li>
-      </ul>
-    </div><div id="listing-categories" class="tab-pane">
-            <ul id="list-popular" class="listview">
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/alerts-updates/">
-          Alerts &amp; Updates</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/appearance/">
-          Appearance</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/bookmarks/">
-          Bookmarks</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/download-management/">
-          Download Management</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/feeds-news-blogging/">
-          Feeds, News &amp; Blogging</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/games-entertainment/">
-          Games &amp; Entertainment</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/language-support/">
-          Language Support</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/photos-music-videos/">
-          Photos, Music &amp; Videos</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/privacy-security/">
-          Privacy &amp; Security</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/shopping/">
-          Shopping</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/social-communication/">
-          Social &amp; Communication</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/tabs/">
-          Tabs</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/web-development/">
-          Web Development</a></li>
-                <li><a href="https://addons.mozilla.org/en-US/firefox/extensions/other/">
-          Other</a></li>
-              </ul>
-    </div>
-  </div>
-  <h2>More Add-ons</h2>
-  <ul id="more-addons" class="listview">
-    <li class="rating"><a href="https://addons.mozilla.org/en-US/firefox/extensions/?sort=rating">
-      Highest Rated</a></li>
-    <li class="created"><a href="https://addons.mozilla.org/en-US/firefox/extensions/?sort=created">
-      Newest</a></li>
-      </ul>
-      </section>
-    </div>
-    <footer id="footer">
-      <form class="languages go" id="lang_form" method="get" action="index.html">
-  <label for="language">Other languages</label>
-  <select id="language" name="lang" dir="ltr">
-    <option value="af" >
-        Afrikaans
-      </option><option value="ar" >
-        عربي
-      </option><option value="bg" >
-        Български
-      </option><option value="ca" >
-        català
-      </option><option value="cs" >
-        Čeština
-      </option><option value="da" >
-        Dansk
-      </option><option value="de" >
-        Deutsch
-      </option><option value="el" >
-        Ελληνικά
-      </option><option value="en-us" selected>
-        English (US)
-      </option><option value="es" >
-        Español
-      </option><option value="eu" >
-        Euskara
-      </option><option value="fa" >
-        فارسی
-      </option><option value="fi" >
-        suomi
-      </option><option value="fr" >
-        Français
-      </option><option value="ga-ie" >
-        Gaeilge (Éire)
-      </option><option value="he" >
-        עברית
-      </option><option value="hu" >
-        magyar
-      </option><option value="id" >
-        Bahasa Indonesia
-      </option><option value="it" >
-        Italiano
-      </option><option value="ja" >
-        日本語
-      </option><option value="ko" >
-        한국어
-      </option><option value="mn" >
-        Монгол
-      </option><option value="nl" >
-        Nederlands
-      </option><option value="pl" >
-        Polski
-      </option><option value="pt-br" >
-        Português (do Brasil)
-      </option><option value="pt-pt" >
-        Português (Europeu)
-      </option><option value="ro" >
-        română
-      </option><option value="ru" >
-        Русский
-      </option><option value="sk" >
-        slovenčina
-      </option><option value="sl" >
-        Slovenščina
-      </option><option value="sq" >
-        Shqip
-      </option><option value="sv-se" >
-        Svenska
-      </option><option value="uk" >
-        Українська
-      </option><option value="vi" >
-        Tiếng Việt
-      </option><option value="zh-cn" >
-        中文 (简体)
-      </option><option value="zh-tw" >
-        正體中文 (繁體)
-      </option>  </select>
-  <noscript><button type="submit">Go</button></noscript>
-</form>              <a class="desktop-link" href="index.html#">View full site</a>
-        <p id="footer-links">
-          <a href="http://mozilla.com/privacy-policy.html">Privacy Policy</a> &nbsp;|&nbsp;
-          <a href="http://mozilla.com/about/legal.html">Legal Notices</a>
-        </p>
-          </footer>
-                      <script src="../../../addons.cdn.mozilla.net/en-US/firefox/jsi18n.js%3Fb=4ddb6c0"></script>
-      <script src="../../../addons.cdn.mozilla.net/media/js/zamboni/mobile-min.js%3Fbuild=4ddb6c0"></script>
-                      <script defer src="../../../addons.cdn.mozilla.net/media/js/webtrends/webtrends-v0.1.js%3Fb=4ddb6c0"></script>
-    <noscript>
-      <img id="DCSIMG" width="1" height="1"
-        src="../../../statse.webtrendslive.com/dcso6de4r0000082npfcmh4rf_4b1e/njs.gif%3Fdcsuri=%252Fnojavascript&amp;WT.js=No&amp;WT.tv=8.6.2" />
-    </noscript>
-      </body>
-</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/reader_mode_pages/not_an_article.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html lang="en-US"  class="no-js">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
+  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" media="(device-height: 568px)">
+  <meta name="apple-mobile-web-app-capable" content="yes" />
+  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+
+  <!-- Don't index mobile optimized pages -->
+  <meta name="robots" content="noindex" />
+
+  <title>Firefox for Android | Mozilla Support</title>
+
+          <link rel="icon" type="image/png" sizes="512x512" href="//support.cdn.mozilla.net/static/img/firefox-512.png?v=1">
+      <link rel="icon" type="image/png" sizes="256x256" href="//support.cdn.mozilla.net/static/img/firefox-256.png?v=1">
+      <link rel="icon" type="image/png" sizes="128x128" href="//support.cdn.mozilla.net/static/img/firefox-128.png?v=1">
+      <link rel="icon" type="image/png" sizes="64x64" href="//support.cdn.mozilla.net/static/img/firefox-64.png?v=1">
+      <link rel="icon" type="image/png" sizes="32x32" href="//support.cdn.mozilla.net/static/img/firefox-32.png?v=1">
+      <link rel="icon" type="image/png" sizes="16x16" href="//support.cdn.mozilla.net/static/img/firefox-16.png?v=1">
+  
+
+  <link rel="search" type="application/opensearchdescription+xml" title="Mozilla Support" href="/en-US/search/xml"/>
+
+  <link rel="stylesheet" media="screen,projection,tv" href="//support.cdn.mozilla.net/static/css/mobile/common-min.css?build=beb7c1e" />
+      <link rel="stylesheet" media="screen,projection,tv" href="//support.cdn.mozilla.net/static/css/mobile/products-min.css?build=beb7c1e" />
+      
+    </head>
+<body class=""
+      data-readonly="false"
+      data-static-url="//support.cdn.mozilla.net/static/"
+      data-orientation="right"
+      data-ga-push="[]"
+            data-usernames-api="/en-US/users/api/usernames"
+>
+
+<nav class="scrollable">
+  <div id="search-bar">
+      <form id="search" action="/en-US/search">
+                  <input type="hidden" name="product" value="mobile" />
+              <input name="q" placeholder="Search Mozilla Support" required="required" type="search" value="">
+    <button class="icon-sprite" type="submit">Search</button>
+  </form>
+
+  </div>
+
+  <a href="/en-US/products">Home</a>
+          <a href="/en-US/questions/new">Ask a question</a>
+  <a href="/en-US/questions">Support Forum</a>
+
+  <header>Navigation</header>
+  <a href="/en-US/get-involved">Help other users</a>
+  <a href="?&amp;mobile=0">Switch to desktop site</a>
+
+  <header>Profile</header>
+      <a href="/en-US/users/login">Sign in</a>
+  
+      <header>Languages</header>
+            <a href="/en-US/locales" class="locale-picker">Switch language</a>
+  </nav>
+
+<header class="slide-on-exposed">
+  <div id="menu-button" class="icon-sprite"></div>
+            <h1>
+              Firefox for Android
+          </h1>
+  </header>
+
+
+<div class="wrapper slide-on-exposed">
+  <section id="content">
+      <ul id="topics">
+          <li>
+        <a href="/en-US/products/mobile/get-started" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-get-started" alt="">
+          Learn the Basics: get started
+        </a>
+      </li>
+                <li>
+        <a href="/en-US/products/mobile/download-and-install" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-download-and-install" alt="">
+          Download, install and migration
+        </a>
+      </li>
+                <li>
+        <a href="/en-US/products/mobile/privacy-and-security" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-privacy-and-security" alt="">
+          Privacy and security settings
+        </a>
+      </li>
+                <li>
+        <a href="/en-US/products/mobile/customize" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-customize" alt="">
+          Customize controls, options and add-ons
+        </a>
+      </li>
+                <li>
+        <a href="/en-US/products/mobile/sync" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-sync" alt="">
+          Firefox Sync settings
+        </a>
+      </li>
+                <li>
+        <a href="/en-US/products/mobile/fix-problems" class="cf">
+          <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-fix-problems" alt="">
+          Fix slowness, crashing, error messages and other problems
+        </a>
+      </li>
+                <li>
+            <a href="/en-US/kb/get-community-support" class="cf">
+              <img src="//support.cdn.mozilla.net/static/img/blank.png" class="topic-sprite topic-get-community-support" alt="">
+              Get community support
+            </a>
+          </li>
+            </ul>
+
+      </section>
+
+  <footer>
+      </footer>
+
+  <ul id="notifications">
+          </ul>
+</div>
+
+
+<script src="//support.cdn.mozilla.net/static/jsi18n/en-us/javascript.js?beb7c1e"></script>
+
+<script src="//support.cdn.mozilla.net/static/js/mobile/common-min.js?build=beb7c1e"></script>
+
+</body>
+</html>
\ No newline at end of file
--- a/mobile/android/base/tests/testReadingListCache.js
+++ b/mobile/android/base/tests/testReadingListCache.js
@@ -19,17 +19,17 @@ let TEST_PAGES = [
     expected: {
       title: "Article title",
       byline: "by Jane Doe",
       excerpt: "This is the article description.",
       length: 1931
     }
   },
   {
-    url: URL_PREFIX + "addons.mozilla.org/en-US/firefox/index.html",
+    url: URL_PREFIX + "not_an_article.html",
     expected: null
   },
   {
     url: URL_PREFIX + "developer.mozilla.org/en/XULRunner/Build_Instructions.html",
     expected: {
       title: "Building XULRunner | MDN",
       byline: null,
       excerpt: "XULRunner is built using basically the same process as Firefox or other applications. Please read and follow the general Build Documentation for instructions on how to get sources and set up build prerequisites.",
--- a/toolkit/components/reader/JSDOMParser.js
+++ b/toolkit/components/reader/JSDOMParser.js
@@ -1,8 +1,15 @@
+/*
+ * DO NOT MODIFY THIS FILE DIRECTLY!
+ *
+ * This is a shared library that is maintained in an external repo:
+ * https://github.com/mozilla/readability
+ */
+
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * This is a relatively lightweight DOMParser that is safe to use in a web
  * worker. This is far from a complete DOM implementation; however, it should
  * contain the minimal set of functionality necessary for Readability.js.
@@ -26,17 +33,17 @@
  */
 (function (global) {
 
   function error(m) {
     dump("JSDOMParser error: " + m + "\n");
   }
 
   // When a style is set in JS, map it to the corresponding CSS attribute
-  let styleMap = {
+  var styleMap = {
     "alignmentBaseline": "alignment-baseline",
     "background": "background",
     "backgroundAttachment": "background-attachment",
     "backgroundClip": "background-clip",
     "backgroundColor": "background-color",
     "backgroundImage": "background-image",
     "backgroundOrigin": "background-origin",
     "backgroundPosition": "background-position",
@@ -218,197 +225,309 @@
     "wordSpacing": "word-spacing",
     "wordWrap": "word-wrap",
     "writingMode": "writing-mode",
     "zIndex": "z-index",
     "zoom": "zoom",
   };
 
   // Elements that can be self-closing
-  let voidElems = {
+  var voidElems = {
     "area": true,
     "base": true,
     "br": true,
     "col": true,
     "command": true,
     "embed": true,
     "hr": true,
     "img": true,
     "input": true,
     "link": true,
     "meta": true,
     "param": true,
     "source": true,
   };
 
+  var whitespace = [" ", "\t", "\n", "\r"];
+
   // See http://www.w3schools.com/dom/dom_nodetype.asp
-  let nodeTypes = {
+  var nodeTypes = {
     ELEMENT_NODE: 1,
     ATTRIBUTE_NODE: 2,
     TEXT_NODE: 3,
     CDATA_SECTION_NODE: 4,
     ENTITY_REFERENCE_NODE: 5,
     ENTITY_NODE: 6,
     PROCESSING_INSTRUCTION_NODE: 7,
     COMMENT_NODE: 8,
     DOCUMENT_NODE: 9,
     DOCUMENT_TYPE_NODE: 10,
     DOCUMENT_FRAGMENT_NODE: 11,
     NOTATION_NODE: 12
   };
 
   function getElementsByTagName(tag) {
     tag = tag.toUpperCase();
-    let elems = [];
-    let allTags = (tag === "*");
+    var elems = [];
+    var allTags = (tag === "*");
     function getElems(node) {
-      let length = node.childNodes.length;
-      for (let i = 0; i < length; i++) {
-        let child = node.childNodes[i];
-        if (child.nodeType !== 1)
-          continue;
+      var length = node.children.length;
+      for (var i = 0; i < length; i++) {
+        var child = node.children[i];
         if (allTags || (child.tagName === tag))
           elems.push(child);
         getElems(child);
       }
     }
     getElems(this);
     return elems;
   }
 
-  let Node = function () {};
+  var Node = function () {};
 
   Node.prototype = {
     attributes: null,
     childNodes: null,
     localName: null,
     nodeName: null,
     parentNode: null,
     textContent: null,
+    nextSibling: null,
+    previousSibling: null,
 
     get firstChild() {
       return this.childNodes[0] || null;
     },
 
-    get nextSibling() {
-      if (this.parentNode) {
-        let childNodes = this.parentNode.childNodes;
-        return childNodes[childNodes.indexOf(this) + 1] || null;
-      }
+    get firstElementChild() {
+      return this.children[0] || null;
+    },
 
-      return null;
+    get lastChild() {
+      return this.childNodes[this.childNodes.length - 1] || null;
+    },
+
+    get lastElementChild() {
+      return this.children[this.children.length - 1] || null;
     },
 
     appendChild: function (child) {
       if (child.parentNode) {
         child.parentNode.removeChild(child);
       }
 
+      var last = this.lastChild;
+      if (last)
+        last.nextSibling = child;
+      child.previousSibling = last;
+
+      if (child.nodeType === Node.ELEMENT_NODE) {
+        child.previousElementSibling = this.children[this.children.length - 1] || null;
+        this.children.push(child);
+        child.previousElementSibling && (child.previousElementSibling.nextElementSibling = child);
+      }
       this.childNodes.push(child);
       child.parentNode = this;
     },
 
     removeChild: function (child) {
-      let childNodes = this.childNodes;
-      let childIndex = childNodes.indexOf(child);
+      var childNodes = this.childNodes;
+      var childIndex = childNodes.indexOf(child);
       if (childIndex === -1) {
         throw "removeChild: node not found";
       } else {
         child.parentNode = null;
+        var prev = child.previousSibling;
+        var next = child.nextSibling;
+        if (prev)
+          prev.nextSibling = next;
+        if (next)
+          next.previousSibling = prev;
+
+        if (child.nodeType === Node.ELEMENT_NODE) {
+          prev = child.previousElementSibling;
+          next = child.nextElementSibling;
+          if (prev)
+            prev.nextElementSibling = next;
+          if (next)
+            next.previousElementSibling = prev;
+          this.children.splice(this.children.indexOf(child), 1);
+        }
+
+        child.previousSibling = child.nextSibling = null;
+        child.previousElementSibling = child.nextElementSibling = null;
+
         return childNodes.splice(childIndex, 1)[0];
       }
     },
 
     replaceChild: function (newNode, oldNode) {
-      let childNodes = this.childNodes;
-      let childIndex = childNodes.indexOf(oldNode);
+      var childNodes = this.childNodes;
+      var childIndex = childNodes.indexOf(oldNode);
       if (childIndex === -1) {
         throw "replaceChild: node not found";
       } else {
+        // This will take care of updating the new node if it was somewhere else before:
         if (newNode.parentNode)
           newNode.parentNode.removeChild(newNode);
 
         childNodes[childIndex] = newNode;
+
+        // update the new node's sibling properties, and its new siblings' sibling properties
+        newNode.nextSibling = oldNode.nextSibling;
+        newNode.previousSibling = oldNode.previousSibling;
+        if (newNode.nextSibling)
+          newNode.nextSibling.previousSibling = newNode;
+        if (newNode.previousSibling)
+          newNode.previousSibling.nextSibling = newNode;
+
         newNode.parentNode = this;
+
+        // Now deal with elements before we clear out those values for the old node,
+        // because it can help us take shortcuts here:
+        if (newNode.nodeType === Node.ELEMENT_NODE) {
+          if (oldNode.nodeType === Node.ELEMENT_NODE) {
+            // Both were elements, which makes this easier, we just swap things out:
+            newNode.previousElementSibling = oldNode.previousElementSibling;
+            newNode.nextElementSibling = oldNode.nextElementSibling;
+            if (newNode.previousElementSibling)
+              newNode.previousElementSibling.nextElementSibling = newNode;
+            if (newNode.nextElementSibling)
+              newNode.nextElementSibling.previousElementSibling = newNode;
+            this.children[this.children.indexOf(oldNode)] = newNode;
+          } else {
+            // Hard way:
+            newNode.previousElementSibling = (function() {
+              for (var i = childIndex - 1; i >= 0; i--) {
+                if (childNodes[i].nodeType === Node.ELEMENT_NODE)
+                  return childNodes[i];
+              }
+              return null;
+            })();
+            if (newNode.previousElementSibling) {
+              newNode.nextElementSibling = newNode.previousElementSibling.nextElementSibling;
+            } else {
+              newNode.nextElementSibling = (function() {
+                for (var i = childIndex + 1; i < childNodes.length; i++) {
+                  if (childNodes[i].nodeType === Node.ELEMENT_NODE)
+                    return childNodes[i];
+                }
+                return null;
+              })();
+            }
+            if (newNode.previousElementSibling)
+              newNode.previousElementSibling.nextElementSibling = newNode;
+            if (newNode.nextElementSibling)
+              newNode.nextElementSibling.previousElementSibling = newNode;
+
+            if (newNode.nextElementSibling)
+              this.children.splice(this.children.indexOf(newNode.nextElementSibling), 0, newNode);
+            else
+              this.children.push(newNode);
+          }
+        } else {
+          // new node is not an element node.
+          // if the old one was, update its element siblings:
+          if (oldNode.nodeType === Node.ELEMENT_NODE) {
+            if (oldNode.previousElementSibling)
+              oldNode.previousElementSibling.nextElementSibling = oldNode.nextElementSibling;
+            if (oldNode.nextElementSibling)
+              oldNode.nextElementSibling.previousElementSibling = oldNode.previousElementSibling;
+            this.children.splice(this.children.indexOf(oldNode), 1);
+          }
+          // If the old node wasn't an element, neither the new nor the old node was an element,
+          // and the children array and its members shouldn't need any updating.
+        }
+
+
         oldNode.parentNode = null;
+        oldNode.previousSibling = null;
+        oldNode.nextSibling = null;
+        if (oldNode.nodeType === Node.ELEMENT_NODE) {
+          oldNode.previousElementSibling = null;
+          oldNode.nextElementSibling = null;
+        }
         return oldNode;
       }
     }
   };
 
-  for (let i in nodeTypes) {
+  for (var i in nodeTypes) {
     Node[i] = Node.prototype[i] = nodeTypes[i];
   }
 
-  let Attribute = function (name, value) {
+  var Attribute = function (name, value) {
     this.name = name;
     this.value = value;
   };
 
-  let Comment = function () {
+  var Comment = function () {
     this.childNodes = [];
   };
 
   Comment.prototype = {
     __proto__: Node.prototype,
 
     nodeName: "#comment",
     nodeType: Node.COMMENT_NODE
   };
 
-  let Text = function () {
+  var Text = function () {
     this.childNodes = [];
   };
 
   Text.prototype = {
     __proto__: Node.prototype,
 
     nodeName: "#text",
     nodeType: Node.TEXT_NODE,
     textContent: ""
   }
 
-  let Document = function () {
+  var Document = function () {
     this.styleSheets = [];
     this.childNodes = [];
+    this.children = [];
   };
 
   Document.prototype = {
     __proto__: Node.prototype,
 
     nodeName: "#document",
     nodeType: Node.DOCUMENT_NODE,
     title: "",
 
     getElementsByTagName: getElementsByTagName,
 
     getElementById: function (id) {
       function getElem(node) {
-        let length = node.childNodes.length;
+        var length = node.children.length;
         if (node.id === id)
           return node;
-        for (let i = 0; i < length; i++) {
-          let el = getElem(node.childNodes[i]);
+        for (var i = 0; i < length; i++) {
+          var el = getElem(node.children[i]);
           if (el)
             return el;
         }
         return null;
       }
       return getElem(this);
     },
 
     createElement: function (tag) {
-      let node = new Element(tag);
+      var node = new Element(tag);
       return node;
     }
   };
 
-  let Element = function (tag) {
+  var Element = function (tag) {
     this.attributes = [];
     this.childNodes = [];
+    this.children = [];
+    this.nextElementSibling = this.previousElementSibling = null;
     this.localName = tag.toLowerCase();
     this.tagName = tag.toUpperCase();
     this.style = new Style(this);
   };
 
   Element.prototype = {
     __proto__: Node.prototype,
 
@@ -449,26 +568,26 @@
     },
 
     get nodeName() {
       return this.tagName;
     },
 
     get innerHTML() {
       function getHTML(node) {
-        let i = 0;
+        var i = 0;
         for (i = 0; i < node.childNodes.length; i++) {
-          let child = node.childNodes[i];
+          var child = node.childNodes[i];
           if (child.localName) {
             arr.push("<" + child.localName);
 
             // serialize attribute list
-            for (let j = 0; j < child.attributes.length; j++) {
-              let attr = child.attributes[j];
-              let quote = (attr.value.indexOf('"') === -1 ? '"' : "'");
+            for (var j = 0; j < child.attributes.length; j++) {
+              var attr = child.attributes[j];
+              var quote = (attr.value.indexOf('"') === -1 ? '"' : "'");
               arr.push(" " + attr.name + '=' + quote + attr.value + quote);
             }
 
             if (child.localName in voidElems) {
               // if this is a self-closing element, end it here
               arr.push("/>");
             } else {
               // otherwise, add its children
@@ -479,155 +598,155 @@
           } else {
             arr.push(child.textContent);
           }
         }
       }
 
       // Using Array.join() avoids the overhead from lazy string concatenation.
       // See http://blog.cdleary.com/2012/01/string-representation-in-spidermonkey/#ropes
-      let arr = [];
+      var arr = [];
       getHTML(this);
       return arr.join("");
     },
 
     set innerHTML(html) {
-      let parser = new JSDOMParser();
-      let node = parser.parse(html);
-      for (let i = this.childNodes.length; --i >= 0;) {
+      var parser = new JSDOMParser();
+      var node = parser.parse(html);
+      for (var i = this.childNodes.length; --i >= 0;) {
         this.childNodes[i].parentNode = null;
       }
       this.childNodes = node.childNodes;
-      for (let i = this.childNodes.length; --i >= 0;) {
+      for (var i = this.childNodes.length; --i >= 0;) {
         this.childNodes[i].parentNode = this;
       }
     },
 
     set textContent(text) {
       // clear parentNodes for existing children
-      for (let i = this.childNodes.length; --i >= 0;) {
+      for (var i = this.childNodes.length; --i >= 0;) {
         this.childNodes[i].parentNode = null;
       }
 
-      let node = new Text();
+      var node = new Text();
       this.childNodes = [ node ];
       node.textContent = text;
       node.parentNode = this;
     },
 
     get textContent() {
       function getText(node) {
-        let nodes = node.childNodes;
-        for (let i = 0; i < nodes.length; i++) {
-          let child = nodes[i];
+        var nodes = node.childNodes;
+        for (var i = 0; i < nodes.length; i++) {
+          var child = nodes[i];
           if (child.nodeType === 3) {
             text.push(child.textContent);
           } else {
             getText(child);
           }
         }
       }
 
       // Using Array.join() avoids the overhead from lazy string concatenation.
       // See http://blog.cdleary.com/2012/01/string-representation-in-spidermonkey/#ropes
-      let text = [];
+      var text = [];
       getText(this);
       return text.join("");
     },
 
     getAttribute: function (name) {
-      for (let i = this.attributes.length; --i >= 0;) {
-        let attr = this.attributes[i];
+      for (var i = this.attributes.length; --i >= 0;) {
+        var attr = this.attributes[i];
         if (attr.name === name)
           return attr.value;
       }
       return undefined;
     },
 
     setAttribute: function (name, value) {
-      for (let i = this.attributes.length; --i >= 0;) {
-        let attr = this.attributes[i];
+      for (var i = this.attributes.length; --i >= 0;) {
+        var attr = this.attributes[i];
         if (attr.name === name) {
           attr.value = value;
           return;
         }
       }
       this.attributes.push(new Attribute(name, value));
     },
 
     removeAttribute: function (name) {
-      for (let i = this.attributes.length; --i >= 0;) {
-        let attr = this.attributes[i];
+      for (var i = this.attributes.length; --i >= 0;) {
+        var attr = this.attributes[i];
         if (attr.name === name) {
           this.attributes.splice(i, 1);
           break;
         }
       }
     }
   };
 
-  let Style = function (node) {
+  var Style = function (node) {
     this.node = node;
   };
 
   // getStyle() and setStyle() use the style attribute string directly. This
   // won't be very efficient if there are a lot of style manipulations, but
   // it's the easiest way to make sure the style attribute string and the JS
   // style property stay in sync. Readability.js doesn't do many style
   // manipulations, so this should be okay.
   Style.prototype = {
     getStyle: function (styleName) {
-      let attr = this.node.getAttribute("style");
+      var attr = this.node.getAttribute("style");
       if (!attr)
         return undefined;
 
-      let styles = attr.split(";");
-      for (let i = 0; i < styles.length; i++) {
-        let style = styles[i].split(":");
-        let name = style[0].trim();
+      var styles = attr.split(";");
+      for (var i = 0; i < styles.length; i++) {
+        var style = styles[i].split(":");
+        var name = style[0].trim();
         if (name === styleName)
           return style[1].trim();
       }
 
       return undefined;
     },
 
     setStyle: function (styleName, styleValue) {
-      let value = this.node.getAttribute("style") || "";
-      let index = 0;
+      var value = this.node.getAttribute("style") || "";
+      var index = 0;
       do {
-        let next = value.indexOf(";", index) + 1;
-        let length = next - index - 1;
-        let style = (length > 0 ? value.substr(index, length) : value.substr(index));
+        var next = value.indexOf(";", index) + 1;
+        var length = next - index - 1;
+        var style = (length > 0 ? value.substr(index, length) : value.substr(index));
         if (style.substr(0, style.indexOf(":")).trim() === styleName) {
           value = value.substr(0, index).trim() + (next ? " " + value.substr(next).trim() : "");
           break;
         }
         index = next;
       } while (index);
 
       value += " " + styleName + ": " + styleValue + ";";
       this.node.setAttribute("style", value.trim());
     }
   };
 
   // For each item in styleMap, define a getter and setter on the style
   // property.
-  for (let jsName in styleMap) {
+  for (var jsName in styleMap) {
     (function (cssName) {
       Style.prototype.__defineGetter__(jsName, function () {
         return this.getStyle(cssName);
       });
       Style.prototype.__defineSetter__(jsName, function (value) {
         this.setStyle(cssName, value);
       });
     }) (styleMap[jsName]);
   }
 
-  let JSDOMParser = function () {
+  var JSDOMParser = function () {
     this.currentChar = 0;
 
     // In makeElementNode() we build up many strings one char at a time. Using
     // += for this results in lots of short-lived intermediate strings. It's
     // better to build an array of single-char strings and then join() them
     // together at the end. And reusing a single array (i.e. |this.strBuf|)
     // over and over for this purpose uses less memory than using a new array
     // for each string.
@@ -654,57 +773,57 @@
       return this.html[this.currentChar++];
     },
 
     /**
      * Called after a quote character is read. This finds the next quote
      * character and returns the text string in between.
      */
     readString: function (quote) {
-      let str;
-      let n = this.html.indexOf(quote, this.currentChar);
+      var str;
+      var n = this.html.indexOf(quote, this.currentChar);
       if (n === -1) {
         this.currentChar = this.html.length;
         str = null;
       } else {
         str = this.html.substring(this.currentChar, n);
         this.currentChar = n + 1;
       }
 
       return str;
     },
 
     /**
      * Called when parsing a node. This finds the next name/value attribute
      * pair and adds the result to the attributes list.
      */
     readAttribute: function (node) {
-      let name = "";
+      var name = "";
 
-      let n = this.html.indexOf("=", this.currentChar);
+      var n = this.html.indexOf("=", this.currentChar);
       if (n === -1) {
         this.currentChar = this.html.length;
       } else {
         // Read until a '=' character is hit; this will be the attribute key
         name = this.html.substring(this.currentChar, n);
         this.currentChar = n + 1;
       }
 
       if (!name)
         return;
 
       // After a '=', we should see a '"' for the attribute value
-      let c = this.nextChar();
+      var c = this.nextChar();
       if (c !== '"' && c !== "'") {
-        error("expecting '\"'");
+        error("Error reading attribute " + name + ", expecting '\"'");
         return;
       }
 
       // Read the attribute value (and consume the matching quote)
-      let value = this.readString(c);
+      var value = this.readString(c);
 
       if (!value)
         return;
 
       node.attributes.push(new Attribute(name, value));
 
       return;
     },
@@ -713,119 +832,131 @@
      * Parses and returns an Element node. This is called after a '<' has been
      * read.
      *
      * @returns an array; the first index of the array is the parsed node;
      *          the second index is a boolean indicating whether this is a void
      *          Element
      */
     makeElementNode: function (retPair) {
-      let c = this.nextChar();
+      var c = this.nextChar();
 
       // Read the Element tag name
-      let strBuf = this.strBuf;
+      var strBuf = this.strBuf;
       strBuf.length = 0;
-      while (c !== " " && c !== ">" && c !== "/") {
+      while (whitespace.indexOf(c) == -1 && c !== ">" && c !== "/") {
         if (c === undefined)
           return false;
         strBuf.push(c);
         c = this.nextChar();
       }
-      let tag = strBuf.join('');
+      var tag = strBuf.join('');
 
       if (!tag)
         return false;
 
-      let node = new Element(tag);
+      var node = new Element(tag);
 
       // Read Element attributes
       while (c !== "/" && c !== ">") {
         if (c === undefined)
           return false;
-        while (this.match(" "));
+        while (whitespace.indexOf(this.html[this.currentChar++]) != -1);
+        this.currentChar--;
         c = this.nextChar();
         if (c !== "/" && c !== ">") {
           --this.currentChar;
           this.readAttribute(node);
         }
       }
 
       // If this is a self-closing tag, read '/>'
-      let closed = tag in voidElems;
+      var closed = tag in voidElems;
       if (c === "/") {
         closed = true;
         c = this.nextChar();
         if (c !== ">") {
-          error("expected '>'");
+          error("expected '>' to close " + tag);
           return false;
         }
       }
 
       retPair[0] = node;
       retPair[1] = closed;
       return true
     },
 
     /**
      * If the current input matches this string, advance the input index;
      * otherwise, do nothing.
      *
      * @returns whether input matched string
      */
     match: function (str) {
-      let strlen = str.length;
+      var strlen = str.length;
       if (this.html.substr(this.currentChar, strlen) === str) {
         this.currentChar += strlen;
         return true;
       }
       return false;
     },
 
     /**
      * Searches the input until a string is found and discards all input up to
      * and including the matched string.
      */
     discardTo: function (str) {
-      let index = this.html.indexOf(str, this.currentChar) + str.length;
+      var index = this.html.indexOf(str, this.currentChar) + str.length;
       if (index === -1)
         this.currentChar = this.html.length;
       this.currentChar = index;
     },
 
     /**
      * Reads child nodes for the given node.
      */
     readChildren: function (node) {
-      let child;
+      var child;
       while ((child = this.readNode())) {
         // Don't keep Comment nodes
         if (child.nodeType !== 8) {
-          node.childNodes.push(child);
-          child.parentNode = node;
+          node.appendChild(child);
         }
       }
     },
 
+    readScript: function (node) {
+      var index = this.html.indexOf("</script>", this.currentChar);
+      if (index === -1) {
+        index = this.html.length;
+      }
+      var txt = new Text();
+      txt.textContent = this.html.substring(this.currentChar, index === -1 ? this.html.length : index);
+      node.appendChild(txt);
+      this.currentChar = index;
+    },
+
+
     /**
      * Reads the next child node from the input. If we're reading a closing
      * tag, or if we've reached the end of input, return null.
      *
      * @returns the node
      */
     readNode: function () {
-      let c = this.nextChar();
+      var c = this.nextChar();
  
       if (c === undefined)
         return null;
 
       // Read any text as Text node
       if (c !== "<") {
         --this.currentChar;
-        let node = new Text();
-        let n = this.html.indexOf("<", this.currentChar);
+        var node = new Text();
+        var n = this.html.indexOf("<", this.currentChar);
         if (n === -1) {
           node.textContent = this.html.substring(this.currentChar, this.html.length);
           this.currentChar = this.html.length;
         } else {
           node.textContent = this.html.substring(this.currentChar, n);
           this.currentChar = n;
         }
         return node;
@@ -837,17 +968,17 @@
       // textContent, but we don't really care about Comment nodes (we throw
       // them away in readChildren()). So just returning an empty Comment node
       // here is sufficient.
       if (c === "!" || c === "?") {
         this.currentChar++;
         if (this.match("--")) {
           this.discardTo("-->");
         } else {
-          let c = this.nextChar();
+          var c = this.nextChar();
           while (c !== ">") {
             if (c === undefined)
               return null;
             if (c === '"' || c === "'")
               this.readString(c);
             c = this.nextChar();
           }
         }
@@ -857,35 +988,42 @@
       // If we're reading a closing tag, return null. This means we've reached
       // the end of this set of child nodes.
       if (c === "/") {
         --this.currentChar;
         return null;
       }
 
       // Otherwise, we're looking at an Element node
-      let result = this.makeElementNode(this.retPair);
+      var result = this.makeElementNode(this.retPair);
       if (!result)
         return null;
 
-      let node = this.retPair[0];
-      let closed = this.retPair[1];
-      let localName = node.localName;
+      var node = this.retPair[0];
+      var closed = this.retPair[1];
+      var localName = node.localName;
 
       // If this isn't a void Element, read its child nodes
       if (!closed) {
-        this.readChildren(node);
-        let closingTag = "</" + localName + ">";
+        if (localName == "script") {
+          this.readScript(node);
+        } else {
+          this.readChildren(node);
+        }
+        var closingTag = "</" + localName + ">";
         if (!this.match(closingTag)) {
           error("expected '" + closingTag + "'");
           return null;
         }
       }
 
-      if (localName === "title") {
+      // Only use the first title, because SVG might have other
+      // title elements which we don't care about (medium.com
+      // does this, at least).
+      if (localName === "title" && !this.doc.title) {
         this.doc.title = node.textContent.trim();
       } else if (localName === "head") {
         this.doc.head = node;
       } else if (localName === "body") {
         this.doc.body = node;
       } else if (localName === "html") {
         this.doc.documentElement = node;
       }
@@ -893,24 +1031,24 @@
       return node;
     },
 
     /**
      * Parses an HTML string and returns a JS implementation of the Document.
      */
     parse: function (html) {
       this.html = html;
-      let doc = this.doc = new Document();
+      var doc = this.doc = new Document();
       this.readChildren(doc);
 
       // If this is an HTML document, remove root-level children except for the
       // <html> node
       if (doc.documentElement) {
-        for (let i = doc.childNodes.length; --i >= 0;) {
-          let child = doc.childNodes[i];
+        for (var i = doc.childNodes.length; --i >= 0;) {
+          var child = doc.childNodes[i];
           if (child !== doc.documentElement) {
             doc.removeChild(child);
           }
         }
       }
 
       return doc;
     }
--- a/toolkit/components/reader/Readability.js
+++ b/toolkit/components/reader/Readability.js
@@ -97,26 +97,28 @@ Readability.prototype = {
   REGEXPS: {
     unlikelyCandidates: /combx|comment|community|disqus|extra|foot|header|menu|remark|rss|shoutbox|sidebar|sponsor|ad-break|agegate|pagination|pager|popup|tweet|twitter/i,
     okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
     positive: /article|body|content|entry|hentry|main|page|pagination|post|text|blog|story/i,
     negative: /hidden|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|outbrain|promo|related|scroll|shoutbox|sidebar|sponsor|shopping|tags|tool|widget/i,
     extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
     byline: /byline|author|dateline|writtenby/i,
     replaceFonts: /<(\/?)font[^>]*>/gi,
-    trim: /^\s+|\s+$/g,
     normalize: /\s{2,}/g,
-    videos: /http:\/\/(www\.)?(youtube|vimeo)\.com/i,
+    videos: /https?:\/\/(www\.)?(youtube|vimeo)\.com/i,
     nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
     prevLink: /(prev|earl|old|new|<|«)/i,
-    whitespace: /^\s*$/
+    whitespace: /^\s*$/,
+    hasContent: /\S$/,
   },
 
   DIV_TO_P_ELEMS: [ "A", "BLOCKQUOTE", "DL", "DIV", "IMG", "OL", "P", "PRE", "TABLE", "UL", "SELECT" ],
 
+  ALTER_TO_DIV_EXCEPTIONS: ["DIV", "ARTICLE", "SECTION", "P"],
+
   /**
    * Run any post-process modifications to article content as necessary.
    *
    * @param Element
    * @return void
   **/
   _postProcessContent: function(articleContent) {
     // Readability cannot open relative uris so we convert them to absolute uris.
@@ -199,17 +201,17 @@ Readability.prototype = {
         curTitle = origTitle.replace(/[^:]*[:](.*)/gi,'$1');
     } else if (curTitle.length > 150 || curTitle.length < 15) {
       var hOnes = doc.getElementsByTagName('h1');
 
       if (hOnes.length === 1)
         curTitle = this._getInnerText(hOnes[0]);
     }
 
-    curTitle = curTitle.replace(this.REGEXPS.trim, "");
+    curTitle = curTitle.trim();
 
     if (curTitle.split(' ').length <= 4)
       curTitle = origTitle;
 
     return curTitle;
   },
 
   /**
@@ -218,18 +220,18 @@ Readability.prototype = {
    *
    * @return void
    **/
   _prepDocument: function() {
     var doc = this._doc;
 
     // Remove all style tags in head
     var styleTags = doc.getElementsByTagName("style");
-    for (var st = 0; st < styleTags.length; st += 1) {
-      styleTags[st].textContent = "";
+    for (var st = styleTags.length - 1; st >= 0; st -= 1) {
+      styleTags[st].parentNode.removeChild(styleTags[st]);
     }
 
     if (doc.body) {
       this._replaceBrs(doc.body);
     }
 
     var fonts = doc.getElementsByTagName("FONT");
     for (var i = fonts.length; --i >=0;) {
@@ -300,16 +302,18 @@ Readability.prototype = {
           p.appendChild(next);
           next = sibling;
         }
       }
     }
   },
 
   _setNodeTag: function (node, tag) {
+    // FIXME this doesn't work on anything but JSDOMParser (ie the node's tag
+    // won't actually be set).
     node.localName = tag.toLowerCase();
     node.tagName = tag.toUpperCase();
   },
 
   /**
    * Prepare the article node for display. Clean out any inline styles,
    * iframes, forms, strip extraneous <p> tags, etc.
    *
@@ -402,16 +406,64 @@ Readability.prototype = {
       case 'TH':
         node.readability.contentScore -= 5;
         break;
     }
 
     node.readability.contentScore += this._getClassWeight(node);
   },
 
+  _removeAndGetNext: function(node) {
+    var nextNode = this._getNextNode(node, true);
+    node.parentNode.removeChild(node);
+    return nextNode;
+  },
+
+  /**
+   * Traverse the DOM from node to node, starting at the node passed in.
+   * Pass true for the second parameter to indicate this node itself
+   * (and its kids) are going away, and we want the next node over.
+   *
+   * Calling this in a loop will traverse the DOM depth-first.
+   */
+  _getNextNode: function(node, ignoreSelfAndKids) {
+    // First check for kids if those aren't being ignored
+    if (!ignoreSelfAndKids && node.firstElementChild) {
+      return node.firstElementChild;
+    }
+    // Then for siblings...
+    if (node.nextElementSibling) {
+      return node.nextElementSibling;
+    }
+    // And finally, move up the parent chain *and* find a sibling
+    // (because this is depth-first traversal, we will have already
+    // seen the parent nodes themselves).
+    do {
+      node = node.parentNode;
+    } while (node && !node.nextElementSibling);
+    return node && node.nextElementSibling;
+  },
+
+  _checkByline: function(node, matchString) {
+    if (this._articleByline) {
+      return false;
+    }
+
+    if (node.getAttribute !== undefined) {
+      var rel = node.getAttribute("rel");
+    }
+
+    if ((rel === "author" || this.REGEXPS.byline.test(matchString)) && this._isValidByline(node.textContent)) {
+      this._articleByline = node.textContent.trim();
+      return true;
+    }
+
+    return false;
+  },
+
   /***
    * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
    *         most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
    *
    * @param page a document to run upon. Needs to be a full document, complete with body.
    * @return Element
   **/
   _grabArticle: function (page) {
@@ -425,123 +477,89 @@ Readability.prototype = {
       return null;
     }
 
     var pageCacheHtml = page.innerHTML;
 
     // Check if any "dir" is set on the toplevel document element
     this._articleDir = doc.documentElement.getAttribute("dir");
 
-    //helper function used below in the 'while' loop:
-    function purgeNode(node, allElements) {
-      for (var i = node.childNodes.length; --i >= 0;) {
-        purgeNode(node.childNodes[i], allElements);
-      }
-      if (node._index !== undefined && allElements[node._index] == node)
-        delete allElements[node._index];
-    }
     while (true) {
       var stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS);
-      var allElements = page.getElementsByTagName('*');
 
       // First, node prepping. Trash nodes that look cruddy (like ones with the
       // class name "comment", etc), and turn divs into P tags where they have been
       // used inappropriately (as in, where they contain no other block level elements.)
-      //
-      // Note: Assignment from index for performance. See http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5
-      // TODO: Shouldn't this be a reverse traversal?
-      var node = null;
-      var nodesToScore = [];
-
-      // var each node know its index in the allElements array.
-      for (var i = allElements.length; --i >= 0;) {
-        allElements[i]._index = i;
-      }
+      var elementsToScore = [];
+      var node = this._doc.documentElement;
 
-      /**
-       * JSDOMParser returns static node lists, not live ones. When we remove
-       * an element from the document, we need to manually remove it - and all
-       * of its children - from the allElements array.
-       */
-      for (var nodeIndex = 0; nodeIndex < allElements.length; nodeIndex++) {
-        if (!(node = allElements[nodeIndex]))
-          continue;
+      while (node) {
+        var matchString = node.className + " " + node.id;
 
-        var matchString = node.className + node.id;
-        if (matchString.search(this.REGEXPS.byline) !== -1 && !this._articleByline) {
-          if (this._isValidByline(node.textContent)) {
-            this._articleByline = node.textContent.trim();
-            node.parentNode.removeChild(node);
-            purgeNode(node, allElements);
-            continue;
-          }
+        // Check to see if this node is a byline, and remove it if it is.
+        if (this._checkByline(node, matchString)) {
+          node = this._removeAndGetNext(node);
+          continue;
         }
 
         // Remove unlikely candidates
         if (stripUnlikelyCandidates) {
-          if (matchString.search(this.REGEXPS.unlikelyCandidates) !== -1 &&
-            matchString.search(this.REGEXPS.okMaybeItsACandidate) === -1 &&
-            node.tagName !== "BODY") {
+          if (this.REGEXPS.unlikelyCandidates.test(matchString) &&
+              !this.REGEXPS.okMaybeItsACandidate.test(matchString) &&
+              node.tagName !== "BODY") {
             this.log("Removing unlikely candidate - " + matchString);
-            node.parentNode.removeChild(node);
-            purgeNode(node, allElements);
+            node = this._removeAndGetNext(node);
             continue;
           }
         }
 
         if (node.tagName === "P" || node.tagName === "TD" || node.tagName === "PRE")
-          nodesToScore[nodesToScore.length] = node;
+          elementsToScore.push(node);
 
         // Turn all divs that don't have children block level elements into p's
         if (node.tagName === "DIV") {
           // Sites like http://mobile.slate.com encloses each paragraph with a DIV
           // element. DIVs with only a P element inside and no text content can be
           // safely converted into plain P elements to avoid confusing the scoring
           // algorithm with DIVs with are, in practice, paragraphs.
-          var pIndex = this._getSinglePIndexInsideDiv(node);
-
-          if (pIndex >= 0 || !this._hasChildBlockElement(node)) {
-            if (pIndex >= 0) {
-              var newNode = node.childNodes[pIndex];
-              node.parentNode.replaceChild(newNode, node);
-              purgeNode(node, allElements);
-            } else {
-              this._setNodeTag(node, "P");
-              nodesToScore[nodesToScore.length] = node;
-            }
+          if (this._hasSinglePInsideElement(node)) {
+            var newNode = node.firstElementChild;
+            node.parentNode.replaceChild(newNode, node);
+            node = newNode;
+          } else if (!this._hasChildBlockElement(node)) {
+            this._setNodeTag(node, "P");
+            elementsToScore.push(node);
           } else {
             // EXPERIMENTAL
             for (var i = 0, il = node.childNodes.length; i < il; i += 1) {
               var childNode = node.childNodes[i];
-              if (!childNode)
-                continue;
-
-              if (childNode.nodeType === 3) { // Node.TEXT_NODE
+              if (childNode.nodeType === Node.TEXT_NODE) {
                 var p = doc.createElement('p');
                 p.textContent = childNode.textContent;
                 p.style.display = 'inline';
                 p.className = 'readability-styled';
-                childNode.parentNode.replaceChild(p, childNode);
+                node.replaceChild(p, childNode);
               }
             }
           }
         }
+        node = this._getNextNode(node);
       }
 
       /**
        * Loop through all paragraphs, and assign a score to them based on how content-y they look.
        * Then add their score to their parent node.
        *
        * A score is determined by things like number of commas, class names, etc. Maybe eventually link density.
       **/
       var candidates = [];
-      for (var pt = 0; pt < nodesToScore.length; pt += 1) {
-        var parentNode = nodesToScore[pt].parentNode;
+      for (var pt = 0; pt < elementsToScore.length; pt += 1) {
+        var parentNode = elementsToScore[pt].parentNode;
         var grandParentNode = parentNode ? parentNode.parentNode : null;
-        var innerText = this._getInnerText(nodesToScore[pt]);
+        var innerText = this._getInnerText(elementsToScore[pt]);
 
         if (!parentNode || typeof(parentNode.tagName) === 'undefined')
           continue;
 
         // If this paragraph is less than 25 characters, don't even count it.
         if (innerText.length < 25)
           continue;
 
@@ -607,101 +625,125 @@ Readability.prototype = {
       var neededToCreateTopCandidate = false;
 
       // If we still have no top candidate, just use the body as a last resort.
       // We also have to copy the body node so it is something we can modify.
       if (topCandidate === null || topCandidate.tagName === "BODY") {
         // Move all of the page's children into topCandidate
         topCandidate = doc.createElement("DIV");
         neededToCreateTopCandidate = true;
-        var children = page.childNodes;
-        while (children.length) {
-          this.log("Moving child out:", children[0]);
-          topCandidate.appendChild(children[0]);
+        // Move everything (not just elements, also text nodes etc.) into the container
+        // so we even include text directly in the body:
+        var kids = page.childNodes;
+        while (kids.length) {
+          this.log("Moving child out:", kids[0]);
+          topCandidate.appendChild(kids[0]);
         }
 
         page.appendChild(topCandidate);
 
         this._initializeNode(topCandidate);
+      } else if (topCandidate) {
+        // Because of our bonus system, parents of candidates might have scores
+        // themselves. They get half of the node. There won't be nodes with higher
+        // scores than our topCandidate, but if we see the score going *up* in the first
+        // few steps up the tree, that's a decent sign that there might be more content
+        // lurking in other places that we want to unify in. The sibling stuff
+        // below does some of that - but only if we've looked high enough up the DOM
+        // tree.
+        var parentOfTopCandidate = topCandidate.parentNode;
+        // The scores shouldn't get too low.
+        var scoreThreshold = topCandidate.readability.contentScore / 3;
+        var lastScore = parentOfTopCandidate.readability.contentScore;
+        while (parentOfTopCandidate && parentOfTopCandidate.readability) {
+          var parentScore = parentOfTopCandidate.readability.contentScore;
+          if (parentScore < scoreThreshold)
+            break;
+          if (parentScore > lastScore) {
+            // Alright! We found a better parent to use.
+            topCandidate = parentOfTopCandidate;
+            break;
+          }
+          parentOfTopCandidate = parentOfTopCandidate.parentNode;
+        }
       }
 
       // Now that we have the top candidate, look through its siblings for content
       // that might also be related. Things like preambles, content split by ads
       // that we removed, etc.
       var articleContent = doc.createElement("DIV");
       if (isPaging)
         articleContent.id = "readability-content";
 
       var siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
-      var siblingNodes = topCandidate.parentNode.childNodes;
+      var siblings = topCandidate.parentNode.children;
 
-      for (var s = 0, sl = siblingNodes.length; s < sl; s += 1) {
-        var siblingNode = siblingNodes[s];
+      for (var s = 0, sl = siblings.length; s < sl; s++) {
+        var sibling = siblings[s];
         var append = false;
 
-        this.log("Looking at sibling node:", siblingNode, ((typeof siblingNode.readability !== 'undefined') ? ("with score " + siblingNode.readability.contentScore) : ''));
-        this.log("Sibling has score " + (siblingNode.readability ? siblingNode.readability.contentScore : 'Unknown'));
-
-        if (siblingNode === topCandidate)
-          append = true;
+        this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : '');
+        this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : 'Unknown');
 
-        var contentBonus = 0;
+        if (sibling === topCandidate) {
+          append = true;
+        } else {
+          var contentBonus = 0;
 
-        // Give a bonus if sibling nodes and top candidates have the example same classname
-        if (siblingNode.className === topCandidate.className && topCandidate.className !== "")
-          contentBonus += topCandidate.readability.contentScore * 0.2;
+          // Give a bonus if sibling nodes and top candidates have the example same classname
+          if (sibling.className === topCandidate.className && topCandidate.className !== "")
+            contentBonus += topCandidate.readability.contentScore * 0.2;
 
-        if (typeof siblingNode.readability !== 'undefined' &&
-          (siblingNode.readability.contentScore+contentBonus) >= siblingScoreThreshold)
-          append = true;
+          if (sibling.readability &&
+              ((sibling.readability.contentScore + contentBonus) >= siblingScoreThreshold)) {
+            append = true;
+          } else if (sibling.nodeName === "P") {
+            var linkDensity = this._getLinkDensity(sibling);
+            var nodeContent = this._getInnerText(sibling);
+            var nodeLength = nodeContent.length;
 
-        if (siblingNode.nodeName === "P") {
-          var linkDensity = this._getLinkDensity(siblingNode);
-          var nodeContent = this._getInnerText(siblingNode);
-          var nodeLength = nodeContent.length;
-
-          if (nodeLength > 80 && linkDensity < 0.25) {
-            append = true;
-          } else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
-            append = true;
+            if (nodeLength > 80 && linkDensity < 0.25) {
+              append = true;
+            } else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
+              append = true;
+            }
           }
         }
 
         if (append) {
-          this.log("Appending node:", siblingNode);
+          this.log("Appending node:", sibling);
 
-          // siblingNodes is a reference to the childNodes array, and
-          // siblingNode is removed from the array when we call appendChild()
-          // below. As a result, we must revisit this index since the nodes
-          // have been shifted.
-          s -= 1;
-          sl -= 1;
+          if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) {
+            // We have a node that isn't a common block level element, like a form or td tag.
+            // Turn it into a div so it doesn't get filtered out later by accident.
+            this.log("Altering sibling:", sibling, 'to div.');
 
-          if (siblingNode.nodeName !== "DIV" && siblingNode.nodeName !== "P") {
-            // We have a node that isn't a common block level element, like a form or td tag.
-            // Turn it into a div so it doesn't get filtered out later by accident. */
-            this.log("Altering siblingNode:", siblingNode, 'to div.');
-
-            this._setNodeTag(siblingNode, "DIV");
+            this._setNodeTag(sibling, "DIV");
           }
 
           // To ensure a node does not interfere with readability styles,
           // remove its classnames.
-          siblingNode.removeAttribute("class");
+          sibling.removeAttribute("class");
 
-          // Append sibling and subtract from our list because it removes
-          // the node when you append to another node.
-          articleContent.appendChild(siblingNode);
+          articleContent.appendChild(sibling);
+          // siblings is a reference to the children array, and
+          // sibling is removed from the array when we call appendChild().
+          // As a result, we must revisit this index since the nodes
+          // have been shifted.
+          s -= 1;
+          sl -= 1;
         }
       }
 
-      this.log("Article content pre-prep: " + articleContent.innerHTML);
+      if (this.ENABLE_LOGGING)
+        this.log("Article content pre-prep: " + articleContent.innerHTML);
       // So we have all of the content that we need. Now we clean it up for presentation.
       this._prepArticle(articleContent);
-      this.log("Article content post-prep: " + articleContent.innerHTML);
+      if (this.ENABLE_LOGGING)
+        this.log("Article content post-prep: " + articleContent.innerHTML);
 
       if (this._curPageNum === 1) {
         if (neededToCreateTopCandidate) {
           // We already created a fake div thing, and there wouldn't have been any siblings left
           // for the previous loop, so there's no point trying to create a new div, and then
           // move all the children over. Just assign IDs and class names here. No need to append
           // because that already happened anyway.
           topCandidate.id = "readability-page-1";
@@ -713,17 +755,18 @@ Readability.prototype = {
           var children = articleContent.childNodes;
           while (children.length) {
             div.appendChild(children[0]);
           }
           articleContent.appendChild(div);
         }
       }
 
-      this.log("Article content after paging: " + articleContent.innerHTML);
+      if (this.ENABLE_LOGGING)
+        this.log("Article content after paging: " + articleContent.innerHTML);
 
       // Now that we've gone through the full algorithm, check to see if
       // we got any meaningful content. If we didn't, we may need to re-run
       // grabArticle with different flags set. This gives us a higher likelihood of
       // finding the content, and the sieve approach gives us a higher likelihood of
       // finding the -right- content.
       if (this._getInnerText(articleContent, true).length < 500) {
         page.innerHTML = pageCacheHtml;
@@ -755,46 +798,44 @@ Readability.prototype = {
     if (typeof byline == 'string' || byline instanceof String) {
       byline = byline.trim();
       return (byline.length > 0) && (byline.length < 100);
     }
     return false;
   },
 
   /**
-   * Attempts to get the excerpt from these
-   * sources in the following order:
-   * - meta description tag
-   * - open-graph description
-   * - twitter cards description
-   * - article's first paragraph
-   * If no excerpt is found, an empty string will be
-   * returned.
-   *
-   * @param Element - root element of the processed version page
-   * @return String - excerpt of the article
-  **/
-  _getExcerpt: function(articleContent) {
+   * Attempts to get excerpt and byline metadata for the article.
+   * 
+   * @return Object with optional "excerpt" and "byline" properties
+   */
+  _getArticleMetadata: function() {
+    var metadata = {};
     var values = {};
     var metaElements = this._doc.getElementsByTagName("meta");
 
     // Match "description", or Twitter's "twitter:description" (Cards)
     // in name attribute.
     var namePattern = /^\s*((twitter)\s*:\s*)?description\s*$/gi;
 
     // Match Facebook's og:description (Open Graph) in property attribute.
     var propertyPattern = /^\s*og\s*:\s*description\s*$/gi;
 
     // Find description tags.
     for (var i = 0; i < metaElements.length; i++) {
       var element = metaElements[i];
       var elementName = element.getAttribute("name");
       var elementProperty = element.getAttribute("property");
 
-      var name;
+      if (elementName === "author") {
+        metadata.byline = element.getAttribute("content");
+        continue;
+      }
+
+      var name = null;
       if (namePattern.test(elementName)) {
         name = elementName;
       } else if (propertyPattern.test(elementProperty)) {
         name = elementProperty;
       }
 
       if (name) {
         var content = element.getAttribute("content");
@@ -803,36 +844,26 @@ Readability.prototype = {
           // so we can match below.
           name = name.toLowerCase().replace(/\s/g, '');
           values[name] = content.trim();
         }
       }
     }
 
     if ("description" in values) {
-      return values["description"];
-    }
-
-    if ("og:description" in values) {
+      metadata.excerpt = values["description"];
+    } else if ("og:description" in values) {
       // Use facebook open graph description.
-      return values["og:description"];
+      metadata.excerpt = values["og:description"];
+    } else if ("twitter:description" in values) {
+      // Use twitter cards description.
+      metadata.excerpt = values["twitter:description"];
     }
 
-    if ("twitter:description" in values) {
-      // Use twitter cards description.
-      return values["twitter:description"];
-    }
-
-    // No description meta tags, use the article's first paragraph.
-    var paragraphs = articleContent.getElementsByTagName("p");
-    if (paragraphs.length > 0) {
-      return paragraphs[0].textContent;
-    }
-
-    return "";
+    return metadata;
   },
 
   /**
    * Removes script tags from the document.
    *
    * @param Element
   **/
   _removeScripts: function(doc) {
@@ -842,72 +873,64 @@ Readability.prototype = {
       scripts[i].removeAttribute('src');
 
       if (scripts[i].parentNode)
           scripts[i].parentNode.removeChild(scripts[i]);
     }
   },
 
   /**
-   * Get child index of the only P element inside a DIV with no
-   * text content. Returns -1 if the DIV node contains non-empty
-   * text nodes or if it contains other element nodes.
+   * Check if this node has only whitespace and a single P element
+   * Returns false if the DIV node contains non-empty text nodes
+   * or if it contains no P or more than 1 element.
    *
    * @param Element
   **/
-  _getSinglePIndexInsideDiv: function(e) {
+  _hasSinglePInsideElement: function(e) {
+    // There should be exactly 1 element child which is a P:
+    if (e.children.length != 1 || e.firstElementChild.tagName !== "P") {
+      return false;
+    }
+    // And there should be no text nodes with real content
     var childNodes = e.childNodes;
-    var pIndex = -1;
-
     for (var i = childNodes.length; --i >= 0;) {
       var node = childNodes[i];
-
-      if (node.nodeType === Node.ELEMENT_NODE) {
-        if (node.tagName !== "P")
-          return -1;
-
-        if (pIndex >= 0)
-          return -1;
-
-        pIndex = i;
-      } else if (node.nodeType == Node.TEXT_NODE && this._getInnerText(node, false)) {
-        return -1;
+      if (node.nodeType == Node.TEXT_NODE &&
+          this.REGEXPS.hasContent.test(node.textContent)) {
+        return false;
       }
     }
 
-    return pIndex;
+    return true;
   },
 
   /**
    * Determine whether element has any children block level elements.
    *
    * @param Element
    */
   _hasChildBlockElement: function (e) {
-    var length = e.childNodes.length;
+    var length = e.children.length;
     for (var i = 0; i < length; i++) {
-      var child = e.childNodes[i];
-      if (child.nodeType != 1)
-        continue;
-
+      var child = e.children[i];
       if (this.DIV_TO_P_ELEMS.indexOf(child.tagName) !== -1 || this._hasChildBlockElement(child))
         return true;
     }
     return false;
   },
 
   /**
    * Get the inner text of a node - cross browser compatibly.
    * This also strips out any excess whitespace to be found.
    *
    * @param Element
    * @return string
   **/
   _getInnerText: function(e, normalizeSpaces) {
-    var textContent = e.textContent.replace(this.REGEXPS.trim, "");
+    var textContent = e.textContent.trim();
     normalizeSpaces = (typeof normalizeSpaces === 'undefined') ? true : normalizeSpaces;
 
     if (normalizeSpaces) {
       return textContent.replace(this.REGEXPS.normalize, " ");
     } else {
       return textContent;
     }
   },
@@ -928,28 +951,27 @@ Readability.prototype = {
    * Remove the style attribute on every e and under.
    * TODO: Test if getElementsByTagName(*) is faster.
    *
    * @param Element
    * @return void
   **/
   _cleanStyles: function(e) {
     e = e || this._doc;
-    var cur = e.firstChild;
-
     if (!e)
       return;
+    var cur = e.firstChild;
 
     // Remove any root styles, if we're able.
     if (typeof e.removeAttribute === 'function' && e.className !== 'readability-styled')
       e.removeAttribute('style');
 
     // Go until there are no more child nodes
     while (cur !== null) {
-      if (cur.nodeType === 1) {
+      if (cur.nodeType === cur.ELEMENT_NODE) {
         // Remove style attribute(s) :
         if (cur.className !== "readability-styled")
           cur.removeAttribute("style");
 
         this._cleanStyles(cur);
       }
 
       cur = cur.nextSibling;
@@ -1350,29 +1372,29 @@ Readability.prototype = {
   _getClassWeight: function(e) {
     if (!this._flagIsActive(this.FLAG_WEIGHT_CLASSES))
       return 0;
 
     var weight = 0;
 
     // Look for a special classname
     if (typeof(e.className) === 'string' && e.className !== '') {
-      if (e.className.search(this.REGEXPS.negative) !== -1)
+      if (this.REGEXPS.negative.test(e.className))
         weight -= 25;
 
-      if (e.className.search(this.REGEXPS.positive) !== -1)
+      if (this.REGEXPS.positive.test(e.className))
         weight += 25;
     }
 
     // Look for a special ID
     if (typeof(e.id) === 'string' && e.id !== '') {
-      if (e.id.search(this.REGEXPS.negative) !== -1)
+      if (this.REGEXPS.negative.test(e.id))
         weight -= 25;
 
-      if (e.id.search(this.REGEXPS.positive) !== -1)
+      if (this.REGEXPS.positive.test(e.id))
         weight += 25;
     }
 
     return weight;
   },
 
   /**
    * Clean a node of all elements of type "tag".
@@ -1390,21 +1412,21 @@ Readability.prototype = {
       // Allow youtube and vimeo videos through as people usually want to see those.
       if (isEmbed) {
         var attributeValues = "";
         for (var i = 0, il = targetList[y].attributes.length; i < il; i += 1) {
           attributeValues += targetList[y].attributes[i].value + '|';
         }
 
         // First, check the elements attributes to see if any of them contain youtube or vimeo
-        if (attributeValues.search(this.REGEXPS.videos) !== -1)
+        if (this.REGEXPS.videos.test(attributeValues))
           continue;
 
         // Then check the elements inside this element for the same.
-        if (targetList[y].innerHTML.search(this.REGEXPS.videos) !== -1)
+        if (this.REGEXPS.videos.test(targetList[y].innerHTML))
           continue;
       }
 
       targetList[y].parentNode.removeChild(targetList[y]);
     }
   },
 
   /**
@@ -1440,17 +1462,17 @@ Readability.prototype = {
         var p = tagsList[i].getElementsByTagName("p").length;
         var img = tagsList[i].getElementsByTagName("img").length;
         var li = tagsList[i].getElementsByTagName("li").length-100;
         var input = tagsList[i].getElementsByTagName("input").length;
 
         var embedCount = 0;
         var embeds = tagsList[i].getElementsByTagName("embed");
         for (var ei = 0, il = embeds.length; ei < il; ei += 1) {
-          if (embeds[ei].src.search(this.REGEXPS.videos) === -1)
+          if (!this.REGEXPS.videos.test(embeds[ei].src))
             embedCount += 1;
         }
 
         var linkDensity = this._getLinkDensity(tagsList[i]);
         var contentLength = this._getInnerText(tagsList[i]).length;
         var toRemove = false;
 
         if (img > p) {
@@ -1527,35 +1549,45 @@ Readability.prototype = {
     // this._parsedPages[uri.spec.replace(/\/$/, '')] = true;
 
     // Pull out any possible next page link first.
     // var nextPageLink = this._findNextPageLink(doc.body);
 
     this._prepDocument();
 
     var articleTitle = this._getArticleTitle();
+    var metadata = this._getArticleMetadata();
+
     var articleContent = this._grabArticle();
     if (!articleContent)
       return null;
 
     this.log("Grabbed: " + articleContent.innerHTML);
 
     this._postProcessContent(articleContent);
 
     // if (nextPageLink) {
     //   // Append any additional pages after a small timeout so that people
     //   // can start reading without having to wait for this to finish processing.
     //   setTimeout((function() {
     //     this._appendNextPage(nextPageLink);
     //   }).bind(this), 500);
     // }
 
-    var excerpt = this._getExcerpt(articleContent);
+    // If we haven't found an excerpt in the article's metadata, use the article's
+    // first paragraph as the excerpt. This is used for displaying a preview of
+    // the article's content.
+    if (!metadata.excerpt) {
+      var paragraphs = articleContent.getElementsByTagName("p");
+      if (paragraphs.length > 0) {
+        metadata.excerpt = paragraphs[0].textContent;
+      }
+    }
 
     return { uri: this._uri,
              title: articleTitle,
-             byline: this._articleByline,
+             byline: metadata.byline || this._articleByline,
              dir: this._articleDir,
              content: articleContent.innerHTML,
              length: articleContent.textContent.length,
-             excerpt: excerpt };
+             excerpt: metadata.excerpt };
   }
 };
--- a/toolkit/locales/en-US/chrome/global/aboutReader.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutReader.properties
@@ -23,11 +23,13 @@ aboutReader.toolbar.typeControls=Type co
 aboutReader.toolbar.addToReadingList=Add to Reading List
 aboutReader.toolbar.removeFromReadingList=Remove from Reading List
 aboutReader.toolbar.openReadingList=Open Reading List
 aboutReader.toolbar.closeReadingList=Close Reading List
 aboutReader.toolbar.share=Share
 
 aboutReader.footer.deleteThisArticle=Delete this article
 
-# Reader View toolbar button
+# These are used for the Reader View toolbar button and the menuitem within the
+# View menu.
 readerView.enter=Enter Reader View
 readerView.close=Close Reader View
+readerView.accesskey=R
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/xpinstallConfirm.properties
@@ -4,13 +4,8 @@
 
 unverified=(Author not verified)
 signed=(%S)
 
 itemWarnIntroMultiple=You have asked to install the following %S items:
 itemWarnIntroSingle=You have asked to install the following item:
 installButtonDisabledLabel=Install (%S)
 installButtonLabel=Install Now
-
-installComplete=Software Installation is complete. You will have to restart %S for changes to take effect.
-installCompleteTitle=Installation Complete
-
-error-203=Error Installing Item