Backed out changeset d6a50efcae08 (bug 1579444) for ES lint failure on OfflineAppsChild.jsm CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Tue, 17 Sep 2019 18:42:36 +0300
changeset 493602 2cf45cdd0a1be73da673a59a3673dcc453e9b83b
parent 493601 cf84fbd266c78c157c7a53682c3a7c9302ab2c38
child 493603 f282753bf7e5db2306ee4769e464d5ac6c4240da
push id95584
push userbtara@mozilla.com
push dateTue, 17 Sep 2019 15:43:08 +0000
treeherderautoland@2cf45cdd0a1b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1579444
milestone71.0a1
backs outd6a50efcae08f98aa970281a84d3593b54de986b
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
Backed out changeset d6a50efcae08 (bug 1579444) for ES lint failure on OfflineAppsChild.jsm CLOSED TREE
browser/actors/OfflineAppsChild.jsm
browser/base/content/browser.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_gZipOfflineChild.js
browser/base/content/test/general/browser_offlineQuotaNotification.js
browser/base/content/test/general/gZipOfflineChild.cacheManifest
browser/base/content/test/general/gZipOfflineChild.cacheManifest^headers^
browser/base/content/test/general/gZipOfflineChild.html
browser/base/content/test/general/gZipOfflineChild.html^headers^
browser/base/content/test/general/offlineQuotaNotification.cacheManifest
browser/base/content/test/general/offlineQuotaNotification.html
browser/base/content/test/general/test_offline_gzip.html
browser/locales/en-US/chrome/browser/browser.properties
mobile/android/app/mobile.js
modules/libpref/init/all.js
--- a/browser/actors/OfflineAppsChild.jsm
+++ b/browser/actors/OfflineAppsChild.jsm
@@ -9,27 +9,37 @@ var EXPORTED_SYMBOLS = ["OfflineAppsChil
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 const { ActorChild } = ChromeUtils.import(
   "resource://gre/modules/ActorChild.jsm"
 );
 
+XPCOMUtils.defineLazyModuleGetters(this, {
+  E10SUtils: "resource://gre/modules/E10SUtils.jsm",
+});
+
 class OfflineAppsChild extends ActorChild {
   constructor(dispatcher) {
     super(dispatcher);
 
     this._docId = 0;
     this._docIdMap = new Map();
 
     this._docManifestSet = new Set();
+
+    this._observerAdded = false;
   }
 
   registerWindow(aWindow) {
+    if (!this._observerAdded) {
+      this._observerAdded = true;
+      Services.obs.addObserver(this, "offline-cache-update-completed", true);
+    }
     let manifestURI = this._getManifestURI(aWindow);
     this._docManifestSet.add(manifestURI.spec);
   }
 
   handleEvent(event) {
     if (event.type == "MozApplicationManifest") {
       this.registerWindow(event.originalTarget.defaultView);
     }
@@ -82,14 +92,27 @@ class OfflineAppsChild extends ActorChil
       let doc = this._docIdMap.get(aMessage.data.docId);
       doc = doc && doc.get();
       if (doc) {
         this._startFetching(doc);
       }
       this._docIdMap.delete(aMessage.data.docId);
     }
   }
+
+  observe(aSubject, aTopic, aState) {
+    if (aTopic == "offline-cache-update-completed") {
+      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
+      let uri = cacheUpdate.manifestURI;
+      if (uri && this._docManifestSet.has(uri.spec)) {
+        this.mm.sendAsyncMessage("OfflineApps:CheckUsage", {
+          uri: uri.spec,
+          principal: E10SUtils.serializePrincipal(cacheUpdate.loadingPrincipal),
+        });
+      }
+    }
+  }
 }
 
 OfflineAppsChild.prototype.QueryInterface = ChromeUtils.generateQI([
   Ci.nsIObserver,
   Ci.nsISupportsWeakReference,
 ]);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1974,16 +1974,20 @@ var gBrowserInit = {
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
     // Bug 1531854 - The hidden window is force-created here
     // until all of its dependencies are handled.
     Services.appShell.hiddenDOMWindow;
 
+    // We need to set the OfflineApps message listeners up before we
+    // load homepages, which might need them.
+    OfflineApps.init();
+
     gBrowser.addEventListener(
       "InsecureLoginFormsStateChange",
       function() {
         gIdentityHandler.refreshForInsecureLoginForms();
       },
       true
     );
 
@@ -7924,16 +7928,125 @@ var BrowserOffline = {
     if (offlineLocked) {
       this._uiElement.setAttribute("disabled", "true");
     }
 
     this._uiElement.setAttribute("checked", aOffline);
   },
 };
 
