Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 10 May 2015 18:48:18 -0700
changeset 243324 2f26a0e2c3320194a521f8c0c551ac37a29253d1
parent 243323 6325283ba40bc00b0b3d0a85331880f1ef18a786 (current diff)
parent 243215 d8420a541d1c1a00f0e4b60a7c55f4c0b928edd9 (diff)
child 243325 e09e6eb08f509cd15cf3d7a930025abbb359b46b
push id28738
push usercbook@mozilla.com
push dateTue, 12 May 2015 14:11:31 +0000
treeherderautoland@bedce1b405a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone40.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
js/src/shell/jsshell.h
--- 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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- 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="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- 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="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- 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="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <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": "5b2a150f6f5d29bddfaac13fcbbf099376f2f275", 
+        "git_revision": "6089234ace8b294a8feef064387604bae16254e3", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "75e6c18c2cdd05073434a8d5e749c72a063a0521", 
+    "revision": "4bcdce164d361f57742d042ff2742e3e6d53d7dd", 
     "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="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="5b2a150f6f5d29bddfaac13fcbbf099376f2f275"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -391,16 +391,19 @@
                                if (!this.parentNode._placesView)
                                  new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                tooltip="bhTooltip" popupsinherittooltip="true">
       <menuitem id="bookmarksShowAll"
                 label="&showAllBookmarks2.label;"
                 command="Browser:ShowAllBookmarks"
                 key="manBookmarkKb"/>
       <menuseparator id="organizeBookmarksSeparator"/>
+      <menuitem id="menu_pocket" label="&pocketMenuitem.label;"
+                oncommand="openUILink(Pocket.listURL, event);"/>
+      <menuseparator id="menu_pocketSeparator"/>
       <menuitem id="menu_bookmarkThisPage"
                 command="Browser:AddBookmarkAs"
                 observes="bookmarkThisPageBroadcaster"
                 key="addBookmarkAsKb"/>
       <menuitem id="subscribeToPageMenuitem"
 #ifndef XP_MACOSX
                 class="menuitem-iconic"
 #endif
@@ -456,19 +459,16 @@
           <menuitem id="viewReadingListSidebar" class="subviewbutton"
                     oncommand="SidebarUI.toggle('readingListSidebar');"
                     label="&readingList.showSidebar.label;">
             <observes element="readingListSidebar" attribute="checked"/>
           </menuitem>
         </menupopup>
       </menu>
 #endif
-      <menuseparator id="menu_pocketSeparator"/>
-      <menuitem id="menu_pocket" label="&pocketMenuitem.label;"
-                oncommand="openUILink(Pocket.listURL, event);"/>
       <menuseparator id="bookmarksMenuItemsSeparator"/>
       <!-- Bookmarks menu items -->
       <menuseparator builder="end"
                      class="hide-if-empty-places-result"/>
       <menuitem id="menu_unsortedBookmarks"
                 label="&unsortedBookmarksCmd.label;"
                 oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"/>
     </menupopup>
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -895,16 +895,21 @@
             </menuitem>
             <!-- NB: temporary solution for bug 985024, this should go away soon. -->
             <menuitem id="BMB_bookmarksShowAllTop"
                       class="menuitem-iconic subviewbutton"
                       label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
                       key="manBookmarkKb"/>
             <menuseparator/>
+            <menuitem id="BMB_pocket"
+                      class="menuitem-iconic bookmark-item subviewbutton"
+                      label="&pocketMenuitem.label;"
+                      oncommand="openUILink(Pocket.listURL, event);"/>
+            <menuseparator id="BMB_pocketSeparator"/>
             <menuitem id="BMB_subscribeToPageMenuitem"
 #ifndef XP_MACOSX
                       class="menuitem-iconic subviewbutton"
 #else
                       class="subviewbutton"
 #endif
                       label="&subscribeToPageMenuitem.label;"
                       oncommand="return FeedHandler.subscribeToFeed(null, event);"
@@ -966,21 +971,16 @@
               <menupopup id="BMB_readingListPopup"
                          placespopup="true"
                          onpopupshowing="ReadingListUI.onReadingListPopupShowing(this);">
                 <menuitem id="BMB_viewReadingListSidebar" class="subviewbutton"
                           oncommand="SidebarUI.show('readingListSidebar');"
                           label="&readingList.showSidebar.label;"/>
               </menupopup>
             </menu>
-            <menuseparator id="BMB_pocketSeparator"/>
-            <menuitem id="BMB_pocket"
-                      class="menuitem-iconic bookmark-item subviewbutton"
-                      label="&pocketMenuitem.label;"
-                      oncommand="openUILink(Pocket.listURL, event);"/>
             <menuseparator/>
             <!-- Bookmarks menu items will go here -->
             <menuitem id="BMB_bookmarksShowAll"
                       class="subviewbutton panel-subview-footer"
                       label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
                       key="manBookmarkKb"/>
           </menupopup>
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -230,19 +230,17 @@
         </vbox>
         <button id="PanelUI-panic-view-button"
                 label="&panicButton.view.forgetButton;"/>
       </vbox>
     </panelview>
 
     <panelview id="PanelUI-pocketView" flex="1">
       <vbox class="panel-subview-body">
-        <hbox id="pocket-panel-container" align="top" flex="1">
-          <iframe id="pocket-panel-iframe" type="content"/>
-        </hbox>
+        <iframe id="pocket-panel-iframe" type="content"/>
       </vbox>
     </panelview>
 
 
   </panelmultiview>
   <!-- These menupopups are located here to prevent flickering,
        see bug 492960 comment 20. -->
   <menupopup id="customizationPanelItemContextMenu">
