Bug 1390985 - Export Screenshots 16.1.0 to Firefox draft
authorIan Bicking <ianb@colorstudy.com>
Fri, 25 Aug 2017 14:16:22 -0500
changeset 653248 4ac821f586926394eac0ff14790b65d741a3598a
parent 653118 56188620cce00b19700fbb8efaafea65e6ca8c61
child 728294 feb3de9eb653e5b262b754d0b9e34b5ed681be91
push id76279
push userbmo:ianb@mozilla.com
push dateFri, 25 Aug 2017 19:16:58 +0000
bugs1390985
milestone57.0a1
Bug 1390985 - Export Screenshots 16.1.0 to Firefox
browser/extensions/screenshots/bootstrap.js
browser/extensions/screenshots/install.rdf
browser/extensions/screenshots/moz.build
browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js
browser/extensions/screenshots/test/browser/head.js
browser/extensions/screenshots/webextension/_locales/ach/messages.json
browser/extensions/screenshots/webextension/_locales/ar/messages.json
browser/extensions/screenshots/webextension/_locales/ast/messages.json
browser/extensions/screenshots/webextension/_locales/az/messages.json
browser/extensions/screenshots/webextension/_locales/be/messages.json
browser/extensions/screenshots/webextension/_locales/bg/messages.json
browser/extensions/screenshots/webextension/_locales/bn_BD/messages.json
browser/extensions/screenshots/webextension/_locales/ca/messages.json
browser/extensions/screenshots/webextension/_locales/cak/messages.json
browser/extensions/screenshots/webextension/_locales/cs/messages.json
browser/extensions/screenshots/webextension/_locales/cy/messages.json
browser/extensions/screenshots/webextension/_locales/da/messages.json
browser/extensions/screenshots/webextension/_locales/de/messages.json
browser/extensions/screenshots/webextension/_locales/dsb/messages.json
browser/extensions/screenshots/webextension/_locales/el/messages.json
browser/extensions/screenshots/webextension/_locales/en_US/messages.json
browser/extensions/screenshots/webextension/_locales/eo/messages.json
browser/extensions/screenshots/webextension/_locales/es_AR/messages.json
browser/extensions/screenshots/webextension/_locales/es_CL/messages.json
browser/extensions/screenshots/webextension/_locales/es_ES/messages.json
browser/extensions/screenshots/webextension/_locales/es_MX/messages.json
browser/extensions/screenshots/webextension/_locales/et/messages.json
browser/extensions/screenshots/webextension/_locales/fa/messages.json
browser/extensions/screenshots/webextension/_locales/fi/messages.json
browser/extensions/screenshots/webextension/_locales/fr/messages.json
browser/extensions/screenshots/webextension/_locales/fy_NL/messages.json
browser/extensions/screenshots/webextension/_locales/ga_IE/messages.json
browser/extensions/screenshots/webextension/_locales/gu_IN/messages.json
browser/extensions/screenshots/webextension/_locales/he/messages.json
browser/extensions/screenshots/webextension/_locales/hi_IN/messages.json
browser/extensions/screenshots/webextension/_locales/hr/messages.json
browser/extensions/screenshots/webextension/_locales/hsb/messages.json
browser/extensions/screenshots/webextension/_locales/hu/messages.json
browser/extensions/screenshots/webextension/_locales/hy_AM/messages.json
browser/extensions/screenshots/webextension/_locales/id/messages.json
browser/extensions/screenshots/webextension/_locales/it/messages.json
browser/extensions/screenshots/webextension/_locales/ja/messages.json
browser/extensions/screenshots/webextension/_locales/ka/messages.json
browser/extensions/screenshots/webextension/_locales/kab/messages.json
browser/extensions/screenshots/webextension/_locales/kk/messages.json
browser/extensions/screenshots/webextension/_locales/ko/messages.json
browser/extensions/screenshots/webextension/_locales/lij/messages.json
browser/extensions/screenshots/webextension/_locales/lo/messages.json
browser/extensions/screenshots/webextension/_locales/lt/messages.json
browser/extensions/screenshots/webextension/_locales/mk/messages.json
browser/extensions/screenshots/webextension/_locales/mr/messages.json
browser/extensions/screenshots/webextension/_locales/ms/messages.json
browser/extensions/screenshots/webextension/_locales/nb_NO/messages.json
browser/extensions/screenshots/webextension/_locales/nl/messages.json
browser/extensions/screenshots/webextension/_locales/nn_NO/messages.json
browser/extensions/screenshots/webextension/_locales/pl/messages.json
browser/extensions/screenshots/webextension/_locales/pt_BR/messages.json
browser/extensions/screenshots/webextension/_locales/pt_PT/messages.json
browser/extensions/screenshots/webextension/_locales/rm/messages.json
browser/extensions/screenshots/webextension/_locales/ru/messages.json
browser/extensions/screenshots/webextension/_locales/sk/messages.json
browser/extensions/screenshots/webextension/_locales/sl/messages.json
browser/extensions/screenshots/webextension/_locales/sr/messages.json
browser/extensions/screenshots/webextension/_locales/sv_SE/messages.json
browser/extensions/screenshots/webextension/_locales/ta/messages.json
browser/extensions/screenshots/webextension/_locales/te/messages.json
browser/extensions/screenshots/webextension/_locales/th/messages.json
browser/extensions/screenshots/webextension/_locales/tl/messages.json
browser/extensions/screenshots/webextension/_locales/tr/messages.json
browser/extensions/screenshots/webextension/_locales/uk/messages.json
browser/extensions/screenshots/webextension/_locales/ur/messages.json
browser/extensions/screenshots/webextension/_locales/vi/messages.json
browser/extensions/screenshots/webextension/_locales/zh_CN/messages.json
browser/extensions/screenshots/webextension/_locales/zh_TW/messages.json
browser/extensions/screenshots/webextension/background/deviceInfo.js
browser/extensions/screenshots/webextension/background/main.js
browser/extensions/screenshots/webextension/background/startBackground.js
browser/extensions/screenshots/webextension/build/inlineSelectionCss.js
browser/extensions/screenshots/webextension/build/onboardingCss.js
browser/extensions/screenshots/webextension/build/onboardingHtml.js
browser/extensions/screenshots/webextension/build/raven.js
browser/extensions/screenshots/webextension/build/shot.js
browser/extensions/screenshots/webextension/domainFromUrl.js
browser/extensions/screenshots/webextension/icons/menu-myshot-white.svg
browser/extensions/screenshots/webextension/manifest.json
browser/extensions/screenshots/webextension/onboarding/slides.html
browser/extensions/screenshots/webextension/onboarding/slides.js
browser/extensions/screenshots/webextension/selector/callBackground.js
browser/extensions/screenshots/webextension/selector/shooter.js
browser/extensions/screenshots/webextension/selector/ui.js
browser/extensions/screenshots/webextension/selector/uicontrol.js
--- a/browser/extensions/screenshots/bootstrap.js
+++ b/browser/extensions/screenshots/bootstrap.js
@@ -6,22 +6,28 @@ const TELEMETRY_ENABLED_PREF = "datarepo
 const PREF_BRANCH = "extensions.screenshots.";
 const USER_DISABLE_PREF = "extensions.screenshots.disabled";
 const SYSTEM_DISABLE_PREF = "extensions.screenshots.system-disabled";
 
 const { interfaces: Ci, utils: Cu } = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Console",
                                   "resource://gre/modules/Console.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
+                                  "resource:///modules/CustomizableUI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LegacyExtensionsUtils",
+                                  "resource://gre/modules/LegacyExtensionsUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
+                                  "resource:///modules/PageActions.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LegacyExtensionsUtils",
-                                  "resource://gre/modules/LegacyExtensionsUtils.jsm");
 
 let addonResourceURI;
 let appStartupDone;
 let appStartupPromise = new Promise((resolve, reject) => {
   appStartupDone = resolve;
 });
 
 const prefs = Services.prefs;
@@ -54,17 +60,73 @@ const appStartupObserver = {
   },
 
   observe() {
     appStartupDone();
     this.unregister();
   }
 }
 
+const LibraryButton = {
+  ITEM_ID: "appMenu-library-screenshots",
+
+  init(webExtension) {
+    this._initialized = true;
+    let permissionPages = [...webExtension.extension.permissions].filter(p => (/^https?:\/\//i).test(p));
+    if (permissionPages.length > 1) {
+      Cu.reportError(new Error("Should not have more than 1 permission page, but got: " + JSON.stringify(permissionPages)));
+    }
+    this.PAGE_TO_OPEN = permissionPages.length == 1 ? permissionPages[0].replace(/\*$/, "") : "https://screenshots.firefox.com/";
+    this.PAGE_TO_OPEN += "shots";
+    this.ICON_URL = webExtension.extension.getURL("icons/icon-16-v2.svg");
+    this.ICON_URL_2X = webExtension.extension.getURL("icons/icon-32-v2.svg");
+    this.LABEL = webExtension.extension.localizeMessage("libraryLabel");
+    CustomizableUI.addListener(this);
+    for (let win of CustomizableUI.windows) {
+      this.onWindowOpened(win);
+    }
+  },
+
+  uninit() {
+    if (!this._initialized) {
+      return;
+    }
+    for (let win of CustomizableUI.windows) {
+      let item = win.document.getElementById(this.ITEM_ID);
+      if (item) {
+        item.remove();
+      }
+    }
+    CustomizableUI.removeListener(this);
+    this._initialized = false;
+  },
+
+  onWindowOpened(win) {
+    let libraryViewInsertionPoint = win.document.getElementById("appMenu-library-remotetabs-button");
+    // If the library view doesn't exist (on non-photon builds, for instance),
+    // this will be null, and we bail out early.
+    if (!libraryViewInsertionPoint) {
+      return;
+    }
+    let parent = libraryViewInsertionPoint.parentNode;
+    let {nextSibling} = libraryViewInsertionPoint;
+    let item = win.document.createElement("toolbarbutton");
+    item.className = "subviewbutton subviewbutton-iconic";
+    item.addEventListener("command", () => win.openUILinkIn(this.PAGE_TO_OPEN, "tab"));
+    item.id = this.ITEM_ID;
+    let iconURL = win.devicePixelRatio >= 1.1 ? this.ICON_URL_2X : this.ICON_URL;
+    item.setAttribute("image", iconURL);
+    item.setAttribute("label", this.LABEL);
+
+    parent.insertBefore(item, nextSibling);
+  },
+};
+
 const APP_STARTUP = 1;
+const APP_SHUTDOWN = 2;
 let startupReason;
 
 function startup(data, reason) { // eslint-disable-line no-unused-vars
   startupReason = reason;
   if (reason === APP_STARTUP) {
     appStartupObserver.register();
   } else {
     appStartupDone();
@@ -113,29 +175,37 @@ function handleStartup() {
   } else if (shouldDisable()) {
     return stop(webExtension, ADDON_DISABLE);
   }
 }
 
 function start(webExtension) {
   return webExtension.startup(startupReason).then((api) => {
     api.browser.runtime.onMessage.addListener(handleMessage);
-    return Promise.resolve(null);
+    LibraryButton.init(webExtension);
+    initPhotonPageAction(api, webExtension);
   }).catch((err) => {
     // The startup() promise will be rejected if the webExtension was
     // already started (a harmless error), or if initializing the
     // WebExtension failed and threw (an important error).
     console.error(err);
     if (err.message !== "This embedded extension has already been started") {
       // TODO: Should we send these errors to Sentry? #2420
     }
   });
 }
 
 function stop(webExtension, reason) {
+  if (reason != APP_SHUTDOWN) {
+    LibraryButton.uninit();
+    if (photonPageAction) {
+      photonPageAction.remove();
+      photonPageAction = null;
+    }
+  }
   return Promise.resolve(webExtension.shutdown(reason));
 }
 
 function handleMessage(msg, sender, sendReply) {
   if (!msg) {
     return;
   }
 
@@ -151,8 +221,89 @@ function handleMessage(msg, sender, send
       if (addon) {
         addon.uninstall();
       }
       sendReply({type: "success", value: !!addon});
     });
     return true;
   }
 }
+
+let photonPageAction;
+
+// If the current Firefox version supports Photon (57 and later), this sets up
+// a Photon page action and removes the UI for the WebExtension browser action.
+// Does nothing otherwise.  Ideally, in the future, WebExtension page actions
+// and Photon page actions would be one in the same, but they aren't right now.
+function initPhotonPageAction(api, webExtension) {
+  // The MOZ_PHOTON_THEME ifdef got removed, but we need to support 55 and 56 as well,
+  // so check if the property exists *and* is false before bailing.
+  if (typeof AppConstants.MOZ_PHOTON_THEME != "undefined" && !AppConstants.MOZ_PHOTON_THEME) {
+    // Photon not supported.  Use the WebExtension's browser action.
+    return;
+  }
+
+  let id = "screenshots";
+  let port = null;
+  let baseIconPath = addonResourceURI.spec + "webextension/";
+
+  let {tabManager} = webExtension.extension;
+
+  // Make the page action.
+  photonPageAction = PageActions.actionForID(id) || PageActions.addAction(new PageActions.Action({
+    id,
+    title: "Take a Screenshot",
+    iconURL: baseIconPath + "icons/icon-32-v2.svg",
+    _insertBeforeActionID: null,
+    onCommand(event, buttonNode) {
+      if (port) {
+        let browserWin = buttonNode.ownerGlobal;
+        let tab = tabManager.getWrapper(browserWin.gBrowser.selectedTab);
+        port.postMessage({
+          type: "click",
+          tab: {id: tab.id, url: tab.url}
+        });
+      }
+    },
+  }));
+
+  // Remove the navbar button of the WebExtension's browser action.
+  let cuiWidgetID = "screenshots_mozilla_org-browser-action";
+  CustomizableUI.addListener({
+    onWidgetAfterCreation(wid, aArea) {
+      if (wid == cuiWidgetID) {
+        CustomizableUI.destroyWidget(cuiWidgetID);
+        CustomizableUI.removeListener(this);
+      }
+    },
+  });
+
+  // Establish a port to the WebExtension side.
+  api.browser.runtime.onConnect.addListener((listenerPort) => {
+    if (listenerPort.name != "photonPageActionPort") {
+      return;
+    }
+    port = listenerPort;
+    port.onMessage.addListener((message) => {
+      switch (message.type) {
+      case "setProperties":
+        if (message.title) {
+          photonPageAction.title = message.title;
+        }
+        if (message.iconPath) {
+          photonPageAction.iconURL = baseIconPath + message.iconPath;
+        }
+        break;
+      default:
+        console.error("Unrecognized message:", message);
+        break;
+      }
+    });
+
+    // It's necessary to tell the WebExtension not to use its browser action,
+    // due to the CUI widget's removal.  Otherwise Firefox's WebExtension
+    // machinery throws exceptions.
+    port.postMessage({
+      type: "setUsePhotonPageAction",
+      value: true
+    });
+  });
+}
--- a/browser/extensions/screenshots/install.rdf
+++ b/browser/extensions/screenshots/install.rdf
@@ -7,14 +7,14 @@
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
         <em:minVersion>51.0a1</em:minVersion>
         <em:maxVersion>*</em:maxVersion>
       </Description>
     </em:targetApplication>
     <em:type>2</em:type>
-    <em:version>10.12.0</em:version>
+    <em:version>16.1.0</em:version>
     <em:bootstrap>true</em:bootstrap>
-    <em:homepageURL>https://pageshot.net/</em:homepageURL>
+    <em:homepageURL>https://screenshots.firefox.com/</em:homepageURL>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
   </Description>
 </RDF>
--- a/browser/extensions/screenshots/moz.build
+++ b/browser/extensions/screenshots/moz.build
@@ -28,16 +28,20 @@ FINAL_TARGET_FILES.features['screenshots
 FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ach"] += [
   'webextension/_locales/ach/messages.json'
 ]
 
 FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ar"] += [
   'webextension/_locales/ar/messages.json'
 ]
 
+FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["ast"] += [
+  'webextension/_locales/ast/messages.json'
+]
+
 FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["az"] += [
   'webextension/_locales/az/messages.json'
 ]
 
 FINAL_TARGET_FILES.features['screenshots@mozilla.org']["webextension"]["_locales"]["be"] += [
   'webextension/_locales/be/messages.json'
 ]
 
@@ -355,16 +359,17 @@ FINAL_TARGET_FILES.features['screenshots
   'webextension/icons/done.svg',
   'webextension/icons/download.svg',
   'webextension/icons/icon-16-v2.svg',
   'webextension/icons/icon-32-v2.svg',
   'webextension/icons/icon-highlight-32-v2.svg',
   'webextension/icons/icon-starred-32-v2.svg',
   'webextension/icons/icon-welcome-face-without-eyes.svg',
   'webextension/icons/menu-fullpage.svg',
+  'webextension/icons/menu-myshot-white.svg',
   'webextension/icons/menu-myshot.svg',
   'webextension/icons/menu-visible.svg',
   'webextension/icons/onboarding-1.png',
   'webextension/icons/onboarding-2.png',
   'webextension/icons/onboarding-3.png',
   'webextension/icons/onboarding-4.png'
 ]
 
--- a/browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js
+++ b/browser/extensions/screenshots/test/browser/browser_screenshots_ui_check.js
@@ -8,14 +8,18 @@ function checkElements(expectPresent, l)
 
 add_task(async function() {
   await promiseScreenshotsEnabled();
 
   registerCleanupFunction(async function() {
     await promiseScreenshotsReset();
   });
 
+  let onPhoton = (typeof AppConstants.MOZ_PHOTON_THEME == "undefined") ||
+                 AppConstants.MOZ_PHOTON_THEME;
+  let id = onPhoton ? "pageAction-panel-screenshots" : "screenshots_mozilla_org-browser-action";
+
   await BrowserTestUtils.waitForCondition(
-    () => document.getElementById("screenshots_mozilla_org-browser-action"),
+    () => document.getElementById(id),
     "Screenshots button should be present", 100, 100);
 
-  checkElements(true, ["screenshots_mozilla_org-browser-action"]);
+  checkElements(true, [id]);
 });