+var OfflineApps = {
+  warnUsage(browser, principal, host) {
+    if (!browser) {
+      return;
+    }
+
+    let mainAction = {
+      label: gNavigatorBundle.getString("offlineApps.manageUsage"),
+      accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
+      callback: this.manage,
+    };
+
+    let warnQuotaKB = Services.prefs.getIntPref("offline-apps.quota.warn");
+    // This message shows the quota in MB, and so we divide the quota (in kb) by 1024.
+    let message = gNavigatorBundle.getFormattedString("offlineApps.usage", [
+      host,
+      warnQuotaKB / 1024,
+    ]);
+
+    let anchorID = "indexedDB-notification-icon";
+    let options = {
+      persistent: true,
+      hideClose: true,
+    };
+    PopupNotifications.show(
+      browser,
+      "offline-app-usage",
+      message,
+      anchorID,
+      mainAction,
+      null,
+      options
+    );
+
+    // Now that we've warned once, prevent the warning from showing up
+    // again.
+    Services.perms.addFromPrincipal(
+      principal,
+      "offline-app",
+      Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN
+    );
+  },
+
+  // XXX: duplicated in preferences/advanced.js
+  _getOfflineAppUsage(host, groups) {
+    let cacheService = Cc[
+      "@mozilla.org/network/application-cache-service;1"
+    ].getService(Ci.nsIApplicationCacheService);
+    if (!groups) {
+      try {
+        groups = cacheService.getGroups();
+      } catch (ex) {
+        return 0;
+      }
+    }
+
+    let usage = 0;
+    for (let group of groups) {
+      let uri = Services.io.newURI(group);
+      if (uri.asciiHost == host) {
+        let cache = cacheService.getActiveCache(group);
+        usage += cache.usage;
+      }
+    }
+
+    return usage;
+  },
+
+  _usedMoreThanWarnQuota(principal, asciiHost) {
+    // if the user has already allowed excessive usage, don't bother checking
+    if (
+      Services.perms.testExactPermissionFromPrincipal(
+        principal,
+        "offline-app"
+      ) != Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN
+    ) {
+      let usageBytes = this._getOfflineAppUsage(asciiHost);
+      let warnQuotaKB = Services.prefs.getIntPref("offline-apps.quota.warn");
+      // The pref is in kb, the usage we get is in bytes, so multiply the quota
+      // to compare correctly:
+      if (usageBytes >= warnQuotaKB * 1024) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  manage() {
+    openPreferences("panePrivacy");
+  },
+
+  receiveMessage(msg) {
+    if (msg.name !== "OfflineApps:CheckUsage") {
+      return;
+    }
+    let uri = makeURI(msg.data.uri);
+    let principal = E10SUtils.deserializePrincipal(msg.data.principal);
+    if (this._usedMoreThanWarnQuota(principal, uri.asciiHost)) {
+      this.warnUsage(msg.target, principal, uri.host);
+    }
+  },
+
+  init() {
+    let mm = window.messageManager;
+    mm.addMessageListener("OfflineApps:CheckUsage", this);
+  },
+};
+
 var IndexedDBPromptHelper = {
   _permissionsPrompt: "indexedDB-permissions-prompt",
   _permissionsResponse: "indexedDB-permissions-response",
 
   _notificationIcon: "indexedDB-notification-icon",
 
   init: function IndexedDBPromptHelper_init() {
     Services.obs.addObserver(this, this._permissionsPrompt);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -297,16 +297,24 @@ skip-if = toolkit == "windows" # Disable
 tags = clipboard
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_minimize.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_modifiedclick_inherit_principal.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_new_http_window_opened_from_file_tab.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
+[browser_offlineQuotaNotification.js]
+support-files = offlineQuotaNotification.cacheManifest offlineQuotaNotification.html
+skip-if = true # Bug 1579444
+# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
+[browser_gZipOfflineChild.js]
+skip-if = true # Bug 1579444
+support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
+# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_page_style_menu.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_page_style_menu_update.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_plainTextLinks.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_printpreview.js]
 skip-if = fission || os == 'win' # Bug 1384127
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_gZipOfflineChild.js
@@ -0,0 +1,92 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const URL =
+  "http://mochi.test:8888/browser/browser/base/content/test/general/test_offline_gzip.html";
+
+registerCleanupFunction(function() {
+  // Clean up after ourself
+  let uri = Services.io.newURI(URL);
+  let principal = Services.scriptSecurityManager.createContentPrincipal(
+    uri,
+    {}
+  );
+  Services.perms.removeFromPrincipal(principal, "offline-app");
+});
+
+//
+// Handle "message" events which are posted from the iframe upon
+// offline cache events.
+//
+function contentTask() {
+  const { clearInterval, setInterval } = ChromeUtils.import(
+    "resource://gre/modules/Timer.jsm"
+  );
+
+  let resolve;
+  let promise = new Promise(r => {
+    resolve = r;
+  });
+
+  var cacheCount = 0;
+  var intervalID = 0;
+
+  function handleMessageEvents(event) {
+    cacheCount++;
+    switch (cacheCount) {
+      case 1:
+        // This is the initial caching off offline data.
+        is(event.data, "oncache", "Child was successfully cached.");
+        // Reload the frame; this will generate an error message
+        // in the case of bug 501422.
+        event.source.location.reload();
+        // Use setInterval to repeatedly call a function which
+        // checks that one of two things has occurred:  either
+        // the offline cache is udpated (which means our iframe
+        // successfully reloaded), or the string "error" appears
+        // in the iframe, as in the case of bug 501422.
+        intervalID = setInterval(function() {
+          // Sometimes document.body may not exist, and trying to access
+          // it will throw an exception, so handle this case.
+          try {
+            var bodyInnerHTML = event.source.document.body.innerHTML;
+          } catch (e) {
+            bodyInnerHTML = "";
+          }
+          if (cacheCount == 2 || bodyInnerHTML.includes("error")) {
+            clearInterval(intervalID);
+            is(cacheCount, 2, "frame not reloaded successfully");
+            if (cacheCount != 2) {
+              finish();
+            }
+          }
+        }, 100);
+        break;
+      case 2:
+        is(event.data, "onupdate", "Child was successfully updated.");
+        clearInterval(intervalID);
+        resolve();
+        break;
+      default:
+        // how'd we get here?
+        ok(false, "cacheCount not 1 or 2");
+    }
+  }
+
+  content.addEventListener("message", handleMessageEvents);
+  return promise;
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  // Open a new tab.
+  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL);
+  registerCleanupFunction(() => gBrowser.removeCurrentTab());
+
+  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+    ContentTask.spawn(gBrowser.selectedBrowser, null, contentTask).then(finish);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -0,0 +1,106 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test offline quota warnings - must be run as a mochitest-browser test or
+// else the test runner gets in the way of notifications due to bug 857897.
+
+const URL =
+  "http://mochi.test:8888/browser/browser/base/content/test/general/offlineQuotaNotification.html";
+
+registerCleanupFunction(function() {
+  // Clean up after ourself
+  let uri = Services.io.newURI(URL);
+  let principal = Services.scriptSecurityManager.createContentPrincipal(
+    uri,
+    {}
+  );
+  Services.perms.removeFromPrincipal(principal, "offline-app");
+  Services.prefs.clearUserPref("offline-apps.quota.warn");
+  let { OfflineAppCacheHelper } = ChromeUtils.import(
+    "resource://gre/modules/offlineAppCache.jsm"
+  );
+  OfflineAppCacheHelper.clear();
+});
+
+// Same as the other one, but for in-content preferences
+function checkInContentPreferences(win) {
+  let doc = win.document;
+  let sel = doc.getElementById("categories").selectedItems[0].id;
+  is(
+    gBrowser.currentURI.spec,
+    "about:preferences#privacy",
+    "about:preferences loaded"
+  );
+  is(sel, "category-privacy", "Privacy pane was selected");
+  // all good, we are done.
+  win.close();
+  finish();
+}
+
+async function test() {
+  waitForExplicitFinish();
+
+  let notificationShown = promiseNotification();
+
+  // Open a new tab.
+  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL);
+  registerCleanupFunction(() => gBrowser.removeCurrentTab());
+
+  // Wait for the tab to load.
+  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  info("Loaded page, adding onCached handler");
+  // Need a promise to keep track of when we've added our handler.
+  let mm = gBrowser.selectedBrowser.messageManager;
+  let onCachedAttached = BrowserTestUtils.waitForMessage(
+    mm,
+    "Test:OnCachedAttached"
+  );
+  let gotCached = ContentTask.spawn(
+    gBrowser.selectedBrowser,
+    null,
+    async function() {
+      return new Promise(resolve => {
+        content.window.applicationCache.oncached = function() {
+          setTimeout(resolve, 0);
+        };
+        sendAsyncMessage("Test:OnCachedAttached");
+      });
+    }
+  );
+  gotCached.then(async function() {
+    // We got cached - now we should have provoked the quota warning.
+    await notificationShown;
+    let notification = PopupNotifications.getNotification("offline-app-usage");
+    ok(notification, "have offline-app-usage notification");
+    // select the default action - this should cause the preferences
+    // tab to open - which we track via an "Initialized" event.
+    PopupNotifications.panel.firstElementChild.button.click();
+    let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+    newTabBrowser.addEventListener(
+      "Initialized",
+      function() {
+        executeSoon(function() {
+          checkInContentPreferences(newTabBrowser.contentWindow);
+        });
+      },
+      { capture: true, once: true }
+    );
+  });
+  onCachedAttached.then(function() {
+    Services.prefs.setIntPref("offline-apps.quota.warn", 1);
+  });
+}
+
+function promiseNotification() {
+  return new Promise(resolve => {
+    PopupNotifications.panel.addEventListener(
+      "popupshown",
+      function() {
+        resolve();
+      },
+      { once: true }
+    );
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/gZipOfflineChild.cacheManifest
@@ -0,0 +1,2 @@
+CACHE MANIFEST
+gZipOfflineChild.html
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/gZipOfflineChild.cacheManifest^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/cache-manifest
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ea2caa12553bc4754c59e9d3007cacd76460a572
GIT binary patch
literal 303
zc$@(*0nq**iwFoCjp|kc17>e;E@*UZYyfqUF;BxV5QTUDiX&5tNTO~?I9WPZEKF?1
zzSLKxwq;)^1@*sUC#}RlCrkJFy}NrSEgwfXDn|n*?yCJeh1X#i$>G-0Xd11w#od)x
zy^t1_=>>p%sB3t$lwhC{A;0@?3IPqU2OUu{g8%C@cdF}b!Neo>vRz&wkH`wNRWV^6
zVd9~FE}fE&6d3p%u&WrqxIL&(wNx_~!{juRcX<Fu$*$6*HKw$NpFG})objh7sECf6
z;1lmMQ+vF9dxGk*TEB>k5GsU`v?^ZPvTozuytaW3ZcSF%_SUGn@+^nTe`@EaU`haY
z^$WjifHC@bUp?bREM&VRk^^g(+X(sL&AFUv)6daouqpUjZp$Tl@eAkxgv$E?007-I
BlHC9R
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/gZipOfflineChild.html^headers^
@@ -0,0 +1,2 @@
+Content-Type: text/html
+Content-Encoding: gzip
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/offlineQuotaNotification.cacheManifest
@@ -0,0 +1,7 @@
+CACHE MANIFEST
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+# store a "large" file so an "over quota warning" will be issued - any file
+# larger than 1kb and in '_BROWSER_FILES' should be right...
+title_test.svg
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/offlineQuotaNotification.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html manifest="offlineQuotaNotification.cacheManifest">
+<head>
+  <meta charset="utf-8">
+  <title>Test offline app quota notification</title>
+  <!-- Any copyright is dedicated to the Public Domain.
+     - http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/test_offline_gzip.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=501422
+
+When content which was transported over the network with
+Content-Type: gzip is added to the offline 
+cache, it can be fetched from the cache successfully.
+-->
+<head>
+  <title>Test gzipped offline resources</title>
+  <meta charset="utf-8">
+</head>
+<body>
+<p id="display">
+<iframe name="testFrame" src="gZipOfflineChild.html"></iframe>
+
+<div id="content" style="display: none">
+</div>
+</body>
+</html>
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -432,16 +432,20 @@ newTabContainer.tooltip=Open a new tab (
 
 # Offline web applications
 offlineApps.available2=Will you allow %S to store data on your computer?
 offlineApps.allowStoring.label=Allow Storing Data
 offlineApps.allowStoring.accesskey=A
 offlineApps.dontAllow.label=Don’t Allow
 offlineApps.dontAllow.accesskey=n
 
+offlineApps.usage=This website (%S) is now storing more than %SMB of data on your computer for offline use.
+offlineApps.manageUsage=Show settings
+offlineApps.manageUsageAccessKey=S
+
 # Canvas permission prompt
 # LOCALIZATION NOTE (canvas.siteprompt): %S is hostname
 canvas.siteprompt=Will you allow %S to use your HTML5 canvas image data? This may be used to uniquely identify your computer.
 canvas.notAllow=Don’t Allow
 canvas.notAllow.accesskey=n
 canvas.allow=Allow Data Access
 canvas.allow.accesskey=A
 canvas.remember=Always remember my decision
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -75,16 +75,17 @@ pref("ui.caretBlinkCount", 10);
 pref("browser.cache.memory_limit", 5120); // 5 MB
 
 /* image cache prefs */
 pref("image.cache.size", 1048576); // bytes
 
 /* offline cache prefs */
 pref("browser.offline-apps.notify", true);
 pref("browser.cache.offline.capacity", 5120); // kilobytes
+pref("offline-apps.quota.warn", 1024); // kilobytes
 
 // Automatically shrink-to-fit image documents.
 pref("browser.enable_automatic_image_resizing", true);
 
 /* disable some protocol warnings */
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.sms", false);
 pref("network.protocol-handler.warn-external.mailto", false);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -222,16 +222,20 @@ pref("browser.cache.check_doc_frequency"
 pref("browser.cache.frecency_half_life_hours", 6);
 
 // AppCache over insecure connection is disabled by default
 pref("browser.cache.offline.insecure.enable",  false);
 
 // offline cache capacity in kilobytes
 pref("browser.cache.offline.capacity",         512000);
 
+// the user should be warned if offline app disk usage exceeds this amount
+// (in kilobytes)
+pref("offline-apps.quota.warn",        51200);
+
 // Don't show "Open with" option on download dialog if true.
 pref("browser.download.forbid_open_with", false);
 
 // Insecure registerProtocolHandler is disabled by default
 pref("dom.registerProtocolHandler.insecure.enabled", false);
 
 // Whether or not indexedDB is enabled.
 pref("dom.indexedDB.enabled", true);