--- a/browser/components/pocket/Pocket.jsm
+++ b/browser/components/pocket/Pocket.jsm
@@ -3,45 +3,60 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 this.EXPORTED_SYMBOLS = ["Pocket"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
   "resource://gre/modules/ReaderMode.jsm");
 
 let Pocket = {
   get site() Services.prefs.getCharPref("browser.pocket.site"),
   get listURL() { return "https://" + Pocket.site; },
 
   /**
    * Functions related to the Pocket panel UI.
    */
   onPanelViewShowing(event) {
-    let window = event.target.ownerDocument.defaultView;
-    window.addEventListener("popupshowing", Pocket.onPocketPanelShowing, true);
-    window.addEventListener("popupshown", Pocket.onPocketPanelShown, true);
+    let document = event.target.ownerDocument;
+    let window = document.defaultView;
+    let iframe = document.getElementById('pocket-panel-iframe');
+
+    // ViewShowing fires immediately before it creates the contents,
+    // in lieu of an AfterViewShowing event, just spin the event loop.
+    window.setTimeout(function() {
+      window.pktUI.pocketButtonOnCommand();
+
+      if (iframe.contentDocument &&
+          iframe.contentDocument.readyState == "complete")
+      {
+        window.pktUI.pocketPanelDidShow();
+      } else {
+        // iframe didn't load yet. This seems to always be the case when in
+        // the toolbar panel, but never the case for a subview.
+        // XXX this only being fired when it's a _capturing_ listener!
+        iframe.addEventListener("load", Pocket.onFrameLoaded, true);
+      }
+    }, 0);
   },
 
-  onPocketPanelShowing(event) {
-    let window = event.target.ownerDocument.defaultView;
-    window.removeEventListener("popupshowing", Pocket.onPocketPanelShowing, true);
-    window.pktUI.pocketButtonOnCommand(event);
-  },
+  onFrameLoaded(event) {
+    let document = event.currentTarget.ownerDocument;
+    let window = document.defaultView;
+    let iframe = document.getElementById('pocket-panel-iframe');
 
-  onPocketPanelShown(event) {
-    let window = event.target.ownerDocument.defaultView;
-    window.removeEventListener("popupshown", Pocket.onPocketPanelShown, true);
-    window.pktUI.pocketPanelDidShow(event);
+    iframe.removeEventListener("load", Pocket.onPanelLoaded, true);
+    window.pktUI.pocketPanelDidShow();
   },
 
   onPanelViewHiding(event) {
     let window = event.target.ownerDocument.defaultView;
     window.pktUI.pocketPanelDidHide(event);
   },
 
   // Called on tab/urlbar/location changes and after customization. Update
--- a/browser/components/pocket/main.js
+++ b/browser/components/pocket/main.js
@@ -35,39 +35,40 @@
  *
  */
 
 // TODO : Get the toolbar icons from Firefox's build (Nikki needs to give us a red saved icon)
 // TODO : [needs clarificaiton from Fx] Firefox's plan was to hide Pocket from context menus until the user logs in. Now that it's an extension I'm wondering if we still need to do this.
 // TODO : [needs clarificaiton from Fx] Reader mode (might be a something they need to do since it's in html, need to investigate their code)
 // TODO : [needs clarificaiton from Fx] Move prefs within pktApi.s to sqlite or a local file so it's not editable (and is safer)
 // TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
-// TODO : Remove console.log entries
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
 
 var pktUI = (function() {
 
 	// -- Initialization (on startup and new windows) -- //
 	var inited = false;
 	var _currentPanelDidShow;
     var _currentPanelDidHide;
 	var _isHidden = false;
 	var _notificationTimeout;
-    
+
     // Init panel id at 0. The first actual panel id will have the number 1 so
     // in case at some point any panel has the id 0 we know there is something
     // wrong
     var _panelId = 0;
 
-    var prefBranch = Services.prefs.getBranch("browser.pocket.settings.");
+	var prefBranch = Services.prefs.getBranch("browser.pocket.settings.");
 
-    var savePanelWidth = 350;
-    var savePanelHeights = {collapsed: 153, expanded: 272};
+	var overflowMenuWidth = 230;
+	var overflowMenuHeight = 475;
+	var savePanelWidth = 350;
+	var savePanelHeights = {collapsed: 153, expanded: 272};
 
 	/**
      * Initalizes Pocket UI and panels
      */
 	function onLoad() {
 		
 		if (inited)
 			return;
@@ -111,23 +112,16 @@ var pktUI = (function() {
 		
 		// Hide the extension based on certain criteria
 		hideIntegrationIfNeeded();
 		
 		inited = true;
 	}
 	
 	/**
-     * Called when window/chrome is unloaded
-     */
-	function onUnload() {
-	
-	}
-	
-	/**
 	 * Mark all Pocket integration chrome elements as hidden if certain criteria apply (ex: legacy Pocket extension users or unsupported languages)
 	 */
 	function hideIntegrationIfNeeded() {
 		
 		var hideIntegration = false;
 		
 		// Check if the user had the legacy extension the last time we looked
 		if (prefBranch.getBoolPref('hasLegacyExtension')) {
@@ -161,17 +155,19 @@ var pktUI = (function() {
 	
 	
 	// -- Event Handling -- //
     
     /**
      * Event handler when Pocket toolbar button is pressed
      */
     function pocketButtonOnCommand(event) {
-        tryToSaveCurrentPage();
+    
+    	tryToSaveCurrentPage();
+    
     }
     
     function pocketPanelDidShow(event) {
     	if (_currentPanelDidShow) {
     		_currentPanelDidShow(event);
         }
     	
     }
@@ -287,54 +283,75 @@ var pktUI = (function() {
     /**
      * Show the sign-up panel
      */
     function showSignUp() {
         getFirefoxAccountSignedInUser(function(userdata)
         {
             var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
             var startheight = 490;
-            if (pktApi.getSignupAB() == 'storyboard')
+            var inOverflowMenu = isInOverflowMenu();
+            
+            if (inOverflowMenu) 
+            {
+            	startheight = overflowMenuHeight;
+            }
+            else if (pktApi.getSignupAB() == 'storyboard')
             {
                 startheight = 460;
                 if (fxasignedin == '1')
                 {
                     startheight = 406;
                 }
             }
             else
             {
                 if (fxasignedin == '1')
                 {
                     startheight = 436;
                 }
             }
-           var panelId = showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + pktApi.getSignupAB(), {
-               onShow: function() {
-                },
-               onHide: panelDidHide,
-               width: 300,
-               height: startheight
-           });
-        });
+            var variant;
+            if (inOverflowMenu)
+            {
+                variant = 'overflow';
+            }
+            else
+            {
+                variant = pktApi.getSignupAB();
+            }
+            var panelId = showPanel("chrome://browser/content/pocket/panels/signup.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&fxasignedin=" + fxasignedin + "&variant=" + variant + '&inoverflowmenu=' + inOverflowMenu + "&locale=" + getUILocale(), {
+            		onShow: function() {
+                    },
+        			onHide: panelDidHide,
+            		width: inOverflowMenu ? overflowMenuWidth : 300,
+            		height: startheight
+            	});
+            });
     }
 
     /**
      * Show the logged-out state / sign-up panel
      */
     function saveAndShowConfirmation(url, title) {
 
         // Validate input parameter
         if (typeof url !== 'undefined' && url.startsWith("about:reader?url=")) {
             url = ReaderMode.getOriginalUrl(url);
         }
 
         var isValidURL = (typeof url !== 'undefined' && (url.startsWith("http") || url.startsWith('https')));
 
-        var panelId = showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0'), {
+        var inOverflowMenu = isInOverflowMenu();
+        var startheight = pktApi.isPremiumUser() && isValidURL ? savePanelHeights.expanded : savePanelHeights.collapsed;
+        if (inOverflowMenu) {
+        	startheight = overflowMenuHeight;
+        }
+
+    	var panelId = showPanel("chrome://browser/content/pocket/panels/saved.html?pockethost=" + Services.prefs.getCharPref("browser.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0') + '&inoverflowmenu='+inOverflowMenu + "&locale=" + getUILocale(), {
     		onShow: function() {
                 var saveLinkMessageId = 'saveLink';
 
                 // Send error message for invalid url
                 if (!isValidURL) {
                     // TODO: Pass key for localized error in error object
                     var error = {
                         message: 'Only links can be saved',
@@ -345,17 +362,18 @@ var pktUI = (function() {
                 }
 
                 // Check online state
                 if (!navigator.onLine) {
                     // TODO: Pass key for localized error in error object
                     var error = {
                         message: 'You must be connected to the Internet in order to save to Pocket. Please connect to the Internet and try again.'
                     };
-                    pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);                    return;
+                    pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
+                    return;
                 }
 
                 // Add url
                 var options = {
                     success: function(data, request) {
                         var item = data.item;
                         var successResponse = {
                             status: "success",
@@ -370,39 +388,40 @@ var pktUI = (function() {
                             return;
                         }
 
                         // If there is no error message in the error use a
                         // complete catch-all
                         var errorMessage = error.message || "There was an error when trying to save to Pocket.";
                         var panelError = { message: errorMessage}
 
-                         // Send error message to panel
+                        // Send error message to panel
                         pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, panelError);
                     }
                 }
 
                 // Add title if given
                 if (typeof title !== "undefined") {
                     options.title = title;
                 }
 
                 // Send the link
 				pktApi.addLink(url, options);
 			},
 			onHide: panelDidHide,
-            width: savePanelWidth,
-            height: pktApi.isPremiumUser() && isValidURL ? savePanelHeights.expanded : savePanelHeights.collapsed
+    		width: inOverflowMenu ? overflowMenuWidth : savePanelWidth,
+    		height: startheight
     	});
     }
 
     /**
      * Open a generic panel
      */
     function showPanel(url, options) {
+
         // Add new panel id
         _panelId += 1;
         url += ("&panelId=" + _panelId);
 
         // We don't have to hide and show the panel again if it's already shown
         // as if the user tries to click again on the toolbar button the overlay
         // will close instead of the button will be clicked
     	var iframe = getPanelFrame();
@@ -418,68 +437,73 @@ var pktUI = (function() {
     	// panel.setAttribute('consumeoutsideclicks', false);
     	//
 
     	// For some reason setting onpopupshown and onpopuphidden on the panel directly didn't work, so
     	// do it this hacky way for now
     	_currentPanelDidShow = options.onShow;
     	_currentPanelDidHide = options.onHide;
 
-        resizePanel({
-            width: options.width,
-            height: options.height
-        });
-
+    	resizePanel({
+    		width: options.width,
+    		height: options.height
+    	});
         return _panelId;
     }
 
     /**
      * Resize the panel
      * options = {
      * 	width: ,
      *	height: ,
      * 	animate [default false]
      * }
      */
     function resizePanel(options) {
         var iframe = getPanelFrame();
-        iframe.width = options.width;
-        iframe.height = options.height;
+        var subview = getSubview();
 
-    	// TODO : Animate the change if given options.animate = true
-    	getPanel().sizeTo(options.width, options.height);
+        if (subview) {
+          // Use the subview's size
+          iframe.style.width = "100%";
+          iframe.style.height = subview.clientHeight + "px";
+        } else {
+          // Set an explicit size, panel will adapt.
+          iframe.style.width  = options.width  + "px";
+          iframe.style.height = options.height + "px";
+        }
     }
 
     /**
      * Called when the signup and saved panel was hidden
      */
     function panelDidHide() {
+        
     }
 
     /**
      * Register all of the messages needed for the panels
      */
     function registerEventMessages() {
     	var iframe = getPanelFrame();
 
     	// Only register the messages once
         var didInitAttributeKey = 'did_init';
         var didInitMessageListener = iframe.getAttribute(didInitAttributeKey);
-        if (typeof didInitMessageListener !== "undefined" && didInitMessageListener == 1) {
+    	if (typeof didInitMessageListener !== "undefined" && didInitMessageListener == 1) {
             return;
         }
-
     	iframe.setAttribute(didInitAttributeKey, 1);
 
 		// When the panel is displayed it generated an event called
 		// "show": we will listen for that event and when it happens,
 		// send our own "show" event to the panel's script, so the
 		// script can prepare the panel for display.
-		var _showMessageId = "show";
-        pktUIMessaging.addMessageListener(_showMessageId, function(panelId, data) {
+        var _showMessageId = "show";
+		pktUIMessaging.addMessageListener(_showMessageId, function(panelId, data) {
 			// Let panel know that it is ready
 			pktUIMessaging.sendMessageToPanel(panelId, _showMessageId);
 		});
 
         // Open a new tab with a given url and activate if
         var _openTabWithUrlMessageId = "openTabWithUrl";
         pktUIMessaging.addMessageListener(_openTabWithUrlMessageId, function(panelId, data) {
 
@@ -490,159 +514,184 @@ var pktUI = (function() {
             }
 
             var url = data.url;
             openTabWithUrl(url, activate);
             pktUIMessaging.sendResponseMessageToPanel(panelId, _openTabWithUrlMessageId, url);
         });
 
 		// Close the panel
-		var _closeMessageId = "close";
-       pktUIMessaging.addMessageListener(_closeMessageId, function(panelId, data) {
+        var _closeMessageId = "close";
+		pktUIMessaging.addMessageListener(_closeMessageId, function(panelId, data) {
 			getPanel().hidePopup();
 		});
 
 		// Send the current url to the panel
-		var _getCurrentURLMessageId = "getCurrentURL";
-       pktUIMessaging.addMessageListener(_getCurrentURLMessageId, function(panelId, data) {
+        var _getCurrentURLMessageId = "getCurrentURL";
+		pktUIMessaging.addMessageListener(_getCurrentURLMessageId, function(panelId, data) {
             pktUIMessaging.sendResponseMessageToPanel(panelId, _getCurrentURLMessageId, getCurrentUrl());
 		});
 
         var _resizePanelMessageId = "resizePanel";
-        pktUIMessaging.addMessageListener(_resizePanelMessageId, function(panelId, data) {
-           resizePanel(data);
+		pktUIMessaging.addMessageListener(_resizePanelMessageId, function(panelId, data) {
+			resizePanel(data);
         });
 
 		// Callback post initialization to tell background script that panel is "ready" for communication.
-       pktUIMessaging.addMessageListener("listenerReady", function(panelId, data) {
+		pktUIMessaging.addMessageListener("listenerReady", function(panelId, data) {
 
-       });
+		});
 
-       pktUIMessaging.addMessageListener("collapseSavePanel", function(panelId, data) {
-           if (!pktApi.isPremiumUser())
-               resizePanel({width:savePanelWidth, height:savePanelHeights.collapsed});
+		pktUIMessaging.addMessageListener("collapseSavePanel", function(panelId, data) {
+			if (!pktApi.isPremiumUser() && !isInOverflowMenu())
+				resizePanel({width:savePanelWidth, height:savePanelHeights.collapsed});
 		});
 
 		pktUIMessaging.addMessageListener("expandSavePanel", function(panelId, data) {
-           resizePanel({width:savePanelWidth, height:savePanelHeights.expanded});
+			if (!isInOverflowMenu())
+				resizePanel({width:savePanelWidth, height:savePanelHeights.expanded});
 		});
 
 		// Ask for recently accessed/used tags for auto complete
 		var _getTagsMessageId = "getTags";
         pktUIMessaging.addMessageListener(_getTagsMessageId, function(panelId, data) {
 			pktApi.getTags(function(tags, usedTags) {
                 pktUIMessaging.sendResponseMessageToPanel(panelId, _getTagsMessageId, {
                     tags: tags,
                     usedTags: usedTags
                 });
 			});
 		});
 
 		// Ask for suggested tags based on passed url
-		var _getSuggestedTagsMessageId = "getSuggestedTags";
-       pktUIMessaging.addMessageListener(_getSuggestedTagsMessageId, function(panelId, data) {
-           pktApi.getSuggestedTagsForURL(data.url, {
+        var _getSuggestedTagsMessageId = "getSuggestedTags";
+		pktUIMessaging.addMessageListener(_getSuggestedTagsMessageId, function(panelId, data) {
+			pktApi.getSuggestedTagsForURL(data.url, {
 				success: function(data, response) {
 					var suggestedTags = data.suggested_tags;
 					var successResponse = {
 						status: "success",
 						value: {
-							suggestedTags : suggestedTags
+							suggestedTags: suggestedTags
 						}
 					}
-					pktUIMessaging.sendResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, successResponse);
+                    pktUIMessaging.sendResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, successResponse);
 				},
 				error: function(error, response) {
-					pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, error);
+                    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, error);
 				}
 			})
 		});
 
 		// Pass url and array list of tags, add to existing save item accordingly
-		var _addTagsMessageId = "addTags";
-       pktUIMessaging.addMessageListener(_addTagsMessageId, function(panelId, data) {
-           pktApi.addTagsToURL(data.url, data.tags, {
+        var _addTagsMessageId = "addTags";
+		pktUIMessaging.addMessageListener(_addTagsMessageId, function(panelId, data) {
+			pktApi.addTagsToURL(data.url, data.tags, {
 				success: function(data, response) {
 				    var successResponse = {status: "success"};
                     pktUIMessaging.sendResponseMessageToPanel(panelId, _addTagsMessageId, successResponse);
 				},
 				error: function(error, response) {
-				  pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _addTagsMessageId, error);
+                    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _addTagsMessageId, error);
 				}
 			});
 		});
 
 		// Based on clicking "remove page" CTA, and passed unique item id, remove the item
-		var _deleteItemMessageId = "deleteItem";
-       pktUIMessaging.addMessageListener(_deleteItemMessageId, function(panelId, data) {
-           pktApi.deleteItem(data.itemId, {
+        var _deleteItemMessageId = "deleteItem";
+		pktUIMessaging.addMessageListener(_deleteItemMessageId, function(panelId, data) {
+			pktApi.deleteItem(data.itemId, {
 				success: function(data, response) {
 				    var successResponse = {status: "success"};
                     pktUIMessaging.sendResponseMessageToPanel(panelId, _deleteItemMessageId, successResponse);
 				},
 				error: function(error, response) {
-					pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _deleteItemMessageId, error);
+				    pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _deleteItemMessageId, error);
 				}
 			})
 		});
 	}
-	
+
 	// -- Browser Navigation -- //
-	
+
 	/**
      * Open a new tab with a given url and notify the iframe panel that it was opened
      */
 
 	function openTabWithUrl(url, activate) {
         var tab = gBrowser.addTab(url);
         if (activate) {
             gBrowser.selectedTab = tab;
         }
 	}
-    
-    
+
+
     // -- Helper Functions -- //
-    
+
     function getCurrentUrl() {
     	return getBrowser().currentURI.spec;
     }
-    
+
     function getCurrentTitle() {
         return getBrowser().contentTitle;
     }
-    
+
     function getPanel() {
         var frame = getPanelFrame();
         var panel = frame;
         while (panel && panel.localName != "panel") {
             panel = panel.parentNode;
         }
     	return panel;
     }
-    
+
     function getPanelFrame() {
     	return document.getElementById('pocket-panel-iframe');
     }
-    
+
+    function getSubview() {
+        var frame = getPanelFrame();
+        var view = frame;
+        while (view && view.localName != "panelview") {
+            view = view.parentNode;
+        }
+
+        if (view && view.getAttribute("current") == "true")
+            return view;
+        return null;
+    }
+
+    function isInOverflowMenu() {
+        var subview = getSubview();
+        return !!subview;
+    }
+
     function hasLegacyExtension() {
     	return !!document.getElementById('RIL_urlbar_add');
     }
-    
+
     function isHidden() {
     	return _isHidden;
     }
-    
+
     function getFirefoxAccountSignedInUser(callback) {
-       fxAccounts.getSignedInUser().then(userData => {
-           callback(userData);
-       }).then(null, error => {
-           callback();
-       });
+	    fxAccounts.getSignedInUser().then(userData => {
+    		callback(userData);
+    	}).then(null, error => {
+      		callback();
+	    });
     }
     
+    function getUILocale() {
+    	var locale = Cc["@mozilla.org/chrome/chrome-registry;1"].
+             getService(Ci.nsIXULChromeRegistry).
+             getSelectedLocale("browser");
+        return locale;
+    }
+
     /**
      * Toolbar animations
      */
     
     function showPocketAnimation() {
     	
     	// Borrowed from bookmark star animation:
     	// https://dxr.mozilla.org/mozilla-central/source/browser/base/content/browser-places.js#1568
@@ -702,18 +751,18 @@ var pktUI = (function() {
 	      notifier.style.transform = starIconTransform;
 	      dropmarkerNotifier.style.transform = dropmarkerTransform;
 	
 	      let dropmarkerAnimationNode = dropmarkerNotifier.firstChild;
 	      dropmarkerAnimationNode.style.MozImageRegion = dropmarkerStyle.MozImageRegion;
 	      dropmarkerAnimationNode.style.listStyleImage = dropmarkerStyle.listStyleImage;
 	    }
 	
-	    let isInOverflowPanel = button.getAttribute("overflowedItem") == "true";
-	    if (!isInOverflowPanel) {
+	    let isInOverflowMenu = button.getAttribute("overflowedItem") == "true";
+	    if (!isInOverflowMenu) {
 	      notifier.setAttribute("notification", "finish");
 	      button.setAttribute("notification", "finish");
 	      dropmarkerNotifier.setAttribute("notification", "finish");
 	    }
 	
 	    _notificationTimeout = setTimeout( () => {
 	      notifier.removeAttribute("notification");
 	      dropmarkerNotifier.removeAttribute("notification");
@@ -735,19 +784,17 @@ var pktUI = (function() {
     	pocketPanelDidShow: pocketPanelDidShow,
     	pocketPanelDidHide: pocketPanelDidHide,
 
         pocketContextSaveLinkOnCommand,
         pocketContextSavePageOnCommand,
 
         pocketBookmarkBarOpenPocketCommand,
 
-    	tryToSaveUrl: tryToSaveUrl,
-    	
-		isHidden
+    	tryToSaveUrl: tryToSaveUrl
     };
 }());
 
 // -- Communication to Background -- //
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
 var pktUIMessaging = (function() {
 
     /**
--- a/browser/components/pocket/panels/css/saved.css
+++ b/browser/components/pocket/panels/css/saved.css
@@ -6,16 +6,18 @@
  *  Contents:
  *  Global
  *  Loading spinner
  *  Core detail
  *  Tag entry
  *  Recent/suggested tags
  *  Premium upsell
  *  Token input/autocomplete
+ *  Overflow mode
+ *  Language overrides
  */
 
 /*=Global
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersaved {
     background-color: #fbfbfb;
     border-radius: 4px;
     display: block;
@@ -271,17 +273,16 @@
 .pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_actions_separator {
     border-left: 2px solid #777;
     height: 0.75em;
     margin-top: 0.3em;
     padding: 0;
     width: 10px;
 }
 .pkt_ext_containersaved .pkt_ext_item_actions a {
-    -webkit-font-feature-settings: normal;
     background: transparent;
     color: #0095dd;
     display: block;
     font-feature-settings: normal;
     font-size: 12px;
     font-weight: normal;
     letter-spacing: normal;
     line-height: inherit;
@@ -451,16 +452,17 @@
 }
 
 /*=Recent/suggested tags
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail,
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detailshown {
     border-top: 1px solid #c1c1c1;
     bottom: 0;
+    box-sizing: border-box;
     background: #ebebeb;
     clear: both;
     left: 0;
     opacity: 0;
     min-height: 110px;
     position: fixed;
     visibility: hidden;
     width: 100%;
@@ -506,17 +508,18 @@
     display: block;
     margin: 0;
     height: 2em; 
     overflow: hidden;
     padding: 2px 0 0 0;
 }
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail ul {
     height: auto;
-    margin: 0 2em 0 0;
+    margin: 0;
+    max-height: 4em;
     padding-top: 6px;
 }
 .pkt_ext_containersaved .pkt_ext_recenttag_detail li,
 .pkt_ext_containersaved .pkt_ext_suggestedtag_detail li {
     background: none;
     float: left;
     height: inherit;
     line-height: 1.5; 
@@ -743,8 +746,80 @@
     }
 }
 .pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token {
     background-color: #f7f7f7;
 }
 .pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token span {
     color: #bbb;
 }
+
+/*=Overflow mode
+--------------------------------------------------------------------------------------- */
+.pkt_ext_saved_overflow .pkt_ext_logo {
+    float: none;
+    margin: 0.5em auto 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_initload {
+    top: -8px;
+}
+.pkt_ext_saved_overflow .pkt_ext_loadingspinner {
+    top: 10em;
+}
+.pkt_ext_saved_overflow .pkt_ext_topdetail {
+    float: none;
+    margin: 0 auto;
+    padding: 0 1em;
+}
+.pkt_ext_saved_overflow h2 {
+    margin-bottom: 0.5em;
+    margin-top: 0;
+    text-align: center;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions ul {
+    display: inline-block;
+    width: auto;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions li {
+    float: none;
+    padding-left: 1em;
+    padding-right: 1em;
+    text-align: center;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_removeitem,
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_openpocket {
+    float: none;
+    text-align: center;
+    padding-left: 0;
+    padding-right: 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_actions_separator {
+    display: none;
+}
+.pkt_ext_saved_overflow .pkt_ext_tag_detail {
+    margin-top: 0;
+}
+.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detail,
+.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detailshown {
+    top: 14.75em;
+}
+.pkt_ext_saved_overflow .pkt_ext_edit_msg {
+    top: 16em;
+}
+.pkt_ext_container_finalerrorstate.pkt_ext_saved_overflow .pkt_ext_errordetail {
+    box-sizing: border-box;
+    left: 0;
+    padding-left: 1em;
+    padding-right: 1em;
+    text-align: center;
+    top: 8.3em;
+    width: 100%;
+}
+
+/*=Language overrides
+--------------------------------------------------------------------------------------- */
+.pkt_ext_saved_es .pkt_ext_btn {
+    min-width: 5em;
+}
+.pkt_ext_saved_de .pkt_ext_btn,
+.pkt_ext_saved_ru .pkt_ext_btn {
+    min-width: 6em;
+}
--- a/browser/components/pocket/panels/css/signup.css
+++ b/browser/components/pocket/panels/css/signup.css
@@ -3,17 +3,18 @@
  *  Description:
  *  With base elements out of the way, this sets all custom styling for the extension.
  *
  *  Contents:
  *  Global
  *  Core detail
  *  Core detail - storyboard
  *  Buttons
- *  Responsive
+ *  Overflow mode
+ *  Language overrides
  */
 
 /*=Global
 --------------------------------------------------------------------------------------- */
 .pkt_ext_containersignup {
     background-color: #ebebeb;
     color: #333;
     display: block;
@@ -121,17 +122,17 @@
     height: 18px;
     margin-top: -9px;
     right: -15px;
     position: absolute;
     text-indent: -9999px;
     width: 18px;
     top: 50%;
 }
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
+@media (min-resolution: 1.1dppx) {
     .pkt_ext_containersignup .ff_signuphelp {
         background-image: url(../img/signup_help@2x.png);
         background-size: 18px 18px;
     }
 }
 .pkt_ext_containersignup .alreadyhave {
     font-size: 12px;
     max-width: 320px;
@@ -300,8 +301,76 @@
 }
 .pkt_ext_containersignup .signup-btn-firefox .text {
     color: #fff;
 }
 .pkt_ext_containersignup .btn-disabled .text {
     color: #ccc;
     color: rgba(255,255,255,0.6);
 }
+
+/*=Overflow mode
+--------------------------------------------------------------------------------------- */
+.pkt_ext_signup_overflow .pkt_ext_tagline {
+    margin-bottom: 1em;
+    padding: 0 1em;
+}
+.pkt_ext_signup_overflow .pkt_ext_introimg {
+    background-size: 200px 98px;
+    height: 98px;
+    width: 200px;
+}
+.pkt_ext_signup_overflow .signup-btn-firefox,
+.pkt_ext_signup_overflow .signup-btn-email {
+    font-size: 14px;
+    min-width: 12.6em;
+    padding-left: 0.75em;
+    padding-right: 0.75em;
+}
+.pkt_ext_signup_overflow .signup-btn-firefox .text {
+    padding-left: 0;
+    padding-right: 0;
+}
+
+/*=Language overrides
+--------------------------------------------------------------------------------------- */
+.pkt_ext_signup_de .pkt_ext_introstoryone_img {
+    margin-right: -5px;
+    padding-left: 0;
+}
+.pkt_ext_signup_de .pkt_ext_introstorytwo .pkt_ext_tagline,
+.pkt_ext_signup_es .pkt_ext_introstorytwo .pkt_ext_tagline,
+.pkt_ext_signup_ja .pkt_ext_introstorytwo .pkt_ext_tagline,
+.pkt_ext_signup_ru .pkt_ext_introstorytwo .pkt_ext_tagline {
+    margin-bottom: 0.5em;
+}
+.pkt_ext_signup_de .signup-btn-firefox .text,
+.pkt_ext_signup_de .signup-btn-email,
+.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-firefox .text,
+.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-email,
+.pkt_ext_signup_ja .signup-btn-firefox .text,
+.pkt_ext_signup_ja .signup-btn-email,
+.pkt_ext_signup_ru .signup-btn-firefox .text,
+.pkt_ext_signup_ru .signup-btn-email {
+    font-size: 15px;
+}
+.pkt_ext_signup_ja .signup-btn-firefox .text,
+.pkt_ext_signup_ru .signup-btn-firefox .text {
+    left: 15px;
+}
+.pkt_ext_signup_de .signup-btn-email,
+.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-email,
+.pkt_ext_signup_ja .signup-btn-email,
+.pkt_ext_signup_ru .signup-btn-email {
+    min-width: 13em;
+    padding: 0.8533em 1.2667em;
+}
+.pkt_ext_signup_de .pkt_ext_logo,
+.pkt_ext_signup_es .pkt_ext_logo,
+.pkt_ext_signup_ru .pkt_ext_logo {
+    padding-top: 15px;
+}
+.pkt_ext_signup_de .pkt_ext_introdetailhero .pkt_ext_tagline,
+.pkt_ext_signup_es .pkt_ext_introdetailhero .pkt_ext_tagline,
+.pkt_ext_signup_ja .pkt_ext_introdetailhero .pkt_ext_tagline,
+.pkt_ext_signup_ru .pkt_ext_introdetailhero .pkt_ext_tagline {
+    font-size: 13px;
+}
\ No newline at end of file
index 80816e1b049b3a2ad0b96df00f5f74bd4cef5eb3..b52db6abf6b50dc2b82104ab284a2db78baa2213
GIT binary patch
literal 264
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`=RI8<Lo7}w|M>sko>_Iq!?n&Y
zY!v?atHm8~XiPJZt6{ssw9!al{*gsLcm!567P8tgD@jTmU_8xz<oARl{EUY7A$lzH
z8SgjT5M4DvxWk<BqnX09rY=jykLe}M^A<{e;_hHxD0Ra@;?y_hNAek2^BYyd5<b3<
zSLjP`U{bJ_>Znf++%BZ@vPXT2b>j}%ECsX5^clR-Cp!+U<?-+nVS030C4+6HXp#e)
z10$nCoO4Us1Sv+g7zT|@_9NX5KNL+K{A2j2tT2yr(%}mZnPqGY*V-(V?>sbP1bU6Z
M)78&qol`;+0H;=71poj5
index ee341860adc7356379cb1c9a5353e2bca982ff75..69aa55b03fa46fd6fc034d88b6ffc4ad89ece38d
GIT binary patch
literal 641
zc$@)&0)G98P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp)
z=>Px#32;bRa{vGf6951U69E94oEQKA0wzgBK~z}7?N>WX13?hJ+Co9JQ5!`YML|@w
z@orBeqM(JBIbR@(oxNb?U$Id{v`r=02!e%$1hENd5iPt&5``$fzlkF8yd<|F#=<OY
zj_mGy^X=|Db`<5`H#bBzwnkgFpa)0LTRHlNE_0i8D~=Z4DVJiOzN_Hp>CXsg^rHOX
z?lZkYph6%Yfjd_-XYC<sbak48#ik^H)A030M@%)r%?baL@&t5R&kk6YFnTd=1#m48
zogVlW>@-@HCgAGf6>rpZhN&HM@jL`9ci8Ci%Gr8&LH-0ZI(5e8tG!^!kkNwUjrqvL
zZ&ahryZ(A&q6%2Y#Tg+4Z^dlgj~&4-Fdxl|?J>S@zdVEa!$L-wE^x0=Yh<Ru3(mwo
zcNV=Hq<Zf>kKyi53Si$efy)5dE0$EL*>8uLjOMi!wthk90leiB*^nXuT`SH;SSY>U
z2x`UJi-5@}qPT+AB>da5bn40Zwos`PYp#Rhv2MlDjSk5ano10r>Q7fQ`Z4E7VL|uj
zozanG2u-`XIsT<wm?4baN@5&kMXK;v5pnz#M|gnmKKiq)JnQ^!xxtOG6fdEOk|Vg-
zWguX)E?+B7=%iu6<1Z?IWF2|^7;ZMn5$YF|-!pcgTEW()`*2TJCA{-1#*`{pQlnH0
z;f)dy*kG)pE2o5NbOU5hvVLGY+GW|2m2jNh0J@Bzvzd7-;K`O9m@sz6B_5t&OMcn0
b(sH$TSOgHh4FJ7600000NkvXXu0mjfmX9PJ
--- a/browser/components/pocket/panels/js/dictionary.js
+++ b/browser/components/pocket/panels/js/dictionary.js
@@ -1,10 +1,10 @@
 Translations = {};
-Translations.en = 
+Translations.en =
 {
 	addtags: "Add Tags",
 	alreadyhaveacct: "Already a Pocket user?",
 	errorgeneric: "There was an error when trying to save to Pocket.",
 	learnmore: "Learn More",
 	loginnow: "Log in",
 	maxtaglength: "Tags are limited to 25 characters",
 	mustbeconnected: "You must be connected to the Internet in order to save to Pocket. Please check your connection and try again.",
@@ -20,11 +20,131 @@ Translations.en =
 	signupemail: "Sign up with email",
 	signuptosave: "Sign up for Pocket. It’s free.",
 	suggestedtags: "Suggested Tags",
 	tagline: "Save articles and videos from Firefox to view in Pocket on any device, any time.",
 	taglinestory_one: "Click the Pocket Button to save any article, video or page from Firefox.",
 	taglinestory_two: "View in Pocket on any device, any time.",
 	tagssaved: "Tags Added",
 	signinfirefox: "Sign in with Firefox",
-	signupfirefox: "Sign up with Firefox",
+  signupfirefox: "Sign up with Firefox",
 	viewlist: "View List"
-}
\ No newline at end of file
+};
+
+Translations.de =
+{
+  addtags: "Tags hinzufügen",
+  alreadyhaveacct: "Sind Sie bereits Pocket-Nutzer?",
+  continueff: "Mit Firefox fortfahren",
+  error1: "Wir konnten keine vorgeschlagenen Tags für dieses Element finden.",
+  error3: "Bitte melden Sie sich bei Pocket an und versuchen Sie es erneut.",
+  learnmore: "Mehr erfahren",
+  loginnow: "Anmelden",
+  maxtaglength: "Tags dürfen höchsten 25 Zeichen lang sein.",
+  onlylinkssaved: "Es können nur Links gespeichert werden",
+  pagenotsaved: "Seite nicht gespeichert",
+  pageremoved: "Seite entfernt",
+  pagesaved: "Bei Pocket gespeichert",
+  processingremove: "Seite wird entfernt…",
+  processingtags: "Tags werden hinzugefügt…",
+  removepage: "Seite entfernen",
+  save: "Speichern",
+  signupemail: "Mit E-Mail registrieren",
+  signuptosave: "Registrieren Sie sich bei Pocket. Das ist kostenlos.",
+  suggestedtags: "Vorgeschlagene Tags",
+  tagline: "Speichern Sie Artikel und Videos aus Firefox bei Pocket, um sie jederzeit und auf jedem Gerät ansehen zu können.",
+  taglinestory_one: "Klicken Sie auf die Pocket-Schaltfläche, um beliebige Artikel, Videos und Seiten aus Firefox zu speichern.",
+  taglinestory_two: "Lesen Sie diese mit Pocket, jederzeit und auf jedem Gerät.",
+  tagssaved: "Tags hinzugefügt",
+  signinfirefox: "Mit Firefox anmelden",
+  signupfirefox: "Mit Firefox registrieren",
+  viewlist: "Liste anzeigen"
+};
+
+Translations.es =
+{
+  addtags: "Añadir etiquetas",
+  alreadyhaveacct: "¿Ya tiene cuenta Pocket?",
+  continueff: "Continuar con Firefox",
+  error1: "No se han encontrado etiquetas sugeridas para este elemento.",
+  error3: "Inicie sesión en Pocket y vuelva a intentarlo.",
+  learnmore: "Saber más",
+  loginnow: "Iniciar sesión",
+  maxtaglength: "Las etiquetas están limitadas a 25 caracteres.",
+  onlylinkssaved: "Solo se pueden guardar enlaces",
+  pagenotsaved: "Página no guardada",
+  pageremoved: "Página eliminada",
+  pagesaved: "Guardada en Pocket",
+  processingremove: "Eliminando página…",
+  processingtags: "Añadiendo etiquetas…",
+  removepage: "Eliminar página",
+  save: "Guardar",
+  signupemail: "Regístrese con su correo.",
+  signuptosave: "Regístrese en Pocket. Es gratis.",
+  suggestedtags: "Etiquetas sugeridas",
+  tagline: "Guarde artículos y vídeos desde Firefox en Pocket para verlos en cualquier dispositivo y en cualquier momento.",
+  taglinestory_one: "Pulse el botón Pocket para guardar cualquier artículo, vídeo o página desde Firefox.",
+  taglinestory_two: "Véalo en Pocket en cualquier dispositivo y en cualquier momento.",
+  tagssaved: "Etiquetas añadidas",
+  signinfirefox: "Inicie sesión con Firefox",
+  signupfirefox: "Regístrese con Firefox",
+  viewlist: "Ver lista"
+};
+
+Translations.ja =
+{
+  addtags: "タグを追加",
+  alreadyhaveacct: "アカウントをお持ちですか?",
+  continueff: "Firefox で続行",
+  error1: "この項目に合うタグが見つかりません。",
+  error3: "Pocket にログインしてやり直してください。",
+  learnmore: "詳細",
+  loginnow: "ログイン",
+  maxtaglength: "タグは 25 文字までです。",
+  onlylinkssaved: "リンクのみ保存できます",
+  pagenotsaved: "ページを保存できませんでした",
+  pageremoved: "ページを削除しました",
+  pagesaved: "Pocket に保存しました",
+  processingremove: "ページを削除中...",
+  processingtags: "タグを追加中...",
+  removepage: "ページを削除",
+  save: "保存",
+  signupemail: "メールでアカウント登録",
+  signuptosave: "Pocket にアカウント登録してください。無料です。",
+  suggestedtags: "タグ候補",
+  tagline: "Pocket でいつでもどこでも見れるよう、Firefox から記事や動画を保存できます。",
+  taglinestory_one: "Firefox から記事や動画やページを保存するには、Pocket ボタンをクリックしてください。",
+  taglinestory_two: "Pocket でいつでもどこでも見れます。",
+  tagssaved: "タグを追加しました",
+  signinfirefox: "Firefox でログイン",
+  signupfirefox: "Firefox でアカウント登録",
+  viewlist: "マイリストを表示"
+};
+
+Translations.ru =
+{
+  addtags: "Добавить теги",
+  alreadyhaveacct: "Уже используете Pocket?",
+  continueff: "Продолжить через Firefox",
+  error1: "Мы не смогли найти для этого элемента рекомендуемые теги.",
+  error3: "Пожалуйста, войдите в Pocket, и попробуйте ещё раз.",
+  learnmore: "Узнайте больше",
+  loginnow: "Войдите",
+  maxtaglength: "Длина тега не должна превышать 25 символов.",
+  onlylinkssaved: "Можно сохранять только ссылки",
+  pagenotsaved: "Страница не сохранена",
+  pageremoved: "Страница удалена",
+  pagesaved: "Сохранено в Pocket",
+  processingremove: "Удаление страницы...",
+  processingtags: "Добавление тегов...",
+  removepage: "Удалить страницу",
+  save: "Сохранить",
+  signupemail: "Регистрация по эл. почте",
+  signuptosave: "Зарегистрируйтесь в Pocket. Это бесплатно.",
+  suggestedtags: "Рекомендуемые теги",
+  tagline: "Сохраняйте статьи и видео из Firefox для просмотра в Pocket на любом устройстве, в любой момент.",
+  taglinestory_one: "Щёлкните по кнопке Pocket, чтобы сохранить любую статью, видео или страницу из Firefox.",
+  taglinestory_two: "Просматривайте их в Pocket на любом устройстве, в любой момент.",
+  tagssaved: "Теги добавлены",
+  signinfirefox: "Войти через Firefox",
+  signupfirefox: "Регистрация через Firefox",
+  viewlist: "Просмотреть список"
+};
\ No newline at end of file
--- a/browser/components/pocket/panels/js/saved.js
+++ b/browser/components/pocket/panels/js/saved.js
@@ -3,26 +3,27 @@ PKT_SAVED_OVERLAY is the view itself and
 It does not contain any logic for saving or communication with the extension or server.
 */
 var PKT_SAVED_OVERLAY = function (options) 
 {
     var myself = this;
     this.inited = false;
     this.active = false;
     this.wrapper = null;
+    this.pockethost = "getpocket.com";
     this.savedItemId = 0;
     this.savedUrl = '';
     this.premiumStatus = false;
     this.panelId = 0;
     this.preventCloseTimerCancel = false;
     this.closeValid = true;
     this.mouseInside = false;
     this.autocloseTimer = null;
+    this.inoverflowmenu = false;
     this.dictJSON = {};
-    // TODO: allow the timer to be editable?
     this.autocloseTiming = 3500;
     this.autocloseTimingFinalState = 2000;
     this.mouseInside = false;
     this.userTags = [];
     this.cxt_suggested_available = 0;
     this.cxt_entered = 0;
     this.cxt_suggested = 0;
     this.cxt_removed = 0;
@@ -50,16 +51,17 @@ var PKT_SAVED_OVERLAY = function (option
     this.fillSuggestedTags = function()
     {
         if (!$('.pkt_ext_suggestedtag_detail').length) 
         {
             myself.suggestedTagsLoaded = true;
             myself.startCloseTimer();
             return;
         }
+
         thePKT_SAVED.sendMessage("getSuggestedTags",
         {
             url: myself.savedUrl || window.location.toString()
         }, function(resp) 
         {
             $('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
             if (resp.status == 'success') 
             {
@@ -247,20 +249,20 @@ var PKT_SAVED_OVERLAY = function (option
             },
             onDelete: function() {
                 myself.checkValidTagSubmit();
                 changestamp = Date.now();
                 myself.showActiveTags();
                 myself.checkPlaceholderStatus();
             },
             onShowDropdown: function() {
-               thePKT_SAVED.sendMessage("expandSavePanel");
+            	thePKT_SAVED.sendMessage("expandSavePanel");
             },
             onHideDropdown: function() {
-               thePKT_SAVED.sendMessage("collapseSavePanel");
+            	thePKT_SAVED.sendMessage("collapseSavePanel");
             }
         });
         $('body').on('keydown',function(e) {
             var key = e.keyCode || e.which;
             if (key == 8) {
                 var selected = $('.token-input-selected-token');
                 if (selected.length) {
                     e.preventDefault();
@@ -304,16 +306,17 @@ var PKT_SAVED_OVERLAY = function (option
             $('.token-input-token').each(function()
             {
                 var text = $.trim($(this).find('p').text());
                 if (text.length)
                 {
                     originaltags.push(text);
                 }
             });
+
             thePKT_SAVED.sendMessage("addTags",
             {
                 url: myself.savedUrl || window.location.toString(),
                 tags: originaltags   
             }, function(resp)
             {
                 if (resp.status == 'success') 
                 {
@@ -331,16 +334,17 @@ var PKT_SAVED_OVERLAY = function (option
             if ($(this).parents('.pkt_ext_item_actions_disabled').length) {
                 e.preventDefault();
                 return;
             }
             if ($(this).hasClass('pkt_ext_removeitem')) {
                 e.preventDefault();
                 myself.disableInput();
                 $('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingremove);
+
                 thePKT_SAVED.sendMessage("deleteItem",
                 {
                     itemId: myself.savedItemId
                 },function(resp) {
                     if (resp.status == 'success') {
                         myself.showStateFinalMsg(myself.dictJSON.pageremoved);
                     }
                     else if (resp.status == 'error') {
@@ -452,17 +456,17 @@ var PKT_SAVED_OVERLAY = function (option
         this.wrapper.find('.pkt_ext_detail h2').text(headline);
         this.wrapper.find('.pkt_ext_detail h3').text(detail);
         this.wrapper.addClass('pkt_ext_container_detailactive pkt_ext_container_finalstate pkt_ext_container_finalerrorstate');
         this.preventCloseTimerCancel = true;
         this.startCloseTimer(myself.autocloseTimingFinalState);
     }
     this.getTranslations = function()
     {
-        var language = window.navigator.language.toLowerCase();
+        var language = this.locale || '';
         this.dictJSON = {};
 
         var dictsuffix = 'en-US';
 
         if (language.indexOf('en') == 0)
         {
             dictsuffix = 'en';
         }
@@ -526,21 +530,25 @@ var PKT_SAVED_OVERLAY = function (option
         {
             dictsuffix = 'ko';
         }
         else if (language.indexOf('pl') == 0)
         {
             dictsuffix = 'pl';
         }
 
-        // TODO: when we add all dictionaries, modify this, but for now hard code to English
-        dictsuffix = 'en';
-
         this.dictJSON = Translations[dictsuffix];
-        
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = Translations['en'];
+        }
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = {};
+        }
     };
 };
 
 PKT_SAVED_OVERLAY.prototype = {
     create : function() 
     {
         if (this.active)
         {
@@ -549,16 +557,28 @@ PKT_SAVED_OVERLAY.prototype = {
         this.active = true;
 
         // set translations
         this.getTranslations();
 
         // set host
         this.dictJSON.pockethost = this.pockethost;
 
+        // extra modifier class for collapsed state
+        if (this.inoverflowmenu)
+        {
+            $('body').addClass('pkt_ext_saved_overflow');
+        }
+
+        // extra modifier class for language
+        if (this.locale)
+        {
+            $('body').addClass('pkt_ext_saved_' + this.locale);
+        }
+
         // Create actual content
         $('body').append(Handlebars.templates.saved_shell(this.dictJSON));
 
         // Add in premium content (if applicable based on premium status)
         this.createPremiumFunctionality();
 
         // Initialize functionality for overlay
         this.wrapper = $('.pkt_ext_containersaved');
@@ -607,16 +627,26 @@ PKT_SAVED.prototype = {
         {
             myself.overlay.premiumStatus = (url[1] == '1');
         }
         var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
         if (host && host.length > 1)
         {
             myself.overlay.pockethost = host[1];
         }
+        var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
+        if (inoverflowmenu && inoverflowmenu.length > 1)
+        {
+            myself.overlay.inoverflowmenu = (inoverflowmenu[1] == 'true');
+        }
+        var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
+        if (locale && locale.length > 1)
+        {
+            myself.overlay.locale = locale[1].toLowerCase();
+        }
 
         myself.overlay.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
 
         myself.overlay.create();
 
         // tell back end we're ready
         thePKT_SAVED.sendMessage("show");
 
@@ -633,17 +663,17 @@ PKT_SAVED.prototype = {
                     else
                     {
                         myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,resp.error.message);
                     }
                 }       
                 else
                 {
                     myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved,myself.overlay.dictJSON.errorgeneric);
-                }
+                }         
                 return;
             }
 
             myself.overlay.showStateSaved(resp);
         });
 
     }
 }
--- a/browser/components/pocket/panels/js/signup.js
+++ b/browser/components/pocket/panels/js/signup.js
@@ -1,30 +1,28 @@
 /*
 PKT_SIGNUP_OVERLAY is the view itself and contains all of the methods to manipute the overlay and messaging.
 It does not contain any logic for saving or communication with the extension or server.
 */
 var PKT_SIGNUP_OVERLAY = function (options) 
 {
     var myself = this;
-    this.baseHost = "getpocket.com";
-
     this.inited = false;
     this.active = false;
     this.delayedStateSaved = false;
     this.wrapper = null;
     this.variant = window.___PKT__SIGNUP_VARIANT;
     this.tagline = window.___PKT__SIGNUP_TAGLINE || '';
     this.preventCloseTimerCancel = false;
-    // TODO: populate this with actual translations
     this.translations = {};
     this.closeValid = true;
     this.mouseInside = false;
     this.autocloseTimer = null;
     this.variant = "";
+    this.inoverflowmenu = false;
     this.pockethost = "getpocket.com";
     this.fxasignedin = false;
     this.panelId = 0;
     this.dictJSON = {};
     this.initCloseTabEvents = function() {
         $('.btn,.pkt_ext_learnmore,.alreadyhave > a').click(function(e)
         {
             e.preventDefault();
@@ -55,17 +53,17 @@ var PKT_SIGNUP_OVERLAY = function (optio
         {
             return String(s).replace(/[&<>"']/g, function (str) {
                 return sanitizeMap[str];
             });
         }
     };
     this.getTranslations = function()
     {
-        var language = window.navigator.language.toLowerCase();
+        var language = this.locale || '';
         this.dictJSON = {};
 
         var dictsuffix = 'en-US';
 
         if (language.indexOf('en') == 0)
         {
             dictsuffix = 'en';
         }
@@ -129,21 +127,25 @@ var PKT_SIGNUP_OVERLAY = function (optio
         {
             dictsuffix = 'ko';
         }
         else if (language.indexOf('pl') == 0)
         {
             dictsuffix = 'pl';
         }
 
-        // TODO: when we add all dictionaries, modify this, but for now hard code to English
-        dictsuffix = 'en';
-
         this.dictJSON = Translations[dictsuffix];
-        
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = Translations['en'];
+        }
+        if (typeof this.dictJSON !== 'object')
+        {
+            this.dictJSON = {};
+        }
     };
 };
 
 PKT_SIGNUP_OVERLAY.prototype = {
     create : function() 
     {
         var myself = this;
 
@@ -157,41 +159,64 @@ PKT_SIGNUP_OVERLAY.prototype = {
         {
             this.fxasignedin = (fxasignedin[1] == '1');
         }
         var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
         if (host && host.length > 1)
         {
             this.pockethost = host[1];
         }
+        var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
+        if (inoverflowmenu && inoverflowmenu.length > 1)
+        {
+            this.inoverflowmenu = (inoverflowmenu[1] == 'true');
+        }
+        var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
+        if (locale && locale.length > 1)
+        {
+           this.locale = locale[1].toLowerCase();
+        }
 
         this.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
 
         if (this.active)
         {
             return;
         }
         this.active = true;
 
         // set translations
         this.getTranslations();
         this.dictJSON.fxasignedin = this.fxasignedin ? 1 : 0;
         this.dictJSON.variant = (this.variant ? this.variant : 'undefined');
         this.dictJSON.pockethost = this.pockethost;
 
+        // extra modifier class for collapsed state
+        if (this.inoverflowmenu)
+        {
+            $('body').addClass('pkt_ext_signup_overflow');
+        }
+
+        // extra modifier class for language
+        if (this.locale)
+        {
+            $('body').addClass('pkt_ext_signup_' + this.locale);
+        }
+
         // Create actual content
         if (this.variant == 'storyboard')
         {
             $('body').append(Handlebars.templates.signupstoryboard_shell(this.dictJSON));
         }
         else
         {
             $('body').append(Handlebars.templates.signup_shell(this.dictJSON));
         }
 
+
         // tell background we're ready
         thePKT_SIGNUP.sendMessage("show");
 
         // close events
         this.initCloseTabEvents();
     }
 };
 
--- a/browser/components/pocket/panels/js/tmpl.js
+++ b/browser/components/pocket/panels/js/tmpl.js
@@ -47,25 +47,25 @@ templates['signup_shell'] = template({"1
     + "</span></a></p>\n	<p class=\"btn-container\"><a href=\"http://"
     + escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
     + "/signup?force=email&src=extension&s="
     + escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
     + "&t=wlm\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
     + escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
     + "</a></p>\n";
 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
-  var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail\">\n	<h2 class=\"pkt_ext_logo\">Pocket</h2>\n	<p class=\"pkt_ext_tagline\">"
+  var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail pkt_ext_introdetailhero\">\n	<h2 class=\"pkt_ext_logo\">Pocket</h2>\n	<p class=\"pkt_ext_tagline\">"
     + escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
-    + "</p>\n	<p><a class=\"pkt_ext_learnmore\" href=\"http://"
+    + "</p>\n	<p class=\"pkt_ext_learnmorecontainer\"><a class=\"pkt_ext_learnmore\" href=\"http://"
     + escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
     + "?s="
     + escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
     + "&t=wlm&src=ff_learn_more\" target=\"_blank\">"
     + escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
-    + "</a></p>\n	<div class=\"pkt_ext_introimg\"></div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n	<h4>"
+    + "</a></p>\n	<div class=\"pkt_ext_introimg\"></div>\n</div>\n<div class=\"pkt_ext_signupdetail pkt_ext_signupdetail_hero\">\n	<h4>"
     + escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
     + "</h4>\n";
   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
   if (stack1 != null) { buffer += stack1; }
   return buffer + "	<p class=\"alreadyhave\">"
     + escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
     + " <a href=\"http://"
     + escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
--- a/browser/components/pocket/panels/tmpl/signup_shell.handlebars
+++ b/browser/components/pocket/panels/tmpl/signup_shell.handlebars
@@ -1,16 +1,16 @@
-<div class="pkt_ext_introdetail">
+<div class="pkt_ext_introdetail pkt_ext_introdetailhero">
 	<h2 class="pkt_ext_logo">Pocket</h2>
 	<p class="pkt_ext_tagline">{{tagline}}</p>
-  <p><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
-  <div class="pkt_ext_introimg"></div>
+	<p class="pkt_ext_learnmorecontainer"><a class="pkt_ext_learnmore" href="http://{{pockethost}}?s={{variant}}&t=wlm&src=ff_learn_more" target="_blank">{{learnmore}}</a></p>
+	<div class="pkt_ext_introimg"></div>
 </div>
-<div class="pkt_ext_signupdetail">
+<div class="pkt_ext_signupdetail pkt_ext_signupdetail_hero">
 	<h4>{{signuptosave}}</h4>
- {{#if fxasignedin}}
- <p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
- {{else}}
- <p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
- <p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
- {{/if}}
- <p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
+	{{#if fxasignedin}}
+	<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
+	{{else}}
+	<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s={{variant}}&t=wlm" target=_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
+	<p class="btn-container"><a href="http://{{pockethost}}/signup?force=email&src=extension&s={{variant}}&t=wlm" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
+	{{/if}}
+	<p class="alreadyhave">{{alreadyhaveacct}} <a href="http://{{pockethost}}/login?ep=3&src=extension&s={{variant}}&t=wlm" target="_blank">{{loginnow}}</a>.</p>
 </div>
\ No newline at end of file
--- a/browser/devtools/netmonitor/test/browser_net_autoscroll.js
+++ b/browser/devtools/netmonitor/test/browser_net_autoscroll.js
@@ -6,92 +6,81 @@
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: aValue.content is undefined");
 
 /**
  * Bug 863102 - Automatically scroll down upon new network requests.
  */
-
-function test() {
+add_task(function*() {
   requestLongerTimeout(2);
   let monitor, debuggee, requestsContainer, scrollTop;
 
-  initNetMonitor(INFINITE_GET_URL).then(([aTab, aDebuggee, aMonitor]) => {
-    monitor = aMonitor;
-    debuggee = aDebuggee;
-    let win = monitor.panelWin;
-    let topNode = win.document.getElementById("requests-menu-contents");
-    requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
-    ok(!!requestsContainer, "Container element exists as expected.");
-  })
+  let [aTab, aDebuggee, aMonitor] = yield initNetMonitor(INFINITE_GET_URL);
+  monitor = aMonitor;
+  debuggee = aDebuggee;
+  let win = monitor.panelWin;
+  let topNode = win.document.getElementById("requests-menu-contents");
+  requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
+  ok(!!requestsContainer, "Container element exists as expected.");
 
   // (1) Check that the scroll position is maintained at the bottom
   // when the requests overflow the vertical size of the container.
-  .then(() => {
-    return waitForRequestsToOverflowContainer(monitor, requestsContainer);
-  })
-  .then(() => {
-    ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
-  })
+  yield waitForRequestsToOverflowContainer(monitor, requestsContainer);
+  yield waitForScroll(monitor);
+  ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
 
   // (2) Now set the scroll position somewhere in the middle and check
   // that additional requests do not change the scroll position.
-  .then(() => {
-    let children = requestsContainer.childNodes;
-    let middleNode = children.item(children.length / 2);
-    middleNode.scrollIntoView();
-    ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
-    scrollTop = requestsContainer.scrollTop; // save for comparison later
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    is(requestsContainer.scrollTop, scrollTop, "Did not scroll.");
-  })
+  let children = requestsContainer.childNodes;
+  let middleNode = children.item(children.length / 2);
+  middleNode.scrollIntoView();
+  ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
+  scrollTop = requestsContainer.scrollTop; // save for comparison later
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitSomeTime();
+  is(requestsContainer.scrollTop, scrollTop, "Did not scroll.");
 
   // (3) Now set the scroll position back at the bottom and check that
   // additional requests *do* cause the container to scroll down.
-  .then(() => {
-    requestsContainer.scrollTop = requestsContainer.scrollHeight;
-    ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
-  })
+  requestsContainer.scrollTop = requestsContainer.scrollHeight;
+  ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitForScroll(monitor);
+  ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
 
   // (4) Now select an item in the list and check that additional requests
   // do not change the scroll position.
-  .then(() => {
-    monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0;
-    return waitForNetworkEvents(monitor, 8);
-  })
-  .then(() => {
-    is(requestsContainer.scrollTop, 0, "Did not scroll.");
-  })
+  monitor.panelWin.NetMonitorView.RequestsMenu.selectedIndex = 0;
+  yield waitForNetworkEvents(monitor, 8);
+  yield waitSomeTime();
+  is(requestsContainer.scrollTop, 0, "Did not scroll.");
 
-  // Done; clean up.
-  .then(() => {
-    return teardown(monitor).then(finish);
-  })
+  // Done: clean up.
+  yield teardown(monitor);
 
-  // Handle exceptions in the chain of promises.
-  .then(null, (err) => {
-    ok(false, err);
-    finish();
-  });
+  finish();
 
-  function waitForRequestsToOverflowContainer (aMonitor, aContainer) {
+  function waitForRequestsToOverflowContainer(aMonitor, aContainer) {
     return waitForNetworkEvents(aMonitor, 1).then(() => {
       if (aContainer.scrollHeight > aContainer.clientHeight) {
-        // Wait for some more just for good measure.
-        return waitForNetworkEvents(aMonitor, 8);
+        return promise.resolve();
       } else {
         return waitForRequestsToOverflowContainer(aMonitor, aContainer);
       }
     });
   }
 
   function scrolledToBottom(aElement) {
     return aElement.scrollTop + aElement.clientHeight >= aElement.scrollHeight;
   }
-}
+
+  function waitSomeTime() {
+    let waitSomeTime = promise.defer();
+    setTimeout(waitSomeTime.resolve, 50); // Wait to make sure no scrolls happen
+    return waitSomeTime.promise;
+  }
+
+  function waitForScroll(aMonitor) {
+    return aMonitor._view.RequestsMenu.widget.once("scroll-to-bottom");
+  }
+});
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -5,19 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
+const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
 
+const SCROLL_FREQUENCY = 16;
+
 /**
  * A simple side menu, with the ability of grouping menu items.
  *
  * Note: this widget should be used in tandem with the WidgetMethods in
  * ViewHelpers.jsm.
  *
  * @param nsIDOMNode aNode
  *        The element associated with the widget.
@@ -108,31 +111,85 @@ SideMenuWidget.prototype = {
     // needlessly expensive operations that may cause reflows):
     let maintainScrollAtBottom =
       // 1. The behavior should be enabled,
       this.autoscrollWithAppendedItems &&
       // 2. There shouldn't currently be any selected item in the list.
       !this._selectedItem &&
       // 3. The new item should be appended at the end of the list.
       (aIndex < 0 || aIndex >= this._orderedMenuElementsArray.length) &&
-      // 4. The list should already be scrolled at the bottom.
-      (this._list.scrollTop + this._list.clientHeight >= this._list.scrollHeight);
+      // 4. We aren't waiting for a scroll to happen.
+      (!this._scrollToBottomTask || !this._scrollToBottomTask.isArmed) &&
+      // 5. The list should already be scrolled at the bottom.
+      this.isScrolledToBottom();
 
     let group = this._getMenuGroupForName(aAttachment.group);
     let item = this._getMenuItemForGroup(group, aContents, aAttachment);
     let element = item.insertSelfAt(aIndex);
 
     if (maintainScrollAtBottom) {
-      this._list.scrollTop = this._list.scrollHeight;
+      this.scrollToBottom();
     }
 
     return element;
   },
 
   /**
+   * Checks to see if the list is scrolled all the way to the bottom.
+   * Uses getBoundsWithoutFlushing to limit the performance impact
+   * of this function.
+   *
+   * @return bool
+   */
+  isScrolledToBottom: function() {
+    if (this._list.lastElementChild) {
+      let utils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+      let childRect = utils.getBoundsWithoutFlushing(this._list.lastElementChild);
+      let listRect = utils.getBoundsWithoutFlushing(this._list);
+
+      // Cheap way to check if it's scrolled all the way to the bottom.
+      return (childRect.height + childRect.top) <= listRect.bottom;
+    }
+
+    return false;
+  },
+
+  /**
+   * Scroll the list to the bottom after a timeout.
+   * If the user scrolls in the meantime, cancel this operation.
+   */
+  scrollToBottom: function() {
+    // Lazily attach this functionality to the object, so it won't get
+    // created unless if this scrollToBottom behavior is needed.
+    if (!this._scrollToBottomTask) {
+      // The scroll event fires asynchronously, so we need to keep a bit to
+      // distinguish between user-initiated events and scrollTop assignment.
+      let ignoreNextScroll = false;
+
+      this._scrollToBottomTask = new DeferredTask(() => {
+        ignoreNextScroll = true;
+        this._list.scrollTop = this._list.scrollHeight;
+        this.emit("scroll-to-bottom");
+      }, SCROLL_FREQUENCY);
+
+      // On a user scroll, cancel any pending calls to the scroll function.
+      this._list.addEventListener("scroll", () => {
+        if (!ignoreNextScroll && this._scrollToBottomTask.isArmed &&
+            !this.isScrolledToBottom()) {
+          this._scrollToBottomTask.disarm();
+        }
+        ignoreNextScroll = false;
+      }, true);
+    }
+
+    this._scrollToBottomTask.arm();
+  },
+
+  /**
    * Returns the child node in this container situated at the specified index.
    *
    * @param number aIndex
    *        The position in the container intended for this item.
    * @return nsIDOMNode
    *         The element associated with the displayed item.
    */
   getItemAtIndex: function(aIndex) {
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties
@@ -69,20 +69,20 @@ docsTooltip.loadDocsError=Could not load
 inspector.collapsePane=Collapse pane
 
 # LOCALIZATION NOTE (inspector.expandPane): This is the tooltip for the button
 # that expands the right panel (rules, computed, box-model, etc...) in the
 # inspector UI.
 inspector.expandPane=Expand pane
 
 # LOCALIZATION NOTE (inspector.searchResultsCount): This is the label that
-# will show up next to the inspector search box showing the current result
-# index alongside the total number of search results.  For example, "3 of 9".
+# will show up next to the inspector search box. %1$S is the current result
+# index and %2$S is the total number of search results. For example: "3 of 9".
 # This won't be visible until the search box is updated in Bug 835896.
-inspector.searchResultsCount=%S of %S
+inspector.searchResultsCount2=%1$S of %2$S
 
 # LOCALIZATION NOTE (inspector.searchResultsNone): This is the label that
 # will show up next to the inspector search box when no matches were found
 # for the given string.
 # This won't be visible until the search box is updated in Bug 835896.
 inspector.searchResultsNone=No matches
 
 # LOCALIZATION NOTE (inspector.menu.openUrlInNewTab.label): This is the label of
--- a/browser/locales/en-US/chrome/browser/newTab.properties
+++ b/browser/locales/en-US/chrome/browser/newTab.properties
@@ -38,10 +38,41 @@ newtab.enhanced.explain=A Mozilla partne
 # active link using string newtab.learn.link as text.
 newtab.intro.paragraph1=When you open a new tab, you’ll see tiles from the sites you frequently visit, along with tiles that we think might be of interest to you. Some of these tiles may be sponsored by Mozilla partners. We’ll always indicate to you which tiles are sponsored. %1$S
 # LOCALIZATION NOTE(newtab.intro.paragraph2): %1$S will be replaced inline by
 # active link using string newtab.privacy.link as text.
 newtab.intro.paragraph2=In order to provide this service, Mozilla collects and uses certain analytics information relating to your use of the tiles in accordance with our %1$S.
 # LOCALIZATION NOTE(newtab.intro.paragraph3): %1$S will be replaced inline by
 # the gear icon used to customize the new tab window.
 newtab.intro.paragraph3=You can turn off the tiles feature by clicking the %1$S button for your preferences.
+# LOCALIZATION NOTE(newtab.intro.paragraph4): %1$S will be replaced inline by
+# the gear icon used to customize the new tab window. %2$S will be replaced by
+# newtab.intro.controls as text
+newtab.intro.paragraph4=You can turn off this feature by clicking the gear (%1$S) button and selecting "Show blank page" in the %2$S menu.
+newtab.intro.paragraph5=New Tab will show the sites you visit most frequently, along with sites we think might be of interest to you. To get started, you'll see several sites from Mozilla.
+# LOCALIZATION NOTE(newtab.intro.paragraph6): %1$S will be replaced by
+# newtab.intro.paragraph6.remove as bold text. %2$S will be replaced by
+# newtab.intro.paragraph6.pin as bold text
+newtab.intro.paragraph6=You can %1$S or %2$S any site by using the controls available on rollover.
+newtab.intro.paragraph6.remove=remove
+newtab.intro.paragraph6.pin=pin
+newtab.intro.paragraph7=Some of the sites you will see may be suggested by Mozilla and may be sponsored by a Mozilla partner. We'll always indicate which sites are sponsored.
+# LOCALIZATION NOTE(newtab.intro.paragraph8): %1$S will be replaced by
+# brandShortName as text. %2$S will be replaced inline by an active link using
+# string newtab.learn.link as text.
+newtab.intro.paragraph8=%1$S will only show sites that most closely match your interests on the Web. %2$S
+newtab.intro.paragraph9=Now when you open New Tab, you'll also see sites we think might be interesting to you.
+# LOCALIZATION NOTE(newtab.intro.controls): the controls in the gear icon
+# menu for customizing the new tab window. Used in newtab.intro.paragraph4
+newtab.intro.controls=New Tab Controls
 newtab.learn.link=Learn more…
 newtab.privacy.link=Privacy Notice
+newtab.learn.link2=More about New Tab
+newtab.privacy.link2=About your privacy
+# LOCALIZATION NOTE(newtab.intro.header.welcome): %1$S will be replaced by
+# brandShortName as bold text.
+newtab.intro.header.welcome=Welcome to New Tab on %1$S!
+newtab.intro.header.update=New Tab got an update!
+newtab.intro.skip=Skip this
+newtab.intro.continue=Continue tour
+newtab.intro.back=Back
+newtab.intro.next=Next
+newtab.intro.gotit=Got it!
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -6,280 +6,30 @@
 
 // OSObject.h - os object for exposing posix system calls in the JS shell
 
 #include "shell/OSObject.h"
 
 #include <errno.h>
 #include <stdlib.h>
 #ifdef XP_WIN
-#include <direct.h>
 #include <process.h>
 #include <string.h>
 #else
 #include <sys/wait.h>
 #include <unistd.h>
 #endif
 
-#include "jsapi.h"
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
-#include "jsobj.h"
-#ifdef XP_WIN
-# include "jswin.h"
-#endif
-#include "jswrapper.h"
 
 #include "js/Conversions.h"
-#include "shell/jsshell.h"
-#include "vm/TypedArrayObject.h"
-
-#include "jsobjinlines.h"
-
-#ifdef XP_WIN
-# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
-# define getcwd _getcwd
-#else
-# include <libgen.h>
-#endif
 
 using namespace JS;
 
-namespace js {
-namespace shell {
-
-/*
- * Resolve a (possibly) relative filename to an absolute path. If
- * |scriptRelative| is true, then the result will be relative to the directory
- * containing the currently-running script, or the current working directory if
- * the currently-running script is "-e" (namely, you're using it from the
- * command line.) Otherwise, it will be relative to the current working
- * directory.
- */
-JSString*
-ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveMode)
-{
-    JSAutoByteString filename(cx, filenameStr);
-    if (!filename)
-        return nullptr;
-
-    const char* pathname = filename.ptr();
-    if (pathname[0] == '/')
-        return filenameStr;
-#ifdef XP_WIN
-    // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
-    // "\..."
-    if (pathname[0] == '\\')
-        return filenameStr;
-    // "C:\..."
-    if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\')
-        return filenameStr;
-    // "\\..."
-    if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\')
-        return filenameStr;
-#endif
-
-    /* Get the currently executing script's name. */
-    JS::AutoFilename scriptFilename;
-    if (!DescribeScriptedCaller(cx, &scriptFilename))
-        return nullptr;
-
-    if (!scriptFilename.get())
-        return nullptr;
-
-    if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0)
-        resolveMode = RootRelative;
-
-    static char buffer[PATH_MAX+1];
-    if (resolveMode == ScriptRelative) {
-#ifdef XP_WIN
-        // The docs say it can return EINVAL, but the compiler says it's void
-        _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr);
-#else
-        strncpy(buffer, scriptFilename.get(), PATH_MAX+1);
-        if (buffer[PATH_MAX] != '\0')
-            return nullptr;
-
-        // dirname(buffer) might return buffer, or it might return a
-        // statically-allocated string
-        memmove(buffer, dirname(buffer), strlen(buffer) + 1);
-#endif
-    } else {
-        const char* cwd = getcwd(buffer, PATH_MAX);
-        if (!cwd)
-            return nullptr;
-    }
-
-    size_t len = strlen(buffer);
-    buffer[len] = '/';
-    strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1));
-    if (buffer[PATH_MAX] != '\0')
-        return nullptr;
-
-    return JS_NewStringCopyZ(cx, buffer);
-}
-
-static JSObject*
-FileAsTypedArray(JSContext* cx, const char* pathname)
-{
-    FILE* file = fopen(pathname, "rb");
-    if (!file) {
-        JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
-        return nullptr;
-    }
-    AutoCloseInputFile autoClose(file);
-
-    RootedObject obj(cx);
-    if (fseek(file, 0, SEEK_END) != 0) {
-        JS_ReportError(cx, "can't seek end of %s", pathname);
-    } else {
-        size_t len = ftell(file);
-        if (fseek(file, 0, SEEK_SET) != 0) {
-            JS_ReportError(cx, "can't seek start of %s", pathname);
-        } else {
-            obj = JS_NewUint8Array(cx, len);
-            if (!obj)
-                return nullptr;
-            char* buf = (char*) obj->as<js::TypedArrayObject>().viewData();
-            size_t cc = fread(buf, 1, len, file);
-            if (cc != len) {
-                JS_ReportError(cx, "can't read %s: %s", pathname,
-                               (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
-                obj = nullptr;
-            }
-        }
-    }
-    return obj;
-}
-
-static bool
-ReadFile(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1 || args.length() > 2) {
-        JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr,
-                             args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
-                             "snarf");
-        return false;
-    }
-
-    if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) {
-        JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf");
-        return false;
-    }
-
-    RootedString givenPath(cx, args[0].toString());
-    RootedString str(cx, js::shell::ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative));
-    if (!str)
-        return false;
-
-    JSAutoByteString filename(cx, str);
-    if (!filename)
-        return false;
-
-    if (args.length() > 1) {
-        JSString* opt = JS::ToString(cx, args[1]);
-        if (!opt)
-            return false;
-        bool match;
-        if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
-            return false;
-        if (match) {
-            JSObject* obj;
-            if (!(obj = FileAsTypedArray(cx, filename.ptr())))
-                return false;
-            args.rval().setObject(*obj);
-            return true;
-        }
-    }
-
-    if (!(str = FileAsString(cx, filename.ptr())))
-        return false;
-    args.rval().setString(str);
-    return true;
-}
-
-static bool
-osfile_readFile(JSContext* cx, unsigned argc, jsval* vp)
-{
-    return ReadFile(cx, argc, vp, false);
-}
-
-static bool
-osfile_readRelativeToScript(JSContext* cx, unsigned argc, jsval* vp)
-{
-    return ReadFile(cx, argc, vp, true);
-}
-
-static bool
-Redirect(JSContext* cx, FILE* fp, HandleString relFilename)
-{
-    RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
-    if (!filename)
-        return false;
-    JSAutoByteString filenameABS(cx, filename);
-    if (!filenameABS)
-        return false;
-    if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
-        JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-static bool
-osfile_redirect(JSContext* cx, unsigned argc, jsval* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1 || args.length() > 2) {
-        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
-        return false;
-    }
-
-    if (args[0].isString()) {
-        RootedString stdoutPath(cx, args[0].toString());
-        if (!stdoutPath)
-            return false;
-        if (!Redirect(cx, stdout, stdoutPath))
-            return false;
-    }
-
-    if (args.length() > 1 && args[1].isString()) {
-        RootedString stderrPath(cx, args[1].toString());
-        if (!stderrPath)
-            return false;
-        if (!Redirect(cx, stderr, stderrPath))
-            return false;
-    }
-
-    args.rval().setUndefined();
-    return true;
-}
-
-static const JSFunctionSpecWithHelp osfile_functions[] = {
-    JS_FN_HELP("readFile", osfile_readFile, 1, 0,
-"readFile(filename, [\"binary\"])",
-"  Read filename into returned string. Filename is relative to the current\n"
-               "  working directory."),
-
-    JS_FN_HELP("readRelativeToScript", osfile_readRelativeToScript, 1, 0,
-"readRelativeToScript(filename, [\"binary\"])",
-"  Read filename into returned string. Filename is relative to the directory\n"
-"  containing the current script."),
-
-    JS_FN_HELP("redirect", osfile_redirect, 2, 0,
-"redirect(stdoutFilename[, stderrFilename])",
-"  Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
-"   redirecting. Filenames are relative to the current working directory."),
-
-    JS_FS_HELP_END
-};
-
 static bool
 os_getenv(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportError(cx, "os.getenv requires 1 argument");
         return false;
     }
@@ -537,58 +287,19 @@ static const JSFunctionSpecWithHelp os_f
 "  passed in must be numeric, if given."),
 
     JS_FN_HELP("waitpid", os_waitpid, 1, 0,
 "waitpid(pid[, nohang])",
 "  Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n"
 "  The return value is an object containing a 'pid' field, if a process was waitable\n"
 "  and an 'exitStatus' field if a pid exited."),
 #endif
-
     JS_FS_HELP_END
 };
 
 bool
-DefineOS(JSContext* cx, HandleObject global)
+js::DefineOS(JSContext* cx, HandleObject global)
 {
     RootedObject obj(cx, JS_NewPlainObject(cx));
-    if (!obj ||
-        !JS_DefineFunctionsWithHelp(cx, obj, os_functions) ||
-        !JS_DefineProperty(cx, global, "os", obj, 0))
-    {
-        return false;
-    }
-
-    RootedObject osfile(cx, JS_NewPlainObject(cx));
-    if (!osfile ||
-        !JS_DefineFunctionsWithHelp(cx, osfile, osfile_functions) ||
-        !JS_DefineProperty(cx, obj, "file", osfile, 0))
-    {
-        return false;
-    }
-
-    // For backwards compatibility, expose various os.file.* functions as
-    // direct methods on the global.
-    RootedValue val(cx);
-
-    struct {
-        const char* src;
-        const char* dst;
-    } osfile_exports[] = {
-        { "readFile", "read" },
-        { "readFile", "snarf" },
-        { "readRelativeToScript", "readRelativeToScript" },
-        { "redirect", "redirect" }
-    };
-
-    for (auto pair : osfile_exports) {
-        if (!JS_GetProperty(cx, osfile, pair.src, &val))
-            return false;
-        RootedObject function(cx, &val.toObject());
-        if (!JS_DefineProperty(cx, global, pair.dst, function, 0))
-            return false;
-    }
-
-    return true;
+    return obj &&
+           JS_DefineFunctionsWithHelp(cx, obj, os_functions) &&
+           JS_DefineProperty(cx, global, "os", obj, 0);
 }
-
-}
-}
--- a/js/src/shell/OSObject.h
+++ b/js/src/shell/OSObject.h
@@ -7,26 +7,16 @@
 // OSObject.h - os object for exposing posix system calls in the JS shell
 
 #ifndef shell_OSObject_h
 #define shell_OSObject_h
 
 #include "jsapi.h"
 
 namespace js {
-namespace shell {
 
 /* Define an os object on the given global object. */
 bool
 DefineOS(JSContext* cx, JS::HandleObject global);
 
-enum PathResolutionMode {
-    RootRelative,
-    ScriptRelative
-};
-
-JSString*
-ResolvePath(JSContext* cx, JS::HandleString filenameStr, PathResolutionMode resolveMode);
-
-}
 }
 
 #endif /* shell_OSObject_h */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -81,35 +81,45 @@
 #include "vm/WrapperObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/Stack-inl.h"
 
+#ifdef XP_WIN
+# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
+#else
+# include <libgen.h>
+#endif
+
 using namespace js;
 using namespace js::cli;
-using namespace js::shell;
 
 using mozilla::ArrayLength;
 using mozilla::MakeUnique;
 using mozilla::Maybe;
 using mozilla::NumberEqualsInt32;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::UniquePtr;
 
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
+enum PathResolutionMode {
+    RootRelative,
+    ScriptRelative
+};
+
 static size_t gStackChunkSize = 8192;
 
 /*
  * Note: This limit should match the stack limit set by the browser in
  *       js/xpconnect/src/XPCJSRuntime.cpp
  */
 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
 static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
@@ -180,26 +190,40 @@ static bool reportWarnings = true;
 static bool compileOnly = false;
 static bool fuzzingSafe = false;
 
 #ifdef DEBUG
 static bool dumpEntrainedVariables = false;
 static bool OOM_printAllocationCount = false;
 #endif
 
+enum JSShellErrNum {
+#define MSG_DEF(name, count, exception, format) \
+    name,
+#include "jsshell.msg"
+#undef MSG_DEF
+    JSShellErr_Limit
+};
+
 static JSContext*
 NewContext(JSRuntime* rt);
 
 static void
 DestroyContext(JSContext* cx, bool withGC);
 
 static JSObject*
 NewGlobalObject(JSContext* cx, JS::CompartmentOptions& options,
                 JSPrincipals* principals);
 
+static const JSErrorFormatString*
+my_GetErrorMessage(void* userRef, const unsigned errorNumber);
+
+static void
+my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report);
+
 /*
  * A toy principals type for the shell.
  *
  * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
  * set bits in P are a superset of those in Q. Thus, the principal 0 is
  * subsumed by everything, and the principal ~0 subsumes everything.
  *
  * As a special case, a null pointer as a principal is treated like 0xffff.
@@ -536,16 +560,28 @@ ReadEvalPrintLoop(JSContext* cx, FILE* i
             // Catch the error, report it, and keep going.
             JS_ReportPendingException(cx);
         }
     } while (!hitEOF && !gQuitting);
 
     fprintf(out, "\n");
 }
 
+class AutoCloseInputFile
+{
+  private:
+    FILE* f_;
+  public:
+    explicit AutoCloseInputFile(FILE* f) : f_(f) {}
+    ~AutoCloseInputFile() {
+        if (f_ && f_ != stdin)
+            fclose(f_);
+    }
+};
+
 static void
 Process(JSContext* cx, const char* filename, bool forceTTY)
 {
     FILE* file;
     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
         file = stdin;
     } else {
         file = fopen(filename, "r");
@@ -590,16 +626,87 @@ Version(JSContext* cx, unsigned argc, js
             return false;
         }
         JS_SetVersionForCompartment(js::GetContextCompartment(cx), JSVersion(v));
         args.rval().setInt32(origVersion);
     }
     return true;
 }
 
+/*
+ * Resolve a (possibly) relative filename to an absolute path. If
+ * |scriptRelative| is true, then the result will be relative to the directory
+ * containing the currently-running script, or the current working directory if
+ * the currently-running script is "-e" (namely, you're using it from the
+ * command line.) Otherwise, it will be relative to the current working
+ * directory.
+ */
+static JSString*
+ResolvePath(JSContext* cx, HandleString filenameStr, PathResolutionMode resolveMode)
+{
+    JSAutoByteString filename(cx, filenameStr);
+    if (!filename)
+        return nullptr;
+
+    const char* pathname = filename.ptr();
+    if (pathname[0] == '/')
+        return filenameStr;
+#ifdef XP_WIN
+    // Various forms of absolute paths per http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
+    // "\..."
+    if (pathname[0] == '\\')
+        return filenameStr;
+    // "C:\..."
+    if (strlen(pathname) > 3 && isalpha(pathname[0]) && pathname[1] == ':' && pathname[2] == '\\')
+        return filenameStr;
+    // "\\..."
+    if (strlen(pathname) > 2 && pathname[1] == '\\' && pathname[2] == '\\')
+        return filenameStr;
+#endif
+
+    /* Get the currently executing script's name. */
+    JS::AutoFilename scriptFilename;
+    if (!DescribeScriptedCaller(cx, &scriptFilename))
+        return nullptr;
+
+    if (!scriptFilename.get())
+        return nullptr;
+
+    if (strcmp(scriptFilename.get(), "-e") == 0 || strcmp(scriptFilename.get(), "typein") == 0)
+        resolveMode = RootRelative;
+
+    static char buffer[PATH_MAX+1];
+    if (resolveMode == ScriptRelative) {
+#ifdef XP_WIN
+        // The docs say it can return EINVAL, but the compiler says it's void
+        _splitpath(scriptFilename.get(), nullptr, buffer, nullptr, nullptr);
+#else
+        strncpy(buffer, scriptFilename.get(), PATH_MAX+1);
+        if (buffer[PATH_MAX] != '\0')
+            return nullptr;
+
+        // dirname(buffer) might return buffer, or it might return a
+        // statically-allocated string
+        memmove(buffer, dirname(buffer), strlen(buffer) + 1);
+#endif
+    } else {
+        const char* cwd = getcwd(buffer, PATH_MAX);
+        if (!cwd)
+            return nullptr;
+    }
+
+    size_t len = strlen(buffer);
+    buffer[len] = '/';
+    strncpy(buffer + len + 1, pathname, sizeof(buffer) - (len+1));
+    if (buffer[PATH_MAX] != '\0')
+        return nullptr;
+
+    return JS_NewStringCopyZ(cx, buffer);
+}
+
 static bool
 CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 3) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
@@ -1254,18 +1361,18 @@ Evaluate(JSContext* cx, unsigned argc, j
             return false;
 
         saveBuffer.forget();
     }
 
     return JS_WrapValue(cx, args.rval());
 }
 
-JSString*
-js::shell::FileAsString(JSContext* cx, const char* pathname)
+static JSString*
+FileAsString(JSContext* cx, const char* pathname)
 {
     FILE* file;
     RootedString str(cx);
     size_t len, cc;
     char* buf;
 
     file = fopen(pathname, "rb");
     if (!file) {
@@ -1300,16 +1407,49 @@ js::shell::FileAsString(JSContext* cx, c
                 JS_free(cx, buf);
             }
         }
     }
 
     return str;
 }
 
+static JSObject*
+FileAsTypedArray(JSContext* cx, const char* pathname)
+{
+    FILE* file = fopen(pathname, "rb");
+    if (!file) {
+        JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
+        return nullptr;
+    }
+    AutoCloseInputFile autoClose(file);
+
+    RootedObject obj(cx);
+    if (fseek(file, 0, SEEK_END) != 0) {
+        JS_ReportError(cx, "can't seek end of %s", pathname);
+    } else {
+        size_t len = ftell(file);
+        if (fseek(file, 0, SEEK_SET) != 0) {
+            JS_ReportError(cx, "can't seek start of %s", pathname);
+        } else {
+            obj = JS_NewUint8Array(cx, len);
+            if (!obj)
+                return nullptr;
+            char* buf = (char*) obj->as<TypedArrayObject>().viewData();
+            size_t cc = fread(buf, 1, len, file);
+            if (cc != len) {
+                JS_ReportError(cx, "can't read %s: %s", pathname,
+                               (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
+                obj = nullptr;
+            }
+        }
+    }
+    return obj;
+}
+
 /*
  * Function to run scripts and return compilation + execution time. Semantics
  * are closely modelled after the equivalent function in WebKit, as this is used
  * to produce benchmark timings by SunSpider.
  */
 static bool
 Run(JSContext* cx, unsigned argc, jsval* vp)
 {
@@ -3447,16 +3587,122 @@ struct FreeOnReturn
         this->ptr = ptr;
     }
 
     ~FreeOnReturn() {
         JS_free(cx, (void*)ptr);
     }
 };
 
+static bool
+ReadFile(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1 || args.length() > 2) {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
+                             args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
+                             "snarf");
+        return false;
+    }
+
+    if (!args[0].isString() || (args.length() == 2 && !args[1].isString())) {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "snarf");
+        return false;
+    }
+
+    RootedString givenPath(cx, args[0].toString());
+    RootedString str(cx, ResolvePath(cx, givenPath, scriptRelative ? ScriptRelative : RootRelative));
+    if (!str)
+        return false;
+
+    JSAutoByteString filename(cx, str);
+    if (!filename)
+        return false;
+
+    if (args.length() > 1) {
+        JSString* opt = JS::ToString(cx, args[1]);
+        if (!opt)
+            return false;
+        bool match;
+        if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
+            return false;
+        if (match) {
+            JSObject* obj;
+            if (!(obj = FileAsTypedArray(cx, filename.ptr())))
+                return false;
+            args.rval().setObject(*obj);
+            return true;
+        }
+    }
+
+    if (!(str = FileAsString(cx, filename.ptr())))
+        return false;
+    args.rval().setString(str);
+    return true;
+}
+
+static bool
+Snarf(JSContext* cx, unsigned argc, jsval* vp)
+{
+    return ReadFile(cx, argc, vp, false);
+}
+
+static bool
+ReadRelativeToScript(JSContext* cx, unsigned argc, jsval* vp)
+{
+    return ReadFile(cx, argc, vp, true);
+}
+
+static bool
+redirect(JSContext* cx, FILE* fp, HandleString relFilename)
+{
+    RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
+    if (!filename)
+        return false;
+    JSAutoByteString filenameABS(cx, filename);
+    if (!filenameABS)
+        return false;
+    if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
+        JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+static bool
+RedirectOutput(JSContext* cx, unsigned argc, jsval* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1 || args.length() > 2) {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
+        return false;
+    }
+
+    if (args[0].isString()) {
+        RootedString stdoutPath(cx, args[0].toString());
+        if (!stdoutPath)
+            return false;
+        if (!redirect(cx, stdout, stdoutPath))
+            return false;
+    }
+
+    if (args.length() > 1 && args[1].isString()) {
+        RootedString stderrPath(cx, args[1].toString());
+        if (!stderrPath)
+            return false;
+        if (!redirect(cx, stderr, stderrPath))
+            return false;
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
 static int sArgc;
 static char** sArgv;
 
 class AutoCStringVector
 {
     Vector<char*> argv_;
   public:
     explicit AutoCStringVector(JSContext* cx) : argv_(cx) {}
@@ -4452,16 +4698,30 @@ static const JSFunctionSpecWithHelp shel
 "arrayInfo(a1, a2, ...)",
 "  Report statistics about arrays."),
 #endif
 
     JS_FN_HELP("sleep", Sleep_fn, 1, 0,
 "sleep(dt)",
 "  Sleep for dt seconds."),
 
+    JS_FN_HELP("snarf", Snarf, 1, 0,
+"snarf(filename, [\"binary\"])",
+"  Read filename into returned string. Filename is relative to the current\n"
+               "  working directory."),
+
+    JS_FN_HELP("read", Snarf, 1, 0,
+"read(filename, [\"binary\"])",
+"  Synonym for snarf."),
+
+    JS_FN_HELP("readRelativeToScript", ReadRelativeToScript, 1, 0,
+"readRelativeToScript(filename, [\"binary\"])",
+"  Read filename into returned string. Filename is relative to the directory\n"
+"  containing the current script."),
+
     JS_FN_HELP("compile", Compile, 1, 0,
 "compile(code)",
 "  Compiles a string to bytecode, potentially throwing."),
 
     JS_FN_HELP("parse", Parse, 1, 0,
 "parse(code)",
 "  Parses a string, potentially throwing."),
 
@@ -4620,16 +4880,21 @@ static const JSFunctionSpecWithHelp fuzz
     JS_FN_HELP("line2pc", LineToPC, 0, 0,
 "line2pc([fun,] line)",
 "  Map line number to PC."),
 
     JS_FN_HELP("pc2line", PCToLine, 0, 0,
 "pc2line(fun[, pc])",
 "  Map PC to line number."),
 
+    JS_FN_HELP("redirect", RedirectOutput, 2, 0,
+"redirect(stdoutFilename[, stderrFilename])",
+"  Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
+"   redirecting. Filenames are relative to the current working directory."),
+
     JS_FN_HELP("nestedShell", NestedShell, 0, 0,
 "nestedShell(shellArgs...)",
 "  Execute the given code in a new JS shell process, passing this nested shell\n"
 "  the arguments passed to nestedShell. argv[0] of the nested shell will be argv[0]\n"
 "  of the current shell (which is assumed to be the actual path to the shell.\n"
 "  arguments[0] (of the call to nestedShell) will be argv[1], arguments[1] will\n"
 "  be argv[2], etc."),
 
@@ -4798,27 +5063,27 @@ Help(JSContext* cx, unsigned argc, jsval
 
 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
 #define MSG_DEF(name, count, exception, format) \
     { format, count, JSEXN_ERR } ,
 #include "jsshell.msg"
 #undef MSG_DEF
 };
 
-const JSErrorFormatString*
-js::shell::my_GetErrorMessage(void* userRef, const unsigned errorNumber)
+static const JSErrorFormatString*
+my_GetErrorMessage(void* userRef, const unsigned errorNumber)
 {
     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
         return nullptr;
 
     return &jsShell_ErrorFormatString[errorNumber];
 }
 
-void
-js::shell::my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
+static void
+my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
 {
     gGotError = PrintError(cx, gErrFile, message, report, reportWarnings);
     if (report->exnType != JSEXN_NONE && !JSREPORT_IS_WARNING(report->flags)) {
         if (report->errorNumber == JSMSG_OUT_OF_MEMORY) {
             gExitCode = EXITCODE_OUT_OF_MEMORY;
         } else {
             gExitCode = EXITCODE_RUNTIME_ERROR;
         }
@@ -5393,17 +5658,17 @@ NewGlobalObject(JSContext* cx, JS::Compa
             return nullptr;
         }
         if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe))
             return nullptr;
 
         if (!fuzzingSafe) {
             if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions))
                 return nullptr;
-            if (!DefineOS(cx, glob))
+            if (!js::DefineOS(cx, glob))
                 return nullptr;
             if (!DefineConsole(cx, glob))
                 return nullptr;
         }
 
         RootedObject performanceObj(cx, JS_NewObject(cx, nullptr));
         if (!performanceObj)
             return nullptr;
deleted file mode 100644
--- a/js/src/shell/jsshell.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef jsshell_js_h
-#define jsshell_js_h
-
-#include "jsapi.h"
-
-namespace js {
-namespace shell {
-
-enum JSShellErrNum {
-#define MSG_DEF(name, count, exception, format) \
-    name,
-#include "jsshell.msg"
-#undef MSG_DEF
-    JSShellErr_Limit
-};
-
-const JSErrorFormatString*
-my_GetErrorMessage(void* userRef, const unsigned errorNumber);
-
-static void
-my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report);
-
-JSString*
-FileAsString(JSContext* cx, const char* pathname);
-
-class AutoCloseInputFile
-{
-  private:
-    FILE* f_;
-  public:
-    explicit AutoCloseInputFile(FILE* f) : f_(f) {}
-    ~AutoCloseInputFile() {
-        if (f_ && f_ != stdin)
-            fclose(f_);
-    }
-};
-
-} /* namespace shell */
-} /* namespace js */
-
-#endif
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -140,30 +140,36 @@ function saveDocument(aDocument, aSkipPr
   try {
     contentDisposition =
       ifreq.getInterface(Components.interfaces.nsIDOMWindowUtils)
            .getDocumentMetadata("content-disposition");
   } catch (ex) {
     // Failure to get a content-disposition is ok
   }
 
-  var cacheKey = null;
+  let cacheKey = null;
   try {
-    cacheKey =
+    let shEntry =
       ifreq.getInterface(Components.interfaces.nsIWebNavigation)
-           .QueryInterface(Components.interfaces.nsIWebPageDescriptor);
+           .QueryInterface(Components.interfaces.nsIWebPageDescriptor)
+           .currentDescriptor
+           .QueryInterface(Components.interfaces.nsISHEntry);
+
+    shEntry.cacheKey.QueryInterface(Components.interfaces.nsISupportsPRUint32);
+
+    // In the event that the cacheKey is a CPOW, we cannot pass it to
+    // nsIWebBrowserPersist, so we create a new one and copy the value
+    // over. This is a workaround until bug 1101100 is fixed.
+    cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]
+                 .createInstance(Ci.nsISupportsPRUint32);
+    cacheKey.data = shEntry.cacheKey.data;
   } catch (ex) {
     // We might not find it in the cache.  Oh, well.
   }
 
-  if (cacheKey && Components.utils.isCrossProcessWrapper(cacheKey)) {
-    // Don't use a cache key from another process. See bug 1128050.
-    cacheKey = null;
-  }
-
   internalSave(aDocument.location.href, aDocument, null, contentDisposition,
                aDocument.contentType, false, null, null,
                aDocument.referrer ? makeURI(aDocument.referrer) : null,
                aDocument, aSkipPrompt, cacheKey);
 }
 
 function DownloadListener(win, transfer) {
   function makeClosure(name) {
@@ -334,24 +340,27 @@ function internalSave(aURL, aDocument, a
     // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
     // If we should save as text, the saveAsType is kSaveAsType_Text.
     var useSaveDocument = aDocument &&
                           (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
                            ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
     // If we're saving a document, and are saving either in complete mode or
     // as converted text, pass the document to the web browser persist component.
     // If we're just saving the HTML (second option in the list), send only the URI.
+    let nonCPOWDocument =
+      aDocument && !Components.utils.isCrossProcessWrapper(aDocument);
+
     var persistArgs = {
       sourceURI         : sourceURI,
       sourceReferrer    : aReferrer,
       sourceDocument    : useSaveDocument ? aDocument : null,
       targetContentType : (saveAsType == kSaveAsType_Text) ? "text/plain" : null,
       targetFile        : file,
       sourceCacheKey    : aCacheKey,
-      sourcePostData    : aDocument ? getPostData(aDocument) : null,
+      sourcePostData    : nonCPOWDocument ? getPostData(aDocument) : null,
       bypassCache       : aShouldBypassCache,
       initiatingWindow  : aInitiatingDocument.defaultView
     };
 
     // Start the actual save process
     internalPersist(persistArgs);
   }
 }