--- a/browser/extensions/screenshots/test/browser/head.js
+++ b/browser/extensions/screenshots/test/browser/head.js
@@ -1,54 +1,78 @@
+/* globals PageActions */
+
 // Currently Screenshots is disabled in tests.  We want these tests to work under
 // either case that Screenshots is disabled or enabled on startup of the browser,
 // and that at the end we're reset to the correct state.
 let enabledOnStartup = false;
 
 // ScreenshotsEnabled/Disabled promises return true if it was already
 // Enabled/Disabled, and false if it need to Enable/Disable.
 function promiseScreenshotsEnabled() {
   if (!Services.prefs.getBoolPref("extensions.screenshots.system-disabled", false)) {
     info("Screenshots was already enabled, assuming enabled by default for tests");
     enabledOnStartup = true;
     return Promise.resolve(true);
   }
   info("Screenshots is not enabled");
   return new Promise((resolve, reject) => {
-    let listener = {
-      onWidgetAfterCreation(widgetid) {
-        if (widgetid == "screenshots_mozilla_org-browser-action") {
-          info("screenshots_mozilla_org-browser-action button created");
-          CustomizableUI.removeListener(listener);
+    if (AppConstants.hasOwnProperty("MOZ_PHOTON_THEME") && !AppConstants.MOZ_PHOTON_THEME) {
+      let listener = {
+        onWidgetAfterCreation(widgetid) {
+          if (widgetid == "screenshots_mozilla_org-browser-action") {
+            info("screenshots_mozilla_org-browser-action button created");
+            CustomizableUI.removeListener(listener);
+            resolve(false);
+          }
+        }
+      }
+      CustomizableUI.addListener(listener);
+    } else {
+      let interval = setInterval(() => {
+        let action = PageActions.actionForID("screenshots");
+        if (action) {
+          info("screenshots page action created");
+          clearInterval(interval);
           resolve(false);
         }
-      }
+      }, 100);
     }
-    CustomizableUI.addListener(listener);
     info("Set Screenshots disabled pref to false.");
     Services.prefs.setBoolPref("extensions.screenshots.system-disabled", false);
   });
 }
 
 function promiseScreenshotsDisabled() {
   if (Services.prefs.getBoolPref("extensions.screenshots.system-disabled", false)) {
     info("Screenshots already disabled");
     return Promise.resolve(true);
   }
   return new Promise((resolve, reject) => {
-    let listener = {
-      onWidgetDestroyed(widgetid) {
-        if (widgetid == "screenshots_mozilla_org-browser-action") {
-          CustomizableUI.removeListener(listener);
-          info("screenshots_mozilla_org-browser-action destroyed");
+    if (AppConstants.hasOwnProperty("MOZ_PHOTON_THEME") && !AppConstants.MOZ_PHOTON_THEME) {
+      let listener = {
+        onWidgetDestroyed(widgetid) {
+          if (widgetid == "screenshots_mozilla_org-browser-action") {
+            CustomizableUI.removeListener(listener);
+            info("screenshots_mozilla_org-browser-action destroyed");
+            resolve(false);
+          }
+        }
+      }
+      CustomizableUI.addListener(listener);
+    } else {
+      let interval = setInterval(() => {
+        let action = PageActions.actionForID("screenshots");
+        if (!action) {
+          info("screenshots page action removed");
+          clearInterval(interval);
           resolve(false);
         }
-      }
+      }, 100);
     }
-    CustomizableUI.addListener(listener);
     info("Set Screenshots disabled pref to true.");
     Services.prefs.setBoolPref("extensions.screenshots.system-disabled", true);
   });
 }
 
 function promiseScreenshotsReset() { // eslint-disable-line no-unused-vars
   if (enabledOnStartup) {
     info("Reset is enabling Screenshots addon");
--- a/browser/extensions/screenshots/webextension/_locales/ach/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ach/messages.json
@@ -101,18 +101,18 @@
     "message": "Cal malubo"
   },
   "tourPrevious": {
     "message": "Cal mukato"
   },
   "tourDone": {
     "message": "Otum"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Tic ki Firefox Screenshots nyuto, ni i yee $TERMSANDPRIVACYNOTICETERMSLINK$ ki $TERMSANDPRIVACYNOTICEPRIVACYLINK$ me tic me Cloud pa Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Tic ki Firefox Screenshots nyuto ni, i yee $TERMSANDPRIVACYNOTICETERMSLINK$ ki $TERMSANDPRIVACYNOTICEPRIVACYLINK$ wa.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/ar/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ar/messages.json
@@ -107,26 +107,29 @@
     "message": "الشريحة التالية"
   },
   "tourPrevious": {
     "message": "الشريحة السابقة"
   },
   "tourDone": {
     "message": "تمّ"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "استخدامك لخدمات «لقطات شاشة فَيَرفُكس» يعني موافقتك على $TERMSANDPRIVACYNOTICETERMSLINK$ و $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "استخدامك للقطات شاشة فَيَرفُكس يُعد موافقة على $TERMSANDPRIVACYNOTICETERMSLINK$ و $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "الشروط"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "تنويه الخصوصية"
+  },
+  "libraryLabel": {
+    "message": "لقطات الشاشة"
   }
 }
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/screenshots/webextension/_locales/ast/messages.json
@@ -0,0 +1,32 @@
+{
+  "addonAuthorsList": {
+    "message": "Mozilla <screenshots-feedback@mozilla.com>"
+  },
+  "saveScreenshotSelectedArea": {
+    "message": "Guardar"
+  },
+  "saveScreenshotVisibleArea": {
+    "message": "Guardar lo visible"
+  },
+  "saveScreenshotFullPage": {
+    "message": "Guardar la páxina completa"
+  },
+  "cancelScreenshot": {
+    "message": "Encaboxar"
+  },
+  "downloadScreenshot": {
+    "message": "Baxar"
+  },
+  "notificationLinkCopiedTitle": {
+    "message": "Copióse l'enllaz"
+  },
+  "unshootablePageErrorTitle": {
+    "message": "Nun pue capturase esta páxina."
+  },
+  "unshootablePageErrorDetails": {
+    "message": "Esta páxina web nun ye estándar, asina que nun pues capturala."
+  },
+  "tourDone": {
+    "message": "Fecho"
+  }
+}
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/az/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/az/messages.json
@@ -107,26 +107,29 @@
     "message": "Növbəti Slayd"
   },
   "tourPrevious": {
     "message": "Əvvəlki Slayd"
   },
   "tourDone": {
     "message": "Tamamlandı"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Firefox Screenshots işlədərək $TERMSANDPRIVACYNOTICETERMSLINK$ və $TERMSANDPRIVACYNOTICEPRIVACYLINK$ ilə razılaşmış olursunuz.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Şərtlər"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Məxfilik Bildirişi"
+  },
+  "libraryLabel": {
+    "message": "Ekran Görüntüləri"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/be/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/be/messages.json
@@ -107,26 +107,29 @@
     "message": "Наступны слайд"
   },
   "tourPrevious": {
     "message": "Папярэдні слайд"
   },
   "tourDone": {
     "message": "Гатова"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Выкарыстоўваючы Firefox Screenshots, вы згаджаецеся з $TERMSANDPRIVACYNOTICETERMSLINK$ і $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Выкарыстоўваючы Firefox Screenshots, вы згаджаецеся з нашымі $TERMSANDPRIVACYNOTICETERMSLINK$ і $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Умовамі выкарыстання"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Паведамленнем аб прыватнасці"
+  },
+  "libraryLabel": {
+    "message": "Скрыншоты"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/bg/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/bg/messages.json
@@ -107,26 +107,29 @@
     "message": "Напред"
   },
   "tourPrevious": {
     "message": "Назад"
   },
   "tourDone": {
     "message": "Готово"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Използвайки Firefox Screenshots вие се съгласявате с $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$ на облачните услуги на Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Използвайки Firefox Screenshots вие се съгласявате с $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Условията"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Политиката на поверителност"
+  },
+  "libraryLabel": {
+    "message": "Снимки"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/bn_BD/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/bn_BD/messages.json
@@ -107,26 +107,29 @@
     "message": "পরবর্তী স্লাইড"
   },
   "tourPrevious": {
     "message": "পূর্ববর্তী স্লাইড"
   },
   "tourDone": {
     "message": "সম্পন্ন"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox Screenshots ব্যবহারে, আপনি Firefox Cloud Services এর $TERMSANDPRIVACYNOTICETERMSLINK$ এবং $TERMSANDPRIVACYNOTICEPRIVACYLINK$ নীতিতে সম্মত হয়েছেন।",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots ব্যবহারের জন্য, আপনি আমাদের $TERMSANDPRIVACYNOTICETERMSLINK$ এবং $TERMSANDPRIVACYNOTICEPRIVACYLINK$ নীতিতে সম্মত হয়েছেন।",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "শর্তাবলী"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "গোপনীয়তা নীতি"
+  },
+  "libraryLabel": {
+    "message": "স্ক্রীনশট"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ca/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ca/messages.json
@@ -107,26 +107,29 @@
     "message": "Diapositiva següent"
   },
   "tourPrevious": {
     "message": "Diapositiva anterior"
   },
   "tourDone": {
     "message": "Fet"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Si utilitzeu el Firefox Screenshots, esteu acceptant les nostres $TERMSANDPRIVACYNOTICETERMSLINK$ i l'$TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "condicions d'ús"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "avís de privadesa"
+  },
+  "libraryLabel": {
+    "message": "Captures de pantalla"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/cak/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/cak/messages.json
@@ -107,26 +107,29 @@
     "message": "Jun chik Q'axewäch"
   },
   "tourPrevious": {
     "message": "Jun kan Q'axewäch"
   },
   "tourDone": {
     "message": "Xb'an"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Rik'in rokisaxik ri Firefox Chapoj Wachib'äl, nawoqaj $TERMSANDPRIVACYNOTICETERMSLINK$ chuqa' $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Taq ojqanem"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Rutzijol Ichinanem"
+  },
+  "libraryLabel": {
+    "message": "Chapoj taq wachib'äl"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/cs/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/cs/messages.json
@@ -107,26 +107,29 @@
     "message": "Další snímek"
   },
   "tourPrevious": {
     "message": "Předchozí snímek"
   },
   "tourDone": {
     "message": "Hotovo"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Používáním služby Firefox Screenshots souhlasíte s $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Používáním služby Firefox Screenshots souhlasíte s našimi $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "podmínkami"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "zásadami ochrany osobních údajů"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/cy/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/cy/messages.json
@@ -107,26 +107,29 @@
     "message": "Sleid Nesaf"
   },
   "tourPrevious": {
     "message": "Sleid Flaenorol"
   },
   "tourDone": {
     "message": "Gorffen"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Drwy ddefnyddio Firefox Screenshots, rydych yn cytuno i Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Drwy ddefnyddio Firefox Screenshots, rydych yn cytuno i'n $TERMSANDPRIVACYNOTICETERMSLINK$ a'n $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Telerau"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Hysbysiad Preifatrwydd"
+  },
+  "libraryLabel": {
+    "message": "Rhannu ar Pinterest"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/da/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/da/messages.json
@@ -28,43 +28,43 @@
   },
   "downloadScreenshot": {
     "message": "Hent"
   },
   "notificationLinkCopiedTitle": {
     "message": "Link kopieret"
   },
   "notificationLinkCopiedDetails": {
-    "message": "Linket til dit skærmbillede er blevet gemt i udklipsholderen. Tryk på $META_KEY$-V for at sætte ind.",
+    "message": "Linket til dit skærmbillede er blevet gemt i udklipsholderen. Tryk på $META_KEY$-V for at sætte ind. ",
     "placeholders": {
       "meta_key": {
         "content": "$1"
       }
     }
   },
   "requestErrorTitle": {
     "message": "Ude af funktion"
   },
   "requestErrorDetails": {
     "message": "Vi kunne desværre ikke gemme dit skærmbillede. Prøv igen senere."
   },
   "connectionErrorTitle": {
     "message": "Vi kan ikke oprette forbindelse til dine skærmbilleder."
   },
   "connectionErrorDetails": {
-    "message": "Kontroller din internet-forbindelse. Hvis du ikke kan oprette forbindelse til internettet, kan der være et midlertidigt teknisk problem med Firefox Screenshots."
+    "message": "Kontroller din internet-forbindelse. Hvis du ikke kan oprette forbindelse til internettet, kan der være et midlertidigt teknisk problem med Firefox Screenshots. "
   },
   "loginErrorDetails": {
-    "message": "Vi kunne ikke gemme dit skærmbillede, fordi der er et teknisk problem med Firefox Screenshots. Prøv igen senere."
+    "message": "Vi kunne ikke gemme dit skærmbillede, fordi der er et teknisk problem med Firefox Screenshots. Prøv igen senere. "
   },
   "unshootablePageErrorTitle": {
-    "message": "Vi kan ikke tage et skærmbillede af denne side."
+    "message": "Vi kan ikke tage et skærmbillede af denne side. "
   },
   "unshootablePageErrorDetails": {
-    "message": "Dette er ikke en almindelig webside, så du kan ikke tage skærmbilleder af den."
+    "message": "Dette er ikke en almindelig webside, så du kan ikke tage skærmbilleder af den. "
   },
   "selfScreenshotErrorTitle": {
     "message": "Du kan ikke tage skærmbilleder af en side i Firefox Screenshots."
   },
   "emptySelectionErrorTitle": {
     "message": "Din markering er for lille"
   },
   "privateWindowErrorTitle": {
@@ -75,17 +75,17 @@
   },
   "genericErrorTitle": {
     "message": "Hov! Noget gik helt galt med Firefox Screenshots."
   },
   "genericErrorDetails": {
     "message": "Vi er ikke sikre på, hvad der lige skete. Vil du prøve igen - eller vil du tage et skærmbillede af en anden side?"
   },
   "tourBodyOne": {
-    "message": "Tag, gem og del skærmbilleder uden at forlade Firefox."
+    "message": "Tag, gem og del skærmbilleder uden at forlade Firefox. "
   },
   "tourHeaderTwo": {
     "message": "Gem lige hvad du vil."
   },
   "tourBodyTwo": {
     "message": "Klik og træk for at tage et udklip af en del af en side. Du kan også holde markøren over for at fremhæve din markering."
   },
   "tourHeaderThree": {
@@ -107,26 +107,29 @@
     "message": "Næste side"
   },
   "tourPrevious": {
     "message": "Forrige side"
   },
   "tourDone": {
     "message": "Færdig"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Ved at anvende Firefox Screenshots godkender du $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$ for Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Ved at anvende Firefox Screenshots godkender du vores $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "vilkår"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "privatlivspolitik"
+  },
+  "libraryLabel": {
+    "message": "Skærmbilleder"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/de/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/de/messages.json
@@ -107,26 +107,29 @@
     "message": "Nächste Folie"
   },
   "tourPrevious": {
     "message": "Vorherige Folie"
   },
   "tourDone": {
     "message": "Fertig"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Durch die Verwendung von Firefox Screenshots stimmen Sie den $TERMSANDPRIVACYNOTICETERMSLINK$ und dem $TERMSANDPRIVACYNOTICEPRIVACYLINK$ von Firefox Cloud Services zu.",
+  "termsAndPrivacyNotice2": {
+    "message": "Durch die Verwendung von Firefox Screenshots stimmen Sie unseren $TERMSANDPRIVACYNOTICETERMSLINK$ und $TERMSANDPRIVACYNOTICEPRIVACYLINK$ zu.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Nutzungsbedingungen"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Datenschutzhinweis"
+  },
+  "libraryLabel": {
+    "message": "Bildschirmfotos"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/dsb/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/dsb/messages.json
@@ -107,26 +107,29 @@
     "message": "Pśiduce foto"
   },
   "tourPrevious": {
     "message": "Pjerwjejšne foto"
   },
   "tourDone": {
     "message": "Gótowo"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Pśez wužywanje Firefox ScreenShots, zwolijośo do $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Pśez wužywanje Firefox ScreenShots, zwolijośo do našych $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Screenshots.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Wuměnjenja"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Powěźeńka priwatnosći"
+  },
+  "libraryLabel": {
+    "message": "Fota wobrazowki"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/el/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/el/messages.json
@@ -107,26 +107,29 @@
     "message": "Επόμενη διαφάνεια"
   },
   "tourPrevious": {
     "message": "Προηγούμενη διαφάνεια"
   },
   "tourDone": {
     "message": "Τέλος"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Χρησιμοποιώντας το Firefox Screenshots, συμφωνείτε με τους $TERMSANDPRIVACYNOTICETERMSLINK$ και την $TERMSANDPRIVACYNOTICEPRIVACYLINK$ των Υπηρεσιών Cloud του Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Χρησιμοποιώντας το Firefox Screenshots, συμφωνείτε με τους $TERMSANDPRIVACYNOTICETERMSLINK$ και την $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Όρους"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Σημείωση απορρήτου"
+  },
+  "libraryLabel": {
+    "message": "Στιγμιότυπα"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/en_US/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/en_US/messages.json
@@ -107,26 +107,29 @@
     "message": "Next Slide"
   },
   "tourPrevious": {
     "message": "Previous Slide"
   },
   "tourDone": {
     "message": "Done"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "By using Firefox Screenshots, you agree to our $TERMSANDPRIVACYNOTICETERMSLINK$ and $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Terms"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Privacy Notice"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/eo/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/eo/messages.json
@@ -107,26 +107,29 @@
     "message": "Venonta ekrano"
   },
   "tourPrevious": {
     "message": "Antaŭa ekrano"
   },
   "tourDone": {
     "message": "Farita"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Se vi uzas Firefox Screenshots, vi akceptas nian $TERMSANDPRIVACYNOTICETERMSLINK$ kaj $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "kondiĉojn"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "rimarkon pri privateco"
+  },
+  "libraryLabel": {
+    "message": "Ekrankopioj"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/es_AR/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/es_AR/messages.json
@@ -107,26 +107,29 @@
     "message": "Próxima diapositiva"
   },
   "tourPrevious": {
     "message": "Diapositiva anterior"
   },
   "tourDone": {
     "message": "Listo"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Al usar Firefox Screenshots, aceptás los $TERMSANDPRIVACYNOTICETERMSLINK$ y $TERMSANDPRIVACYNOTICEPRIVACYLINK$ de los servicios en la nube de Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Al usar Firefox Screenshots, aceptás los $TERMSANDPRIVACYNOTICETERMSLINK$ y $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Términos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Nota de privacidad"
+  },
+  "libraryLabel": {
+    "message": "Capturas"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/es_CL/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/es_CL/messages.json
@@ -107,26 +107,29 @@
     "message": "Siguiente diapositiva"
   },
   "tourPrevious": {
     "message": "Diapositiva anterior"
   },
   "tourDone": {
     "message": "Hecho"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Al usar Firefox Screenshots, aceptas los $TERMSANDPRIVACYNOTICETERMSLINK$ y el $TERMSANDPRIVACYNOTICEPRIVACYLINK$ de Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Al usar Firefox Screenshots, aceptas nuestros $TERMSANDPRIVACYNOTICETERMSLINK$ y el $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Términos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Aviso de privacidad"
+  },
+  "libraryLabel": {
+    "message": "Capturas de pantalla"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/es_ES/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/es_ES/messages.json
@@ -107,26 +107,29 @@
     "message": "Diapositiva siguiente"
   },
   "tourPrevious": {
     "message": "Diapositiva anterior"
   },
   "tourDone": {
     "message": "Hecho"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Al usar Firefox Screenshots, aceptas los $TERMSANDPRIVACYNOTICETERMSLINK$ y el $TERMSANDPRIVACYNOTICEPRIVACYLINK$ de Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Al usar Firefox Screenshots, estás de acuerdo con nuestros $TERMSANDPRIVACYNOTICETERMSLINK$ y $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Términos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Aviso de privacidad"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/es_MX/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/es_MX/messages.json
@@ -107,26 +107,29 @@
     "message": "Siguiente diapositiva"
   },
   "tourPrevious": {
     "message": "Diapositiva anterior"
   },
   "tourDone": {
     "message": "Terminado"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Al usar Firefox Screenshots, estás de acuerdo con los servicios de Firefox Cloud $TERMSANDPRIVACYNOTICETERMSLINK$ y $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Al usar Firefox Screenshots, estás de acuerdo con nuestros $TERMSANDPRIVACYNOTICETERMSLINK$ y $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Términos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Aviso de privacidad"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/et/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/et/messages.json
@@ -107,18 +107,18 @@
     "message": "Järgmine slaid"
   },
   "tourPrevious": {
     "message": "Eelmine slaid"
   },
   "tourDone": {
     "message": "Valmis"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox Screenshots kasutamisel nõustud Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$ ja $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots'i kasutades nõustud meie $TERMSANDPRIVACYNOTICETERMSLINK$ ja $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/fa/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/fa/messages.json
@@ -107,26 +107,29 @@
     "message": "اسلاید بعدی"
   },
   "tourPrevious": {
     "message": "اسلاید قبلی"
   },
   "tourDone": {
     "message": "انجام شد"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "با استفاده از سرویس تصاویرِ صفحه فایرفاکس، شما با شرایط سرویس‌های ابری فایرفاکس $TERMSANDPRIVACYNOTICETERMSLINK$ و $TERMSANDPRIVACYNOTICEPRIVACYLINK$ موافقت می‌کنید.",
+  "termsAndPrivacyNotice2": {
+    "message": "با استفاده از سرویس تصاویر صفحه فایرفاکس، شما با $TERMSANDPRIVACYNOTICETERMSLINK$ ما و $TERMSANDPRIVACYNOTICEPRIVACYLINK$ موافقت می‌کنید.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "شرایط"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "نکات حریم‌خصوصی"
+  },
+  "libraryLabel": {
+    "message": "تصاویر صفحه"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/fi/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/fi/messages.json
@@ -107,26 +107,29 @@
     "message": "Seuraava sivu"
   },
   "tourPrevious": {
     "message": "Edellinen sivu"
   },
   "tourDone": {
     "message": "Valmis"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Käyttämällä Firefox Screenshots –ominaisuutta hyväksyt Firefoxin pilvipalveluiden $TERMSANDPRIVACYNOTICETERMSLINK$ ja $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Käyttämällä Firefox Screenshots -ominaisuutta hyväksyt meidän $TERMSANDPRIVACYNOTICETERMSLINK$ ja $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "käyttöehdot"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "tietosuojakäytännön"
+  },
+  "libraryLabel": {
+    "message": "Kuvakaappaukset"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/fr/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/fr/messages.json
@@ -107,26 +107,29 @@
     "message": "Écran suivant"
   },
   "tourPrevious": {
     "message": "Écran précédent"
   },
   "tourDone": {
     "message": "Terminé"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "En utilisant Firefox Screenshots, vous acceptez les $TERMSANDPRIVACYNOTICETERMSLINK$ et la $TERMSANDPRIVACYNOTICEPRIVACYLINK$ des services en ligne de Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "En utilisant Firefox Screenshots, vous acceptez nos $TERMSANDPRIVACYNOTICETERMSLINK$ et notre $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "mentions légales"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "politique de confidentialité"
+  },
+  "libraryLabel": {
+    "message": "Captures d’écran"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/fy_NL/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/fy_NL/messages.json
@@ -107,26 +107,29 @@
     "message": "Folgjende ôfbylding"
   },
   "tourPrevious": {
     "message": "Foarige ôfbylding"
   },
   "tourDone": {
     "message": "Dien"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Troch Firefox Screenshots te brûken, geane jo akkoard mei de $TERMSANDPRIVACYNOTICETERMSLINK$ en $TERMSANDPRIVACYNOTICEPRIVACYLINK$ fan Firefox-cloudtsjinsten.",
+  "termsAndPrivacyNotice2": {
+    "message": "Troch Firefox Screenshots te brûken, gean jo akkoard mei ús $TERMSANDPRIVACYNOTICETERMSLINK$ en $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Betingsten"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Privacyferklearring"
+  },
+  "libraryLabel": {
+    "message": "Skermôfbyldingen"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ga_IE/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ga_IE/messages.json
@@ -102,17 +102,25 @@
   },
   "tourPrevious": {
     "message": "An sleamhnán roimhe seo"
   },
   "tourDone": {
     "message": "Críochnaithe"
   },
   "termsAndPrivacyNotice2": {
-    "message": "Má úsáideann tú Gabhálacha Scáileáin Firefox, glacann tú leis na {termsAndPrivacyNoticeTermsLink} agus leis an {termsAndPrivacyNoticePrivacyLink}."
+    "message": "Má úsáideann tú Gabhálacha Scáileáin Firefox, glacann tú leis na $TERMSANDPRIVACYNOTICETERMSLINK$ agus leis an $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+    "placeholders": {
+      "termsandprivacynoticetermslink": {
+        "content": "$1"
+      },
+      "termsandprivacynoticeprivacylink": {
+        "content": "$2"
+      }
+    }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Téarmaí"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "bhFógra Príobháideachais"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/gu_IN/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/gu_IN/messages.json
@@ -107,26 +107,29 @@
     "message": "આગલી સ્લાઇડ"
   },
   "tourPrevious": {
     "message": "પહેલાની સ્લાઇડ"
   },
   "tourDone": {
     "message": "થઈ ગયું"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox સ્ક્રીનશોટ્સ વાપરીને, તમે Firefox Cloud સેવાઓ સાથે સંમત થાઓ છો $TERMSANDPRIVACYNOTICETERMSLINK$ અને $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox સ્ક્રિનશોટનો ઉપયોગ કરીને, તમે અમારી સાથે સંમત થાઓ છો $TERMSANDPRIVACYNOTICETERMSLINK$ અને $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "શરતો"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "ખાનગી સૂચના"
+  },
+  "libraryLabel": {
+    "message": "સ્ક્રીનશૉટ્સ"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/he/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/he/messages.json
@@ -102,25 +102,17 @@
   },
   "tourPrevious": {
     "message": "השקופית הקודמת"
   },
   "tourDone": {
     "message": "סיום"
   },
   "termsAndPrivacyNoticeCloudServices": {
-    "message": "מעצם השימוש ב־Firefox Screenshots הכללים של שירותי הענן של Firefox‏ $TERMSANDPRIVACYNOTICETERMSLINK$ ו$TERMSANDPRIVACYNOTICEPRIVACYLINK$ מוסכמים עליך.",
-    "placeholders": {
-      "termsandprivacynoticetermslink": {
-        "content": "$1"
-      },
-      "termsandprivacynoticeprivacylink": {
-        "content": "$2"
-      }
-    }
+    "message": "מעצם השימוש ב־Firefox Screenshots הכללים של שירותי הענן של Firefox‏ {termsAndPrivacyNoticeTermsLink} ו{termsAndPrivacyNoticePrivacyLink} מוסכמים עליך."
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "תנאים"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "הצהרת פרטיות"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/hi_IN/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/hi_IN/messages.json
@@ -107,26 +107,29 @@
     "message": "अगली स्लाइड"
   },
   "tourPrevious": {
     "message": "पिछली स्लाइड"
   },
   "tourDone": {
     "message": "पूर्ण"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox स्क्रीनशॉट का उपयोग करके, आप Firefox क्लाउड सेवाओं $TERMSANDPRIVACYNOTICETERMSLINK$ और $TERMSANDPRIVACYNOTICEPRIVACYLINK$ के लिए सहमत हैं.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox स्क्रीनशॉट्स का उपयोग करके, आप हमारी $TERMSANDPRIVACYNOTICETERMSLINK$ और $TERMSANDPRIVACYNOTICEPRIVACYLINK$ से सहमत हैं.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "शर्तें"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "गोपनीयता सूचना"
+  },
+  "libraryLabel": {
+    "message": "स्क्रीनशॉट"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/hr/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/hr/messages.json
@@ -62,16 +62,22 @@
     "message": "Ovo nije standardna Web stranica stoga ju ne možete snimiti."
   },
   "selfScreenshotErrorTitle": {
     "message": "Ne možete snimiti Firefox Screenshots stranicu!"
   },
   "emptySelectionErrorTitle": {
     "message": "Vaš odabir je premalen"
   },
+  "privateWindowErrorTitle": {
+    "message": "Snimke ekrana su onemogućene u načinu privatnog pretraživanja"
+  },
+  "privateWindowErrorDetails": {
+    "message": "Žao nam je na neugodnosti. Radimo na ovoj mogućnosti za buduća izdanja."
+  },
   "genericErrorTitle": {
     "message": "Uf! Firefox Screenshots se zbrkao."
   },
   "genericErrorDetails": {
     "message": "Nismo sigurno što se upravo dogodilo. Možete li pokušati ponovno ili snimiti drukčiju stranicu?"
   },
   "tourBodyOne": {
     "message": "Snimite, spremite i dijelite snimke bez da napuštate Firefox."
@@ -101,15 +107,29 @@
     "message": "Sljedeći slajd"
   },
   "tourPrevious": {
     "message": "Prijašnji slajd"
   },
   "tourDone": {
     "message": "Gotovo"
   },
+  "termsAndPrivacyNotice2": {
+    "message": "Koristeći Firefox Screenshots slažete se s našim $TERMSANDPRIVACYNOTICETERMSLINK$ i $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+    "placeholders": {
+      "termsandprivacynoticetermslink": {
+        "content": "$1"
+      },
+      "termsandprivacynoticeprivacylink": {
+        "content": "$2"
+      }
+    }
+  },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Uvjeti"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Pravila o privatnosti"
+  },
+  "libraryLabel": {
+    "message": "Snimke ekrana"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/hsb/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/hsb/messages.json
@@ -107,26 +107,29 @@
     "message": "Přichodne foto"
   },
   "tourPrevious": {
     "message": "Předchadne foto"
   },
   "tourDone": {
     "message": "Hotowo"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Přez wužiwanje Firefox ScreenShots, zwoliće do $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Přez wužiwanje Firefox ScreenShots, zwoliće do našich $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Screenshots.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Wuměnjenja"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Pokaz priwatnosće"
+  },
+  "libraryLabel": {
+    "message": "Fota wobrazowki"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/hu/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/hu/messages.json
@@ -107,26 +107,29 @@
     "message": "Következő dia"
   },
   "tourPrevious": {
     "message": "Előző dia"
   },
   "tourDone": {
     "message": "Kész"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "A Firefox képernyőképek használatával beleegyezik a Firefox felhőszolgáltatások $TERMSANDPRIVACYNOTICETERMSLINK$ és $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "A Firefox képernyőképek használatával, Ön beleegyezik a $TERMSANDPRIVACYNOTICETERMSLINK$ és $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Feltételekbe"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Adatvédelmi nyilatkozatba"
+  },
+  "libraryLabel": {
+    "message": "Képernyőképek"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/hy_AM/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/hy_AM/messages.json
@@ -101,18 +101,18 @@
     "message": "Հաջորդ սահիկը"
   },
   "tourPrevious": {
     "message": "Նախորդ սահիկը"
   },
   "tourDone": {
     "message": "Պատրաստ է"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Օգտագործելով Firefox Screenshots-ը՝ դուք ընդունում եք Firefox Cloud ծառայությունների $TERMSANDPRIVACYNOTICETERMSLINK$ը և $TERMSANDPRIVACYNOTICEPRIVACYLINK$ը:",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots-ը օգտագործելով՝ դուք ընդունեւմ եք $TERMSANDPRIVACYNOTICETERMSLINK$ը և $TERMSANDPRIVACYNOTICEPRIVACYLINK$ը:",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/id/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/id/messages.json
@@ -107,26 +107,29 @@
     "message": "Salindia Selanjutnya"
   },
   "tourPrevious": {
     "message": "Salindia Sebelumnya"
   },
   "tourDone": {
     "message": "Selesai"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Dengan menggunakan Firefox Screenshots, Anda setuju dengan $TERMSANDPRIVACYNOTICETERMSLINK$ dan $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Dengan menggunakan Firefox Screenshots, Anda setuju dengan $TERMSANDPRIVACYNOTICETERMSLINK$ dan $TERMSANDPRIVACYNOTICEPRIVACYLINK$ kami.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Ketentuan"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Kebijakan Privasi"
+  },
+  "libraryLabel": {
+    "message": "Tangkapan Layar"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/it/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/it/messages.json
@@ -107,26 +107,29 @@
     "message": "Schermata successiva"
   },
   "tourPrevious": {
     "message": "Schermata precedente"
   },
   "tourDone": {
     "message": "Fine"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Utilizzando Firefox Screenshots si accettano le $TERMSANDPRIVACYNOTICETERMSLINK$ e l’$TERMSANDPRIVACYNOTICEPRIVACYLINK$ di Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Utilizzando Firefox Screenshots si accettano le $TERMSANDPRIVACYNOTICETERMSLINK$ e l’$TERMSANDPRIVACYNOTICEPRIVACYLINK$ del servizio.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "condizioni di utilizzo"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "informativa sulla privacy"
+  },
+  "libraryLabel": {
+    "message": "Screenshot"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ja/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ja/messages.json
@@ -107,26 +107,29 @@
     "message": "次のスライド"
   },
   "tourPrevious": {
     "message": "前のスライド"
   },
   "tourDone": {
     "message": "完了"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox Screenshots を使うことで、あなたは Firefox Cloud Services の $TERMSANDPRIVACYNOTICETERMSLINK$ と $TERMSANDPRIVACYNOTICEPRIVACYLINK$ に同意したことになります。",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots を使うことで、あなたは $TERMSANDPRIVACYNOTICETERMSLINK$ と $TERMSANDPRIVACYNOTICEPRIVACYLINK$ に同意したことになります。",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "利用規約"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "プライバシー通知"
+  },
+  "libraryLabel": {
+    "message": "スクリーンショット"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ka/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ka/messages.json
@@ -107,26 +107,29 @@
     "message": "შემდეგი"
   },
   "tourPrevious": {
     "message": "წინა"
   },
   "tourDone": {
     "message": "მზადაა"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Firefox Screenshots-ის გამოყენებით, თქვენ ეთანხმებით $TERMSANDPRIVACYNOTICETERMSLINK$ და $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "პირობებს"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "პირადი მონაცემების შესახებ განცხადებას"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/kab/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/kab/messages.json
@@ -107,26 +107,29 @@
     "message": "Tigri n zdat"
   },
   "tourPrevious": {
     "message": "Tigri n deffir"
   },
   "tourDone": {
     "message": "Immed"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "S useqdec n Firefox Screenshots, ad tqebleḍ tiwuriwin n usigna Firefox $TERMSANDPRIVACYNOTICETERMSLINK$ akked $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "S useqdec Firefox Screenshots, ad tqebleḍ $TERMSANDPRIVACYNOTICETERMSLINK$ akked $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Tiwtilin"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Tasertit n tbaḍnit"
+  },
+  "libraryLabel": {
+    "message": "Tuṭṭfiwin n ugdil"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/kk/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/kk/messages.json
@@ -107,18 +107,18 @@
     "message": "Келесі слайд"
   },
   "tourPrevious": {
     "message": "Алдыңғы слайд"
   },
   "tourDone": {
     "message": "Дайын"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox скриншоттарын қолдану арқылы, сіз Firefox бұлттық қызметтерінің $TERMSANDPRIVACYNOTICETERMSLINK$ және $TERMSANDPRIVACYNOTICEPRIVACYLINK$ келісесіз.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox скриншоттарын қолдану арқылы, сіз біздің $TERMSANDPRIVACYNOTICETERMSLINK$ және $TERMSANDPRIVACYNOTICEPRIVACYLINK$ келісесіз.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/ko/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ko/messages.json
@@ -107,26 +107,29 @@
     "message": "다음 슬라이드"
   },
   "tourPrevious": {
     "message": "이전 슬라이드"
   },
   "tourDone": {
     "message": "완료"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox Screenshots을 사용함으로써, Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$과 $TERMSANDPRIVACYNOTICEPRIVACYLINK$에 동의하게 됩니다.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots을 사용함으로써, $TERMSANDPRIVACYNOTICETERMSLINK$과 $TERMSANDPRIVACYNOTICEPRIVACYLINK$에 동의하게 됩니다.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "이용약관"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "개인 정보 취급 방침"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/lij/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/lij/messages.json
@@ -99,25 +99,17 @@
   },
   "tourPrevious": {
     "message": "Schermâ de primma"
   },
   "tourDone": {
     "message": "Fæto"
   },
   "termsAndPrivacyNoticeCloudServices": {
-    "message": "Se ti deuvi Firefox Screenshots, ti e d'acordio con $TERMSANDPRIVACYNOTICETERMSLINK$ e $TERMSANDPRIVACYNOTICEPRIVACYLINK$ de Firefox Cloud Services.",
-    "placeholders": {
-      "termsandprivacynoticetermslink": {
-        "content": "$1"
-      },
-      "termsandprivacynoticeprivacylink": {
-        "content": "$2"
-      }
-    }
+    "message": "Se ti deuvi Firefox Screenshots, ti e d'acordio con {termsAndPrivacyNoticeTermsLink} e {termsAndPrivacyNoticePrivacyLink} de Firefox Cloud Services."
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Termini"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Informativa in sciâ privacy"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/lo/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/lo/messages.json
@@ -101,18 +101,18 @@
     "message": "ສະໄລດ໌ຕໍ່ໄປ"
   },
   "tourPrevious": {
     "message": "ສະໄລດ໌ກ່ອນຫນ້ານີ້"
   },
   "tourDone": {
     "message": "ສຳເລັດ"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "ການນຳໃຊ້ Firefox Screenshots ແມ່ນທ່ານໄດ້ຍອມຮັບເງືອນໄຂການໃຫ້ບໍລິການຂອງ Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$ ແລະ $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "ເພື່ອນຳໃຊ້ Firefox Screenshots ທ່ານໄດ້ຍອມຮັບ $TERMSANDPRIVACYNOTICETERMSLINK$ ແລະ $TERMSANDPRIVACYNOTICEPRIVACYLINK$ ຂອງພວກເຮົາ.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/lt/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/lt/messages.json
@@ -107,26 +107,29 @@
     "message": "Kita skaidrė"
   },
   "tourPrevious": {
     "message": "Buvusi skaidrė"
   },
   "tourDone": {
     "message": "Baigta"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Naudodami „Firefox Screenshots“ sutinkate su „Firefox“ tinklo paslaugų $TERMSANDPRIVACYNOTICETERMSLINK$ bei $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Naudodamiesi „Firefox“ ekrano nuotraukomis, sutinkate su mūsų $TERMSANDPRIVACYNOTICETERMSLINK$ bei $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "sąlygomis"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "privatumo nuostatais"
+  },
+  "libraryLabel": {
+    "message": "Ekrano nuotraukos"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/mk/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/mk/messages.json
@@ -107,26 +107,29 @@
     "message": "Следен слајд"
   },
   "tourPrevious": {
     "message": "Претходен слајд"
   },
   "tourDone": {
     "message": "Готово"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Со користење на Firefox Screenshots, се согласувате со нашите $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Услови"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Известување за приватност"
+  },
+  "libraryLabel": {
+    "message": "Слики од екран"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/mr/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/mr/messages.json
@@ -107,26 +107,29 @@
     "message": "पुढील स्लाइड"
   },
   "tourPrevious": {
     "message": "मागची स्लाइड"
   },
   "tourDone": {
     "message": "झाले"
   },
-  "termsAndPrivacyNoticeCloudServices": {
+  "termsAndPrivacyNotice2": {
     "message": "Firefox Screenshots वापरून, आपण आमच्या $TERMSANDPRIVACYNOTICETERMSLINK$आणि $TERMSANDPRIVACYNOTICEPRIVACYLINK$ शी सहमत आहात.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "अटी"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "गोपनीयता सूचना"
+  },
+  "libraryLabel": {
+    "message": "स्क्रीनशॉट"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ms/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ms/messages.json
@@ -107,26 +107,29 @@
     "message": "Slaid Seterusnya"
   },
   "tourPrevious": {
     "message": "Slaid Sebelumnya"
   },
   "tourDone": {
     "message": "Selesai"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Dengan menggunakan Firefox Screenshots, anda bersetuju dengan $TERMSANDPRIVACYNOTICETERMSLINK$ dan $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Apabila menggunakan Firefox Screenshots, anda bersetuju dengan $TERMSANDPRIVACYNOTICETERMSLINK$ dan $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Terma"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Notis Privasi"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/nb_NO/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/nb_NO/messages.json
@@ -107,26 +107,29 @@
     "message": "Neste slide"
   },
   "tourPrevious": {
     "message": "Forrige slide"
   },
   "tourDone": {
     "message": "Ferdig"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Ved å bruke Firefox Screenshots, godtar du $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$ for Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Ved å bruke Firefox Screenshots, godtar du vår $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "vilkår"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "personvernbestemmelser"
+  },
+  "libraryLabel": {
+    "message": "Skjermbilder"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/nl/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/nl/messages.json
@@ -107,26 +107,29 @@
     "message": "Volgende slide"
   },
   "tourPrevious": {
     "message": "Vorige slide"
   },
   "tourDone": {
     "message": "Gereed"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Door Firefox Screenshots te gebruiken, gaat u akkoord met de $TERMSANDPRIVACYNOTICETERMSLINK$ en $TERMSANDPRIVACYNOTICEPRIVACYLINK$ van Firefox-cloudservices.",
+  "termsAndPrivacyNotice2": {
+    "message": "Door Firefox Screenshots te gebruiken, gaat u akkoord met onze $TERMSANDPRIVACYNOTICETERMSLINK$ en $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Voorwaarden"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Privacyverklaring"
+  },
+  "libraryLabel": {
+    "message": "Schermafbeeldingen"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/nn_NO/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/nn_NO/messages.json
@@ -107,26 +107,29 @@
     "message": "Neste slide"
   },
   "tourPrevious": {
     "message": "Føregåande slide"
   },
   "tourDone": {
     "message": "Ferdig"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Ved å bruke Firefox Screenshots, godtar du $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$ for Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Ved å bruke Firefox Screenshots, seier du deg samd i $TERMSANDPRIVACYNOTICETERMSLINK$ og $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Vilkår"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Personvernmerknad"
+  },
+  "libraryLabel": {
+    "message": "Skjermbilde"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/pl/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/pl/messages.json
@@ -107,26 +107,29 @@
     "message": "Dalej"
   },
   "tourPrevious": {
     "message": "Wstecz"
   },
   "tourDone": {
     "message": "Zamknij"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Używając Firefox Screenshots, zgadzasz się na $TERMSANDPRIVACYNOTICETERMSLINK$ i $TERMSANDPRIVACYNOTICEPRIVACYLINK$ usług Firefox Cloud.",
+  "termsAndPrivacyNotice2": {
+    "message": "Używając Firefox Screenshots, zgadzasz się na $TERMSANDPRIVACYNOTICETERMSLINK$ i $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "warunki korzystania"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "zasady ochrony prywatności"
+  },
+  "libraryLabel": {
+    "message": "Zrzuty ekranu"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/pt_BR/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/pt_BR/messages.json
@@ -107,26 +107,29 @@
     "message": "Próximo slide"
   },
   "tourPrevious": {
     "message": "Slide anterior"
   },
   "tourDone": {
     "message": "Concluído"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Usando o Firefox Screenshots, você concorda com os $TERMSANDPRIVACYNOTICETERMSLINK$ e $TERMSANDPRIVACYNOTICEPRIVACYLINK$ dos serviços na nuvem do Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Ao usar o Firefox Screenshots, você concorda com os $TERMSANDPRIVACYNOTICETERMSLINK$ e $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Termos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Política de privacidade"
+  },
+  "libraryLabel": {
+    "message": "Screenshots"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/pt_PT/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/pt_PT/messages.json
@@ -107,26 +107,29 @@
     "message": "Diapositivo seguinte"
   },
   "tourPrevious": {
     "message": "Diapositivo anterior"
   },
   "tourDone": {
     "message": "Feito"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Ao utilizar o Firefox Screenshots, você concorda com os $TERMSANDPRIVACYNOTICETERMSLINK$ e com o $TERMSANDPRIVACYNOTICEPRIVACYLINK$ do Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Ao utilizar o Firefox Screenshots, concorda com os nossos $TERMSANDPRIVACYNOTICETERMSLINK$ e com o $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Termos"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Aviso de privacidade"
+  },
+  "libraryLabel": {
+    "message": "Capturas de ecrã"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/rm/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/rm/messages.json
@@ -62,16 +62,22 @@
     "message": "Quai n'è betg ina pagina web da standard, perquai n'èsi betg pussaivel da far in maletg da visur dad ella."
   },
   "selfScreenshotErrorTitle": {
     "message": "Impussibel da far in maletg da visur dad ina pagina da Firefox Screenshots."
   },
   "emptySelectionErrorTitle": {
     "message": "La zona selecziunada è memia pitschna"
   },
+  "privateWindowErrorTitle": {
+    "message": "Screenshots è deactivà en il modus privat"
+  },
+  "privateWindowErrorDetails": {
+    "message": "Perstgisa las malempernaivladads. Nus furnin questa funcziun en ina da las proximas versiuns."
+  },
   "genericErrorTitle": {
     "message": "Oh dieu! Firefox Screenshots ha il singlut."
   },
   "genericErrorDetails": {
     "message": "Nus na savain betg tge ch'è gist capità. Vuls empruvar anc ina giada, forsa cun in'autra pagina?"
   },
   "tourBodyOne": {
     "message": "Far, memorisar e cundivider maletgs da visur senza bandunar Firefox."
@@ -101,26 +107,29 @@
     "message": "Proxim pass"
   },
   "tourPrevious": {
     "message": "Ultim pass"
   },
   "tourDone": {
     "message": "Finì"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Cun utilisar Firefox Screenshots accepteschas ti $TERMSANDPRIVACYNOTICETERMSLINK$ e $TERMSANDPRIVACYNOTICEPRIVACYLINK$ da Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Cun utilisar Firefox Screenshots acceptas ti $TERMSANDPRIVACYNOTICETERMSLINK$ e $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "las cundiziuns d'utilisaziun"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "la decleraziun da protecziun da datas"
+  },
+  "libraryLabel": {
+    "message": "Maletgs dal visur"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ru/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ru/messages.json
@@ -107,26 +107,29 @@
     "message": "Следующий слайд"
   },
   "tourPrevious": {
     "message": "Предыдущий слайд"
   },
   "tourDone": {
     "message": "Готово"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Используя Скриншоты Firefox, вы соглашаетесь с $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$ облачных сервисов Firefox.",
+  "termsAndPrivacyNotice2": {
+    "message": "Используя Firefox Screenshots, вы соглашаетесь с нашими $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Условиями использования"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Уведомлением о приватности"
+  },
+  "libraryLabel": {
+    "message": "Скриншоты"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/sk/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/sk/messages.json
@@ -107,26 +107,29 @@
     "message": "Ďalšia snímka"
   },
   "tourPrevious": {
     "message": "Predchádzajúca snímka"
   },
   "tourDone": {
     "message": "Hotovo"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Používaním služby Firefox Screenshots súhlasíte s $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$ Firefox Cloud Services.",
+  "termsAndPrivacyNotice2": {
+    "message": "Používaním služby Firefox Screenshots súhlasíte s našimi $TERMSANDPRIVACYNOTICETERMSLINK$ a $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "podmienkami"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "zásadami ochrany súkromia"
+  },
+  "libraryLabel": {
+    "message": "Snímky obrazovky"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/sl/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/sl/messages.json
@@ -107,26 +107,29 @@
     "message": "Naslednji diapozitiv"
   },
   "tourPrevious": {
     "message": "Prejšnji diapozitiv"
   },
   "tourDone": {
     "message": "Končano"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Z uporabo Firefox Screenshots se strinjate s $TERMSANDPRIVACYNOTICETERMSLINK$ Firefoxovih storitev v oblaku in $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Z uporabo razširitve Firefox Screenshots se strinjate z našimi $TERMSANDPRIVACYNOTICETERMSLINK$ in $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "pogoji"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "obvestilom o zasebnosti"
+  },
+  "libraryLabel": {
+    "message": "Posnetki zaslona"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/sr/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/sr/messages.json
@@ -107,18 +107,18 @@
     "message": "Следећи слајд"
   },
   "tourPrevious": {
     "message": "Претходни слајд"
   },
   "tourDone": {
     "message": "Готово"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Коришћењем Firefox Screenshots-а, прихватате Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Коришћењем Firefox Screenshots прихватате наше $TERMSANDPRIVACYNOTICETERMSLINK$ и $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
--- a/browser/extensions/screenshots/webextension/_locales/sv_SE/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/sv_SE/messages.json
@@ -107,26 +107,29 @@
     "message": "Nästa sida"
   },
   "tourPrevious": {
     "message": "Föregående sida"
   },
   "tourDone": {
     "message": "Färdig"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Genom att använda Firefox Screenshots, godkänner du $TERMSANDPRIVACYNOTICETERMSLINK$ och $TERMSANDPRIVACYNOTICEPRIVACYLINK$ för Firefox molntjänster.",
+  "termsAndPrivacyNotice2": {
+    "message": "Genom att använda Firefox Screenshots, godkänner du $TERMSANDPRIVACYNOTICETERMSLINK$ och $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Villkor"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Sekretesspolicy"
+  },
+  "libraryLabel": {
+    "message": "Skärmbilder"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ta/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ta/messages.json
@@ -69,17 +69,17 @@
   },
   "privateWindowErrorTitle": {
     "message": "அந்தரங்க உலாவல் முறையில் திரைபிடிப்பு முடக்கப்பட்டுள்ளது"
   },
   "privateWindowErrorDetails": {
     "message": "சிரமத்திற்கு வருந்துகிறோம். எதிர்கால வெளியீடுகளில் நாங்கள் இந்த வசதியைச் செய்து தருகிறோம்."
   },
   "genericErrorTitle": {
-    "message": "அய் அய்யோ! பயர்பாஃசு திரைப்பிடிப்பு வீணாய் போனது."
+    "message": "அய் அய்யோ! பயர்பாஃசு திரைப்பிடிப்பு வீணாய் போனது. "
   },
   "genericErrorDetails": {
     "message": "என்ன நடந்தது என எங்களுக்குத் தெரியவில்லை. முடிந்தால் மீண்டும் முயற்சியுங்கள் (அ) வேறொரு பக்கத்தில் முயற்சியுங்கள்?"
   },
   "tourBodyOne": {
     "message": "பயர்பாஃசை விட்டு வெளியேறாமல் திரைப்பிடிப்புகளை எடுக்கலாம், சேமித்து மற்றவருடன் பகிர்ந்துக்கொள்ளலாம்."
   },
   "tourHeaderTwo": {
@@ -107,26 +107,29 @@
     "message": "அடுத்த வில்லை"
   },
   "tourPrevious": {
     "message": "முந்தைய வில்லை"
   },
   "tourDone": {
     "message": "முடிந்தது"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "பயர்பாஃசு திரைப்பிடிப்பைப் பயன்படுத்துவதன் மூலம் எங்களின் முகில் கணிமச் சேவைகளுக்கான பின்வரும் $TERMSANDPRIVACYNOTICETERMSLINK$ $TERMSANDPRIVACYNOTICEPRIVACYLINK$ நிபந்தனைகளை ஏற்றுக் கொள்கிறீர்கள்.",
+  "termsAndPrivacyNotice2": {
+    "message": "பயர்பாஃசு திரைப்பிடிப்புகளைப் பயன்படுத்துவதன் மூலம் $TERMSANDPRIVACYNOTICETERMSLINK$ மற்றும் $TERMSANDPRIVACYNOTICEPRIVACYLINK$ சேவை நிபற்தனைகளை ஏற்கிறீர்கள்.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "நிபந்தனைகள்"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "தனியுரிம கொள்கை"
+  },
+  "libraryLabel": {
+    "message": "திரைபிடிப்புகள்"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/te/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/te/messages.json
@@ -71,10 +71,13 @@
   "tourDone": {
     "message": "పూర్తయింది"
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "నియమాలు"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "గోప్యతా నోటీసు"
+  },
+  "libraryLabel": {
+    "message": "తెరపట్లు"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/th/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/th/messages.json
@@ -107,26 +107,29 @@
     "message": "ภาพนิ่งถัดไป"
   },
   "tourPrevious": {
     "message": "ภาพนิ่งก่อนหน้า"
   },
   "tourDone": {
     "message": "เสร็จสิ้น"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "เพื่อใช้ Firefox Screenshots คุณยอมรับ $TERMSANDPRIVACYNOTICETERMSLINK$ และ $TERMSANDPRIVACYNOTICEPRIVACYLINK$ ของบริการกลุ่มเมฆ Firefox",
+  "termsAndPrivacyNotice2": {
+    "message": "เพื่อใช้ Firefox Screenshots คุณยอมรับ $TERMSANDPRIVACYNOTICETERMSLINK$ และ $TERMSANDPRIVACYNOTICEPRIVACYLINK$ ของเรา",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "ข้อกำหนด"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "ประกาศความเป็นส่วนตัว"
+  },
+  "libraryLabel": {
+    "message": "ภาพหน้าจอ"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/tl/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/tl/messages.json
@@ -99,25 +99,17 @@
   },
   "tourPrevious": {
     "message": "Nakaraan na Slide"
   },
   "tourDone": {
     "message": "Tapos"
   },
   "termsAndPrivacyNoticeCloudServices": {
-    "message": "Sa paggamit ng Firefox Screenshots, tinatanggap mo ang Firefox Cloud Services $TERMSANDPRIVACYNOTICETERMSLINK$ at $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
-    "placeholders": {
-      "termsandprivacynoticetermslink": {
-        "content": "$1"
-      },
-      "termsandprivacynoticeprivacylink": {
-        "content": "$2"
-      }
-    }
+    "message": "Sa paggamit ng Firefox Screenshots, tinatanggap mo ang Firefox Cloud Services {termsAndPrivacyNoticeTermsLink} at {termsAndPrivacyNoticePrivacyLink}."
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Mga tuntunin"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Abiso sa Privacy"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/tr/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/tr/messages.json
@@ -107,26 +107,29 @@
     "message": "Sonraki slayt"
   },
   "tourPrevious": {
     "message": "Önceki slayt"
   },
   "tourDone": {
     "message": "Tamam"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Firefox Screenshots'ı kullandığınızda Firefox Bulut Hizmetleri'nin $TERMSANDPRIVACYNOTICETERMSLINK$ ve $TERMSANDPRIVACYNOTICEPRIVACYLINK$ kabul etmiş sayılırsınız.",
+  "termsAndPrivacyNotice2": {
+    "message": "Firefox Screenshots'ı kullandığınızda $TERMSANDPRIVACYNOTICETERMSLINK$ ve $TERMSANDPRIVACYNOTICEPRIVACYLINK$ kabul etmiş sayılırsınız.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Koşullarımızı"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Gizlilik Bildirimimizi"
+  },
+  "libraryLabel": {
+    "message": "Ekran görüntüleri"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/uk/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/uk/messages.json
@@ -107,26 +107,29 @@
     "message": "Наступний слайд"
   },
   "tourPrevious": {
     "message": "Попередній слайд"
   },
   "tourDone": {
     "message": "Готово"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "Використовуючи Firefox Screenshots, ви погоджуєтеся з умовами хмарних послуг Firefox: $TERMSANDPRIVACYNOTICETERMSLINK$ та $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
+  "termsAndPrivacyNotice2": {
+    "message": "Використовуючи Firefox Screenshots, ви погоджуєтеся з нашими $TERMSANDPRIVACYNOTICETERMSLINK$ та $TERMSANDPRIVACYNOTICEPRIVACYLINK$.",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Умовами використання"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Повідомленням про приватність"
+  },
+  "libraryLabel": {
+    "message": "Знімки екрану"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/ur/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/ur/messages.json
@@ -101,26 +101,15 @@
     "message": "اگلى سلائيڈ"
   },
   "tourPrevious": {
     "message": "پچھلی سلائڈ"
   },
   "tourDone": {
     "message": "ہوگیا"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "۔Firefox Screenshots کے استعمال کے ساتھ  آپ Firefox Cloud Services کے $TERMSANDPRIVACYNOTICETERMSLINK$ اور $TERMSANDPRIVACYNOTICEPRIVACYLINK$ کے ساتھ متفق ہیں۔",
-    "placeholders": {
-      "termsandprivacynoticetermslink": {
-        "content": "$1"
-      },
-      "termsandprivacynoticeprivacylink": {
-        "content": "$2"
-      }
-    }
-  },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "شرائط"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "اطلاع نامہ نجی نوعیت"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/vi/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/vi/messages.json
@@ -41,10 +41,13 @@
   "tourDone": {
     "message": "Xong"
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "Điều khoản"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "Chính sách riêng tư"
+  },
+  "libraryLabel": {
+    "message": "Các ảnh chụp màn hình"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/zh_CN/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/zh_CN/messages.json
@@ -7,17 +7,17 @@
   },
   "contextMenuLabel": {
     "message": "截图"
   },
   "myShotsLink": {
     "message": "我的截图"
   },
   "screenshotInstructions": {
-    "message": "在页面上拖拽或单击即可选择要截图的区域。按 ESC 键可取消。"
+    "message": "在此页上拖拽或单击选择截图区域。按 ESC 键取消截图。"
   },
   "saveScreenshotSelectedArea": {
     "message": "保存"
   },
   "saveScreenshotVisibleArea": {
     "message": "截取可见范围"
   },
   "saveScreenshotFullPage": {
@@ -107,26 +107,29 @@
     "message": "下一页"
   },
   "tourPrevious": {
     "message": "上一页"
   },
   "tourDone": {
     "message": "完成"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "使用 Firefox Screenshots 即代表您同意 Firefox 云服务的$TERMSANDPRIVACYNOTICETERMSLINK$和$TERMSANDPRIVACYNOTICEPRIVACYLINK$。",
+  "termsAndPrivacyNotice2": {
+    "message": "使用 Firefox Screenshots 即代表您同意我们的$TERMSANDPRIVACYNOTICETERMSLINK$和$TERMSANDPRIVACYNOTICEPRIVACYLINK$。",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "条款"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "隐私声明"
+  },
+  "libraryLabel": {
+    "message": "屏幕截图"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/_locales/zh_TW/messages.json
+++ b/browser/extensions/screenshots/webextension/_locales/zh_TW/messages.json
@@ -107,26 +107,29 @@
     "message": "下一頁"
   },
   "tourPrevious": {
     "message": "上一頁"
   },
   "tourDone": {
     "message": "完成"
   },
-  "termsAndPrivacyNoticeCloudServices": {
-    "message": "繼續使用 Firefox Screenshots,代表您同意 Firefox 雲端服務的 $TERMSANDPRIVACYNOTICETERMSLINK$ 以及 $TERMSANDPRIVACYNOTICEPRIVACYLINK$。",
+  "termsAndPrivacyNotice2": {
+    "message": "使用 Firefox Screenshots 時,代表您同意我們的 $TERMSANDPRIVACYNOTICETERMSLINK$ 及 $TERMSANDPRIVACYNOTICEPRIVACYLINK$。",
     "placeholders": {
       "termsandprivacynoticetermslink": {
         "content": "$1"
       },
       "termsandprivacynoticeprivacylink": {
         "content": "$2"
       }
     }
   },
   "termsAndPrivacyNoticeTermsLink": {
     "message": "使用條款"
   },
   "termsAndPrivacyNoticyPrivacyLink": {
     "message": "隱私權保護政策"
+  },
+  "libraryLabel": {
+    "message": "擷圖"
   }
 }
\ No newline at end of file
--- a/browser/extensions/screenshots/webextension/background/deviceInfo.js
+++ b/browser/extensions/screenshots/webextension/background/deviceInfo.js
@@ -6,19 +6,19 @@ this.deviceInfo = (function() {
   let manifest = browser.runtime.getManifest();
 
   let platformInfo = {};
   catcher.watchPromise(browser.runtime.getPlatformInfo().then((info) => {
     platformInfo = info;
   }));
 
   return function deviceInfo() {
-    let match = navigator.userAgent.match(/Chrom(?:e|ium)\/([0-9\.]{1,1000})/);
+    let match = navigator.userAgent.match(/Chrom(?:e|ium)\/([0-9.]{1,1000})/);
     let chromeVersion = match ? match[1] : null;
-    match = navigator.userAgent.match(/Firefox\/([0-9\.]{1,1000})/);
+    match = navigator.userAgent.match(/Firefox\/([0-9.]{1,1000})/);
     let firefoxVersion = match ? match[1] : null;
     let appName = chromeVersion ? "chrome" : "firefox";
 
     return {
       addonVersion: manifest.version,
       platform: platformInfo.os,
       architecture: platformInfo.arch,
       version: firefoxVersion || chromeVersion,
--- a/browser/extensions/screenshots/webextension/background/main.js
+++ b/browser/extensions/screenshots/webextension/background/main.js
@@ -1,9 +1,9 @@
-/* globals selectorLoader, analytics, communication, catcher, log, makeUuid, auth, senderror */
+/* globals selectorLoader, analytics, communication, catcher, log, makeUuid, auth, senderror, startBackground */
 
 "use strict";
 
 this.main = (function() {
   let exports = {};
 
   const pasteSymbol = (window.navigator.platform.match(/Mac/i)) ? "\u2318" : "Ctrl";
   const { sendEvent } = analytics;
@@ -13,19 +13,26 @@ this.main = (function() {
 
   let hasSeenOnboarding;
 
   browser.storage.local.get(["hasSeenOnboarding"]).then((result) => {
     hasSeenOnboarding = !!result.hasSeenOnboarding;
     if (!hasSeenOnboarding) {
       setIconActive(false, null);
       // Note that the branded name 'Firefox Screenshots' is not localized:
-      browser.browserAction.setTitle({
-        title: "Firefox Screenshots"
-      });
+      if (!startBackground.usePhotonPageAction) {
+        browser.browserAction.setTitle({
+          title: "Firefox Screenshots"
+        });
+      } else {
+        startBackground.photonPageActionPort.postMessage({
+          type: "setProperties",
+          title: "Firefox Screenshots"
+        });
+      }
     }
   }).catch((error) => {
     log.error("Error getting hasSeenOnboarding:", error);
   });
 
   exports.setBackend = function(newBackend) {
     backend = newBackend;
     backend = backend.replace(/\/*$/, "");
@@ -50,24 +57,31 @@ this.main = (function() {
     }
   }
 
   function setIconActive(active, tabId) {
     let path = active ? "icons/icon-highlight-32-v2.svg" : "icons/icon-32-v2.svg";
     if ((!hasSeenOnboarding) && !active) {
       path = "icons/icon-starred-32-v2.svg";
     }
-    browser.browserAction.setIcon({path, tabId}).catch((error) => {
-      // FIXME: use errorCode
-      if (error.message && /Invalid tab ID/.test(error.message)) {
-        // This is a normal exception that we can ignore
-      } else {
-        catcher.unhandled(error);
-      }
-    });
+    if (!startBackground.usePhotonPageAction) {
+      browser.browserAction.setIcon({path, tabId}).catch((error) => {
+        // FIXME: use errorCode
+        if (error.message && /Invalid tab ID/.test(error.message)) {
+          // This is a normal exception that we can ignore
+        } else {
+          catcher.unhandled(error);
+        }
+      });
+    } else {
+      startBackground.photonPageActionPort.postMessage({
+        type: "setProperties",
+        iconPath: path
+      });
+    }
   }
 
   function toggleSelector(tab) {
     return analytics.refreshTelemetryPref()
       .then(() => selectorLoader.toggle(tab.id, hasSeenOnboarding))
       .then(active => {
         setIconActive(active, tab.id);
         return active;
@@ -89,20 +103,21 @@ this.main = (function() {
         sendEvent("start-shot", "site-request");
         setIconActive(true, tab.id);
         selectorLoader.toggle(tab.id, false);
       }
     });
   }
 
   function shouldOpenMyShots(url) {
-    return /^about:(?:newtab|blank)/i.test(url) || /^resource:\/\/activity-streams\//i.test(url);
+    return /^about:(?:newtab|blank|home)/i.test(url) || /^resource:\/\/activity-streams\//i.test(url);
   }
 
   // This is called by startBackground.js, directly in response to browser.browserAction.onClicked
+  // and clicks on the Photon page action
   exports.onClicked = catcher.watchFunction((tab) => {
     if (tab.incognito) {
       senderror.showError({
         popupMessage: "PRIVATE_WINDOW"
       });
       return;
     }
     if (shouldOpenMyShots(tab.url)) {
@@ -269,19 +284,26 @@ this.main = (function() {
       });
     }
   }));
 
   communication.register("hasSeenOnboarding", () => {
     hasSeenOnboarding = true;
     catcher.watchPromise(browser.storage.local.set({hasSeenOnboarding}));
     setIconActive(false, null);
-    browser.browserAction.setTitle({
-      title: browser.i18n.getMessage("contextMenuLabel")
-    });
+    if (!startBackground.usePhotonPageAction) {
+      browser.browserAction.setTitle({
+        title: browser.i18n.getMessage("contextMenuLabel")
+      });
+    } else {
+      startBackground.photonPageActionPort.postMessage({
+        type: "setProperties",
+        title: browser.i18n.getMessage("contextMenuLabel")
+      });
+    }
   });
 
   communication.register("abortFrameset", () => {
     sendEvent("abort-start-shot", "frame-page");
     // Note, we only show the error but don't report it, as we know that we can't
     // take shots of these pages:
     senderror.showError({
       popupMessage: "UNSHOOTABLE_PAGE"
--- a/browser/extensions/screenshots/webextension/background/startBackground.js
+++ b/browser/extensions/screenshots/webextension/background/startBackground.js
@@ -1,18 +1,21 @@
 /* globals browser, main, communication */
 /* This file handles:
      browser.browserAction.onClicked
+     clicks on the Photon page action
      browser.contextMenus.onClicked
      browser.runtime.onMessage
    and loads the rest of the background page in response to those events, forwarding
    the events to main.onClicked, main.onClickedContextMenu, or communication.onMessage
 */
 
 this.startBackground = (function() {
+  let exports = {};
+
   const backgroundScripts = [
     "log.js",
     "makeUuid.js",
     "catcher.js",
     "background/selectorLoader.js",
     "background/communication.js",
     "background/auth.js",
     "background/senderror.js",
@@ -47,35 +50,50 @@ this.startBackground = (function() {
       main.onClickedContextMenu(info, tab);
     }).catch((error) => {
       console.error("Error loading Screenshots:", error);
     });
   });
 
   // Note this duplicates functionality in main.js, but we need to change
   // the onboarding icon before main.js loads up
+  let iconPath = null;
   browser.storage.local.get(["hasSeenOnboarding"]).then((result) => {
     let hasSeenOnboarding = !!result.hasSeenOnboarding;
     if (!hasSeenOnboarding) {
       let path = "icons/icon-starred-32-v2.svg";
-      browser.browserAction.setIcon({path});
+      if (!usePhotonPageAction) {
+        browser.browserAction.setIcon({path});
+      } else {
+        iconPath = path;
+        if (photonPageActionPort) {
+          photonPageActionPort.postMessage({
+            type: "setProperties",
+            iconPath
+          });
+        }
+      }
     }
   }).catch((error) => {
     console.error("Error loading Screenshots onboarding flag:", error);
   });
 
   browser.runtime.onMessage.addListener((req, sender, sendResponse) => {
     loadIfNecessary().then(() => {
       return communication.onMessage(req, sender, sendResponse);
     }).catch((error) => {
       console.error("Error loading Screenshots:", error);
     });
     return true;
   });
 
+  let usePhotonPageAction = false;
+  let photonPageActionPort = null;
+  initPhotonPageAction();
+
   // We delay this check (by CHECK_MIGRATION_DELAY) just to avoid piling too
   // many things onto browser/add-on startup
   requestIdleCallback(() => {
     browser.runtime.sendMessage({funcName: "getOldDeviceInfo"}).then((result) => {
       if (result && result.type == "success" && result.value) {
         // There is a possible migration to run, so we'll load the entire background
         // page and continue the process
         return loadIfNecessary();
@@ -117,9 +135,55 @@ this.startBackground = (function() {
           };
           document.head.appendChild(tag);
         });
       });
     });
     return loadedPromise;
   }
 
+  function initPhotonPageAction() {
+    // Set up this side of the Photon page action port.  The other side is in
+    // bootstrap.js.  Ideally, in the future, WebExtension page actions and
+    // Photon page actions would be one in the same, but they aren't right now.
+    photonPageActionPort = browser.runtime.connect({ name: "photonPageActionPort" });
+    photonPageActionPort.onMessage.addListener((message) => {
+      switch (message.type) {
+      case "setUsePhotonPageAction":
+        usePhotonPageAction = message.value;
+        break;
+      case "click":
+        loadIfNecessary().then(() => {
+          main.onClicked(message.tab);
+        }).catch((error) => {
+          console.error("Error loading Screenshots:", error);
+        });
+        break;
+      default:
+        console.error("Unrecognized message:", message);
+        break;
+      }
+    });
+    photonPageActionPort.postMessage({
+      type: "setProperties",
+      title: browser.i18n.getMessage("contextMenuLabel"),
+      iconPath
+    });
+
+    // Export these so that main.js can use them.
+    Object.defineProperties(exports, {
+      "photonPageActionPort": {
+        enumerable: true,
+        get() {
+          return photonPageActionPort;
+        }
+      },
+      "usePhotonPageAction": {
+        enumerable: true,
+        get() {
+          return usePhotonPageAction;
+        }
+      }
+    });
+  }
+
+  return exports;
 })();
--- a/browser/extensions/screenshots/webextension/build/inlineSelectionCss.js
+++ b/browser/extensions/screenshots/webextension/build/inlineSelectionCss.js
@@ -1,11 +1,11 @@
 /* Created from build/server/static/css/inline-selection.css */
 window.inlineSelectionCss = `
-.button, .highlight-button-cancel, .highlight-button-save, .highlight-button-download {
+.button, .highlight-button-cancel, .highlight-button-save, .highlight-button-download, .preview-button-save {
   display: flex;
   align-items: center;
   justify-content: center;
   border: 0;
   border-radius: 1px;
   cursor: pointer;
   font-size: 16px;
   font-weight: 400;
@@ -14,150 +14,213 @@ window.inlineSelectionCss = `
   outline: none;
   padding: 0 10px;
   position: relative;
   text-align: center;
   text-decoration: none;
   transition: background 150ms cubic-bezier(0.07, 0.95, 0, 1), border 150ms cubic-bezier(0.07, 0.95, 0, 1);
   user-select: none;
   white-space: nowrap; }
-  .button.small, .small.highlight-button-cancel, .small.highlight-button-save, .small.highlight-button-download {
+  .button.small, .small.highlight-button-cancel, .small.highlight-button-save, .small.highlight-button-download, .small.preview-button-save {
     height: 32px;
     line-height: 32px;
     padding: 0 8px; }
-  .button.tiny, .tiny.highlight-button-cancel, .tiny.highlight-button-save, .tiny.highlight-button-download {
+  .button.tiny, .tiny.highlight-button-cancel, .tiny.highlight-button-save, .tiny.highlight-button-download, .tiny.preview-button-save {
     font-size: 14px;
     height: 26px;
     border: 1px solid #c7c7c7; }
-    .button.tiny:hover, .tiny.highlight-button-cancel:hover, .tiny.highlight-button-save:hover, .tiny.highlight-button-download:hover, .button.tiny:focus, .tiny.highlight-button-cancel:focus, .tiny.highlight-button-save:focus, .tiny.highlight-button-download:focus {
+    .button.tiny:hover, .tiny.highlight-button-cancel:hover, .tiny.highlight-button-save:hover, .tiny.highlight-button-download:hover, .tiny.preview-button-save:hover, .button.tiny:focus, .tiny.highlight-button-cancel:focus, .tiny.highlight-button-save:focus, .tiny.highlight-button-download:focus, .tiny.preview-button-save:focus {
       background: #ebebeb;
       border-color: #989898; }
-    .button.tiny:active, .tiny.highlight-button-cancel:active, .tiny.highlight-button-save:active, .tiny.highlight-button-download:active {
+    .button.tiny:active, .tiny.highlight-button-cancel:active, .tiny.highlight-button-save:active, .tiny.highlight-button-download:active, .tiny.preview-button-save:active {
       background: #dedede;
       border-color: #989898; }
-  .button.block-button, .block-button.highlight-button-cancel, .block-button.highlight-button-save, .block-button.highlight-button-download {
+  .button.block-button, .block-button.highlight-button-cancel, .block-button.highlight-button-save, .block-button.highlight-button-download, .block-button.preview-button-save {
     display: flex;
     align-items: center;
     justify-content: center;
     box-sizing: border-box;
-    border: none;
+    border: 0;
     border-right: 1px solid #c7c7c7;
-    box-shadow: none;
+    box-shadow: 0;
     border-radius: 0;
     flex-shrink: 0;
     font-size: 20px;
     height: 100px;
     line-height: 100%;
     overflow: hidden; }
     @media (max-width: 719px) {
-      .button.block-button, .block-button.highlight-button-cancel, .block-button.highlight-button-save, .block-button.highlight-button-download {
+      .button.block-button, .block-button.highlight-button-cancel, .block-button.highlight-button-save, .block-button.highlight-button-download, .block-button.preview-button-save {
         justify-content: flex-start;
         font-size: 16px;
         height: 72px;
         margin-right: 10px;
         padding: 0 5px; } }
-    .button.block-button:hover, .block-button.highlight-button-cancel:hover, .block-button.highlight-button-save:hover, .block-button.highlight-button-download:hover {
+    .button.block-button:hover, .block-button.highlight-button-cancel:hover, .block-button.highlight-button-save:hover, .block-button.highlight-button-download:hover, .block-button.preview-button-save:hover {
       background: #ebebeb; }
-    .button.block-button:active, .block-button.highlight-button-cancel:active, .block-button.highlight-button-save:active, .block-button.highlight-button-download:active {
+    .button.block-button:active, .block-button.highlight-button-cancel:active, .block-button.highlight-button-save:active, .block-button.highlight-button-download:active, .block-button.preview-button-save:active {
       background: #dedede; }
+  .button.download, .download.highlight-button-cancel, .download.highlight-button-save, .download.highlight-button-download, .download.preview-button-save, .button.trash, .trash.highlight-button-cancel, .trash.highlight-button-save, .trash.highlight-button-download, .trash.preview-button-save, .button.share, .share.highlight-button-cancel, .share.highlight-button-save, .share.highlight-button-download, .share.preview-button-save, .button.flag, .flag.highlight-button-cancel, .flag.highlight-button-save, .flag.highlight-button-download, .flag.preview-button-save {
+    background-image: url("../img/icon-sprite.svg");
+    background-size: 480px 40px;
+    background-repeat: no-repeat;
+    background-position: 0 0;
+    margin-right: 10px;
+    transition: background-color 150ms cubic-bezier(0.07, 0.95, 0, 1); }
+  .button.download:hover, .download.highlight-button-cancel:hover, .download.highlight-button-save:hover, .download.highlight-button-download:hover, .download.preview-button-save:hover {
+    background-position: -40px 0; }
+  .button.download:active, .download.highlight-button-cancel:active, .download.highlight-button-save:active, .download.highlight-button-download:active, .download.preview-button-save:active {
+    background-position: -80px 0; }
+  .button.share, .share.highlight-button-cancel, .share.highlight-button-save, .share.highlight-button-download, .share.preview-button-save {
+    background-position: -120px 0; }
+    .button.share:hover, .share.highlight-button-cancel:hover, .share.highlight-button-save:hover, .share.highlight-button-download:hover, .share.preview-button-save:hover {
+      background-position: -160px 0; }
+    .button.share:active, .share.highlight-button-cancel:active, .share.highlight-button-save:active, .share.highlight-button-download:active, .share.preview-button-save:active, .button.share.active, .share.active.highlight-button-cancel, .share.active.highlight-button-save, .share.active.highlight-button-download, .share.active.preview-button-save {
+      background-position: -200px 0; }
+  .button.trash, .trash.highlight-button-cancel, .trash.highlight-button-save, .trash.highlight-button-download, .trash.preview-button-save {
+    background-position: -240px 0; }
+    .button.trash:hover, .trash.highlight-button-cancel:hover, .trash.highlight-button-save:hover, .trash.highlight-button-download:hover, .trash.preview-button-save:hover {
+      background-position: -280px 0; }
+    .button.trash:active, .trash.highlight-button-cancel:active, .trash.highlight-button-save:active, .trash.highlight-button-download:active, .trash.preview-button-save:active {
+      background-position: -320px 0; }
+  .button.flag, .flag.highlight-button-cancel, .flag.highlight-button-save, .flag.highlight-button-download, .flag.preview-button-save {
+    background-position: -360px 0; }
+    .button.flag:hover, .flag.highlight-button-cancel:hover, .flag.highlight-button-save:hover, .flag.highlight-button-download:hover, .flag.preview-button-save:hover {
+      background-position: -400px 0; }
+    .button.flag:active, .flag.highlight-button-cancel:active, .flag.highlight-button-save:active, .flag.highlight-button-download:active, .flag.preview-button-save:active {
+      background-position: -440px 0; }
 
 .inverse-color-scheme {
   background: #3e3d40;
-  color: #f5f5f7; }
+  color: #f6f6f8; }
   .inverse-color-scheme a {
     color: #e1e1e6; }
 
 .default-color-scheme {
-  background: #f5f5f7;
+  background: #f6f6f8;
   color: #3e3d40; }
   .default-color-scheme a {
     color: #009ec0; }
 
 .highlight-color-scheme {
   background: #009ec0;
   color: #fff; }
   .highlight-color-scheme a {
     color: #fff;
     text-decoration: underline; }
 
 .alt-color-scheme {
-  background: #31365A;
-  color: #f5f5f7; }
+  background: #31365a;
+  color: #f6f6f8; }
   .alt-color-scheme h1 {
-    color: #6F7FB6; }
+    color: #6f7fb6; }
   .alt-color-scheme a {
     color: #e1e1e6;
     text-decoration: underline; }
 
-.button.primary, .primary.highlight-button-cancel, .highlight-button-save, .primary.highlight-button-download {
+.button.primary, .primary.highlight-button-cancel, .highlight-button-save, .primary.highlight-button-download, .preview-button-save {
   background-color: #009ec0;
   color: #fff; }
-  .button.primary:hover, .primary.highlight-button-cancel:hover, .highlight-button-save:hover, .primary.highlight-button-download:hover, .button.primary:focus, .primary.highlight-button-cancel:focus, .highlight-button-save:focus, .primary.highlight-button-download:focus {
+  .button.primary:hover, .primary.highlight-button-cancel:hover, .highlight-button-save:hover, .primary.highlight-button-download:hover, .preview-button-save:hover, .button.primary:focus, .primary.highlight-button-cancel:focus, .highlight-button-save:focus, .primary.highlight-button-download:focus, .preview-button-save:focus {
     background-color: #00819c; }
-  .button.primary:active, .primary.highlight-button-cancel:active, .highlight-button-save:active, .primary.highlight-button-download:active {
+  .button.primary:active, .primary.highlight-button-cancel:active, .highlight-button-save:active, .primary.highlight-button-download:active, .preview-button-save:active {
     background-color: #006c83; }
 
-.button.secondary, .highlight-button-cancel, .secondary.highlight-button-save, .highlight-button-download {
-  background-color: #f5f5f7;
+.button.secondary, .highlight-button-cancel, .secondary.highlight-button-save, .highlight-button-download, .secondary.preview-button-save {
+  background-color: #f6f6f8;
   color: #3e3d40; }
-  .button.secondary:hover, .highlight-button-cancel:hover, .secondary.highlight-button-save:hover, .highlight-button-download:hover {
+  .button.secondary:hover, .highlight-button-cancel:hover, .secondary.highlight-button-save:hover, .highlight-button-download:hover, .secondary.preview-button-save:hover {
     background-color: #ebebeb; }
-  .button.secondary:hover, .highlight-button-cancel:hover, .secondary.highlight-button-save:hover, .highlight-button-download:hover {
+  .button.secondary:active, .highlight-button-cancel:active, .secondary.highlight-button-save:active, .highlight-button-download:active, .secondary.preview-button-save:active {
     background-color: #dedede; }
 
-.button.transparent, .transparent.highlight-button-cancel, .transparent.highlight-button-save, .transparent.highlight-button-download {
+.button.transparent, .transparent.highlight-button-cancel, .transparent.highlight-button-save, .transparent.highlight-button-download, .transparent.preview-button-save {
   background-color: transparent;
   color: #3e3d40; }
-  .button.transparent:hover, .transparent.highlight-button-cancel:hover, .transparent.highlight-button-save:hover, .transparent.highlight-button-download:hover, .button.transparent:focus, .transparent.highlight-button-cancel:focus, .transparent.highlight-button-save:focus, .transparent.highlight-button-download:focus, .button.transparent:active, .transparent.highlight-button-cancel:active, .transparent.highlight-button-save:active, .transparent.highlight-button-download:active {
+  .button.transparent:hover, .transparent.highlight-button-cancel:hover, .transparent.highlight-button-save:hover, .transparent.highlight-button-download:hover, .transparent.preview-button-save:hover, .button.transparent:focus, .transparent.highlight-button-cancel:focus, .transparent.highlight-button-save:focus, .transparent.highlight-button-download:focus, .transparent.preview-button-save:focus, .button.transparent:active, .transparent.highlight-button-cancel:active, .transparent.highlight-button-save:active, .transparent.highlight-button-download:active, .transparent.preview-button-save:active {
     background-color: rgba(0, 0, 0, 0.05); }
 
-.button.warning, .warning.highlight-button-cancel, .warning.highlight-button-save, .warning.highlight-button-download {
+.button.warning, .warning.highlight-button-cancel, .warning.highlight-button-save, .warning.highlight-button-download, .warning.preview-button-save {
   color: #fff;
   background: #d92215; }
-  .button.warning:hover, .warning.highlight-button-cancel:hover, .warning.highlight-button-save:hover, .warning.highlight-button-download:hover, .button.warning:focus, .warning.highlight-button-cancel:focus, .warning.highlight-button-save:focus, .warning.highlight-button-download:focus {
+  .button.warning:hover, .warning.highlight-button-cancel:hover, .warning.highlight-button-save:hover, .warning.highlight-button-download:hover, .warning.preview-button-save:hover, .button.warning:focus, .warning.highlight-button-cancel:focus, .warning.highlight-button-save:focus, .warning.highlight-button-download:focus, .warning.preview-button-save:focus {
     background: #b81d12; }
-  .button.warning:active, .warning.highlight-button-cancel:active, .warning.highlight-button-save:active, .warning.highlight-button-download:active {
+  .button.warning:active, .warning.highlight-button-cancel:active, .warning.highlight-button-save:active, .warning.highlight-button-download:active, .warning.preview-button-save:active {
     background: #a11910; }
 
 .subtitle-link {
   color: #009ec0; }
 
+.loader {
+  background: #2e2d30;
+  border-radius: 2px;
+  height: 4px;
+  overflow: hidden;
+  position: relative;
+  width: 200px; }
+  #shot-index .loader {
+    background-color: #dedede; }
+
+.loader-inner {
+  animation: bounce infinite alternate 1250ms cubic-bezier(0.7, 0, 0.3, 1);
+  background: #04d1e6;
+  border-radius: 2px;
+  height: 4px;
+  transform: translateX(-40px);
+  width: 50px; }
+
+@keyframes bounce {
+  0% {
+    transform: translateX(-40px); }
+  100% {
+    transform: translate(190px); } }
+
 @keyframes fade-in {
   0% {
     opacity: 0; }
   100% {
     opacity: 1; } }
 
 @keyframes pop {
   0% {
     transform: scale(1); }
   97% {
     transform: scale(1.04); }
   100% {
     transform: scale(1); } }
 
 @keyframes pulse {
   0% {
-    opacity: .3;
+    opacity: 0.3;
     transform: scale(1); }
   70% {
-    opacity: .25;
+    opacity: 0.25;
     transform: scale(1.04); }
   100% {
-    opacity: .3;
+    opacity: 0.3;
     transform: scale(1); } }
 
 @keyframes slide-left {
   0% {
     opacity: 0;
     transform: translate3d(160px, 0, 0); }
   100% {
     opacity: 1;
     transform: translate3d(0, 0, 0); } }
 
+@keyframes bounce-in {
+  0% {
+    opacity: 0;
+    transform: scale(1); }
+  60% {
+    opacity: 1;
+    transform: scale(1.02); }
+  100% {
+    transform: scale(1); } }
+
 .mover-target {
   display: flex;
   align-items: center;
   justify-content: center;
   pointer-events: auto;
   position: absolute;
   z-index: 5; }
 
@@ -172,20 +235,20 @@ window.inlineSelectionCss = `
 
 .hover-highlight {
   animation: fade-in 125ms forwards cubic-bezier(0.07, 0.95, 0, 1);
   background: rgba(255, 255, 255, 0.2);
   border-radius: 1px;
   pointer-events: none;
   position: absolute;
   z-index: 10000000000; }
-  .hover-highlight:before {
+  .hover-highlight::before {
     border: 2px dashed rgba(255, 255, 255, 0.4);
     bottom: 0;
-    content: '';
+    content: "";
     left: 0;
     position: absolute;
     right: 0;
     top: 0; }
 
 .mover-target.direction-topLeft {
   cursor: nwse-resize;
   height: 60px;
@@ -351,21 +414,57 @@ window.inlineSelectionCss = `
   display: block;
   margin: 5px;
   width: 40px; }
 
 .pixel-dimensions {
   position: absolute;
   pointer-events: none;
   font-weight: bold;
-  font-family: sans-serif;
+  font-family: -apple-system, BlinkMacSystemFont, "segoe ui", "helvetica neue", helvetica, ubuntu, roboto, noto, arial, sans-serif;
   font-size: 70%;
   color: #000;
   text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff; }
 
+.preview-buttons {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: absolute;
+  right: 0;
+  top: -2px; }
+
+.preview-image {
+  position: relative;
+  height: 80%;
+  max-width: 100%;
+  margin: auto 2em;
+  text-align: center;
+  animation-delay: 50ms;
+  animation: bounce-in 300ms forwards ease-in-out; }
+
+.preview-image img {
+  display: block;
+  width: auto;
+  height: auto;
+  max-width: 100%;
+  max-height: 90%;
+  margin: 50px auto;
+  border: 1px solid rgba(255, 255, 255, 0.8); }
+
+.preview-button-save {
+  background-image: url("MOZ_EXTENSION/icons/cloud.svg");
+  background-position: 8px center;
+  background-repeat: no-repeat;
+  background-size: 20px 18px;
+  font-size: 18px;
+  margin: 5px;
+  min-width: 80px;
+  padding-left: 34px; }
+
 .fixed-container {
   align-items: center;
   display: flex;
   flex-direction: column;
   height: 100vh;
   justify-content: center;
   left: 0;
   margin: 0;
@@ -413,17 +512,17 @@ window.inlineSelectionCss = `
   margin-left: 20px; }
 
 .preview-instructions {
   display: flex;
   align-items: center;
   justify-content: center;
   animation: pulse 125mm cubic-bezier(0.07, 0.95, 0, 1);
   color: #fff;
-  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  font-family: -apple-system, BlinkMacSystemFont, "segoe ui", "helvetica neue", helvetica, ubuntu, roboto, noto, arial, sans-serif;
   font-size: 24px;
   line-height: 32px;
   text-align: center;
   padding-top: 20px;
   width: 400px; }
 
 .myshots-all-buttons-container {
   display: flex;
--- a/browser/extensions/screenshots/webextension/build/onboardingCss.js
+++ b/browser/extensions/screenshots/webextension/build/onboardingCss.js
@@ -1,47 +1,81 @@
 /* Created from build/server/static/css/onboarding.css */
 window.onboardingCss = `
+.loader {
+  background: #2e2d30;
+  border-radius: 2px;
+  height: 4px;
+  overflow: hidden;
+  position: relative;
+  width: 200px; }
+  #shot-index .loader {
+    background-color: #dedede; }
+
+.loader-inner {
+  animation: bounce infinite alternate 1250ms cubic-bezier(0.7, 0, 0.3, 1);
+  background: #04d1e6;
+  border-radius: 2px;
+  height: 4px;
+  transform: translateX(-40px);
+  width: 50px; }
+
+@keyframes bounce {
+  0% {
+    transform: translateX(-40px); }
+  100% {
+    transform: translate(190px); } }
+
 @keyframes fade-in {
   0% {
     opacity: 0; }
   100% {
     opacity: 1; } }
 
 @keyframes pop {
   0% {
     transform: scale(1); }
   97% {
     transform: scale(1.04); }
   100% {
     transform: scale(1); } }
 
 @keyframes pulse {
   0% {
-    opacity: .3;
+    opacity: 0.3;
     transform: scale(1); }
   70% {
-    opacity: .25;
+    opacity: 0.25;
     transform: scale(1.04); }
   100% {
-    opacity: .3;
+    opacity: 0.3;
     transform: scale(1); } }
 
 @keyframes slide-left {
   0% {
     opacity: 0;
     transform: translate3d(160px, 0, 0); }
   100% {
     opacity: 1;
     transform: translate3d(0, 0, 0); } }
 
+@keyframes bounce-in {
+  0% {
+    opacity: 0;
+    transform: scale(1); }
+  60% {
+    opacity: 1;
+    transform: scale(1.02); }
+  100% {
+    transform: scale(1); } }
+
 html,
 body {
   box-sizing: border-box;
-  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  font-family: -apple-system, BlinkMacSystemFont, "segoe ui", "helvetica neue", helvetica, ubuntu, roboto, noto, arial, sans-serif;
   height: 100%;
   margin: 0;
   width: 100%; }
 
 #slide-overlay {
   display: flex;
   align-items: center;
   flex-direction: column;
@@ -57,17 +91,17 @@ body {
   animation: fade-in 250ms forwards cubic-bezier(0.07, 0.95, 0, 1);
   opacity: 0; }
 
 .slide {
   display: flex;
   align-items: center;
   flex-direction: column;
   justify-content: center;
-  background-color: #f5f5f7;
+  background-color: #f6f6f8;
   border-radius: 5px;
   height: 520px;
   overflow: hidden;
   width: 700px; }
   .slide .slide-image {
     background-size: 700px 378px;
     flex: 0 0 360px;
     font-size: 16px;
@@ -140,17 +174,17 @@ body {
 #slide-status-container {
   display: flex;
   align-items: center;
   justify-content: center;
   padding-top: 15px; }
 
 .goto-slide {
   background: transparent;
-  background-color: #f5f5f7;
+  background-color: #f6f6f8;
   border-radius: 50%;
   border: 0;
   flex: 0 0 9px;
   height: 9px;
   margin: 0 4px;
   opacity: 0.7;
   padding: 0;
   transition: height 100ms cubic-bezier(0.07, 0.95, 0, 1), opacity 100ms cubic-bezier(0.07, 0.95, 0, 1); }
@@ -226,24 +260,24 @@ body {
   top: 50%;
   transition: opacity 100ms cubic-bezier(0.07, 0.95, 0, 1);
   z-index: 10; }
 
 #skip:hover {
   opacity: 1; }
 
 .active-slide-1 #prev,
-.active-slide-3 #next {
+.active-slide-4 #next {
   display: none; }
 
 #done {
   background-image: url("MOZ_EXTENSION/icons/done.svg");
   display: none; }
 
-.active-slide-3 #done {
+.active-slide-4 #done {
   display: inline-block; }
 
 /* for smaller screen sizes */
 @media screen and (max-width: 768px) {
   .slide {
     height: 360px;
     width: 450px; }
     .slide .slide-image {
--- a/browser/extensions/screenshots/webextension/build/onboardingHtml.js
+++ b/browser/extensions/screenshots/webextension/build/onboardingHtml.js
@@ -5,17 +5,17 @@ window.onboardingHtml = `
   <head>
     <!-- onboarding.scss is automatically inserted here: -->
     <style></style>
     <!-- Here and in onboarding.scss use MOZ_EXTENSION/path to refer to local files -->
   </head>
   <body>
     <div id="slide-overlay">
       <!-- The current slide is set by having .active-slide-1, .active-slide-2, etc on #slide element: -->
-      <div id="slide-container" data-number-of-slides="3" class="active-slide-1">
+      <div id="slide-container" data-number-of-slides="4" class="active-slide-1">
         <div class="slide slide-1">
           <!-- Note: all images must be listed in manifest.json.template under web_accessible_resources -->
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-1.png');"></div>
           <div class="slide-content">
             <div class="slide-content-aligner">
               <h1><span><strong>Firefox</strong> Screenshots</span><sup>Beta</sup></h1>
               <p data-l10n-id="tourBodyOne"></p>
             </div>
@@ -25,32 +25,40 @@ window.onboardingHtml = `
         <div class="slide slide-2">
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-2.png');"></div>
           <div class="slide-content">
             <h1 data-l10n-id="tourHeaderTwo"></h1>
             <p data-l10n-id="tourBodyTwo"></p>
           </div>
         </div>
         <div class="slide slide-3">
+          <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-3.png');"></div>
+          <div class="slide-content">
+            <h1 data-l10n-id="tourHeaderThree"></h1>
+            <p data-l10n-id="tourBodyThree"></p>
+          </div>
+        </div>
+        <div class="slide slide-4">
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-4.png');"></div>
           <div class="slide-content">
             <h1 data-l10n-id="tourHeaderFour"></h1>
             <p data-l10n-id="tourBodyFour"></p>
           </div>
         </div>
 
         <!-- Clickable elements should be buttons for accessibility -->
         <button id="skip" data-l10n-id="tourSkip" tabindex=1>Skip</button>
         <button id="prev" tabindex=2 data-l10n-label-id="tourPrevious"></button>
         <button id="next" tabindex=3 data-l10n-label-id="tourNext"></button>
         <button id="done" tabindex=4 data-l10n-label-id="tourDone"></button>
         <div id="slide-status-container">
           <button class="goto-slide goto-slide-1" data-number="1" tabindex=4></button>
           <button class="goto-slide goto-slide-2" data-number="2" tabindex=5></button>
           <button class="goto-slide goto-slide-3" data-number="3" tabindex=6></button>
+          <button class="goto-slide goto-slide-4" data-number="4" tabindex=7></button>
         </div>
         <!-- FIXME: Need to put in privacy / etc links -->
       </div>
     </div>
   </body>
 </html>
 
 `;
--- a/browser/extensions/screenshots/webextension/build/raven.js
+++ b/browser/extensions/screenshots/webextension/build/raven.js
@@ -1,9 +1,9 @@
-/*! Raven.js 3.15.0 (d49a1b8) | github.com/getsentry/raven-js */
+/*! Raven.js 3.17.0 (6384830) | github.com/getsentry/raven-js */
 
 /*
  * Includes TraceKit
  * https://github.com/getsentry/TraceKit
  *
  * Copyright 2017 Matt Robenolt and other contributors
  * Released under the BSD license
  * https://github.com/getsentry/raven-js/blob/master/LICENSE
@@ -86,16 +86,23 @@ function now() {
 // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
 var _window = typeof window !== 'undefined' ? window
             : typeof global !== 'undefined' ? global
             : typeof self !== 'undefined' ? self
             : {};
 var _document = _window.document;
 var _navigator = _window.navigator;
 
+
+function keepOriginalCallback(original, callback) {
+    return isFunction(callback) ?
+    function (data) { return callback(data, original) } :
+    callback;
+}
+
 // First, check for JSON support
 // If there is no JSON, we no-op the core features of Raven
 // since JSON is required to encode the payload
 function Raven() {
     this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
     // Raven can run in contexts where there's no document (react-native)
     this._hasDocument = !isUndefined(_document);
     this._hasNavigator = !isUndefined(_navigator);
@@ -151,17 +158,17 @@ function Raven() {
  * @this {Raven}
  */
 
 Raven.prototype = {
     // Hardcode version string so that raven source can be loaded directly via
     // webpack (using a build step causes webpack #1617). Grunt verifies that
     // this value matches package.json during build.
     //   See: https://github.com/getsentry/raven-js/issues/465
-    VERSION: '3.15.0',
+    VERSION: '3.17.0',
 
     debug: false,
 
     TraceKit: TraceKit, // alias to TraceKit
 
     /*
      * Configure Raven with a DSN and extra options
      *
@@ -633,52 +640,46 @@ Raven.prototype = {
      * Set the dataCallback option
      *
      * @param {function} callback The callback to run which allows the
      *                            data blob to be mutated before sending
      * @return {Raven}
      */
     setDataCallback: function(callback) {
         var original = this._globalOptions.dataCallback;
-        this._globalOptions.dataCallback = isFunction(callback)
-          ? function (data) { return callback(data, original); }
-          : callback;
-
+        this._globalOptions.dataCallback =
+          keepOriginalCallback(original, callback);
         return this;
     },
 
     /*
      * Set the breadcrumbCallback option
      *
      * @param {function} callback The callback to run which allows filtering
      *                            or mutating breadcrumbs
      * @return {Raven}
      */
     setBreadcrumbCallback: function(callback) {
         var original = this._globalOptions.breadcrumbCallback;
-        this._globalOptions.breadcrumbCallback = isFunction(callback)
-          ? function (data) { return callback(data, original); }
-          : callback;
-
+        this._globalOptions.breadcrumbCallback =
+          keepOriginalCallback(original, callback);
         return this;
     },
 
     /*
      * Set the shouldSendCallback option
      *
      * @param {function} callback The callback to run which allows
      *                            introspecting the blob before sending
      * @return {Raven}
      */
     setShouldSendCallback: function(callback) {
         var original = this._globalOptions.shouldSendCallback;
-        this._globalOptions.shouldSendCallback = isFunction(callback)
-            ? function (data) { return callback(data, original); }
-            : callback;
-
+        this._globalOptions.shouldSendCallback =
+          keepOriginalCallback(original, callback);
         return this;
     },
 
     /**
      * Override the default HTTP transport mechanism that transmits data
      * to the Sentry server.
      *
      * @param {function} transport Function invoked instead of the default
@@ -1444,26 +1445,27 @@ Raven.prototype = {
         // TODO: also consider arbitrary prop values that start with (https?)?://
         var urlProps = ['to', 'from', 'url'],
             urlProp,
             crumb,
             data;
 
         for (var i = 0; i < breadcrumbs.values.length; ++i) {
             crumb = breadcrumbs.values[i];
-            if (!crumb.hasOwnProperty('data') || !isObject(crumb.data))
+            if (!crumb.hasOwnProperty('data') || !isObject(crumb.data) || objectFrozen(crumb.data))
                 continue;
 
-            data = crumb.data;
+            data = objectMerge({}, crumb.data);
             for (var j = 0; j < urlProps.length; ++j) {
                 urlProp = urlProps[j];
                 if (data.hasOwnProperty(urlProp)) {
                     data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength);
                 }
             }
+            breadcrumbs.values[i].data = data;
         }
     },
 
     _getHttpData: function() {
         if (!this._hasNavigator && !this._hasDocument) return;
         var httpData = {};
 
         if (this._hasNavigator && _navigator.userAgent) {
@@ -1838,16 +1840,31 @@ function objectMerge(obj1, obj2) {
         return obj1;
     }
     each(obj2, function(key, value){
         obj1[key] = value;
     });
     return obj1;
 }
 
+/**
+ * This function is only used for react-native.
+ * react-native freezes object that have already been sent over the
+ * js bridge. We need this function in order to check if the object is frozen.
+ * So it's ok that objectFrozen returns false if Object.isFrozen is not
+ * supported because it's not relevant for other "platforms". See related issue:
+ * https://github.com/getsentry/react-native-sentry/issues/57
+ */
+function objectFrozen(obj) {
+    if (!Object.isFrozen) {
+        return false;
+    }
+    return Object.isFrozen(obj);
+}
+
 function truncate(str, max) {
     return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
 }
 
 /**
  * hasKey, a better form of hasOwnProperty
  * Example: hasKey(MainHostObject, property) === true/false
  *
@@ -2163,19 +2180,32 @@ function isError(value) {
   switch ({}.toString.call(value)) {
     case '[object Error]': return true;
     case '[object Exception]': return true;
     case '[object DOMException]': return true;
     default: return value instanceof Error;
   }
 }
 
+function wrappedCallback(callback) {
+    function dataCallback(data, original) {
+      var normalizedData = callback(data) || data;
+      if (original) {
+          return original(normalizedData) || normalizedData;
+      }
+      return normalizedData;
+    }
+
+    return dataCallback;
+}
+
 module.exports = {
     isObject: isObject,
-    isError: isError
+    isError: isError,
+    wrappedCallback: wrappedCallback
 };
 
 },{}],6:[function(_dereq_,module,exports){
 (function (global){
 'use strict';
 
 var utils = _dereq_(5);
 
@@ -2542,17 +2572,17 @@ TraceKit.computeStackTrace = (function c
      * Chrome and Gecko use this property.
      * @param {Error} ex
      * @return {?Object.<string, *>} Stack trace information.
      */
     function computeStackTraceFromStackProp(ex) {
         if (typeof ex.stack === 'undefined' || !ex.stack) return;
 
         var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
-            gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i,
+            gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,
             winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
 
             // Used to additionally parse URL/line/column from eval frames
             geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,
             chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/,
 
             lines = ex.stack.split('\n'),
             stack = [],
--- a/browser/extensions/screenshots/webextension/build/shot.js
+++ b/browser/extensions/screenshots/webextension/build/shot.js
@@ -24,17 +24,17 @@ function isUrl(url) {
     return true;
   }
   if ((/^chrome:.{0,8000}/i).test(url)) {
     return true;
   }
   if ((/^view-source:/i).test(url)) {
     return isUrl(url.substr("view-source:".length));
   }
-  return (/^https?:\/\/[a-z0-9\.\-]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
+  return (/^https?:\/\/[a-z0-9.-]{1,8000}[a-z0-9](:[0-9]{1,8000})?\/?/i).test(url);
 }
 
 function assertUrl(url) {
   if (!url) {
     throw new Error("Empty value is not URL");
   }
   if (!isUrl(url)) {
     let exc = new Error("Not a URL");
@@ -116,17 +116,17 @@ function resolveUrl(base, url) {
     return url;
   }
   if (url.indexOf("//") === 0) {
     // Protocol-relative URL
     return (/^https?:/i).exec(base)[0] + url;
   }
   if (url.indexOf("/") === 0) {
     // Domain-relative URL
-    return (/^https?:\/\/[a-z0-9\.\-]{1,4000}/i).exec(base)[0] + url;
+    return (/^https?:\/\/[a-z0-9.-]{1,4000}/i).exec(base)[0] + url;
   }
   // Otherwise, a full relative URL
   while (url.indexOf("./") === 0) {
     url = url.substr(2);
   }
   if (!base) {
     // It's not an absolute URL, and we don't have a base URL, so we have
     // to throw away the URL
@@ -196,17 +196,17 @@ function makeRandomId() {
   }
   return id;
 }
 
 class AbstractShot {
 
   constructor(backend, id, attrs) {
     attrs = attrs || {};
-    assert((/^[a-zA-Z0-9]{1,4000}\/[a-z0-9\.-]{1,4000}$/).test(id), "Bad ID (should be alphanumeric):", JSON.stringify(id));
+    assert((/^[a-zA-Z0-9]{1,4000}\/[a-z0-9.-]{1,4000}$/).test(id), "Bad ID (should be alphanumeric):", JSON.stringify(id));
     this._backend = backend;
     this._id = id;
     this.origin = attrs.origin || null;
     this.fullUrl = attrs.fullUrl || null;
     if ((!attrs.fullUrl) && attrs.url) {
       console.warn("Received deprecated attribute .url");
       this.fullUrl = attrs.url;
     }
@@ -346,17 +346,17 @@ class AbstractShot {
       assertOrigin(val);
     }
     this._origin = val || undefined;
   }
 
   get filename() {
     let filenameTitle = this.title;
     let date = new Date(this.createdDate);
-    filenameTitle = filenameTitle.replace(/[:\\<>\/!@&*.|\n\r\t]/g, " ");
+    filenameTitle = filenameTitle.replace(/[:\\<>/!@&*.|\n\r\t]/g, " ");
     filenameTitle = filenameTitle.replace(/\s{1,4000}/g, " ");
     let clipFilename = `Screenshot-${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${filenameTitle}`;
     const clipFilenameBytesSize = clipFilename.length * 2; // JS STrings are UTF-16
     if (clipFilenameBytesSize > 251) { // 255 bytes (Usual filesystems max) - 4 for the ".png" file extension string
       const excedingchars = (clipFilenameBytesSize - 246) / 2; // 251 - 5 for ellipsis "[...]"
       clipFilename = clipFilename.substring(0, clipFilename.length - excedingchars);
       clipFilename = clipFilename + '[...]';
     }
--- a/browser/extensions/screenshots/webextension/domainFromUrl.js
+++ b/browser/extensions/screenshots/webextension/domainFromUrl.js
@@ -9,20 +9,20 @@ this.domainFromUrl = (function() {
   return function urlDomainForId(location) { // eslint-disable-line no-unused-vars
     let domain = location.hostname;
     if (!domain) {
       domain = location.origin.split(":")[0];
       if (!domain) {
         domain = "unknown";
       }
     }
-    if (domain.search(/^[a-z0-9.\-]{1,1000}$/i) === -1) {
+    if (domain.search(/^[a-z0-9.-]{1,1000}$/i) === -1) {
       // Probably a unicode domain; we could use punycode but it wouldn't decode
       // well in the URL anyway.  Instead we'll punt.
-      domain = domain.replace(/[^a-z0-9.\-]/ig, "");
+      domain = domain.replace(/[^a-z0-9.-]/ig, "");
       if (!domain) {
         domain = "site";
       }
     }
     return domain;
   };
 
 })();
new file mode 100644
--- /dev/null
+++ b/browser/extensions/screenshots/webextension/icons/menu-myshot-white.svg
@@ -0,0 +1,1 @@
+<svg width="40" height="40" viewBox="0 0 46 46" xmlns="http://www.w3.org/2000/svg"><title>Screenshots</title><path d="M11 11.995c0-.55.455-.995.995-.995h23.01c.55 0 .995.455.995.995v23.01c0 .55-.455.995-.995.995h-23.01c-.55 0-.995-.455-.995-.995v-23.01zM11 25v-2h7v2h-7zm9-5h7v-2h-7v2zm9 5h7v-2h-7v2zm-9 4h7v-2h-7v2zm-2-18h2v25h-2V11zm9 0h2v25h-2V11z" fill="#FFF" fill-rule="evenodd"/></svg>
--- a/browser/extensions/screenshots/webextension/manifest.json
+++ b/browser/extensions/screenshots/webextension/manifest.json
@@ -1,12 +1,12 @@
 {
   "manifest_version": 2,
   "name": "Firefox Screenshots",
-  "version": "10.12.0",
+  "version": "16.1.0",
   "description": "__MSG_addonDescription__",
   "author": "__MSG_addonAuthorsList__",
   "homepage_url": "https://github.com/mozilla-services/screenshots",
   "applications": {
     "gecko": {
       "id": "screenshots@mozilla.org"
     }
   },
--- a/browser/extensions/screenshots/webextension/onboarding/slides.html
+++ b/browser/extensions/screenshots/webextension/onboarding/slides.html
@@ -3,17 +3,17 @@
   <head>
     <!-- onboarding.scss is automatically inserted here: -->
     <style></style>
     <!-- Here and in onboarding.scss use MOZ_EXTENSION/path to refer to local files -->
   </head>
   <body>
     <div id="slide-overlay">
       <!-- The current slide is set by having .active-slide-1, .active-slide-2, etc on #slide element: -->
-      <div id="slide-container" data-number-of-slides="3" class="active-slide-1">
+      <div id="slide-container" data-number-of-slides="4" class="active-slide-1">
         <div class="slide slide-1">
           <!-- Note: all images must be listed in manifest.json.template under web_accessible_resources -->
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-1.png');"></div>
           <div class="slide-content">
             <div class="slide-content-aligner">
               <h1><span><strong>Firefox</strong> Screenshots</span><sup>Beta</sup></h1>
               <p data-l10n-id="tourBodyOne"></p>
             </div>
@@ -23,30 +23,38 @@
         <div class="slide slide-2">
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-2.png');"></div>
           <div class="slide-content">
             <h1 data-l10n-id="tourHeaderTwo"></h1>
             <p data-l10n-id="tourBodyTwo"></p>
           </div>
         </div>
         <div class="slide slide-3">
+          <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-3.png');"></div>
+          <div class="slide-content">
+            <h1 data-l10n-id="tourHeaderThree"></h1>
+            <p data-l10n-id="tourBodyThree"></p>
+          </div>
+        </div>
+        <div class="slide slide-4">
           <div class="slide-image" style="background-image: url('MOZ_EXTENSION/icons/onboarding-4.png');"></div>
           <div class="slide-content">
             <h1 data-l10n-id="tourHeaderFour"></h1>
             <p data-l10n-id="tourBodyFour"></p>
           </div>
         </div>
 
         <!-- Clickable elements should be buttons for accessibility -->
         <button id="skip" data-l10n-id="tourSkip" tabindex=1>Skip</button>
         <button id="prev" tabindex=2 data-l10n-label-id="tourPrevious"></button>
         <button id="next" tabindex=3 data-l10n-label-id="tourNext"></button>
         <button id="done" tabindex=4 data-l10n-label-id="tourDone"></button>
         <div id="slide-status-container">
           <button class="goto-slide goto-slide-1" data-number="1" tabindex=4></button>
           <button class="goto-slide goto-slide-2" data-number="2" tabindex=5></button>
           <button class="goto-slide goto-slide-3" data-number="3" tabindex=6></button>
+          <button class="goto-slide goto-slide-4" data-number="4" tabindex=7></button>
         </div>
         <!-- FIXME: Need to put in privacy / etc links -->
       </div>
     </div>
   </body>
 </html>
--- a/browser/extensions/screenshots/webextension/onboarding/slides.js
+++ b/browser/extensions/screenshots/webextension/onboarding/slides.js
@@ -27,17 +27,17 @@ this.slides = (function() {
       iframe.style.border = "none";
       iframe.style.position = "fixed";
       iframe.style.top = "0";
       iframe.style.left = "0";
       iframe.style.margin = "0";
       iframe.scrolling = "no";
       updateIframeSize();
       let html = onboardingHtml.replace('<style></style>', `<style>${onboardingCss}</style>`);
-      html = html.replace(/MOZ_EXTENSION([^\"]+)/g, (match, filename) => {
+      html = html.replace(/MOZ_EXTENSION([^"]+)/g, (match, filename) => {
         return browser.extension.getURL(filename);
       });
       iframe.addEventListener("load", catcher.watchFunction(() => {
         doc = iframe.contentDocument;
         assertIsBlankDocument(doc);
         let parsedDom = (new DOMParser()).parseFromString(
           html,
           "text/html"
@@ -94,17 +94,17 @@ this.slides = (function() {
       [termsSentinel]: browser.i18n.getMessage("termsAndPrivacyNoticeTermsLink"),
       [privacySentinel]: browser.i18n.getMessage("termsAndPrivacyNoticyPrivacyLink")
     };
     let linkUrls = {
       [termsSentinel]: "https://www.mozilla.org/about/legal/terms/services/",
       [privacySentinel]: "https://www.mozilla.org/privacy/firefox/"
     };
     let text = browser.i18n.getMessage(
-      "termsAndPrivacyNoticeCloudServices",
+      "termsAndPrivacyNotice2",
       [sentinelSplitter + termsSentinel + sentinelSplitter,
        sentinelSplitter + privacySentinel + sentinelSplitter]);
     let parts = text.split(sentinelSplitter);
     for (let part of parts) {
       let el;
       if (part === termsSentinel || part === privacySentinel) {
         el = doc.createElement("a");
         el.href = linkUrls[part];
--- a/browser/extensions/screenshots/webextension/selector/callBackground.js
+++ b/browser/extensions/screenshots/webextension/selector/callBackground.js
@@ -1,27 +1,27 @@
 /* globals log */
 
 "use strict";
 
 this.callBackground = function callBackground(funcName, ...args) {
   return browser.runtime.sendMessage({funcName, args}).then((result) => {
-    if (result.type === "success") {
+    if (result && result.type === "success") {
       return result.value;
-    } else if (result.type === "error") {
-      let exc = new Error(result.message);
+    } else if (result && result.type === "error") {
+      let exc = new Error(result.message || "Unknown error");
       exc.name = "BackgroundError";
       if ('errorCode' in result) {
         exc.errorCode = result.errorCode;
       }
       if ('popupMessage' in result) {
         exc.popupMessage = result.popupMessage;
       }
       throw exc;
     } else {
       log.error("Unexpected background result:", result);
-      let exc = new Error(`Bad response type from background page: ${result.type || undefined}`);
-      exc.resultType = result.type || "undefined";
+      let exc = new Error(`Bad response type from background page: ${result && result.type || undefined}`);
+      exc.resultType = result ? (result.type || "undefined") : "undefined result";
       throw exc;
     }
   });
 }
 null;
--- a/browser/extensions/screenshots/webextension/selector/shooter.js
+++ b/browser/extensions/screenshots/webextension/selector/shooter.js
@@ -16,17 +16,17 @@ this.shooter = (function() { // eslint-d
 
   function regexpEscape(str) {
     // http://stackoverflow.com/questions/3115150/how-to-escape-regular-expression-special-characters-using-javascript
     return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
   }
 
   function sanitizeError(data) {
     const href = new RegExp(regexpEscape(window.location.href), 'g');
-    const origin = new RegExp(`${regexpEscape(window.location.origin)}[^\s",>]*`, 'g');
+    const origin = new RegExp(`${regexpEscape(window.location.origin)}[^ \t\n\r",>]*`, 'g');
     const json = JSON.stringify(data)
       .replace(href, 'REDACTED_HREF')
       .replace(origin, 'REDACTED_URL');
     const result = JSON.parse(json);
     return result;
   }
 
   catcher.registerHandler((errorObj) => {
@@ -34,41 +34,46 @@ this.shooter = (function() { // eslint-d
   });
 
   catcher.watchFunction(() => {
     let canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
     let ctx = canvas.getContext('2d');
     supportsDrawWindow = !!ctx.drawWindow;
   })();
 
-  function screenshotPage(selectedPos) {
+  let screenshotPage = exports.screenshotPage = function(selectedPos, captureType) {
     if (!supportsDrawWindow) {
       return null;
     }
     let height = selectedPos.bottom - selectedPos.top;
     let width = selectedPos.right - selectedPos.left;
     let canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
-    canvas.width = width * window.devicePixelRatio;
-    canvas.height = height * window.devicePixelRatio;
     let ctx = canvas.getContext('2d');
-    if (window.devicePixelRatio !== 1) {
+    if (captureType == 'fullPage') {
+      canvas.width = width;
+      canvas.height = height;
+    } else {
+      canvas.width = width * window.devicePixelRatio;
+      canvas.height = height * window.devicePixelRatio;
+    }
+    if (window.devicePixelRatio !== 1 && captureType != 'fullPage') {
       ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
     }
     ui.iframe.hide();
     try {
       ctx.drawWindow(window, selectedPos.left, selectedPos.top, width, height, "#fff");
     } finally {
       ui.iframe.unhide();
     }
     return canvas.toDataURL();
-  }
+  };
 
   let isSaving = null;
 
-  exports.takeShot = function(captureType, selectedPos) {
+  exports.takeShot = function(captureType, selectedPos, url) {
     // isSaving indicates we're aleady in the middle of saving
     // we use a timeout so in the case of a failure the button will
     // still start working again
     if (Math.floor(selectedPos.left) == Math.floor(selectedPos.right) ||
         Math.floor(selectedPos.top) == Math.floor(selectedPos.bottom)) {
         let exc = new Error("Empty selection");
         exc.popupMessage = "EMPTY_SELECTION";
         exc.noReport = true;
@@ -87,17 +92,17 @@ this.shooter = (function() { // eslint-d
       }
       isSaving = null;
     }, 1000);
     selectedPos = selectedPos.asJson();
     let captureText = "";
     if (buildSettings.captureText) {
       captureText = util.captureEnclosedText(selectedPos);
     }
-    let dataUrl = screenshotPage(selectedPos);
+    let dataUrl = url || screenshotPage(selectedPos, captureType);
     if (dataUrl) {
       shotObject.delAllClips();
       shotObject.addClip({
         createdDate: Date.now(),
         image: {
           url: dataUrl,
           captureType,
           text: captureText,
--- a/browser/extensions/screenshots/webextension/selector/ui.js
+++ b/browser/extensions/screenshots/webextension/selector/ui.js
@@ -38,17 +38,17 @@ this.ui = (function() { // eslint-disabl
            el.classList.contains("full-page"))) {
         return true;
       }
       el = el.parentNode;
     }
     return false;
   }
 
-  let substitutedCss = inlineSelectionCss.replace(/MOZ_EXTENSION([^\"]+)/g, (match, filename) => {
+  let substitutedCss = inlineSelectionCss.replace(/MOZ_EXTENSION([^"]+)/g, (match, filename) => {
     return browser.extension.getURL(filename);
   });
 
   function makeEl(tagName, className) {
     if (!iframe.document()) {
       throw new Error("Attempted makeEl before iframe was initialized");
     }
     let el = iframe.document().createElement(tagName);
@@ -62,44 +62,60 @@ this.ui = (function() { // eslint-disabl
     if (this.sizeTracking.windowDelayer) {
       clearTimeout(this.sizeTracking.windowDelayer);
     }
     this.sizeTracking.windowDelayer = setTimeout(watchFunction(() => {
       this.updateElementSize(true);
     }), 50);
   }
 
+  function localizeText(doc) {
+    let els = doc.querySelectorAll("[data-l10n-id]");
+    for (let el of els) {
+      let id = el.getAttribute("data-l10n-id");
+      let text = browser.i18n.getMessage(id);
+      el.textContent = text;
+    }
+  }
+
+  function initializeIframe() {
+    let el = document.createElement("iframe");
+    el.src = browser.extension.getURL("blank.html");
+    el.style.zIndex = "99999999999";
+    el.style.border = "none";
+    el.style.top = "0";
+    el.style.left = "0";
+    el.style.margin = "0";
+    el.scrolling = "no";
+    el.style.clip = "auto";
+    return el;
+  }
+
   let iframeSelection = exports.iframeSelection = {
     element: null,
     addClassName: "",
     sizeTracking: {
       timer: null,
       windowDelayer: null,
       lastHeight: null,
       lastWidth: null
     },
     document: null,
     display(installHandlerOnDocument) {
       return new Promise((resolve, reject) => {
         if (!this.element) {
-          this.element = document.createElement("iframe");
-          this.element.src = browser.extension.getURL("blank.html");
+          this.element = initializeIframe();
           this.element.id = "firefox-screenshots-selection-iframe";
           this.element.style.display = "none";
-          this.element.style.zIndex = "99999999999";
-          this.element.style.border = "none";
-          this.element.style.position = "absolute";
-          this.element.style.top = "0";
-          this.element.style.left = "0";
-          this.element.style.margin = "0";
-          this.element.scrolling = "no";
+          this.element.style.setProperty('position', 'absolute', 'important');
           this.updateElementSize();
           this.element.addEventListener("load", watchFunction(() => {
             this.document = this.element.contentDocument;
             assertIsBlankDocument(this.document);
+            // eslint-disable-next-line no-unsanitized/property
             this.document.documentElement.innerHTML = `
                <head>
                 <style>${substitutedCss}</style>
                 <title></title>
                </head>
                <body></body>`;
             installHandlerOnDocument(this.document);
             if (this.addClassName) {
@@ -198,97 +214,84 @@ this.ui = (function() { // eslint-disabl
     }
   };
 
   iframeSelection.onResize = watchFunction(assertIsTrusted(onResize.bind(iframeSelection)), true);
 
   let iframePreSelection = exports.iframePreSelection = {
     element: null,
     document: null,
-    sizeTracking: {
-      windowDelayer: null
-    },
     display(installHandlerOnDocument, standardOverlayCallbacks) {
       return new Promise((resolve, reject) => {
         if (!this.element) {
-          this.element = document.createElement("iframe");
-          this.element.src = browser.extension.getURL("blank.html");
+          this.element = initializeIframe();
           this.element.id = "firefox-screenshots-preselection-iframe";
-          this.element.style.zIndex = "99999999999";
-          this.element.style.border = "none";
-          this.element.style.position = "fixed";
-          this.element.style.top = "0";
-          this.element.style.left = "0";
-          this.element.style.margin = "0";
-          this.element.scrolling = "no";
-          this.updateElementSize();
+          this.element.style.setProperty('position', 'fixed', 'important');
+          this.element.style.width = "100%";
+          this.element.style.height = "100%";
           this.element.addEventListener("load", watchFunction(() => {
             this.document = this.element.contentDocument;
             assertIsBlankDocument(this.document)
+            // eslint-disable-next-line no-unsanitized/property
             this.document.documentElement.innerHTML = `
                <head>
                 <style>${substitutedCss}</style>
                 <title></title>
                </head>
                <body>
                  <div class="preview-overlay">
                    <div class="fixed-container">
                      <div class="face-container">
                        <div class="eye left"><div class="eyeball"></div></div>
                        <div class="eye right"><div class="eyeball"></div></div>
                        <div class="face"></div>
                      </div>
-                     <div class="preview-instructions"></div>
+                     <div class="preview-instructions" data-l10n-id="screenshotInstructions"></div>
                      <div class="myshots-all-buttons-container">
-                       <button class="myshots-button myshots-link" tabindex="1"></button>
+                       <button class="myshots-button myshots-link" tabindex="1" data-l10n-id="myShotsLink"></button>
+                       <div class="spacer"></div>
+                       <button class="myshots-button visible" tabindex="2" data-l10n-id="saveScreenshotVisibleArea"></button>
+                       <button class="myshots-button full-page" tabindex="3" data-l10n-id="saveScreenshotFullPage"></button>
                      </div>
                    </div>
                  </div>
                </body>`;
             installHandlerOnDocument(this.document);
             if (this.addClassName) {
               this.document.body.className = this.addClassName;
             }
             this.document.documentElement.dir = browser.i18n.getMessage("@@bidi_dir");
             this.document.documentElement.lang = browser.i18n.getMessage("@@ui_locale");
             const overlay = this.document.querySelector(".preview-overlay");
-            overlay.querySelector(".preview-instructions").textContent = browser.i18n.getMessage("screenshotInstructions");
-            overlay.querySelector(".myshots-link").textContent = browser.i18n.getMessage("myShotsLink");
+            localizeText(this.document);
             overlay.querySelector(".myshots-button").addEventListener(
               "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.onOpenMyShots)));
+            overlay.querySelector(".visible").addEventListener(
+              "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.onClickVisible)));
+            overlay.querySelector(".full-page").addEventListener(
+              "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.onClickFullPage)));
             resolve();
           }), {once: true});
           document.body.appendChild(this.element);
           this.unhide();
         } else {
           resolve();
         }
       });
     },
 
-    updateElementSize() {
-      if (!this.element) {
-        // This can happen if the selector is unloaded during the resize adjustment
-        // time-delay
-        return;
-      }
-      this.element.style.height = window.innerHeight + "px";
-      this.element.style.width = window.innerWidth + "px";
-    },
-
     hide() {
       window.removeEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
       window.removeEventListener("resize", this.onResize, true);
       if (this.element) {
         this.element.style.display = "none";
       }
     },
 
     unhide() {
-      this.updateElementSize();
       window.addEventListener("scroll", watchFunction(assertIsTrusted(this.onScroll)));
       window.addEventListener("resize", this.onResize, true);
       this.element.style.display = "";
       this.element.focus();
     },
 
     onScroll() {
       exports.HoverBox.hide();
@@ -308,23 +311,94 @@ this.ui = (function() { // eslint-disabl
     remove() {
       this.hide();
       util.removeNode(this.element);
       this.element = null;
       this.document = null;
     }
   };
 
+  let iframePreview = exports.iframePreview = {
+    element: null,
+    document: null,
+    display(installHandlerOnDocument, standardOverlayCallbacks) {
+      return new Promise((resolve, reject) => {
+        if (!this.element) {
+          this.element = initializeIframe();
+          this.element.id = "firefox-screenshots-preview-iframe";
+          this.element.style.display = "none";
+          this.element.style.setProperty('position', 'fixed', 'important');
+          this.element.style.height = "100%";
+          this.element.style.width = "100%";
+          this.element.onload = watchFunction(() => {
+            this.document = this.element.contentDocument;
+            // eslint-disable-next-line no-unsanitized/property
+            this.document.documentElement.innerHTML = `
+              <head>
+                <style>${substitutedCss}</style>
+                <title></title>
+              </head>
+              <body>
+                <div class="preview-overlay">
+                  <div class="preview-image">
+                    <div class="preview-buttons">
+                      <button class="highlight-button-cancel"></button>
+                      <button class="highlight-button-download"></button>
+                      <button class="preview-button-save" data-l10n-id="saveScreenshotSelectedArea"></button>
+                    </div>
+                  </div>
+                </div>
+              </body>`;
+            installHandlerOnDocument(this.document);
+            this.document.documentElement.dir = browser.i18n.getMessage("@@bidi_dir");
+            this.document.documentElement.lang = browser.i18n.getMessage("@@ui_locale");
+            localizeText(this.document);
+            const overlay = this.document.querySelector(".preview-overlay");
+            overlay.querySelector(".highlight-button-download").addEventListener(
+              "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.onDownloadPreview)));
+            overlay.querySelector(".preview-button-save").addEventListener(
+              "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.onSavePreview)));
+            overlay.querySelector(".highlight-button-cancel").addEventListener(
+              "click", watchFunction(assertIsTrusted(standardOverlayCallbacks.cancel)));
+            resolve();
+          });
+          document.body.appendChild(this.element);
+        } else {
+          resolve();
+        }
+      });
+    },
+
+    hide() {
+      if (this.element) {
+        this.element.style.display = "none";
+      }
+    },
+
+    unhide() {
+      this.element.style.display = "";
+      this.element.focus();
+    },
+
+    remove() {
+      this.hide();
+      util.removeNode(this.element);
+      this.element = null;
+      this.document = null;
+    }
+  };
+
   iframePreSelection.onResize = watchFunction(onResize.bind(iframePreSelection), true);
 
   let iframe = exports.iframe = {
     currentIframe: iframePreSelection,
     display(installHandlerOnDocument, standardOverlayCallbacks) {
       return iframeSelection.display(installHandlerOnDocument)
-        .then(() => iframePreSelection.display(installHandlerOnDocument, standardOverlayCallbacks));
+        .then(() => iframePreSelection.display(installHandlerOnDocument, standardOverlayCallbacks))
+        .then(() => iframePreview.display(installHandlerOnDocument, standardOverlayCallbacks));
     },
 
     hide() {
       this.currentIframe.hide();
     },
 
     unhide() {
       this.currentIframe.unhide();
@@ -332,36 +406,45 @@ this.ui = (function() { // eslint-disabl
 
     getElementFromPoint(x, y) {
       return this.currentIframe.getElementFromPoint(x, y);
     },
 
     remove() {
       iframeSelection.remove();
       iframePreSelection.remove();
+      iframePreview.remove();
     },
 
     document() {
       return this.currentIframe.document;
     },
 
     useSelection() {
-      if (this.currentIframe === iframePreSelection) {
+      if (this.currentIframe === iframePreSelection || this.currentIframe === iframePreview) {
         this.hide();
       }
       this.currentIframe = iframeSelection;
       this.unhide();
     },
 
     usePreSelection() {
-      if (this.currentIframe === iframeSelection) {
+      if (this.currentIframe === iframeSelection || this.currentIframe === iframePreview) {
         this.hide();
       }
       this.currentIframe = iframePreSelection;
       this.unhide();
+    },
+
+    usePreview() {
+      if (this.currentIframe === iframeSelection || this.currentIframe === iframePreSelection) {
+        this.hide();
+      }
+      this.currentIframe = iframePreview;
+      this.unhide();
     }
   };
 
   let movements = ["topLeft", "top", "topRight", "left", "right", "bottomLeft", "bottom", "bottomRight"];
 
   /** Creates the selection box */
   exports.Box = {
 
@@ -398,17 +481,16 @@ this.ui = (function() { // eslint-disabl
         }));
         this.download.style.display = "";
       } else {
         this.download.style.display = "none";
       }
       let bodyRect = getBodyRect();
       // Note, document.documentElement.scrollHeight is zero on some strange pages (such as the page created when you load an image):
       let docHeight = Math.max(document.documentElement.scrollHeight || 0, document.body.scrollHeight);
-      let docWidth = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
 
       let winBottom = window.innerHeight;
       let pageYOffset = window.pageYOffset;
 
       if ((pos.right - pos.left) < 78 || (pos.bottom - pos.top) < 78) {
         this.el.classList.add("small-selection");
       } else {
         this.el.classList.remove("small-selection");
@@ -429,29 +511,29 @@ this.ui = (function() { // eslint-disabl
       }
       this.el.style.top = (pos.top - bodyRect.top) + "px";
       this.el.style.left = (pos.left - bodyRect.left) + "px";
       this.el.style.height = (pos.bottom - pos.top - bodyRect.top) + "px";
       this.el.style.width = (pos.right - pos.left - bodyRect.left) + "px";
       this.bgTop.style.top = "0px";
       this.bgTop.style.height = (pos.top - bodyRect.top) + "px";
       this.bgTop.style.left = "0px";
-      this.bgTop.style.width = docWidth + "px";
+      this.bgTop.style.width = "100%";
       this.bgBottom.style.top = (pos.bottom - bodyRect.top) + "px";
       this.bgBottom.style.height = docHeight - (pos.bottom - bodyRect.top) + "px";
       this.bgBottom.style.left = "0px";
-      this.bgBottom.style.width = docWidth + "px";
+      this.bgBottom.style.width = "100%";
       this.bgLeft.style.top = (pos.top - bodyRect.top) + "px";
       this.bgLeft.style.height = pos.bottom - pos.top + "px";
       this.bgLeft.style.left = "0px";
       this.bgLeft.style.width = (pos.left - bodyRect.left) + "px";
       this.bgRight.style.top = (pos.top - bodyRect.top) + "px";
       this.bgRight.style.height = pos.bottom - pos.top + "px";
       this.bgRight.style.left = (pos.right - bodyRect.left) + "px";
-      this.bgRight.style.width = docWidth - (pos.right - bodyRect.left) + "px";
+      this.bgRight.style.width = "100%";
 
       if (!(this.isElementInViewport(this.buttons))) {
         this.cancel.style.position = this.download.style.position = this.save.style.position = "fixed";
         this.cancel.style.left = (pos.left - bodyRect.left - 50) + "px";
         this.download.style.left = ((pos.left - bodyRect.left - 100)) + "px";
         this.save.style.left = ((pos.left - bodyRect.left) - 190) + "px";
         this.cancel.style.top = this.download.style.top = this.save.style.top = (pos.top - bodyRect.top) + "px";
       } else {
@@ -552,16 +634,21 @@ this.ui = (function() { // eslint-disabl
     },
 
     isElementInViewport(el) {
       let rect = el.getBoundingClientRect();
       return (rect.right <= window.innerWidth);
     },
 
     clearSaveDisabled() {
+      if (!this.save) {
+        // Happens if we try to remove the disabled status after the worker
+        // has been shut down
+        return;
+      }
       this.save.removeAttribute("disabled");
     },
 
     el: null,
     boxTopEl: null,
     boxLeftEl: null,
     boxRightEl: null,
     boxBottomEl: null
@@ -614,16 +701,24 @@ this.ui = (function() { // eslint-disabl
       this.el.style.left = (xPos + 12) + "px";
     },
     remove() {
       util.removeNode(this.el);
       this.el = this.xEl = this.yEl = null;
     }
   };
 
+  exports.Preview = {
+    display(dataUrl) {
+      let img = makeEl("IMG");
+      img.src = dataUrl;
+      iframe.document().querySelector(".preview-image").appendChild(img);
+    }
+  };
+
   /** Removes every UI this module creates */
   exports.remove = function() {
     for (let name in exports) {
       if (name.startsWith("iframe")) {
         continue;
       }
       if (typeof exports[name] == "object" && exports[name].remove) {
         exports[name].remove();
--- a/browser/extensions/screenshots/webextension/selector/uicontrol.js
+++ b/browser/extensions/screenshots/webextension/selector/uicontrol.js
@@ -19,16 +19,18 @@ this.uicontrol = (function() {
   "dragging":
     The user has pressed down a mouse button, and is dragging out an area far enough to show a selection
   "selected":
     The user has selected an area
   "resizing":
     The user is resizing the selection
   "cancelled":
     Everything has been cancelled
+  "previewing":
+    The user is previewing the full-screen/visible image
 
   A mousedown goes from crosshairs to dragging.
   A mouseup goes from dragging to selected
   A click outside of the selection goes from selected to crosshairs
   A click on one of the draggers goes from selected to resizing
 
   State variables:
 
@@ -115,44 +117,51 @@ this.uicontrol = (function() {
     H1: true,
     H2: true,
     H3: true,
     H4: true,
     H5: true,
     H6: true
   };
 
+  let captureType;
+
   let standardDisplayCallbacks = {
     cancel: () => {
       sendEvent("cancel-shot", "overlay-cancel-button");
       exports.deactivate();
     }, save: () => {
       sendEvent("save-shot", "overlay-save-button");
       shooter.takeShot("selection", selectedPos);
     }, download: () => {
       sendEvent("download-shot", "overlay-download-button");
       shooter.downloadShot(selectedPos);
     }
   };
 
   let standardOverlayCallbacks = {
+    cancel: () => {
+      sendEvent("cancel-shot", "cancel-preview-button");
+      exports.deactivate();
+    },
     onOpenMyShots: () => {
       sendEvent("goto-myshots", "selection-button");
       callBackground("openMyShots")
         .then(() => exports.deactivate())
         .catch(() => {
           // Handled in communication.js
         });
     },
     onClickVisible: () => {
       sendEvent("capture-visible", "selection-button");
       selectedPos = new Selection(
         window.scrollX, window.scrollY,
         window.scrollX + window.innerWidth, window.scrollY + window.innerHeight);
-      shooter.takeShot("visible", selectedPos);
+      captureType = 'visible';
+      setState("previewing");
     },
     onClickFullPage: () => {
       sendEvent("capture-full-page", "selection-button");
       let width = Math.max(
         document.body.clientWidth,
         document.documentElement.clientWidth,
         document.body.scrollWidth,
         document.documentElement.scrollWidth);
@@ -161,19 +170,28 @@ this.uicontrol = (function() {
         document.body.clientHeight,
         document.documentElement.clientHeight,
         document.body.scrollHeight,
         document.documentElement.scrollHeight);
       height = Math.min(height, MAX_PAGE_HEIGHT);
       selectedPos = new Selection(
         0, 0,
         width, height);
-      shooter.takeShot("fullPage", selectedPos);
+      captureType = 'fullPage';
+      setState("previewing");
+    },
+    onSavePreview: () => {
+      sendEvent(`save-${captureType.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}`, "save-preview-button");
+      shooter.takeShot(captureType, selectedPos, dataUrl);
+    },
+    onDownloadPreview: () => {
+      sendEvent(`download-${captureType.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}`, "download-preview-button");
+      shooter.downloadShot(selectedPos);
     }
-  }
+  };
 
   /** Holds all the objects that handle events for each state: */
   let stateHandlers = {};
 
   function getState() {
     return getState.state;
   }
   getState.state = "cancel";
@@ -339,16 +357,26 @@ this.uicontrol = (function() {
       return Math.sqrt(Math.pow(this.x - x, 2), Math.pow(this.y - y));
     }
   }
 
   /** *********************************************
    * all stateHandlers
    */
 
+  let dataUrl;
+
+  stateHandlers.previewing = {
+    start() {
+      dataUrl = shooter.screenshotPage(selectedPos, captureType);
+      ui.iframe.usePreview();
+      ui.Preview.display(dataUrl);
+    }
+  };
+
   stateHandlers.onboarding = {
     start() {
       if (typeof slides == "undefined") {
         throw new Error("Attempted to set state to onboarding without loading slides");
       }
       catcher.watchPromise(slides.display({
         onEnd: this.slidesOnEnd.bind(this)
       }));