Backed out 2 changesets (bug 1591803) for causing build busatges on PermissionManager.idl. CLOSED TREE
authorArthur Iakab <aiakab@mozilla.com>
Mon, 28 Oct 2019 22:12:01 +0200
changeset 499510 d06b8a3a4cf7bc930025714a5b274294b901a33b
parent 499509 58527612bae7e266dc6bff0f9b7298304b3899a1
child 499511 d33a6058d98bb3508d237525e15bcb992e9e9315
push id99013
push useraiakab@mozilla.com
push dateMon, 28 Oct 2019 20:22:08 +0000
treeherderautoland@011a4f15afe9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1591803
milestone72.0a1
backs out060f159fa43d25021c43a009b1ee26c50813d765
b53a3d8c408d5be75b6a7ad94a07bdb711270793
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 2 changesets (bug 1591803) for causing build busatges on PermissionManager.idl. CLOSED TREE Backed out changeset 060f159fa43d (bug 1591803) Backed out changeset b53a3d8c408d (bug 1591803)
browser/base/content/browser-siteProtections.js
browser/base/content/test/plugins/head.js
browser/base/content/test/popups/browser_popup_blocker.js
browser/base/content/test/sanitize/browser_sanitize-sitepermissions.js
browser/components/BrowserGlue.jsm
browser/components/preferences/permissions.js
browser/components/preferences/sitePermissions.js
browser/components/preferences/translation.js
browser/components/translation/test/browser_translation_exceptions.js
browser/modules/PermissionUITelemetry.jsm
browser/modules/Sanitizer.jsm
browser/modules/SiteDataManager.jsm
dom/notification/Notification.cpp
dom/notification/Notification.h
extensions/permissions/nsPermissionManager.cpp
extensions/permissions/test/unit/head.js
extensions/permissions/test/unit/test_permmanager_defaults.js
extensions/permissions/test/unit/test_permmanager_migrate_4-7.js
extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js
extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js
extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js
extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js
extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js
extensions/permissions/test/unit/test_permmanager_migrate_7-8.js
extensions/permissions/test/unit/test_permmanager_migrate_9-10.js
extensions/permissions/test/unit/test_permmanager_notifications.js
extensions/permissions/test/unit/test_permmanager_removebytype.js
extensions/permissions/test/unit/test_permmanager_removebytypesince.js
extensions/permissions/test/unit/test_permmanager_removepermission.js
layout/build/components.conf
netwerk/base/nsIPermissionManager.idl
netwerk/test/unit/test_permmgr.js
toolkit/components/cleardata/ClearDataService.jsm
toolkit/components/passwordmgr/LoginManager.jsm
toolkit/components/passwordmgr/test/unit/test_storage_mozStorage.js
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/histogram-allowlists.json
--- a/browser/base/content/browser-siteProtections.js
+++ b/browser/base/content/browser-siteProtections.js
@@ -788,17 +788,17 @@ var ThirdPartyCookies = {
       }
     }
 
     // OAs don't matter here, so we can just use the hostname.
     let host = Services.io.newURI(origin).host;
 
     // Cookie exceptions get "inherited" from parent- to sub-domain, so we need to
     // clear any cookie permissions from parent domains as well.
