Bug 1390985 - Export Screenshots 16.1.0 to Firefox r=kmag
authorIan Bicking <ianb@colorstudy.com>
Fri, 25 Aug 2017 14:16:22 -0500
changeset 429361 7acf5f94b0ada29b34cfdc0d21c66eddbd6505fc
parent 429360 6257f5ada1814787d8a1cefebdce151fbe00e837
child 429362 aee7b8d6a8867cdba422c0a9485750d622b37bb0
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1390985
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1390985 - Export Screenshots 16.1.0 to Firefox r=kmag
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
@@ -31,16 +31,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'
 ]
 
@@ -358,16 +362,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)
       }));