Bug 1323395 - Show warning when disk space available for firefox is not enough, r=jaws draft
authorFischer.json <fischer.json@gmail.com>
Wed, 28 Dec 2016 16:53:38 +0800
changeset 485180 fabe7a2a7e9be57023453c169b9f68cae99a9492
parent 485098 a9ec72f82299250e6023988e238931bbca0ef7fa
child 545957 7fd42ecfcc1166d8ed486a9f60f8d2f298510343
push id45668
push userbmo:fliu@mozilla.com
push dateThu, 16 Feb 2017 09:49:33 +0000
reviewersjaws
bugs1323395
milestone54.0a1
Bug 1323395 - Show warning when disk space available for firefox is not enough, r=jaws MozReview-Commit-ID: HttJ2RFCTtS
browser/base/content/browser.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_storagePressure_notification.js
browser/locales/en-US/chrome/browser/preferences/preferences.properties
modules/libpref/init/all.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -426,16 +426,91 @@ const gSessionHistoryObserver = {
     // Hide session restore button on about:home
     window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");
 
     // Clear undo history of the URL bar
     gURLBar.editor.transactionManager.clear()
   }
 };
 
+const gStoragePressureObserver = {
+  _lastNotificationTime: -1,
+
+  observe(subject, topic, data) {
+    if (topic != "QuotaManager::StoragePressure" ||
+        !Services.prefs.getBoolPref("browser.storageManager.enabled")) {
+      return;
+    }
+
+    // Don't display notification twice within the given interval.
+    // This is because
+    //   - not to annoy user
+    //   - give user some time to clean space.
+    //     Even user sees notification and starts acting, it still takes some time.
+    const MIN_NOTIFICATION_INTERVAL_MS =
+      Services.prefs.getIntPref("browser.storageManager.pressureNotification.minIntervalMS");
+    let duration = Date.now() - this._lastNotificationTime;
+    if (duration <= MIN_NOTIFICATION_INTERVAL_MS) {
+      return;
+    }
+    this._lastNotificationTime = Date.now();
+
+    const BYTES_IN_GIGABYTE = 1073741824;
+    const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
+      Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
+    let msg = "";
+    let buttons = [];
+    let usage = parseInt(data);
+    let prefStrBundle = document.getElementById("bundle_preferences");
+    let notificationBox = document.getElementById("high-priority-global-notificationbox");
+    buttons.push({
+      label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
+      accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"),
+      callback(notificationBar, button) {
+        let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
+        gBrowser.selectedTab = gBrowser.addTab(learnMoreURL);
+      }
+    });
+    if (usage < USAGE_THRESHOLD_BYTES) {
+      // The firefox-used space < 5GB, then warn user to free some disk space.
+      // This is because this usage is small and not the main cause for space issue.
+      // In order to avoid the bad and wrong impression among users that
+      // firefox eats disk space a lot, indicate users to clean up other disk space.
+      msg = prefStrBundle.getString("spaceAlert.under5GB.description");
+      buttons.push({
+        label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"),
+        accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"),
+        callback() {}
+      });
+    } else {
+      // The firefox-used space >= 5GB, then guide users to about:preferences
+      // to clear some data stored on firefox by websites.
+      let descriptionStringID = "spaceAlert.over5GB.description";
+      let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label";
+      let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey";
+      if (AppConstants.platform == "win") {
+        descriptionStringID = "spaceAlert.over5GB.descriptionWin";
+        prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label";
+        prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey";
+      }
+      msg = prefStrBundle.getString(descriptionStringID);
+      buttons.push({
+        label: prefStrBundle.getString(prefButtonLabelStringID),
+        accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID),
+        callback(notificationBar, button) {
+          gBrowser.ownerGlobal.openPreferences("advanced", { advancedTab: "networkTab" });
+        }
+      });
+    }
+
+    notificationBox.appendNotification(
+      msg, "storage-pressure-notification", null, notificationBox.PRIORITY_WARNING_HIGH, buttons, null);
+  }
+};
+
 /**
  * Given a starting docshell and a URI to look up, find the docshell the URI
  * is loaded in.
  * @param   aDocument
  *          A document to find instead of using just a URI - this is more specific.
  * @param   aDocShell
  *          The doc shell to start at
  * @param   aSoughtURI
@@ -1265,16 +1340,17 @@ var gBrowserInit = {
       }
     }
 
     // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
     setTimeout(function() { SafeBrowsing.init(); }, 2000);
 
     Services.obs.addObserver(gIdentityHandler, "perm-changed", false);
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+    Services.obs.addObserver(gStoragePressureObserver, "QuotaManager::StoragePressure", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
@@ -1599,16 +1675,17 @@ var gBrowserInit = {
       gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
       ctrlTab.uninit();
       SocialUI.uninit();
       gBrowserThumbnails.uninit();
       FullZoom.destroy();
 
       Services.obs.removeObserver(gIdentityHandler, "perm-changed");
       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+      Services.obs.removeObserver(gStoragePressureObserver, "QuotaManager::StoragePressure");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-origin-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-confirmation");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
       window.messageManager.removeMessageListener("Browser:URIFixup", gKeywordURIFixup);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -338,16 +338,17 @@ skip-if = e10s && debug && os == "win" #
 support-files =
   contentSearchUI.html
   contentSearchUI.js
 [browser_selectpopup.js]
 skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
 [browser_selectTabAtIndex.js]
 [browser_ssl_error_reports.js]
 [browser_star_hsts.js]
+[browser_storagePressure_notification.js]
 [browser_subframe_favicons_not_used.js]
 [browser_syncui.js]
 skip-if = os == 'linux' # Bug 1304272
 [browser_tab_close_dependent_window.js]
 [browser_tabDrop.js]
 [browser_tabReorder.js]
 [browser_tab_detach_restore.js]
 [browser_tab_drag_drop_perwindow.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_storagePressure_notification.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function notifyStoragePressure(usage = 100) {
+  let notifyPromise = TestUtils.topicObserved("QuotaManager::StoragePressure", () => true);
+  Services.obs.notifyObservers(null, "QuotaManager::StoragePressure", usage);
+  return notifyPromise;
+}
+
+function advancedAboutPrefPromise() {
+  let promises = [
+    BrowserTestUtils.waitForLocationChange(gBrowser, "about:preferences#advanced"),
+    TestUtils.topicObserved("advanced-pane-loaded", () => true)
+  ];
+  return Promise.all(promises);
+}
+
+// Test only displaying notification once within the given interval
+add_task(function* () {
+  const TEST_NOTIFICATION_INTERVAL_MS = 2000;
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]});
+
+  yield notifyStoragePressure();
+  let notificationbox = document.getElementById("high-priority-global-notificationbox");
+  let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification");
+  notification.close();
+
+  yield notifyStoragePressure();
+  notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  is(notification, null, "Should not display storage pressure notification more than once within the given interval");
+
+  yield new Promise(resolve => setTimeout(resolve, TEST_NOTIFICATION_INTERVAL_MS + 1));
+  yield notifyStoragePressure();
+  notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification after the given interval");
+  notification.close();
+});
+
+// Test guiding user to about:preferences when usage exceeds the given threshold
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", 0]]});
+
+  const BYTES_IN_GIGABYTE = 1073741824;
+  const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
+    Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
+  yield notifyStoragePressure(USAGE_THRESHOLD_BYTES);
+  let notificationbox = document.getElementById("high-priority-global-notificationbox");
+  let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
+  ok(notification instanceof XULElement, "Should display storage pressure notification");
+
+  let prefBtn = notification.getElementsByTagName("button")[1];
+  let aboutPrefPromise = advancedAboutPrefPromise();
+  prefBtn.doCommand();
+  yield aboutPrefPromise;
+  let prefDoc = gBrowser.selectedBrowser.contentDocument;
+  let advancedPrefs = prefDoc.getElementById("advancedPrefs");
+  is(advancedPrefs.selectedIndex, 2, "Should open the Network tab in about:preferences#advanced");
+});
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -195,16 +195,29 @@ siteUsage=%1$S %2$S
 # never displayed together and can share the same accesskey.
 # When only partial sites are shown as a result of keyword search,
 # removeAllShown is displayed as button label.
 # removeAll is displayed when no keyword search and all sites are shown.
 removeAllSiteData.label=Remove All
 removeAllSiteData.accesskey=e
 removeAllSiteDataShown.label=Remove All Shown
 removeAllSiteDataShown.accesskey=e
+spaceAlert.learnMoreButton.label=Learn More
+spaceAlert.learnMoreButton.accesskey=L
+spaceAlert.over5GB.prefButton.label=Open Preferences
+spaceAlert.over5GB.prefButton.accesskey=O
+# LOCALIZATION NOTE (spaceAlert.over5GB.prefButtonWin.label): On Windows Preferences is called Options
+spaceAlert.over5GB.prefButtonWin.label=Open Options
+spaceAlert.over5GB.prefButtonWin.accesskey=O
+spaceAlert.over5GB.description=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data.
+# LOCALIZATION NOTE (spaceAlert.over5GB.descriptionWin): On Windows Preferences is called Options
+spaceAlert.over5GB.descriptionWin=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data.
+spaceAlert.under5GB.okButton.label=OK, Got it
+spaceAlert.under5GB.okButton.accesskey=K
+spaceAlert.under5GB.description=Firefox is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.
 
 # LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
 shouldRestartTitle=Restart %S
 okToRestartButton=Restart %S now
 revertNoRestartButton=Revert
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5581,16 +5581,18 @@ pref("dom.storageManager.enabled", false
 // a single web page in a row, all following authentication dialogs will
 // be blocked (automatically canceled) for that page. The counter resets
 // when the page is reloaded. To turn this feature off, just set the limit to 0.
 pref("prompts.authentication_dialog_abuse_limit", 3);
 
 // Enable the Storage management in about:preferences and persistent-storage permission request
 // To enable the DOM implementation, turn on "dom.storageManager.enabled"
 pref("browser.storageManager.enabled", false);
+pref("browser.storageManager.pressureNotification.minIntervalMS", 1200000);
+pref("browser.storageManager.pressureNotification.usageThresholdGB", 5);
 pref("dom.IntersectionObserver.enabled", false);
 
 // Whether module scripts (<script type="module">) are enabled for content.
 pref("dom.moduleScripts.enabled", false);
 
 #ifdef FUZZING
 pref("fuzzing.enabled", false);
 #endif