-    for (let perm of Services.perms.all) {
+    for (let perm of Services.perms.enumerator) {
       if (
         perm.type == "cookie" &&
         Services.eTLD.hasRootDomain(host, perm.principal.URI.host)
       ) {
         Services.perms.removePermission(perm);
       }
     }
   },
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -196,17 +196,19 @@ function promiseReloadPlugin(aId, aBrows
     // eslint-disable-next-line no-self-assign
     plugin.src = plugin.src;
   });
 }
 
 // after a test is done using the plugin doorhanger, we should just clear
 // any permissions that may have crept in
 function clearAllPluginPermissions() {
-  for (let perm of Services.perms.all) {
+  let perms = Services.perms.enumerator;
+  while (perms.hasMoreElements()) {
+    let perm = perms.getNext();
     if (perm.type.startsWith("plugin")) {
       info(
         "removing permission:" + perm.principal.origin + " " + perm.type + "\n"
       );
       Services.perms.removePermission(perm);
     }
   }
 }
--- a/browser/base/content/test/popups/browser_popup_blocker.js
+++ b/browser/base/content/test/popups/browser_popup_blocker.js
@@ -3,17 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const baseURL = getRootDirectory(gTestPath).replace(
   "chrome://mochitests/content",
   "http://example.com"
 );
 
 function clearAllPermissionsByPrefix(aPrefix) {
-  for (let perm of Services.perms.all) {
+  let perms = Services.perms.enumerator;
+  while (perms.hasMoreElements()) {
+    let perm = perms.getNext();
     if (perm.type.startsWith(aPrefix)) {
       Services.perms.removePermission(perm);
     }
   }
 }
 
 add_task(async function setup() {
   // Enable the popup blocker.
--- a/browser/base/content/test/sanitize/browser_sanitize-sitepermissions.js
+++ b/browser/base/content/test/sanitize/browser_sanitize-sitepermissions.js
@@ -1,12 +1,18 @@
 // Bug 380852 - Delete permission manager entries in Clear Recent History
 
 function countPermissions() {
-  return Services.perms.all.length;
+  let result = 0;
+  let enumerator = Services.perms.enumerator;
+  while (enumerator.hasMoreElements()) {
+    result++;
+    enumerator.getNext();
+  }
+  return result;
 }
 
 add_task(async function test() {
   // sanitize before we start so we have a good baseline.
   await Sanitizer.sanitize(["siteSettings"], { ignoreTimespan: false });
 
   // Count how many permissions we start with - some are defaults that
   // will not be sanitized.
@@ -16,17 +22,17 @@ add_task(async function test() {
   PermissionTestUtils.add(
     "http://example.com",
     "testing",
     Services.perms.ALLOW_ACTION
   );
 
   // Sanity check
   ok(
-    !!Services.perms.all.length,
+    Services.perms.enumerator.hasMoreElements(),
     "Permission manager should have elements, since we just added one"
   );
 
   // Clear it
   await Sanitizer.sanitize(["siteSettings"], { ignoreTimespan: false });
 
   // Make sure it's gone
   is(
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -1743,17 +1743,17 @@ BrowserGlue.prototype = {
       .add(!tpPBDisabled);
 
     let cookieBehavior = Services.prefs.getIntPref(
       "network.cookie.cookieBehavior"
     );
     Services.telemetry.getHistogramById("COOKIE_BEHAVIOR").add(cookieBehavior);
 
     let exceptions = 0;
-    for (let permission of Services.perms.all) {
+    for (let permission of Services.perms.enumerator) {
       if (permission.type == "trackingprotection") {
         exceptions++;
       }
     }
     Services.telemetry.scalarSet("contentblocking.exceptions", exceptions);
 
     let fpEnabled = Services.prefs.getBoolPref(
       "privacy.trackingprotection.fingerprinting.enabled"
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -294,17 +294,17 @@ var gPermissionManager = {
     )[0];
     if (permissionlistitem) {
       permissionlistitem.remove();
     }
   },
 
   _loadPermissions() {
     // load permissions into a table.
-    for (let nextPermission of Services.perms.all) {
+    for (let nextPermission of Services.perms.enumerator) {
       this._addPermissionToList(nextPermission);
     }
   },
 
   _createPermissionListItem(permission) {
     let richlistitem = document.createXULElement("richlistitem");
     richlistitem.setAttribute("origin", permission.origin);
     let row = document.createXULElement("hbox");
--- a/browser/components/preferences/sitePermissions.js
+++ b/browser/components/preferences/sitePermissions.js
@@ -325,17 +325,17 @@ var gSitePermissionsManager = {
     )[0];
     if (permissionlistitem) {
       permissionlistitem.remove();
     }
   },
 
   _loadPermissions() {
     // load permissions into a table.
-    for (let nextPermission of Services.perms.all) {
+    for (let nextPermission of Services.perms.enumerator) {
       this._addPermissionToList(nextPermission);
     }
   },
 
   _createPermissionListItem(permission) {
     let width = "75";
     let richlistitem = document.createXULElement("richlistitem");
     richlistitem.setAttribute("origin", permission.origin);
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -89,17 +89,17 @@ var gTranslationExceptions = {
   onLoad() {
     if (this._siteTree) {
       // Re-using an open dialog, clear the old observers.
       this.uninit();
     }
 
     // Load site permissions into an array.
     this._sites = [];
-    for (let perm of Services.perms.all) {
+    for (let perm of Services.perms.enumerator) {
       if (
         perm.type == kPermissionType &&
         perm.capability == Services.perms.DENY_ACTION
       ) {
         this._sites.push(perm.principal.origin);
       }
     }
     Services.obs.addObserver(this, "perm-changed");
--- a/browser/components/translation/test/browser_translation_exceptions.js
+++ b/browser/components/translation/test/browser_translation_exceptions.js
@@ -41,17 +41,17 @@ function test() {
 
 function getLanguageExceptions() {
   let langs = Services.prefs.getCharPref(kLanguagesPref);
   return langs ? langs.split(",") : [];
 }
 
 function getDomainExceptions() {
   let results = [];
-  for (let perm of Services.perms.all) {
+  for (let perm of Services.perms.enumerator) {
     if (
       perm.type == "translate" &&
       perm.capability == Services.perms.DENY_ACTION
     ) {
       results.push(perm.principal);
     }
   }
 
--- a/browser/modules/PermissionUITelemetry.jsm
+++ b/browser/modules/PermissionUITelemetry.jsm
@@ -89,17 +89,17 @@ var PermissionUITelemetry = {
 
     let commonPermissions = [
       "geo",
       "desktop-notification",
       "camera",
       "microphone",
       "screen",
     ];
-    for (let perm of Services.perms.all) {
+    for (let perm of Services.perms.enumerator) {
       if (!commonPermissions.includes(perm.type)) {
         continue;
       }
 
       if (perm.capability == Services.perms.ALLOW_ACTION) {
         allPermsGranted++;
         if (perm.type == prompt.permissionKey) {
           thisPermGranted++;
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -902,17 +902,17 @@ async function sanitizeOnShutdown(progre
 
     progress.advancement = "done";
     return;
   }
 
   progress.advancement = "session-permission";
 
   // Let's see if we have to forget some particular site.
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     if (
       permission.type != "cookie" ||
       permission.capability != Ci.nsICookiePermission.ACCESS_SESSION
     ) {
       continue;
     }
 
     // We consider just permissions set for http, https and file URLs.
@@ -986,17 +986,17 @@ function cookiesAllowedForDomainOrSubDom
   }
 
   // This is an old profile with unsupported permission values
   if (p != Ci.nsICookiePermission.ACCESS_DEFAULT) {
     log("Not supported cookie permission: " + p);
     return false;
   }
 
-  for (let perm of Services.perms.all) {
+  for (let perm of Services.perms.enumerator) {
     if (perm.type != "cookie") {
       continue;
     }
 
     // We consider just permissions set for http, https and file URLs.
     if (!isSupportedURI(perm.principal.URI)) {
       continue;
     }
--- a/browser/modules/SiteDataManager.jsm
+++ b/browser/modules/SiteDataManager.jsm
@@ -432,18 +432,20 @@ var SiteDataManager = {
     }
     site.cookies = [];
   },
 
   // Returns a list of permissions from the permission manager that
   // we consider part of "site data and cookies".
   _getDeletablePermissions() {
     let perms = [];
+    let enumerator = Services.perms.enumerator;
 
-    for (let permission of Services.perms.all) {
+    while (enumerator.hasMoreElements()) {
+      let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
       if (
         permission.type == "persistent-storage" ||
         permission.type == "storage-access"
       ) {
         perms.push(permission);
       }
     }
 
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -588,16 +588,135 @@ nsresult NotificationPermissionRequest::
     RefPtr<NotificationPermissionCallback> callback(mCallback);
     callback->Call(mPermission, error);
     rv = error.StealNSResult();
   }
   mPromise->MaybeResolve(mPermission);
   return rv;
 }
 
+NS_IMPL_ISUPPORTS(NotificationTelemetryService, nsIObserver)
+
+NotificationTelemetryService::NotificationTelemetryService()
+    : mDNDRecorded(false) {}
+
+NotificationTelemetryService::~NotificationTelemetryService() {}
+
+/* static */
+already_AddRefed<NotificationTelemetryService>
+NotificationTelemetryService::GetInstance() {
+  nsCOMPtr<nsISupports> telemetrySupports =
+      do_GetService(NOTIFICATIONTELEMETRYSERVICE_CONTRACTID);
+  if (!telemetrySupports) {
+    return nullptr;
+  }
+  RefPtr<NotificationTelemetryService> telemetry =
+      static_cast<NotificationTelemetryService*>(telemetrySupports.get());
+  return telemetry.forget();
+}
+
+nsresult NotificationTelemetryService::Init() {
+  // Only perform permissions telemetry collection in the parent process.
+  if (!XRE_IsParentProcess()) {
+    return NS_OK;
+  }
+
+  RecordPermissions();
+
+  return NS_OK;
+}
+
+void NotificationTelemetryService::RecordPermissions() {
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "RecordPermissions may only be called in the parent process");
+
+  if (!Telemetry::CanRecordBase() || !Telemetry::CanRecordExtended()) {
+    return;
+  }
+
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+      services::GetPermissionManager();
+  if (!permissionManager) {
+    return;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  for (;;) {
+    bool hasMoreElements;
+    nsresult rv = enumerator->HasMoreElements(&hasMoreElements);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    if (!hasMoreElements) {
+      break;
+    }
+    nsCOMPtr<nsISupports> supportsPermission;
+    rv = enumerator->GetNext(getter_AddRefs(supportsPermission));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    uint32_t capability;
+    if (!GetNotificationPermission(supportsPermission, &capability)) {
+      continue;
+    }
+  }
+}
+
+bool NotificationTelemetryService::GetNotificationPermission(
+    nsISupports* aSupports, uint32_t* aCapability) {
+  nsCOMPtr<nsIPermission> permission = do_QueryInterface(aSupports);
+  if (!permission) {
+    return false;
+  }
+  nsAutoCString type;
+  permission->GetType(type);
+  if (!type.EqualsLiteral("desktop-notification")) {
+    return false;
+  }
+  permission->GetCapability(aCapability);
+  return true;
+}
+
+void NotificationTelemetryService::RecordDNDSupported() {
+  if (mDNDRecorded) {
+    return;
+  }
+
+  nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service();
+  if (!alertService) {
+    return;
+  }
+
+  nsCOMPtr<nsIAlertsDoNotDisturb> alertServiceDND =
+      do_QueryInterface(alertService);
+  if (!alertServiceDND) {
+    return;
+  }
+
+  mDNDRecorded = true;
+  bool isEnabled;
+  nsresult rv = alertServiceDND->GetManualDoNotDisturb(&isEnabled);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  Telemetry::Accumulate(Telemetry::ALERTS_SERVICE_DND_SUPPORTED_FLAG, true);
+}
+
+NS_IMETHODIMP
+NotificationTelemetryService::Observe(nsISupports* aSubject, const char* aTopic,
+                                      const char16_t* aData) {
+  return NS_OK;
+}
+
 // Observer that the alert service calls to do common tasks and/or dispatch to
 // the specific observer for the context e.g. main thread, worker, or service
 // worker.
 class NotificationObserver final : public nsIObserver {
  public:
   nsCOMPtr<nsIObserver> mObserver;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   bool mInPrivateBrowsing;
@@ -1060,16 +1179,23 @@ NotificationObserver::Observe(nsISupport
       return Notification::OpenSettings(mPrincipal);
     }
     // `ContentParent::RecvOpenNotificationSettings` notifies observers in the
     // parent process.
     ContentChild::GetSingleton()->SendOpenNotificationSettings(
         IPC::Principal(mPrincipal));
     return NS_OK;
   } else if (!strcmp("alertshow", aTopic) || !strcmp("alertfinished", aTopic)) {
+    RefPtr<NotificationTelemetryService> telemetry =
+        NotificationTelemetryService::GetInstance();
+    if (telemetry) {
+      // Record whether "do not disturb" is supported after the first
+      // notification, to account for falling back to XUL alerts.
+      telemetry->RecordDNDSupported();
+    }
     Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
   }
 
   return mObserver->Observe(aSubject, aTopic, aData);
 }
 
 nsresult NotificationObserver::AdjustPushQuota(const char* aTopic) {
   nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -14,28 +14,54 @@
 #include "nsIObserver.h"
 #include "nsISupports.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "nsWeakReference.h"
 
+#define NOTIFICATIONTELEMETRYSERVICE_CONTRACTID \
+  "@mozilla.org/notificationTelemetryService;1"
+
 class nsIPrincipal;
 class nsIVariant;
 
 namespace mozilla {
 namespace dom {
 
 class NotificationRef;
 class WorkerNotificationObserver;
 class Promise;
 class StrongWorkerRef;
 class WorkerPrivate;
 
+// Records telemetry probes at application startup, when a notification is
+// shown, and when the notification permission is revoked for a site.
+class NotificationTelemetryService final : public nsIObserver {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  NotificationTelemetryService();
+
+  static already_AddRefed<NotificationTelemetryService> GetInstance();
+
+  nsresult Init();
+  void RecordDNDSupported();
+  void RecordPermissions();
+
+ private:
+  virtual ~NotificationTelemetryService();
+
+  bool GetNotificationPermission(nsISupports* aSupports, uint32_t* aCapability);
+
+  bool mDNDRecorded;
+};
+
 /*
  * Notifications on workers introduce some lifetime issues. The property we
  * are trying to satisfy is:
  *   Whenever a task is dispatched to the main thread to operate on
  *   a Notification, the Notification should be addrefed on the worker thread
  *   and a feature should be added to observe the worker lifetime. This main
  *   thread owner should ensure it properly releases the reference to the
  *   Notification, additionally removing the feature if necessary.
@@ -89,16 +115,17 @@ class Notification : public DOMEventTarg
   friend class CloseNotificationRunnable;
   friend class NotificationTask;
   friend class NotificationPermissionRequest;
   friend class MainThreadNotificationObserver;
   friend class NotificationStorageCallback;
   friend class ServiceWorkerNotificationObserver;
   friend class WorkerGetRunnable;
   friend class WorkerNotificationObserver;
+  friend class NotificationTelemetryService;
 
  public:
   IMPL_EVENT_HANDLER(click)
   IMPL_EVENT_HANDLER(show)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(close)
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/extensions/permissions/nsPermissionManager.cpp
+++ b/extensions/permissions/nsPermissionManager.cpp
@@ -12,16 +12,18 @@
 #include "mozilla/ContentPrincipal.h"
 #include "mozilla/Pair.h"
 #include "mozilla/Services.h"
 #include "mozilla/SystemGroup.h"
 #include "nsPermissionManager.h"
 #include "nsPermission.h"
 #include "nsCRT.h"
 #include "nsNetUtil.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
 #include "nsTArray.h"
 #include "nsReadableUtils.h"
 #include "nsILineInputStream.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozilla/storage.h"
 #include "mozilla/Attributes.h"
 #include "nsXULAppAPI.h"
@@ -2528,19 +2530,30 @@ nsPermissionManager::GetPermissionHashKe
                                   aExactHostMatch);
     }
   }
 
   // No entry, really...
   return nullptr;
 }
 
-NS_IMETHODIMP nsPermissionManager::GetAll(
-    nsTArray<RefPtr<nsIPermission>>& aResult) {
-  return GetAllWithTypePrefix(NS_LITERAL_CSTRING(""), aResult);
+NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator** aEnum) {
+  nsTArray<RefPtr<nsIPermission>> array;
+  nsresult rv = GetAllWithTypePrefix(NS_LITERAL_CSTRING(""), array);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMArray<nsIPermission> comArray;
+  comArray.SetCapacity(array.Length());
+  for (size_t i = 0; i < array.Length(); i++) {
+    comArray.AppendElement(array[i].forget());
+  }
+
+  return NS_NewArrayEnumerator(aEnum, comArray, NS_GET_IID(nsIPermission));
 }
 
 NS_IMETHODIMP nsPermissionManager::GetAllWithTypePrefix(
     const nsACString& aPrefix, nsTArray<RefPtr<nsIPermission>>& aResult) {
   aResult.Clear();
   if (XRE_IsContentProcess()) {
     NS_WARNING(
         "nsPermissionManager's getAllWithTypePrefix is not available in the "
--- a/extensions/permissions/test/unit/head.js
+++ b/extensions/permissions/test/unit/head.js
@@ -17,11 +17,16 @@ function do_run_generator(generator) {
 // Helper to finish a generator function test.
 function do_finish_generator_test(generator) {
   executeSoon(function() {
     generator.return();
     do_test_finished();
   });
 }
 
-function do_count_array(all) {
-  return all.length;
+function do_count_enumerator(enumerator) {
+  let i = 0;
+  for (let item of enumerator) {
+    void item;
+    ++i;
+  }
+  return i;
 }
--- a/extensions/permissions/test/unit/test_permmanager_defaults.js
+++ b/extensions/permissions/test/unit/test_permmanager_defaults.js
@@ -347,17 +347,17 @@ add_task(async function do_test() {
   file.remove(false);
 });
 
 // use an enumerator to find the requested permission.  Returns the permission
 // value (ie, the "capability" in nsIPermission parlance) or null if it can't
 // be found.
 function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
   let result = undefined;
-  for (let perm of Services.perms.all) {
+  for (let perm of Services.perms.enumerator) {
     if (perm.matchesURI(origin, true) && perm.type == type) {
       if (result !== undefined) {
         // we've already found one previously - that's bad!
         do_throw("enumerator found multiple entries");
       }
       result = perm.capability;
     }
   }
--- a/extensions/permissions/test/unit/test_permmanager_migrate_4-7.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_4-7.js
@@ -190,17 +190,17 @@ add_task(async function test() {
   await PlacesTestUtils.addVisits(
     Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
   );
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -207,17 +207,17 @@ add_task(function test() {
   ];
 
   let found = expected.map(it => 0);
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_5-7a.js
@@ -258,17 +258,17 @@ add_task(async function test() {
   await PlacesTestUtils.addVisits(
     Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
   );
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_5-7b.js
@@ -112,17 +112,17 @@ add_task(function test() {
   ];
 
   let found = expected.map(it => 0);
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_6-7a.js
@@ -258,17 +258,17 @@ add_task(async function test() {
   await PlacesTestUtils.addVisits(
     Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory")
   );
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_6-7b.js
@@ -106,17 +106,17 @@ add_task(function test() {
   ];
 
   let found = expected.map(it => 0);
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_7-8.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_7-8.js
@@ -245,17 +245,17 @@ add_task(async function test() {
   );
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
   await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_migrate_9-10.js
+++ b/extensions/permissions/test/unit/test_permmanager_migrate_9-10.js
@@ -182,17 +182,17 @@ add_task(async function test() {
   );
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
   await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
 
   // This will force the permission-manager to reload the data.
   Services.obs.notifyObservers(null, "testonly-reload-permissions-from-disk");
 
   // Force initialization of the nsPermissionManager
-  for (let permission of Services.perms.all) {
+  for (let permission of Services.perms.enumerator) {
     let isExpected = false;
 
     expected.forEach((it, i) => {
       if (
         permission.principal.origin == it[0] &&
         permission.type == it[1] &&
         permission.capability == it[2] &&
         permission.expireType == it[3] &&
--- a/extensions/permissions/test/unit/test_permmanager_notifications.js
+++ b/extensions/permissions/test/unit/test_permmanager_notifications.js
@@ -131,17 +131,17 @@ permission_observer.prototype = {
           Assert.equal(this.type, perm.type);
           break;
         default:
           do_throw("too many delete notifications posted.");
       }
     } else if (data == "cleared") {
       // only clear once: at the end
       Assert.ok(!this.cleared);
-      Assert.equal(do_count_array(Services.perms.all), 0);
+      Assert.equal(do_count_enumerator(Services.perms.enumerator), 0);
       this.cleared = true;
     } else {
       do_throw("unexpected data '" + data + "'!");
     }
 
     // Continue the test.
     do_run_generator(this.generator);
   },
--- a/extensions/permissions/test/unit/test_permmanager_removebytype.js
+++ b/extensions/permissions/test/unit/test_permmanager_removebytype.js
@@ -4,17 +4,17 @@
 function run_test() {
   Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
 
   // initialize the permission manager service
   let pm = Cc["@mozilla.org/permissionmanager;1"].getService(
     Ci.nsIPermissionManager
   );
 
-  Assert.equal(pm.all.length, 0);
+  Assert.equal(perm_count(), 0);
 
   // add some permissions
   let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://amazon.com:8080"
   );
   let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://google.com:2048"
   );
@@ -27,49 +27,60 @@ function run_test() {
   pm.addFromPrincipal(principal, "cucumber", 1);
 
   pm.addFromPrincipal(principal2, "apple", 2);
   pm.addFromPrincipal(principal2, "pear", 2);
 
   pm.addFromPrincipal(principal3, "cucumber", 3);
   pm.addFromPrincipal(principal3, "apple", 1);
 
-  Assert.equal(pm.all.length, 7);
+  Assert.equal(perm_count(), 7);
 
   pm.removeByType("apple");
-  Assert.equal(pm.all.length, 4);
+  Assert.equal(perm_count(), 4);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 3);
 
   pm.removeByType("cucumber");
-  Assert.equal(pm.all.length, 2);
+  Assert.equal(perm_count(), 2);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
 
   pm.removeByType("pear");
-  Assert.equal(pm.all.length, 0);
+  Assert.equal(perm_count(), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+
+  function perm_count() {
+    let enumerator = pm.enumerator;
+    let count = 0;
+    while (enumerator.hasMoreElements()) {
+      count++;
+      enumerator.getNext();
+    }
+
+    return count;
+  }
 }
--- a/extensions/permissions/test/unit/test_permmanager_removebytypesince.js
+++ b/extensions/permissions/test/unit/test_permmanager_removebytypesince.js
@@ -4,17 +4,17 @@
 add_task(async function test() {
   Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
 
   // initialize the permission manager service
   let pm = Cc["@mozilla.org/permissionmanager;1"].getService(
     Ci.nsIPermissionManager
   );
 
-  Assert.equal(pm.all.length, 0);
+  Assert.equal(perm_count(), 0);
 
   // add some permissions
   let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://amazon.com:8080"
   );
   let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://google.com:2048"
   );
@@ -39,50 +39,61 @@ add_task(async function test() {
   await new Promise(resolve => do_timeout(20, resolve));
 
   pm.addFromPrincipal(principal2, "apple", 2);
   pm.addFromPrincipal(principal2, "pear", 2);
 
   pm.addFromPrincipal(principal3, "cucumber", 3);
   pm.addFromPrincipal(principal3, "apple", 1);
 
-  Assert.equal(pm.all.length, 7);
+  Assert.equal(perm_count(), 7);
 
   pm.removeByTypeSince("apple", since);
 
-  Assert.equal(pm.all.length, 5);
+  Assert.equal(perm_count(), 5);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 3);
 
   pm.removeByTypeSince("cucumber", since);
-  Assert.equal(pm.all.length, 4);
+  Assert.equal(perm_count(), 4);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 2);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
 
   pm.removeByTypeSince("pear", since);
-  Assert.equal(pm.all.length, 3);
+  Assert.equal(perm_count(), 3);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "pear"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "pear"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "apple"), 3);
   Assert.equal(pm.testPermissionFromPrincipal(principal2, "apple"), 0);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "apple"), 0);
 
   Assert.equal(pm.testPermissionFromPrincipal(principal, "cucumber"), 1);
   Assert.equal(pm.testPermissionFromPrincipal(principal3, "cucumber"), 0);
+
+  function perm_count() {
+    let enumerator = pm.enumerator;
+    let count = 0;
+    while (enumerator.hasMoreElements()) {
+      count++;
+      enumerator.getNext();
+    }
+
+    return count;
+  }
 });
--- a/extensions/permissions/test/unit/test_permmanager_removepermission.js
+++ b/extensions/permissions/test/unit/test_permmanager_removepermission.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function run_test() {
   // initialize the permission manager service
   let pm = Cc["@mozilla.org/permissionmanager;1"].getService(
     Ci.nsIPermissionManager
   );
 
-  Assert.equal(pm.all.length, 0);
+  Assert.equal(perm_count(), 0);
 
   // add some permissions
   let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://amazon.com:8080"
   );
   let principal2 = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
     "http://google.com:2048"
   );
@@ -25,34 +25,47 @@ function run_test() {
   pm.addFromPrincipal(principal, "cucumber", 1);
   pm.addFromPrincipal(principal, "cucumber", 1);
 
   pm.addFromPrincipal(principal2, "apple", 2);
   pm.addFromPrincipal(principal2, "pear", 0);
   pm.addFromPrincipal(principal2, "pear", 2);
 
   // Make sure that removePermission doesn't remove more than one permission each time
-  Assert.equal(pm.all.length, 5);
+  Assert.equal(perm_count(), 5);
+
+  remove_one_by_type("apple");
+  Assert.equal(perm_count(), 4);
 
   remove_one_by_type("apple");
-  Assert.equal(pm.all.length, 4);
+  Assert.equal(perm_count(), 3);
 
-  remove_one_by_type("apple");
-  Assert.equal(pm.all.length, 3);
+  remove_one_by_type("pear");
+  Assert.equal(perm_count(), 2);
+
+  remove_one_by_type("cucumber");
+  Assert.equal(perm_count(), 1);
 
   remove_one_by_type("pear");
-  Assert.equal(pm.all.length, 2);
+  Assert.equal(perm_count(), 0);
 
-  remove_one_by_type("cucumber");
-  Assert.equal(pm.all.length, 1);
+  function perm_count() {
+    let enumerator = pm.enumerator;
+    let count = 0;
+    while (enumerator.hasMoreElements()) {
+      count++;
+      enumerator.getNext();
+    }
 
-  remove_one_by_type("pear");
-  Assert.equal(pm.all.length, 0);
+    return count;
+  }
 
   function remove_one_by_type(type) {
-    for (let perm of pm.all) {
-      if (perm.type == type) {
-        pm.removePermission(perm);
+    let enumerator = pm.enumerator;
+    while (enumerator.hasMoreElements()) {
+      let it = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+      if (it.type == type) {
+        pm.removePermission(it);
         break;
       }
     }
   }
 }
--- a/layout/build/components.conf
+++ b/layout/build/components.conf
@@ -301,16 +301,23 @@ Classes = [
     {
         'cid': '{ac9e3e82-bfbd-4f26-941e-f58c8ee178c1}',
         'contract_ids': ['@mozilla.org/no-data-protocol-content-policy;1'],
         'type': 'nsNoDataProtocolContentPolicy',
         'headers': ['/dom/base/nsNoDataProtocolContentPolicy.h'],
         'categories': {'content-policy': '@mozilla.org/no-data-protocol-content-policy;1'},
     },
     {
+        'cid': '{5995b782-6a0e-4066-aac5-276f0a9ad8cf}',
+        'contract_ids': ['@mozilla.org/notificationTelemetryService;1'],
+        'type': 'mozilla::dom::NotificationTelemetryService',
+        'headers': ['mozilla/dom/Notification.h'],
+        'init_method': 'Init',
+    },
+    {
         'cid': '{bd066e5f-146f-4472-8331-7bfd05b1ed90}',
         'contract_ids': ['@mozilla.org/nullprincipal;1'],
         'type': 'mozilla::NullPrincipal',
         'headers': ['/caps/NullPrincipal.h'],
         'init_method': 'Init',
     },
     {
         'cid': '{2a058404-fb85-44ec-8cfd-e8cbdc988dc1}',
--- a/netwerk/base/nsIPermissionManager.idl
+++ b/netwerk/base/nsIPermissionManager.idl
@@ -29,16 +29,17 @@
  */
 
 #include "nsISupports.idl"
 
 interface nsIObserver;
 interface nsIPrincipal;
 interface mozIDOMWindow;
 interface nsIPermission;
+interface nsISimpleEnumerator;
 interface nsIRunnable;
 
 %{ C++
 namespace IPC {
 struct Permission;
 }
 #include "nsTArrayForwardDeclare.h"
 %}
@@ -199,20 +200,21 @@ interface nsIPermissionManager : nsISupp
    *       need to know the specific stored details.
    * @note This method will always return null for the system principal.
    */
   nsIPermission getPermissionObject(in nsIPrincipal principal,
                                     in ACString type,
                                     in boolean exactHost);
 
   /**
-   * Returns all stored permissions.
-   * @return an array of nsIPermission objects
+   * Allows enumeration of all stored permissions
+   * @return an nsISimpleEnumerator interface that allows access to
+   *         nsIPermission objects
    */
-  readonly attribute Array<nsIPermission> all;
+  readonly attribute nsISimpleEnumerator enumerator;
 
   /**
    * Remove all permissions that will match the origin pattern.
    */
   void removePermissionsWithAttributes(in AString patternAsJSON);
 
   /**
    * If the current permission is set to expire, reset the expiration time. If
--- a/netwerk/test/unit/test_permmgr.js
+++ b/netwerk/test/unit/test_permmgr.js
@@ -66,18 +66,18 @@ function run_test() {
       results[i][2]
     );
     Assert.equal(
       pm.testExactPermissionFromPrincipal(principal, results[i][1]),
       results[i][3]
     );
   }
 
-  // test the all property ...
-  var perms = pm.all;
+  // test the enumerator ...
+  var perms = Array.from(pm.enumerator);
   Assert.equal(perms.length, hosts.length);
 
   // ... remove all the hosts ...
   for (var j = 0; j < perms.length; ++j) {
     pm.removePermission(perms[j]);
   }
 
   // ... ensure each and every element is equal ...
@@ -91,39 +91,40 @@ function run_test() {
         perms.splice(j, 1);
         break;
       }
     }
   }
   Assert.equal(perms.length, 0);
 
   // ... and check the permmgr's empty
-  Assert.equal(pm.all.length, 0);
+  Assert.equal(pm.enumerator.hasMoreElements(), false);
 
   // test UTF8 normalization behavior: expect ASCII/ACE host encodings
   var utf8 = "b\u00FCcher.dolske.org"; // "b├╝cher.dolske.org"
   var aceref = "xn--bcher-kva.dolske.org";
   var principal = secMan.createContentPrincipal(
     ioService.newURI("http://" + utf8),
     {}
   );
   pm.addFromPrincipal(principal, "utf8", 1);
-  Assert.notEqual(Services.perms.all.length, 0);
-  var ace = Services.perms.all[0];
+  var enumerator = pm.enumerator;
+  Assert.equal(enumerator.hasMoreElements(), true);
+  var ace = enumerator.getNext().QueryInterface(Ci.nsIPermission);
   Assert.equal(ace.principal.URI.asciiHost, aceref);
-  Assert.equal(Services.perms.all.length > 1, false);
+  Assert.equal(enumerator.hasMoreElements(), false);
 
   // test removeAll()
   pm.removeAll();
-  Assert.equal(Services.perms.all.length, 0);
+  Assert.equal(pm.enumerator.hasMoreElements(), false);
 
   principal = secMan.createContentPrincipalFromOrigin(
     "https://www.example.com"
   );
   pm.addFromPrincipal(principal, "offline-app", pm.ALLOW_ACTION);
   // Remove existing entry.
   let perm = pm.getPermissionObject(principal, "offline-app", true);
   pm.removePermission(perm);
   // Try to remove already deleted entry.
   perm = pm.getPermissionObject(principal, "offline-app", true);
   pm.removePermission(perm);
-  Assert.equal(Services.perms.all.length, 0);
+  Assert.equal(pm.enumerator.hasMoreElements(), false);
 }
--- a/toolkit/components/cleardata/ClearDataService.jsm
+++ b/toolkit/components/cleardata/ClearDataService.jsm
@@ -707,17 +707,17 @@ const PushNotificationsCleaner = {
       });
     });
   },
 };
 
 const StorageAccessCleaner = {
   deleteByHost(aHost, aOriginAttributes) {
     return new Promise(aResolve => {
-      for (let perm of Services.perms.all) {
+      for (let perm of Services.perms.enumerator) {
         if (perm.type == "storageAccessAPI") {
           let toBeRemoved = false;
           try {
             toBeRemoved = Services.eTLD.hasRootDomain(
               perm.principal.URI.host,
               aHost
             );
           } catch (ex) {
@@ -827,17 +827,17 @@ const AuthCacheCleaner = {
       aResolve();
     });
   },
 };
 
 const PermissionsCleaner = {
   deleteByHost(aHost, aOriginAttributes) {
     return new Promise(aResolve => {
-      for (let perm of Services.perms.all) {
+      for (let perm of Services.perms.enumerator) {
         let toBeRemoved;
         try {
           toBeRemoved = Services.eTLD.hasRootDomain(
             perm.principal.URI.host,
             aHost
           );
         } catch (ex) {
           continue;
--- a/toolkit/components/passwordmgr/LoginManager.jsm
+++ b/toolkit/components/passwordmgr/LoginManager.jsm
@@ -417,17 +417,17 @@ LoginManager.prototype = {
    *
    * @return {String[]} of disabled origins. If there are no disabled origins,
    *                    the array is empty.
    */
   getAllDisabledHosts() {
     log.debug("Getting a list of all disabled origins");
 
     let disabledHosts = [];
-    for (let perm of Services.perms.all) {
+    for (let perm of Services.perms.enumerator) {
       if (
         perm.type == PERMISSION_SAVE_LOGINS &&
         perm.capability == Services.perms.DENY_ACTION
       ) {
         disabledHosts.push(perm.principal.URI.displayPrePath);
       }
     }
 
--- a/toolkit/components/passwordmgr/test/unit/test_storage_mozStorage.js
+++ b/toolkit/components/passwordmgr/test/unit/test_storage_mozStorage.js
@@ -69,17 +69,17 @@ function checkStorageData(storage, ref_d
   LoginTestUtils.assertDisabledHostsEqual(
     getAllDisabledHostsFromPermissionManager(),
     ref_disabledHosts
   );
 }
 
 function getAllDisabledHostsFromPermissionManager() {
   let disabledHosts = [];
-  for (let perm of Services.perms.all) {
+  for (let perm of Services.perms.enumerator) {
     if (
       perm.type == PERMISSION_SAVE_LOGINS &&
       perm.capability == Services.perms.DENY_ACTION
     ) {
       disabledHosts.push(perm.principal.URI.prePath);
     }
   }
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13344,16 +13344,25 @@
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["firefox-dev@mozilla.org"],
     "bug_numbers": [1219030],
     "expires_in_version": "50",
     "kind": "boolean",
     "description": "XUL-only: whether the user has toggled do not disturb."
   },
+  "ALERTS_SERVICE_DND_SUPPORTED_FLAG": {
+    "record_in_processes": ["main", "content"],
+    "products": ["firefox", "fennec", "geckoview"],
+    "alert_emails": ["firefox-dev@mozilla.org"],
+    "bug_numbers": [1219030],
+    "expires_in_version": "50",
+    "kind": "flag",
+    "description": "Whether the do not disturb option is supported. True if the browser uses XUL alerts."
+  },
   "PLUGIN_DRAWING_MODEL": {
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "bug_numbers": [1229961],
     "n_values": 12,
--- a/toolkit/components/telemetry/histogram-allowlists.json
+++ b/toolkit/components/telemetry/histogram-allowlists.json
@@ -1256,16 +1256,17 @@
     "GEOLOCATION_WIN8_SOURCE_IS_MLS"
   ],
   "kind": [
     "A11Y_IATABLE_USAGE_FLAG",
     "A11Y_INSTANTIATED_FLAG",
     "A11Y_ISIMPLEDOM_USAGE_FLAG",
     "ADDON_FORBIDDEN_CPOW_USAGE",
     "ADDON_MANAGER_UPGRADE_UI_SHOWN",
+    "ALERTS_SERVICE_DND_SUPPORTED_FLAG",
     "AUTO_REJECTED_TRANSLATION_OFFERS",
     "BROWSER_SHIM_USAGE_BLOCKED",
     "CANVAS_WEBGL_ACCL_FAILURE_ID",
     "CANVAS_WEBGL_FAILURE_ID",
     "CHANGES_OF_TARGET_LANGUAGE",
     "COMPONENTS_SHIM_ACCESSED_BY_CONTENT",
     "CONTENT_DOCUMENTS_DESTROYED",
     "CSP_DOCUMENTS_COUNT",