Merge inbound to mozilla-central. a=merge FIREFOX_BETA_63_BASE
authorTiberius Oros <toros@mozilla.com>
Fri, 24 Aug 2018 12:43:45 +0300
changeset 433262 190b827aaa2b
parent 433193 043aff7fda61 (current diff)
parent 433261 81d946cdc170 (diff)
child 433263 2ad668767032
child 433270 067c58fa95f1
child 433313 6f0c41281310
push id34501
push usertoros@mozilla.com
push dateFri, 24 Aug 2018 09:45:02 +0000
treeherdermozilla-central@190b827aaa2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
190b827aaa2b / 63.0a1 / 20180824100112 / files
nightly linux64
190b827aaa2b / 63.0a1 / 20180824100112 / files
nightly mac
190b827aaa2b / 63.0a1 / 20180824100112 / files
nightly win32
190b827aaa2b / 63.0a1 / 20180824100112 / files
nightly win64
190b827aaa2b / 63.0a1 / 20180824100112 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/base/content/browser-pageActions.js
browser/components/extensions/parent/ext-tabs.js
browser/components/extensions/test/browser/browser-common.ini
browser/components/uitour/UITour.jsm
browser/extensions/pocket/content/pktApi.jsm
browser/modules/PageActions.jsm
dom/chrome-webidl/moz.build
gfx/2d/RwAssert.h
layout/reftests/bugs/reftest.list
netwerk/protocol/http/nsHttpChannel.cpp
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini
toolkit/components/extensions/parent/ext-management.js
toolkit/components/extensions/schemas/management.json
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2443,16 +2443,19 @@ DocAccessible::IsLoadEventTarget() const
   // It's content (not chrome) root document.
   return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
 }
 
 void
 DocAccessible::DispatchScrollingEvent(uint32_t aEventType)
 {
   nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
+  if (!sf) {
+    return;
+  }
 
   int32_t appUnitsPerDevPixel = mPresShell->GetPresContext()->AppUnitsPerDevPixel();
   LayoutDevicePoint scrollPoint = LayoutDevicePoint::FromAppUnits(
     sf->GetScrollPosition(), appUnitsPerDevPixel) * mPresShell->GetResolution();
 
   LayoutDeviceRect scrollRange = LayoutDeviceRect::FromAppUnits(
     sf->GetScrollRange(), appUnitsPerDevPixel);
   scrollRange.ScaleRoundOut(mPresShell->GetResolution());
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -62,19 +62,18 @@ var AccessFu = {
     // Check for output notification
     this._notifyOutputPref =
       new PrefCache("accessibility.accessfu.notify_output");
 
     Services.obs.addObserver(this, "remote-browser-shown");
     Services.obs.addObserver(this, "inprocess-browser-shown");
     Services.ww.registerNotification(this);
 
-    let windows = Services.wm.getEnumerator(null);
-    while (windows.hasMoreElements()) {
-      this._attachWindow(windows.getNext());
+    for (let win of Services.wm.getEnumerator(null)) {
+      this._attachWindow(win);
     }
 
     Logger.info("AccessFu:Enabled");
   },
 
   /**
    * Disable AccessFu and return to default interaction mode.
    */
@@ -84,19 +83,18 @@ var AccessFu = {
     }
 
     this._enabled = false;
 
     Services.obs.removeObserver(this, "remote-browser-shown");
     Services.obs.removeObserver(this, "inprocess-browser-shown");
     Services.ww.unregisterNotification(this);
 
-    let windows = Services.wm.getEnumerator(null);
-    while (windows.hasMoreElements()) {
-      this._detachWindow(windows.getNext());
+    for (let win of Services.wm.getEnumerator(null)) {
+      this._detachWindow(win);
     }
 
     delete this._notifyOutputPref;
 
     if (this.doneCallback) {
       this.doneCallback();
       delete this.doneCallback;
     }
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -155,23 +155,19 @@ var Utils = { // jshint ignore:line
       return new State(state.value, extState.value);
 
   },
 
   getAttributes: function getAttributes(aAccessible) {
     let attributes = {};
 
     if (aAccessible && aAccessible.attributes) {
-      let attributesEnum = aAccessible.attributes.enumerate();
-
       // Populate |attributes| object with |aAccessible|'s attribute key-value
       // pairs.
-      while (attributesEnum.hasMoreElements()) {
-        let attribute = attributesEnum.getNext().QueryInterface(
-          Ci.nsIPropertyElement);
+      for (let attribute of aAccessible.attributes.enumerate()) {
         attributes[attribute.key] = attribute.value;
       }
     }
 
     return attributes;
   },
 
   getVirtualCursor: function getVirtualCursor(aDocument) {
@@ -752,19 +748,18 @@ PivotContext.prototype = {
       try {
         return aAccessible.QueryInterface(Ci.nsIAccessibleTableCell);
       } catch (x) {
         Logger.logException(x);
         return null;
       }
     };
     let getHeaders = function* getHeaders(aHeaderCells) {
-      let enumerator = aHeaderCells.enumerate();
-      while (enumerator.hasMoreElements()) {
-        yield enumerator.getNext().QueryInterface(Ci.nsIAccessible).name;
+      for (let {name} of aHeaderCells.enumerate(Ci.nsIAccessible)) {
+        yield name;
       }
     };
 
     cellInfo.current = getAccessibleCell(aAccessible);
 
     if (!cellInfo.current) {
       Logger.warning(aAccessible,
         "does not support nsIAccessibleTableCell interface.");
--- a/accessible/tests/browser/shared-head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -241,21 +241,18 @@ function addAccessibleTask(doc, task) {
     let url;
     if (doc.includes("doc_")) {
       url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
     } else {
       url = snippetToURL(doc);
     }
 
     registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers("accessible-event");
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          "accessible-event");
+      for (let observer of Services.obs.enumerateObservers("accessible-event")) {
+        Services.obs.removeObserver(observer, "accessible-event");
       }
     });
 
     let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, "body");
 
     await BrowserTestUtils.withNewTab({
       gBrowser,
       url: url
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/1484778.html
@@ -0,0 +1,26 @@
+<style>
+#a { border-left: solid -moz-hyperlinktext 93em }
+</style>
+<script>
+/*
+  I dont't know why but this seems to be required to trigger the crash...
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+*/
+function go() {
+  var b = document.elementFromPoint(0,0);
+  window.scroll({left: 97, top: -1});
+  document.adoptNode(b);
+}
+</script>
+<body onload=go()>
+<ins id="a">
--- a/accessible/tests/crashtests/crashtests.list
+++ b/accessible/tests/crashtests/crashtests.list
@@ -1,12 +1,13 @@
 load 448064.xhtml # This test instantiates a11y, so be careful about adding tests before it
 load 471493.xul
 asserts-if(!browserIsRemote,2) load 884202.html
 load 890760.html
 load 893515.html
 load 1072792.xhtml
 load 1380199.html
 load 1402999.html
+load 1484778.html
 
 # last_test_to_unload_testsuite.xul MUST be the last test in the list because it
 # is responsible for shutting down accessibility service affecting later tests.
 load last_test_to_unload_testsuite.xul
--- a/accessible/tests/mochitest/attributes.js
+++ b/accessible/tests/mochitest/attributes.js
@@ -314,20 +314,17 @@ function testAttrsInternal(aAccOrElmOrID
 
   var errorMsg = " for " + prettyName(aAccOrElmOrID);
   compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs, aAbsentAttrs);
 }
 
 function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs,
                       aAbsentAttrs) {
   // Check if all obtained attributes are expected and have expected value.
-  var enumerate = aAttrs.enumerate();
-  while (enumerate.hasMoreElements()) {
-    let prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
-
+  for (let prop of aAttrs.enumerate()) {
     if (!(prop.key in aExpectedAttrs)) {
       if (!aSkipUnexpectedAttrs)
         ok(false, "Unexpected attribute '" + prop.key + "' having '" +
            prop.value + "'" + aErrorMsg);
     } else {
       var msg = "Attribute '" + prop.key + "' has wrong value" + aErrorMsg;
       var expectedValue = aExpectedAttrs[prop.key];
 
@@ -350,19 +347,17 @@ function compareAttrs(aErrorMsg, aAttrs,
          "There is no expected attribute '" + name + "' " + aErrorMsg);
   }
 
   // Check if all unexpected attributes are absent.
   if (aAbsentAttrs) {
     for (var name in aAbsentAttrs) {
       var wasFound = false;
 
-      enumerate = aAttrs.enumerate();
-      while (enumerate.hasMoreElements()) {
-        let prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
+      for (let prop of aAttrs.enumerate()) {
         if (prop.key == name)
           wasFound = true;
       }
     }
 
     ok(!wasFound,
        "There is an unexpected attribute '" + name + "' " + aErrorMsg);
   }
--- a/accessible/tests/mochitest/relations.js
+++ b/accessible/tests/mochitest/relations.js
@@ -71,32 +71,28 @@ function testRelation(aIdentifier, aRelT
   if (targets.length != relatedIds.length)
     return;
 
   var actualTargets = relation.getTargets();
 
   // Check if all given related accessibles are targets of obtained relation.
   for (let idx = 0; idx < targets.length; idx++) {
     var isFound = false;
-    let enumerate = actualTargets.enumerate();
-    while (enumerate.hasMoreElements()) {
-      let relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+    for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
       if (targets[idx] == relatedAcc) {
         isFound = true;
         break;
       }
     }
 
     ok(isFound, prettyName(relatedIds[idx]) + " is not a target of" + relDescr);
   }
 
   // Check if all obtained targets are given related accessibles.
-  let enumerate = actualTargets.enumerate();
-  while (enumerate.hasMoreElements()) {
-    let relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+  for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
     let idx;
     for (idx = 0; idx < targets.length && relatedAcc != targets[idx]; idx++);
 
     if (idx == targets.length)
       ok(false, "There is unexpected target" + prettyName(relatedAcc) + "of" + relDescr);
   }
 }
 
@@ -135,19 +131,17 @@ function testAbsentRelation(aIdentifier,
   if (targets.length != relatedIds.length)
     return;
 
   var actualTargets = relation.getTargets();
 
   // Any found targets that match given accessibles should be called out.
   for (let idx = 0; idx < targets.length; idx++) {
     var notFound = true;
-    var enumerate = actualTargets.enumerate();
-    while (enumerate.hasMoreElements()) {
-      var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+    for (let relatedAcc of actualTargets.enumerate(Ci.nsIAccessible)) {
       if (targets[idx] == relatedAcc) {
         notFound = false;
         break;
       }
     }
 
     ok(notFound, prettyName(relatedIds[idx]) + " is a target of " + relDescr);
   }
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -78,22 +78,19 @@ class NetErrorChild extends ActorChild {
 
   isAboutCertError(doc) {
     return doc.documentURI.startsWith("about:certerror");
   }
 
   _getCertValidityRange(docShell) {
     let {securityInfo} = docShell.failedChannel;
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-    let certs = securityInfo.failedCertChain.getEnumerator();
     let notBefore = 0;
     let notAfter = Number.MAX_SAFE_INTEGER;
-    while (certs.hasMoreElements()) {
-      let cert = certs.getNext();
-      cert.QueryInterface(Ci.nsIX509Cert);
+    for (let cert of securityInfo.failedCertChain.getEnumerator()) {
       notBefore = Math.max(notBefore, cert.validity.notBefore);
       notAfter = Math.min(notAfter, cert.validity.notAfter);
     }
     // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
     notBefore /= 1000;
     notAfter /= 1000;
     return {notBefore, notAfter};
   }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -331,20 +331,18 @@ var gInitialPages = [
   "about:sessionrestore",
   "about:welcome"
 ];
 
 function isInitialPage(url) {
   return gInitialPages.includes(url) || url == BROWSER_NEW_TAB_URL;
 }
 
-function* browserWindows() {
-  let windows = Services.wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements())
-    yield windows.getNext();
+function browserWindows() {
+  return Services.wm.getEnumerator("navigator:browser");
 }
 
 function UpdateBackForwardCommands(aWebNavigation) {
   var backCommand = document.getElementById("Browser:Back");
   var forwardCommand = document.getElementById("Browser:Forward");
 
   // Avoid setting attributes on commands if the value hasn't changed!
   // Remember, guys, setting attributes on elements is expensive!  They
@@ -2631,23 +2629,21 @@ function BrowserPageInfo(documentURL, in
   if (documentURL instanceof HTMLDocument) {
     Deprecated.warning("Please pass the location URL instead of the document " +
                        "to BrowserPageInfo() as the first argument.",
                        "https://bugzilla.mozilla.org/show_bug.cgi?id=1238180");
     documentURL = documentURL.location;
   }
 
   let args = { initialTab, imageElement, frameOuterWindowID, browser };
-  var windows = Services.wm.getEnumerator("Browser:page-info");
 
   documentURL = documentURL || window.gBrowser.selectedBrowser.currentURI.spec;
 
   // Check for windows matching the url
-  while (windows.hasMoreElements()) {
-    var currentWindow = windows.getNext();
+  for (let currentWindow of Services.wm.getEnumerator("Browser:page-info")) {
     if (currentWindow.closed) {
       continue;
     }
     if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
       currentWindow.focus();
       currentWindow.resetPageInfo(args);
       return currentWindow;
     }
@@ -3304,20 +3300,17 @@ function getDetailedCertErrorInfo(locati
                       gNavigatorBundle.getFormattedString("certErrorDetailsHSTS.label",
                                                           [hasHSTS]);
   certErrorDetails += "\r\n" +
                       gNavigatorBundle.getFormattedString("certErrorDetailsKeyPinning.label",
                                                           [hasHPKP]);
 
   let certChain = "";
   if (securityInfo.failedCertChain) {
-    let certs = securityInfo.failedCertChain.getEnumerator();
-    while (certs.hasMoreElements()) {
-      let cert = certs.getNext();
-      cert.QueryInterface(Ci.nsIX509Cert);
+    for (let cert of securityInfo.failedCertChain.getEnumerator()) {
       certChain += getPEMString(cert);
     }
   }
 
   certErrorDetails += "\r\n\r\n" +
                       gNavigatorBundle.getString("certErrorDetailsCertChain.label") +
                       "\r\n\r\n" + certChain;
 
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -491,20 +491,17 @@ add_task(async function checkUnknownIssu
 });
 
 function getCertChain(securityInfoAsString) {
   let certChain = "";
   const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
   let securityInfo = serhelper.deserializeObject(securityInfoAsString);
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-  let certs = securityInfo.failedCertChain.getEnumerator();
-  while (certs.hasMoreElements()) {
-    let cert = certs.getNext();
-    cert.QueryInterface(Ci.nsIX509Cert);
+  for (let cert of securityInfo.failedCertChain.getEnumerator()) {
     certChain += getPEMString(cert);
   }
   return certChain;
 }
 
 function getDERString(cert) {
   var length = {};
   var derArray = cert.getRawDER(length);
--- a/browser/base/content/test/general/browser_bug484315.js
+++ b/browser/base/content/test/general/browser_bug484315.js
@@ -1,14 +1,11 @@
 function test() {
   var contentWin = window.open("about:blank", "", "width=100,height=100");
-  var enumerator = Services.wm.getEnumerator("navigator:browser");
-
-  while (enumerator.hasMoreElements()) {
-    let win = enumerator.getNext();
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
     if (win.content == contentWin) {
       Services.prefs.setBoolPref("browser.tabs.closeWindowWithLastTab", false);
       win.gBrowser.removeCurrentTab();
       ok(win.closed, "popup is closed");
 
       // clean up
       if (!win.closed)
         win.close();
--- a/browser/base/content/test/general/browser_save_link_when_window_navigates.js
+++ b/browser/base/content/test/general/browser_save_link_when_window_navigates.js
@@ -60,19 +60,17 @@ function triggerSave(aWindow, aCallback)
     function doLoad() {
       content.document.querySelector("iframe").remove();
     }
     testBrowser.messageManager.loadFrameScript("data:,(" + doLoad.toString() + ")()", false);
     executeSoon(continueDownloading);
   }
 
   function continueDownloading() {
-    let windows = Services.wm.getEnumerator("");
-    while (windows.hasMoreElements()) {
-      let win = windows.getNext();
+    for (let win of Services.wm.getEnumerator("")) {
       if (win.location && win.location.href == UCT_URI) {
         win.document.documentElement._fireButtonEvent("accept");
         win.close();
         return;
       }
     }
     ok(false, "No Unknown Content Type dialog yet?");
   }
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -528,31 +528,20 @@ async function loadBadCertPage(url) {
     await exceptionDialogResolved;
   }
   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 }
 
 // Utility function to get a handle on the certificate exception dialog.
 // Modified from toolkit/components/passwordmgr/test/prompt_common.js
 function getCertExceptionDialog(aLocation) {
-  let enumerator = Services.wm.getXULWindowEnumerator(null);
-
-  while (enumerator.hasMoreElements()) {
-    let win = enumerator.getNext();
-    let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
-
-    let containedDocShells = windowDocShell.getDocShellEnumerator(
-                                      Ci.nsIDocShellTreeItem.typeChrome,
-                                      Ci.nsIDocShell.ENUMERATE_FORWARDS);
-    while (containedDocShells.hasMoreElements()) {
-      // Get the corresponding document for this docshell
-      let childDocShell = containedDocShells.getNext();
-      let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
-                                  .contentViewer
-                                  .DOMDocument;
-
-      if (childDoc.location.href == aLocation) {
-        return childDoc;
+  for (let {docShell} of Services.wm.getXULWindowEnumerator(null)) {
+    let containedDocShells = docShell.getDocShellEnumerator(
+                                      docShell.typeChrome,
+                                      docShell.ENUMERATE_FORWARDS);
+    for (let {domWindow} of containedDocShells) {
+      if (domWindow.location.href == aLocation) {
+        return domWindow.document;
       }
     }
   }
   return undefined;
 }
--- a/browser/base/content/test/plugins/browser_CTP_crashreporting.js
+++ b/browser/base/content/test/plugins/browser_CTP_crashreporting.js
@@ -12,19 +12,17 @@ const PLUGIN_SMALL_PAGE = gTestRoot + "p
  * @param aBag
  *        The nsIPropertyBag to convert.
  * @return Object
  *        Keyed on the names of the nsIProperty's within the nsIPropertyBag,
  *        and mapping to their values.
  */
 function convertPropertyBag(aBag) {
   let result = {};
-  let enumerator = aBag.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let { name, value } = enumerator.getNext().QueryInterface(Ci.nsIProperty);
+  for (let { name, value } of aBag.enumerator) {
     if (value instanceof Ci.nsIPropertyBag) {
       value = convertPropertyBag(value);
     }
     result[name] = value;
   }
   return result;
 }
 
--- a/browser/base/content/test/siteIdentity/head.js
+++ b/browser/base/content/test/siteIdentity/head.js
@@ -289,31 +289,22 @@ async function loadBadCertPage(url) {
     await exceptionDialogResolved;
   }
   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 }
 
 // Utility function to get a handle on the certificate exception dialog.
 // Modified from toolkit/components/passwordmgr/test/prompt_common.js
 function getCertExceptionDialog(aLocation) {
-  let enumerator = Services.wm.getXULWindowEnumerator(null);
-
-  while (enumerator.hasMoreElements()) {
-    let win = enumerator.getNext();
-    let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
+  for (let win of Services.wm.getXULWindowEnumerator(null)) {
+    let windowDocShell = win.docShell;
 
     let containedDocShells = windowDocShell.getDocShellEnumerator(
                                       Ci.nsIDocShellTreeItem.typeChrome,
                                       Ci.nsIDocShell.ENUMERATE_FORWARDS);
-    while (containedDocShells.hasMoreElements()) {
-      // Get the corresponding document for this docshell
-      let childDocShell = containedDocShells.getNext();
-      let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
-                                  .contentViewer
-                                  .DOMDocument;
-
-      if (childDoc.location.href == aLocation) {
-        return childDoc;
+    for (let {domWindow} of containedDocShells) {
+      if (domWindow.location.href == aLocation) {
+        return domWindow.document;
       }
     }
   }
   return undefined;
 }
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -214,20 +214,18 @@ add_task(async function checkAllThePrope
   // This asynchronously produces a list of URLs (sadly, mostly sync on our
   // test infrastructure because it runs against jarfiles there, and
   // our zipreader APIs are all sync)
   let uris = await getAllTheFiles(".properties");
   ok(uris.length, `Found ${uris.length} .properties files to scan for misused characters`);
 
   for (let uri of uris) {
     let bundle = Services.strings.createBundle(uri.spec);
-    let enumerator = bundle.getSimpleEnumeration();
 
-    while (enumerator.hasMoreElements()) {
-      let entity = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+    for (let entity of bundle.getSimpleEnumeration()) {
       testForErrors(uri.spec, entity.key, entity.value);
     }
   }
 });
 
 var checkDTD = async function(aURISpec) {
   let rawContents = await fetchFile(aURISpec);
   // The regular expression below is adapted from:
--- a/browser/base/content/test/tabcrashed/head.js
+++ b/browser/base/content/test/tabcrashed/head.js
@@ -49,19 +49,17 @@ function promiseCrashReport(expectedExtr
     file.remove(false);
 
     let extra = getPropertyBagValue(subject, "extra");
     if (!(extra instanceof Ci.nsIPropertyBag2)) {
       throw new Error("extra was not a Ci.nsIPropertyBag2");
     }
 
     info("Iterating crash report extra keys");
-    let enumerator = extra.enumerator;
-    while (enumerator.hasMoreElements()) {
-      let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
+    for (let {name: key} of extra.enumerator) {
       let value = extra.getPropertyAsAString(key);
       if (key in expectedExtra) {
         if (expectedExtra[key] == null) {
           ok(false, `Got unexpected key ${key} with value ${value}`);
         } else {
           is(value, expectedExtra[key],
              `Crash report had the right extra value for ${key}`);
         }
--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
+++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
@@ -134,19 +134,17 @@ function loadTab(tab, url) {
   info("Loading page: " + url);
   tab.linkedBrowser.loadURI(url);
   return Promise.all([ loaded, visited ]);
 }
 
 function ensure_opentabs_match_db() {
   var tabs = {};
 
-  var winEnum = Services.wm.getEnumerator("navigator:browser");
-  while (winEnum.hasMoreElements()) {
-    let browserWin = winEnum.getNext();
+  for (let browserWin of Services.wm.getEnumerator("navigator:browser")) {
     // skip closed-but-not-destroyed windows
     if (browserWin.closed)
       continue;
 
     for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) {
       let browser = browserWin.gBrowser.getBrowserAtIndex(i);
       let url = browser.currentURI.spec;
       if (browserWin.isBlankPageURL(url))
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -105,19 +105,17 @@ async function assertWebRTCIndicatorStat
       expectAudio = true;
     if (expected.screen)
       expectScreen = expected.screen;
   }
   is(ui.showCameraIndicator, expectVideo, "camera global indicator as expected");
   is(ui.showMicrophoneIndicator, expectAudio, "microphone global indicator as expected");
   is(ui.showScreenSharingIndicator, expectScreen, "screen global indicator as expected");
 
-  let windows = Services.wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements()) {
-    let win = windows.getNext();
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
     let menu = win.document.getElementById("tabSharingMenu");
     is(!!menu && !menu.hidden, !!expected, "WebRTC menu should be " + expectedState);
   }
 
   if (!("nsISystemStatusBar" in Ci)) {
     if (!expected) {
       let win = Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
       if (win) {
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -785,20 +785,18 @@ function isBidiEnabled() {
 
   if (isRTL) {
     Services.prefs.setBoolPref("bidi.browser.ui", true);
   }
   return isRTL;
 }
 
 function openAboutDialog() {
-  var enumerator = Services.wm.getEnumerator("Browser:About");
-  while (enumerator.hasMoreElements()) {
+  for (let win of Services.wm.getEnumerator("Browser:About")) {
     // Only open one about window (Bug 599573)
-    let win = enumerator.getNext();
     if (win.closed) {
       continue;
     }
     win.focus();
     return;
   }
 
   var features = "chrome,";
--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
@@ -67,19 +67,17 @@ add_task(async function test_cookie_getC
 
     let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
     is(foundCookie.name, cookieName, "Check cookie name");
     is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
   }
 
   // Using getCookiesWithOriginAttributes() to get all cookies for a certain
   // domain by using the originAttributes pattern, and clear all these cookies.
-  let enumerator = Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST);
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST)) {
     Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
   }
 
   // Check that whether cookies has been cleared.
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     let e = getCookiesForOA(TEST_HOST, userContextId);
     ok(!e.hasMoreElements(), "No Cookie should be here");
   }
--- a/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
+++ b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
@@ -60,19 +60,17 @@ function checkCookies(ignoreContext = nu
     is(foundCookie.name, COOKIE_NAME, "Check cookie name");
     is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
   }
 }
 
 function deleteCookies(onlyContext = null) {
   // Using getCookiesWithOriginAttributes() to get all cookies for a certain
   // domain by using the originAttributes pattern, and clear all these cookies.
-  let enumerator = Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST);
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST)) {
     if (!onlyContext || cookie.originAttributes.userContextId == onlyContext) {
       Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
     }
   }
 }
 
 add_task(async function test_cookie_getCookiesWithOriginAttributes() {
   let tabs = [];
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -511,16 +511,25 @@ var PanelMultiView = class extends Assoc
       }
       // We have to set canCancel to false before opening the popup because the
       // hidePopup method of PanelMultiView can be re-entered by event handlers.
       // If the openPopup call fails, however, we still have to dispatch the
       // "popuphidden" event even if canCancel was set to false.
       try {
         canCancel = false;
         this._panel.openPopup(...args);
+
+        // On Windows, if another popup is hiding while we call openPopup, the
+        // call won't fail but the popup won't open. In this case, we have to
+        // dispatch an artificial "popuphidden" event to reset our state.
+        if (this._panel.state == "closed" && this.openViews.length) {
+          this.dispatchCustomEvent("popuphidden");
+          return false;
+        }
+
         return true;
       } catch (ex) {
         this.dispatchCustomEvent("popuphidden");
         throw ex;
       }
     });
   }
 
@@ -1051,18 +1060,20 @@ var PanelMultiView = class extends Assoc
     // sense for all platforms. If the arrow visuals change significantly,
     // this value will be easy to adjust.
     const EXTRA_MARGIN_PX = 20;
     maxHeight -= EXTRA_MARGIN_PX;
     return maxHeight;
   }
 
   handleEvent(aEvent) {
-    if (aEvent.type.startsWith("popup") && aEvent.target != this._panel) {
-      // Shouldn't act on e.g. context menus being shown from within the panel.
+    // Only process actual popup events from the panel or events we generate
+    // ourselves, but not from menus being shown from within the panel.
+    if (aEvent.type.startsWith("popup") && aEvent.target != this._panel &&
+                                           aEvent.target != this.node) {
       return;
     }
     switch (aEvent.type) {
       case "keydown":
         // Since we start listening for the "keydown" event when the popup is
         // already showing and stop listening when the panel is hidden, we
         // always have at least one view open.
         let currentView = this.openViews[this.openViews.length - 1];
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -139,16 +139,17 @@ skip-if = os == "linux" # crashing on Li
 [browser_1087303_button_fullscreen.js]
 tags = fullscreen
 skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 skip-if = verify
+[browser_1484275_PanelMultiView_toggle_with_other_popup.js]
 [browser_allow_dragging_removable_false.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_currentset_post_reset.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
 [browser_customizemode_dragspace.js]
 skip-if = os == "linux" # linux doesn't get drag space (no tabsintitlebar)
 [browser_customizemode_uidensity.js]
 [browser_drag_outside_palette.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1484275_PanelMultiView_toggle_with_other_popup.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URL = "data:text/html,<html><body></body></html>";
+
+// This code can be consolidated in the EventUtils module (bug 1126772).
+const isWindows = AppConstants.platform == "win";
+const isMac = AppConstants.platform == "macosx";
+const mouseDown = isWindows ? 2 : isMac ? 1 : 4; // eslint-disable-line no-nested-ternary
+const mouseUp = isWindows ? 4 : isMac ? 2 : 7; // eslint-disable-line no-nested-ternary
+const utils = window.windowUtils;
+const scale = utils.screenPixelsPerCSSPixel;
+function synthesizeNativeMouseClick(aElement) {
+  let rect = aElement.getBoundingClientRect();
+  let win = aElement.ownerGlobal;
+  let x = win.mozInnerScreenX + (rect.left + rect.right) / 2;
+  let y = win.mozInnerScreenY + (rect.top + rect.bottom) / 2;
+
+  // Wait for the mouseup event to occur before continuing.
+  return new Promise((resolve, reject) => {
+    function eventOccurred(e) {
+      aElement.removeEventListener("mouseup", eventOccurred, true);
+      resolve();
+    }
+
+    aElement.addEventListener("mouseup", eventOccurred, true);
+
+    utils.sendNativeMouseEvent(x * scale, y * scale, mouseDown, 0, null);
+    utils.sendNativeMouseEvent(x * scale, y * scale, mouseUp, 0, null);
+  });
+}
+
+/**
+ * Test steps that may lead to the panel being stuck on Windows (bug 1484275).
+ */
+add_task(async function test_PanelMultiView_toggle_with_other_popup() {
+  // For proper cleanup, create a bookmark that we will remove later.
+  let bookmark = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    url: TEST_URL,
+  });
+  registerCleanupFunction(() => PlacesUtils.bookmarks.remove(bookmark));
+
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: TEST_URL,
+  }, async function(browser) {
+    // 1. Open the main menu.
+    await gCUITestUtils.openMainMenu();
+
+    // 2. Open another popup not managed by PanelMultiView.
+    let bookmarkPanel = document.getElementById("editBookmarkPanel");
+    let shown = BrowserTestUtils.waitForEvent(bookmarkPanel, "popupshown");
+    let hidden = BrowserTestUtils.waitForEvent(bookmarkPanel, "popuphidden");
+    EventUtils.synthesizeKey("D", { accelKey: true });
+    await shown;
+
+    // 3. Click the button to which the main menu is anchored. We need a native
+    // mouse event to simulate the exact platform behavior with popups.
+    let clickFn = () => synthesizeNativeMouseClick(
+      document.getElementById("PanelUI-button"));
+
+    if (AppConstants.platform == "win") {
+      // On Windows, the operation will close both popups.
+      await gCUITestUtils.hidePanelMultiView(PanelUI.panel, clickFn);
+      await new Promise(resolve => executeSoon(resolve));
+
+      // 4. Test that the popup can be opened again after it's been closed.
+      await gCUITestUtils.openMainMenu();
+      Assert.equal(PanelUI.panel.state, "open");
+    } else {
+      // On other platforms, the operation will close both popups and reopen the
+      // main menu immediately, so we wait for the reopen to occur.
+      shown = BrowserTestUtils.waitForEvent(PanelUI.mainView, "ViewShown");
+      clickFn();
+      await shown;
+    }
+
+    await gCUITestUtils.hideMainMenu();
+
+    // Make sure the events for the bookmarks panel have also been processed
+    // before closing the tab and removing the bookmark.
+    await hidden;
+  });
+});
--- a/browser/components/customizableui/test/browser_PanelMultiView.js
+++ b/browser/components/customizableui/test/browser_PanelMultiView.js
@@ -401,16 +401,17 @@ add_task(async function test_cancel_main
                            "bottomcenter topright");
   await promiseHidden;
 
   stopRecordingEvents(gPanels[0]);
 
   Assert.deepEqual(recordArray, [
     "panelview-0: ViewShowing",
     "panelview-0: ViewHiding",
+    "panelmultiview-0: PanelMultiViewHidden",
     "panelmultiview-0: popuphidden",
   ]);
 });
 
 /**
  * Tests the event sequence when opening a subview is canceled.
  */
 add_task(async function test_cancel_subview_event_sequence() {
@@ -470,18 +471,19 @@ add_task(async function test_close_while
                            "bottomcenter topright");
   await promiseHiding;
   await promiseHidden;
 
   stopRecordingEvents(gPanels[0]);
 
   Assert.deepEqual(recordArray, [
     "panelview-0: ViewShowing",
+    "panelview-0: ViewShowing > panelview-0: ViewHiding",
+    "panelview-0: ViewShowing > panelmultiview-0: PanelMultiViewHidden",
     "panelview-0: ViewShowing > panelmultiview-0: popuphidden",
-    "panelview-0: ViewShowing > panelview-0: ViewHiding",
   ]);
 });
 
 /**
  * Tests the event sequence when closing the panel while opening a subview.
  */
 add_task(async function test_close_while_showing_subview_event_sequence() {
   let recordArray = [];
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -135,24 +135,22 @@ DirectoryProvider::GetFiles(const char *
     nsCOMPtr<nsIProperties> dirSvc
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (!dirSvc)
       return NS_ERROR_FAILURE;
 
     nsCOMArray<nsIFile> distroFiles;
     AppendDistroSearchDirs(dirSvc, distroFiles);
 
-    return NS_NewArrayEnumerator(aResult, distroFiles);
+    return NS_NewArrayEnumerator(aResult, distroFiles, NS_GET_IID(nsIFile));
   }
 
   return NS_ERROR_FAILURE;
 }
 
-NS_IMPL_ISUPPORTS(DirectoryProvider::AppendingEnumerator, nsISimpleEnumerator)
-
 NS_IMETHODIMP
 DirectoryProvider::AppendingEnumerator::HasMoreElements(bool *aResult)
 {
   *aResult = mNext ? true : false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/browser/components/dirprovider/DirectoryProvider.h
+++ b/browser/components/dirprovider/DirectoryProvider.h
@@ -2,18 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DirectoryProvider_h__
 #define DirectoryProvider_h__
 
 #include "nsIDirectoryService.h"
 #include "nsComponentManagerUtils.h"
-#include "nsISimpleEnumerator.h"
 #include "nsIFile.h"
+#include "nsSimpleEnumerator.h"
 #include "mozilla/Attributes.h"
 
 #define NS_BROWSERDIRECTORYPROVIDER_CONTRACTID \
   "@mozilla.org/browser/directory-provider;1"
 
 namespace mozilla {
 namespace browser {
 
@@ -22,27 +22,26 @@ class DirectoryProvider final : public n
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDIRECTORYSERVICEPROVIDER
   NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
 
 private:
   ~DirectoryProvider() {}
 
-  class AppendingEnumerator final : public nsISimpleEnumerator
+  class AppendingEnumerator final : public nsSimpleEnumerator
   {
   public:
-    NS_DECL_ISUPPORTS
     NS_DECL_NSISIMPLEENUMERATOR
 
     AppendingEnumerator(nsISimpleEnumerator* aBase,
                         char const *const *aAppendList);
 
   private:
-    ~AppendingEnumerator() {}
+    ~AppendingEnumerator() override = default;
 
     nsCOMPtr<nsISimpleEnumerator> mBase;
     char const *const *const      mAppendList;
     nsCOMPtr<nsIFile>             mNext;
   };
 };
 
 } // namespace browser
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -137,19 +137,17 @@ var DownloadsCommon = {
   /**
    * Returns an object whose keys are the string names from the downloads string
    * bundle, and whose values are either the translated strings or functions
    * returning formatted strings.
    */
   get strings() {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
-    let enumerator = sb.getSimpleEnumeration();
-    while (enumerator.hasMoreElements()) {
-      let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+    for (let string of sb.getSimpleEnumeration()) {
       let stringName = string.key;
       if (stringName in kDownloadsStringsRequiringFormatting) {
         strings[stringName] = function() {
           // Convert "arguments" to a real array before calling into XPCOM.
           return sb.formatStringFromName(stringName,
                                          Array.slice(arguments, 0),
                                          arguments.length);
         };
--- a/browser/components/enterprisepolicies/tests/browser/browser_policy_clear_blocked_cookies.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_clear_blocked_cookies.js
@@ -21,19 +21,17 @@ add_task(async function setup() {
         ]
       }
     }
   });
 });
 
 function retrieve_all_cookies(host) {
   const values = [];
-  const cookies = Services.cookies.getCookiesFromHost(host, {});
-  while (cookies.hasMoreElements()) {
-    const cookie = cookies.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of Services.cookies.getCookiesFromHost(host, {})) {
     values.push({
       host: cookie.host,
       name: cookie.name,
       path: cookie.path
     });
   }
   return values;
 }
--- a/browser/components/extensions/parent/ext-browsingData.js
+++ b/browser/components/extensions/parent/ext-browsingData.js
@@ -39,17 +39,17 @@ const clearCache = () => {
 
 const clearCookies = async function(options) {
   let cookieMgr = Services.cookies;
   // This code has been borrowed from Sanitizer.jsm.
   let yieldCounter = 0;
 
   if (options.since || options.hostnames) {
     // Iterate through the cookies and delete any created after our cutoff.
-    for (const cookie of XPCOMUtils.IterSimpleEnumerator(cookieMgr.enumerator, Ci.nsICookie2)) {
+    for (const cookie of cookieMgr.enumerator) {
       if ((!options.since || cookie.creationTime >= PlacesUtils.toPRTime(options.since)) &&
           (!options.hostnames || options.hostnames.includes(cookie.host.replace(/^\./, "")))) {
         // This cookie was created after our cutoff, clear it.
         cookieMgr.remove(cookie.host, cookie.name, cookie.path,
                          false, cookie.originAttributes);
 
         if (++yieldCounter % YIELD_PERIOD == 0) {
           await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
--- a/browser/components/extensions/parent/ext-pkcs11.js
+++ b/browser/components/extensions/parent/ext-pkcs11.js
@@ -8,18 +8,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
 
 XPCOMUtils.defineLazyServiceGetter(this, "pkcs11db",
                                    "@mozilla.org/security/pkcs11moduledb;1",
                                    "nsIPKCS11ModuleDB");
 
 var {DefaultMap} = ExtensionUtils;
 
 const findModuleByPath = function(path) {
-  let modules = pkcs11db.listModules();
-  for (let module of XPCOMUtils.IterSimpleEnumerator(modules, Ci.nsIPKCS11Module)) {
+  for (let module of pkcs11db.listModules()) {
     if (module && module.libName === path) {
       return module;
     }
   }
   return null;
 };
 
 this.pkcs11 = class extends ExtensionAPI {
@@ -113,17 +112,17 @@ this.pkcs11 = class extends ExtensionAPI
          */
         async getModuleSlots(name) {
           let manifest = await manifestCache.get(name);
           let module = findModuleByPath(manifest.path);
           if (!module) {
             return Promise.reject({message: `The module ${name} is not installed`});
           }
           let rv = [];
-          for (let slot of XPCOMUtils.IterSimpleEnumerator(module.listSlots(), Ci.nsIPKCS11Slot)) {
+          for (let slot of module.listSlots()) {
             let token = slot.getToken();
             let slotobj = {
               name: slot.name,
               token: null,
             };
             if (slot.status != 1 /* SLOT_NOT_PRESENT */) {
               slotobj.token = {
                 name: token.tokenName,
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -45,19 +45,17 @@ XPCOMUtils.defineLazyGetter(this, "tabHi
       return BrowserUtils.getLocalizedFragment(doc, message, addonDetails, image);
     },
     learnMoreMessageId: "tabHideControlled.learnMore",
     learnMoreLink: "extension-hiding-tabs",
   });
 });
 
 function showHiddenTabs(id) {
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-  while (windowsEnum.hasMoreElements()) {
-    let win = windowsEnum.getNext();
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
     if (win.closed || !win.gBrowser) {
       continue;
     }
 
     for (let tab of win.gBrowser.tabs) {
       if (tab.hidden && tab.ownerGlobal &&
           SessionStore.getCustomTabValue(tab, "hiddenBy") === id) {
         win.gBrowser.showTab(tab);
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -23,16 +23,18 @@ support-files =
   file_language_fr_en.html
   file_language_ja.html
   file_language_tlh.html
   file_dummy.html
   file_title.html
   file_inspectedwindow_reload_target.sjs
   file_indexedDB.html
   file_serviceWorker.html
+  install_other-1.0-fx.xpi
+  install_theme-1.0-fx.xpi
   webNav_createdTarget.html
   webNav_createdTargetSource.html
   webNav_createdTargetSource_subframe.html
   serviceWorker.js
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
   ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
   ../../../../../toolkit/components/extensions/test/mochitest/redirection.sjs
@@ -100,16 +102,17 @@ support-files =
 [browser_ext_find.js]
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 [browser_ext_getViews.js]
 [browser_ext_history_redirect.js]
 [browser_ext_identity_indication.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
+[browser_ext_management.js]
 [browser_ext_menus.js]
 [browser_ext_menus_accesskey.js]
 [browser_ext_menus_activeTab.js]
 [browser_ext_menus_errors.js]
 [browser_ext_menus_event_order.js]
 [browser_ext_menus_events.js]
 [browser_ext_menus_refresh.js]
 [browser_ext_menus_targetElement.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_management.js
@@ -0,0 +1,80 @@
+"use strict";
+
+const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
+
+function waitForTransition(element, propertyName) {
+  return BrowserTestUtils.waitForEvent(element, "transitionend", false, event => {
+    return event.target == element && event.propertyName == propertyName;
+  });
+}
+
+add_task(async function test_management_install() {
+  await SpecialPowers.pushPrefEnv({set: [
+    ["xpinstall.signatures.required", false],
+  ]});
+
+  registerCleanupFunction(async () => {
+    await SpecialPowers.popPrefEnv();
+  });
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      browser_action: {
+        "browser_style": false,
+      },
+      permissions: ["management"],
+    },
+    background() {
+      let addons;
+      browser.test.onMessage.addListener((msg, init) => {
+        addons = init;
+        browser.test.sendMessage("ready");
+      });
+      browser.browserAction.onClicked.addListener(async () => {
+        try {
+          let {url, hash} = addons.shift();
+          browser.test.log(`Installing XPI from ${url} with hash ${hash || "missing"}`);
+          let {id} = await browser.management.install({url, hash});
+          let {type} = await browser.management.get(id);
+          browser.test.sendMessage("installed", {id, type});
+        } catch (e) {
+          browser.test.log(`management.install() throws ${e}`);
+          browser.test.sendMessage("failed", e.message);
+        }
+      });
+    },
+  });
+
+  let addons = [{
+    url: BASE + "install_theme-1.0-fx.xpi",
+    hash: "sha256:aa232d8391d82a9c1014364efbe1657ff6d8dfc88b3c71e99881b1f3843fdad3",
+  }, {
+    url: BASE + "install_other-1.0-fx.xpi",
+  }];
+
+  await extension.startup();
+  extension.sendMessage("addons", addons);
+  await extension.awaitMessage("ready");
+
+  // Test installing a static WE theme.
+  let transitionDone = waitForTransition(document.documentElement, "background-color");
+  clickBrowserAction(extension);
+
+  let {id, type} = await extension.awaitMessage("installed");
+  is(id, "tiger@persona.beard", "Static web extension theme installed");
+  is(type, "theme", "Extension type is correct");
+
+  await transitionDone;
+  let style = window.getComputedStyle(document.documentElement);
+  is(style.backgroundColor, "rgb(255, 165, 0)", "Background is the new black");
+
+  let addon = await AddonManager.getAddonByID("tiger@persona.beard");
+  await addon.uninstall();
+
+  // Test installing a standard WE.
+  clickBrowserAction(extension);
+  let error = await extension.awaitMessage("failed");
+  is(error, "Incompatible addon", "Standard web extension rejected");
+
+  await extension.unload();
+});
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -503,19 +503,17 @@ function awaitEvent(eventName, id) {
       }
     };
 
     Management.on(eventName, listener);
   });
 }
 
 function* BrowserWindowIterator() {
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-  while (windowsEnum.hasMoreElements()) {
-    let currentWindow = windowsEnum.getNext();
+  for (let currentWindow of Services.wm.getEnumerator("navigator:browser")) {
     if (!currentWindow.closed) {
       yield currentWindow;
     }
   }
 }
 
 async function locationChange(tab, url, task) {
   let locationChanged = BrowserTestUtils.waitForLocationChange(gBrowser, url);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..66be85efd5903e4b541700afd26f03b1aa9af6f4
GIT binary patch
literal 246
zc$^FHW@Zs#U}E54u;ml-t$Fjzq>6!o;UEz60&#9)US?Wqafx15aem(FXHSC|n3mo-
zvggc}D~~4q*mPylqceYORJB=^Wmm++CFN;NST8bZCf}5hf|KWi^#ecn>S%iE1~sj8
z%m_BRV%)&3E++CMMNNGg!_uzCqN@Ul8$znGipuO(Nd%Y}v!=MzdAemybgs;r6qn`d
xnI(54rDanThp-2uAVYvRBa;XNZfAl3BZC5zh2rP{Z&o&lG$VsGkX{N_0ssgvNwoj~
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0ef2b3d67222966d9a4b4d31185256616aba4f60
GIT binary patch
literal 274
zc$^FHW@Zs#U}E54P~{WyEv--DpT@wzAi>DMzzd{v6Z10DQj1IUvWoNba{D&&9a0cz
z`>s~Yekez(wB!@(ojYee{Z+QHH+oNB_WqsY+EC@hhX2j)_itP`MdvQ_PHDA$JQ9;X
zFX7MHUuoDR_jKL)b6g?n3CC85@2b17JMDL$V#_(n1V_&s7ROm5P5A%M^?Y?}&4W9I
zJdeJxoE25vR#j_u_IRe|L+fc5?cN?Mw_6rpJMV;qik^k}PmYw2{d?A>*fR!rGct)V
d;PwItFfu4WStuR~@MdL$NHa261L=ccB>>-AT%rH~
--- a/browser/components/migration/AutoMigrate.jsm
+++ b/browser/components/migration/AutoMigrate.jsm
@@ -271,19 +271,17 @@ const AutoMigrate = {
     }, true);
 
     this._purgeUndoState(this.UNDO_REMOVED_REASON_UNDO_USED);
     histogram.add(30);
     TelemetryStopwatch.finishKeyed("FX_STARTUP_MIGRATION_UNDO_TOTAL_MS", browserId);
   },
 
   _removeNotificationBars() {
-    let browserWindows = Services.wm.getEnumerator("navigator:browser");
-    while (browserWindows.hasMoreElements()) {
-      let win = browserWindows.getNext();
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
       if (!win.closed) {
         for (let browser of win.gBrowser.browsers) {
           let nb = win.gBrowser.getNotificationBox(browser);
           let notification = nb.getNotificationWithValue(kNotificationId);
           if (notification) {
             nb.removeNotification(notification);
           }
         }
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ b/browser/components/migration/FirefoxProfileMigrator.js
@@ -32,22 +32,19 @@ ChromeUtils.defineModuleGetter(this, "Pr
 function FirefoxProfileMigrator() {
   this.wrappedJSObject = this; // for testing...
 }
 
 FirefoxProfileMigrator.prototype = Object.create(MigratorPrototype);
 
 FirefoxProfileMigrator.prototype._getAllProfiles = function() {
   let allProfiles = new Map();
-  let profiles =
-    Cc["@mozilla.org/toolkit/profile-service;1"]
-      .getService(Ci.nsIToolkitProfileService)
-      .profiles;
-  while (profiles.hasMoreElements()) {
-    let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
+  let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
+      .getService(Ci.nsIToolkitProfileService);
+  for (let profile of profileService.profiles) {
     let rootDir = profile.rootDir;
 
     if (rootDir.exists() && rootDir.isReadable() &&
         !rootDir.equals(MigrationUtils.profileStartup.directory)) {
       allProfiles.set(profile.name, rootDir);
     }
   }
   return allProfiles;
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -33,20 +33,18 @@ History.prototype = {
 
   get exists() {
     return true;
   },
 
   migrate: function H_migrate(aCallback) {
     let pageInfos = [];
     let typedURLs = MSMigrationUtils.getTypedURLs("Software\\Microsoft\\Internet Explorer");
-    let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
-                            createInstance(Ci.nsISimpleEnumerator);
-    while (historyEnumerator.hasMoreElements()) {
-      let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
+    for (let entry of Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"]
+            .createInstance(Ci.nsISimpleEnumerator)) {
       let url = entry.get("uri").QueryInterface(Ci.nsIURI);
       // MSIE stores some types of URLs in its history that we don't handle,
       // like HTMLHelp and others.  Since we don't properly map handling for
       // all of them we just avoid importing them.
       if (!["http", "https", "ftp", "file"].includes(url.scheme)) {
         continue;
       }
 
@@ -110,21 +108,19 @@ IE7FormPasswords.prototype = {
       key.close();
       return count > 0;
     } catch (e) {
       return false;
     }
   },
 
   async migrate(aCallback) {
-    let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
-                            createInstance(Ci.nsISimpleEnumerator);
     let uris = []; // the uris of the websites that are going to be migrated
-    while (historyEnumerator.hasMoreElements()) {
-      let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
+    for (let entry of Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"]
+            .createInstance(Ci.nsISimpleEnumerator)) {
       let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
       // MSIE stores some types of URLs in its history that we don't handle, like HTMLHelp
       // and others. Since we are not going to import the logins that are performed in these URLs
       // we can just skip them.
       if (!["http", "https", "ftp"].includes(uri.scheme)) {
         continue;
       }
 
--- a/browser/components/migration/nsIEHistoryEnumerator.cpp
+++ b/browser/components/migration/nsIEHistoryEnumerator.cpp
@@ -14,18 +14,16 @@
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsWindowsMigrationUtils.h"
 #include "prtime.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIEHistoryEnumerator
 
-NS_IMPL_ISUPPORTS(nsIEHistoryEnumerator, nsISimpleEnumerator)
-
 nsIEHistoryEnumerator::nsIEHistoryEnumerator()
 {
   ::CoInitialize(nullptr);
 }
 
 nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
 {
   ::CoUninitialize();
--- a/browser/components/migration/nsIEHistoryEnumerator.h
+++ b/browser/components/migration/nsIEHistoryEnumerator.h
@@ -4,29 +4,33 @@
 
 #ifndef iehistoryenumerator___h___
 #define iehistoryenumerator___h___
 
 #include <urlhist.h>
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
-#include "nsISimpleEnumerator.h"
 #include "nsIWritablePropertyBag2.h"
+#include "nsSimpleEnumerator.h"
 
-class nsIEHistoryEnumerator final : public nsISimpleEnumerator
+class nsIEHistoryEnumerator final : public nsSimpleEnumerator
 {
 public:
-  NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
   nsIEHistoryEnumerator();
 
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsIWritablePropertyBag2);
+  }
+
 private:
-  ~nsIEHistoryEnumerator();
+  ~nsIEHistoryEnumerator() override;
 
   /**
    * Initializes the history reader, if needed.
    */
   void EnsureInitialized();
 
   RefPtr<IUrlHistoryStg2> mIEHistory;
   RefPtr<IEnumSTATURL> mURLEnumerator;
--- a/browser/components/migration/tests/unit/test_fx_telemetry.js
+++ b/browser/components/migration/tests/unit/test_fx_telemetry.js
@@ -14,19 +14,17 @@ function readFile(file) {
   let contents = sis.read(file.fileSize);
   sis.close();
   return contents;
 }
 
 function checkDirectoryContains(dir, files) {
   print("checking " + dir.path + " - should contain " + Object.keys(files));
   let seen = new Set();
-  let enumerator = dir.directoryEntries;
-  while (enumerator.hasMoreElements()) {
-    let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
+  for (let file of dir.directoryEntries) {
     print("found file: " + file.path);
     Assert.ok(file.leafName in files, file.leafName + " exists, but shouldn't");
 
     let expectedContents = files[file.leafName];
     if (typeof expectedContents != "string") {
       // it's a subdir - recurse!
       Assert.ok(file.isDirectory(), "should be a subdir");
       let newDir = dir.clone();
--- a/browser/components/newtab/lib/ASRouterTriggerListeners.jsm
+++ b/browser/components/newtab/lib/ASRouterTriggerListeners.jsm
@@ -31,19 +31,17 @@ this.ASRouterTriggerListeners = new Map(
     init(triggerHandler, hosts = []) {
       if (!this._initialized) {
         this.onLocationChange = this.onLocationChange.bind(this);
 
         // Listen for new windows being opened
         Services.ww.registerNotification(this);
 
         // Add listeners to all existing browser windows
-        const winEnum = Services.wm.getEnumerator("navigator:browser");
-        while (winEnum.hasMoreElements()) {
-          let win = winEnum.getNext();
+        for (let win of Services.wm.getEnumerator("navigator:browser")) {
           if (win.closed || PrivateBrowsingUtils.isWindowPrivate(win)) {
             continue;
           }
           win.gBrowser.addTabsProgressListener(this);
         }
 
         this._initialized = true;
       }
@@ -54,19 +52,17 @@ this.ASRouterTriggerListeners = new Map(
         this._hosts = new Set(hosts); // Clone the hosts to avoid unexpected behaviour
       }
     },
 
     uninit() {
       if (this._initialized) {
         Services.ww.unregisterNotification(this);
 
-        const winEnum = Services.wm.getEnumerator("navigator:browser");
-        while (winEnum.hasMoreElements()) {
-          let win = winEnum.getNext();
+        for (let win of Services.wm.getEnumerator("navigator:browser")) {
           if (win.closed || PrivateBrowsingUtils.isWindowPrivate(win)) {
             continue;
           }
 
           win.gBrowser.removeTabsProgressListener(this);
         }
 
         this._initialized = false;
--- a/browser/components/newtab/lib/Screenshots.jsm
+++ b/browser/components/newtab/lib/Screenshots.jsm
@@ -61,19 +61,18 @@ this.Screenshots = {
   },
 
   /**
    * Checks if all the open windows are private browsing windows. If so, we do not
    * want to collect screenshots. If there exists at least 1 non-private window,
    * we are ok to collect screenshots.
    */
   _shouldGetScreenshots() {
-    const windows = Services.wm.getEnumerator("navigator:browser");
-    while (windows.hasMoreElements()) {
-      if (!PrivateBrowsingUtils.isWindowPrivate(windows.getNext())) {
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      if (!PrivateBrowsingUtils.isWindowPrivate(win)) {
         // As soon as we encounter 1 non-private window, screenshots are fair game.
         return true;
       }
     }
     return false;
   },
 
   /**
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -637,19 +637,18 @@ BrowserGlue.prototype = {
 
       if (prefDelay > 0)
         return;
     }
 
     // delays are in seconds
     const MAX_DELAY = 300;
     let delay = 3;
-    let browserEnum = Services.wm.getEnumerator("navigator:browser");
-    while (browserEnum.hasMoreElements()) {
-      delay += browserEnum.getNext().gBrowser.tabs.length;
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      delay += win.gBrowser.tabs.length;
     }
     delay = delay <= MAX_DELAY ? delay : MAX_DELAY;
 
     ChromeUtils.import("resource://services-sync/main.js");
     Weave.Service.scheduler.delayedAutoConnect(delay);
   },
 
   /**
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
@@ -42,21 +42,19 @@ add_task(async function principal_test()
 /**
  * Test for the cookie jars of the top-level document and child iframe should be
  * isolated by firstPartyDomain.
  */
 add_task(async function cookie_test() {
   let tab = BrowserTestUtils.addTab(gBrowser, BASE_URL + "test_firstParty_cookie.html");
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser, true);
 
-  let iter = Services.cookies.enumerator;
   let count = 0;
-  while (iter.hasMoreElements()) {
+  for (let cookie of Services.cookies.enumerator) {
     count++;
-    let cookie = iter.getNext().QueryInterface(Ci.nsICookie2);
     Assert.equal(cookie.value, "foo", "Cookie value should be foo");
     Assert.equal(cookie.originAttributes.firstPartyDomain, BASE_DOMAIN, "Cookie's origin attributes should be " + BASE_DOMAIN);
   }
 
   // one cookie is from requesting test.js from top-level doc, and the other from
   // requesting test2.js from iframe test2.html.
   Assert.equal(count, 2, "Should have two cookies");
 
--- a/browser/components/payments/paymentUIService.js
+++ b/browser/components/payments/paymentUIService.js
@@ -149,19 +149,17 @@ PaymentUIService.prototype = {
       return false;
     }
     this.log.debug(`closing: ${win.name}`);
     win.close();
     return true;
   },
 
   findDialog(requestId) {
-    let enu = Services.wm.getEnumerator(null);
-    let win;
-    while ((win = enu.getNext())) {
+    for (let win of Services.wm.getEnumerator(null)) {
       if (win.name == `${this.REQUEST_ID_PREFIX}${requestId}`) {
         return win;
       }
     }
 
     return null;
   },
 
--- a/browser/components/payments/test/browser/head.js
+++ b/browser/components/payments/test/browser/head.js
@@ -19,22 +19,17 @@ const paymentSrv = Cc["@mozilla.org/dom/
 const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
                      .getService().wrappedJSObject;
 const {formAutofillStorage} = ChromeUtils.import(
   "resource://formautofill/FormAutofillStorage.jsm", {});
 const {PaymentTestUtils: PTU} = ChromeUtils.import(
   "resource://testing-common/PaymentTestUtils.jsm", {});
 
 function getPaymentRequests() {
-  let requestsEnum = paymentSrv.enumerate();
-  let requests = [];
-  while (requestsEnum.hasMoreElements()) {
-    requests.push(requestsEnum.getNext().QueryInterface(Ci.nsIPaymentRequest));
-  }
-  return requests;
+  return Array.from(paymentSrv.enumerate());
 }
 
 /**
  * Return the container (e.g. dialog or overlay) that the payment request contents are shown in.
  * This abstracts away the details of the widget used so that this can more earily transition from a
  * dialog to another kind of overlay.
  * Consumers shouldn't rely on a dialog window being returned.
  * @returns {Promise}
--- a/browser/components/preferences/applicationManager.js
+++ b/browser/components/preferences/applicationManager.js
@@ -25,24 +25,20 @@ var gAppManagerDialog = {
       });
     } else {
       document.l10n.setAttributes(appDescElem, "app-manager-handle-protocol", {
         type: this.handlerInfo.typeDescription,
       });
     }
 
     var list = document.getElementById("appList");
-    var apps = this.handlerInfo.possibleApplicationHandlers.enumerate();
-    while (apps.hasMoreElements()) {
-      let app = apps.getNext();
+    for (let app of this.handlerInfo.possibleApplicationHandlers.enumerate()) {
       if (!gMainPane.isValidHandlerApp(app))
         continue;
 
-      app.QueryInterface(Ci.nsIHandlerApp);
-
       // Ensure the XBL binding is created eagerly.
       // eslint-disable-next-line no-undef
       list.appendChild(MozXULElement.parseXULToFragment("<richlistitem/>"));
       var item = list.lastChild;
       item.app = app;
 
       var image = document.createElement("image");
       image.setAttribute("src", gMainPane._getIconURLForHandlerApp(app));
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1392,20 +1392,17 @@ var gMainPane = {
       handlerInfoWrapper.pluginName = mimeType.enabledPlugin.name;
     }
   },
 
   /**
    * Load the set of handlers defined by the application datastore.
    */
   _loadApplicationHandlers() {
-    var wrappedHandlerInfos = gHandlerService.enumerate();
-    while (wrappedHandlerInfos.hasMoreElements()) {
-      let wrappedHandlerInfo =
-        wrappedHandlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+    for (let wrappedHandlerInfo of gHandlerService.enumerate()) {
       let type = wrappedHandlerInfo.type;
 
       let handlerInfoWrapper;
       if (type in this._handledTypes)
         handlerInfoWrapper = this._handledTypes[type];
       else {
         handlerInfoWrapper = new HandlerInfoWrapper(type, wrappedHandlerInfo);
         this._handledTypes[type] = handlerInfoWrapper;
@@ -1656,20 +1653,18 @@ var gMainPane = {
       defaultMenuItem.setAttribute("tooltiptext", handlerInfo.defaultDescription);
       defaultMenuItem.setAttribute("image", handlerInfo.iconURLForSystemDefault);
 
       menuPopup.appendChild(defaultMenuItem);
     }
 
     // Create menu items for possible handlers.
     let preferredApp = handlerInfo.preferredApplicationHandler;
-    let possibleApps = handlerInfo.possibleApplicationHandlers.enumerate();
     var possibleAppMenuItems = [];
-    while (possibleApps.hasMoreElements()) {
-      let possibleApp = possibleApps.getNext();
+    for (let possibleApp of handlerInfo.possibleApplicationHandlers.enumerate()) {
       if (!this.isValidHandlerApp(possibleApp))
         continue;
 
       let menuItem = document.createElement("menuitem");
       menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
       let label;
       if (possibleApp instanceof Ci.nsILocalHandlerApp)
         label = getFileDisplayName(possibleApp.executable);
@@ -1687,20 +1682,18 @@ var gMainPane = {
       menuPopup.appendChild(menuItem);
       possibleAppMenuItems.push(menuItem);
     }
     // Add gio handlers
     if (Cc["@mozilla.org/gio-service;1"]) {
       let gIOSvc = Cc["@mozilla.org/gio-service;1"].
                    getService(Ci.nsIGIOService);
       var gioApps = gIOSvc.getAppsForURIScheme(handlerInfo.type);
-      let enumerator = gioApps.enumerate();
       let possibleHandlers = handlerInfo.possibleApplicationHandlers;
-      while (enumerator.hasMoreElements()) {
-        let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
+      for (let handler of gioApps.enumerate()) {
         // OS handler share the same name, it's most likely the same app, skipping...
         if (handler.name == handlerInfo.defaultDescription) {
           continue;
         }
         // Check if the handler is already in possibleHandlers
         let appAlreadyInHandlers = false;
         for (let i = possibleHandlers.length - 1; i >= 0; --i) {
           let app = possibleHandlers.queryElementAt(i, Ci.nsIHandlerApp);
@@ -2395,16 +2388,20 @@ function getLocalHandlerApp(aFile) {
 function ArrayEnumerator(aItems) {
   this._index = 0;
   this._contents = aItems;
 }
 
 ArrayEnumerator.prototype = {
   _index: 0,
 
+  [Symbol.iterator]() {
+    return this._contents.values();
+  },
+
   hasMoreElements() {
     return this._index < this._contents.length;
   },
 
   getNext() {
     return this._contents[this._index++];
   }
 };
@@ -2705,19 +2702,18 @@ class HandlerInfoWrapper {
       this.addPossibleApplicationHandler(aNewValue);
   }
 
   get possibleApplicationHandlers() {
     return this.wrappedHandlerInfo.possibleApplicationHandlers;
   }
 
   addPossibleApplicationHandler(aNewHandler) {
-    var possibleApps = this.possibleApplicationHandlers.enumerate();
-    while (possibleApps.hasMoreElements()) {
-      if (possibleApps.getNext().equals(aNewHandler))
+    for (let app of this.possibleApplicationHandlers.enumerate()) {
+      if (app.equals(aNewHandler))
         return;
     }
     this.possibleApplicationHandlers.appendElement(aNewHandler);
   }
 
   removePossibleApplicationHandler(aHandler) {
     var defaultApp = this.preferredApplicationHandler;
     if (defaultApp && aHandler.equals(defaultApp)) {
--- a/browser/components/preferences/languages.js
+++ b/browser/components/preferences/languages.js
@@ -49,25 +49,20 @@ var gLanguagesDialog = {
 
     function LocaleInfo(aLocaleName, aLocaleCode, aIsVisible) {
       this.name = aLocaleName;
       this.code = aLocaleCode;
       this.isVisible = aIsVisible;
     }
 
     // 1) Read the available languages out of language.properties
-    var strings = bundleAccepted.strings;
 
     let localeCodes = [];
     let localeValues = [];
-    while (strings.hasMoreElements()) {
-      var currString = strings.getNext();
-      if (!(currString instanceof Ci.nsIPropertyElement))
-        break;
-
+    for (let currString of bundleAccepted.strings) {
       var property = currString.key.split("."); // ab[-cd].accept
       if (property[1] == "accept") {
         localeCodes.push(property[0]);
         localeValues.push(currString.value);
       }
     }
 
     let localeNames = Services.intl.getLocaleDisplayNames(undefined, localeCodes);
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -255,19 +255,17 @@ var gPermissionManager = {
     let permissionlistitem = document.getElementsByAttribute("origin", origin)[0];
     if (permissionlistitem) {
       permissionlistitem.remove();
     }
   },
 
   _loadPermissions() {
     // load permissions into a table.
-    let enumerator = Services.perms.enumerator;
-    while (enumerator.hasMoreElements()) {
-      let nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+    for (let nextPermission of Services.perms.enumerator) {
       this._addPermissionToList(nextPermission);
     }
   },
 
   _createPermissionListItem(permission) {
     let richlistitem = document.createElement("richlistitem");
     richlistitem.setAttribute("origin", permission.origin);
     let row = document.createElement("hbox");
--- a/browser/components/preferences/sitePermissions.js
+++ b/browser/components/preferences/sitePermissions.js
@@ -187,19 +187,17 @@ var gSitePermissionsManager = {
     let permissionlistitem = document.getElementsByAttribute("origin", origin)[0];
     if (permissionlistitem) {
       permissionlistitem.remove();
     }
   },
 
   _loadPermissions() {
     // load permissions into a table.
-    let enumerator = Services.perms.enumerator;
-    while (enumerator.hasMoreElements()) {
-      let nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+    for (let nextPermission of Services.perms.enumerator) {
       this._addPermissionToList(nextPermission);
     }
   },
 
   _createPermissionListItem(permission) {
     let richlistitem = document.createElement("richlistitem");
     richlistitem.setAttribute("origin", permission.origin);
     let row = document.createElement("hbox");
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -88,20 +88,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 = [];
-    let enumerator = Services.perms.enumerator;
-    while (enumerator.hasMoreElements()) {
-      let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
-
+    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");
     this._sites.sort();
 
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -171,19 +171,18 @@ var SessionCookiesInternal = {
   _reloadCookies() {
     CookieStore.clear();
 
     // Bail out if we're not supposed to store cookies at all.
     if (!PrivacyLevel.canSave(false)) {
       return;
     }
 
-    let iter = Services.cookies.sessionEnumerator;
-    while (iter.hasMoreElements()) {
-      this._addCookie(iter.getNext());
+    for (let cookie of Services.cookies.sessionEnumerator) {
+      this._addCookie(cookie);
     }
   }
 };
 
 /**
  * The internal storage that keeps track of session cookies.
  */
 var CookieStore = {
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -592,19 +592,17 @@ var SessionStoreInternal = {
    * This was added to support the sessions.restore WebExtensions API.
    */
   get lastClosedObjectType() {
     if (this._closedWindows.length) {
       // Since there are closed windows, we need to check if there's a closed tab
       // in one of the currently open windows that was closed after the
       // last-closed window.
       let tabTimestamps = [];
-      let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-      while (windowsEnum.hasMoreElements()) {
-        let window = windowsEnum.getNext();
+      for (let window of Services.wm.getEnumerator("navigator:browser")) {
         let windowState = this._windows[window.__SSi];
         if (windowState && windowState._closedTabs[0]) {
           tabTimestamps.push(windowState._closedTabs[0].closedAt);
         }
       }
       if (!tabTimestamps.length ||
           (tabTimestamps.sort((a, b) => b - a)[0] < this._closedWindows[0].closedAt)) {
         return "window";
@@ -2717,19 +2715,17 @@ var SessionStoreInternal = {
     // Check for a window first.
     for (let i = 0, l = this._closedWindows.length; i < l; i++) {
       if (this._closedWindows[i].closedId == aClosedId) {
         return this.undoCloseWindow(i);
       }
     }
 
     // Check for a tab.
-    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-    while (windowsEnum.hasMoreElements()) {
-      let window = windowsEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       let windowState = this._windows[window.__SSi];
       if (windowState) {
         for (let j = 0, l = windowState._closedTabs.length; j < l; j++) {
           if (windowState._closedTabs[j].closedId == aClosedId) {
             return this.undoCloseTab(window, j);
           }
         }
       }
@@ -2780,19 +2776,17 @@ var SessionStoreInternal = {
     if ("image" in tabData) {
       win.gBrowser.setIcon(tab, tabData.image, undefined, tabData.iconLoadingPrincipal);
       TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
     }
   },
 
   // This method deletes all the closedTabs matching userContextId.
   _forgetTabsWithUserContextId(userContextId) {
-    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-    while (windowsEnum.hasMoreElements()) {
-      let window = windowsEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       let windowState = this._windows[window.__SSi];
       if (windowState) {
         // In order to remove the tabs in the correct order, we store the
         // indexes, into an array, then we revert the array and remove closed
         // data from the last one going backward.
         let indexes = [];
         windowState._closedTabs.forEach((closedTab, index) => {
           if (closedTab.state.userContextId == userContextId) {
@@ -2967,19 +2961,17 @@ var SessionStoreInternal = {
       forceOnDemand: true,
     });
   },
 
   /**
    * Revive all crashed tabs and reset the crashed tabs count to 0.
    */
   reviveAllCrashedTabs() {
-    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-    while (windowsEnum.hasMoreElements()) {
-      let window = windowsEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       for (let tab of window.gBrowser.tabs) {
         this.reviveCrashedTab(tab);
       }
     }
   },
 
   /**
    * Navigate the given |tab| by first collecting its current state and then
@@ -4324,21 +4316,18 @@ var SessionStoreInternal = {
   },
 
   /**
    * Calls onClose for windows that are determined to be closed but aren't
    * destroyed yet, which would otherwise cause getBrowserState and
    * setBrowserState to treat them as open windows.
    */
   _handleClosedWindows: function ssi_handleClosedWindows() {
-    var windowsEnum = Services.wm.getEnumerator("navigator:browser");
-
     let promises = [];
-    while (windowsEnum.hasMoreElements()) {
-      var window = windowsEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       if (window.closed) {
         promises.push(this.onClose(window));
       }
     }
     return Promise.all(promises);
   },
 
   /**
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -54,19 +54,18 @@ const IS_MAC = navigator.platform.match(
  * Returns an Object with two properties:
  *   open (int):
  *     A count of how many non-closed navigator:browser windows there are.
  *   winstates (int):
  *     A count of how many windows there are in the SessionStore state.
  */
 function getBrowserWindowsCount() {
   let open = 0;
-  let e = Services.wm.getEnumerator("navigator:browser");
-  while (e.hasMoreElements()) {
-    if (!e.getNext().closed)
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
+    if (!win.closed)
       ++open;
   }
 
   let winstates = JSON.parse(ss.getBrowserState()).windows.length;
 
   return { open, winstates };
 }
 
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -18,36 +18,30 @@ add_task(async function() {
   let tab = BrowserTestUtils.addTab(gBrowser, testURL);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   await TabStateFlusher.flush(tab.linkedBrowser);
 
   // get the sessionstore state for the window
   let state = ss.getBrowserState();
 
   // verify our cookie got set during pageload
-  let enumerator = Services.cookies.enumerator;
-  let cookie;
   let i = 0;
-  while (enumerator.hasMoreElements()) {
-    cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (var cookie of Services.cookies.enumerator) {
     i++;
   }
   Assert.equal(i, 1, "expected one cookie");
 
   // remove the cookie
   Services.cookies.removeAll();
 
   // restore the window state
   await setBrowserState(state);
 
   // at this point, the cookie should be restored...
-  enumerator = Services.cookies.enumerator;
-  let cookie2;
-  while (enumerator.hasMoreElements()) {
-    cookie2 = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (var cookie2 of Services.cookies.enumerator) {
     if (cookie.name == cookie2.name)
       break;
   }
   is(cookie.name, cookie2.name, "cookie name successfully restored");
   is(cookie.value, cookie2.value, "cookie value successfully restored");
   is(cookie.path, cookie2.path, "cookie path successfully restored");
 
   // clean up
--- a/browser/components/sessionstore/test/browser_526613.js
+++ b/browser/components/sessionstore/test/browser_526613.js
@@ -5,19 +5,18 @@
 function test() {
   /** Test for Bug 526613 **/
 
   // test setup
   waitForExplicitFinish();
 
   function browserWindowsCount(expected) {
     let count = 0;
-    let e = Services.wm.getEnumerator("navigator:browser");
-    while (e.hasMoreElements()) {
-      if (!e.getNext().closed)
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      if (!win.closed)
         ++count;
     }
     is(count, expected,
        "number of open browser windows according to nsIWindowMediator");
     let state = ss.getBrowserState();
     info(state);
     is(JSON.parse(state).windows.length, expected,
        "number of open browser windows according to getBrowserState");
--- a/browser/components/sessionstore/test/browser_528776.js
+++ b/browser/components/sessionstore/test/browser_528776.js
@@ -1,13 +1,12 @@
 function browserWindowsCount(expected) {
   var count = 0;
-  var e = Services.wm.getEnumerator("navigator:browser");
-  while (e.hasMoreElements()) {
-    if (!e.getNext().closed)
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
+    if (!win.closed)
       ++count;
   }
   is(count, expected,
      "number of open browser windows according to nsIWindowMediator");
   is(JSON.parse(ss.getBrowserState()).windows.length, expected,
      "number of open browser windows according to getBrowserState");
 }
 
--- a/browser/components/sessionstore/test/browser_600545.js
+++ b/browser/components/sessionstore/test/browser_600545.js
@@ -62,20 +62,18 @@ function testBug600545() {
       done();
     });
   });
 }
 
 function done() {
   // Enumerate windows and close everything but our primary window. We can't
   // use waitForFocus() because apparently it's buggy. See bug 599253.
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
   let closeWinPromises = [];
-  while (windowsEnum.hasMoreElements()) {
-    let currentWindow = windowsEnum.getNext();
+  for (let currentWindow of Services.wm.getEnumerator("navigator:browser")) {
     if (currentWindow != window)
       closeWinPromises.push(BrowserTestUtils.closeWindow(currentWindow));
   }
 
   Promise.all(closeWinPromises).then(() => {
     waitForBrowserState(stateBackup, finish);
   });
 }
--- a/browser/components/sessionstore/test/browser_618151.js
+++ b/browser/components/sessionstore/test/browser_618151.js
@@ -21,20 +21,18 @@ function test() {
 
 // Just a subset of tests from bug 615394 that causes a timeout.
 var tests = [test_setup, test_hang];
 function runNextTest() {
   // set an empty state & run the next test, or finish
   if (tests.length) {
     // Enumerate windows and close everything but our primary window. We can't
     // use waitForFocus() because apparently it's buggy. See bug 599253.
-    var windowsEnum = Services.wm.getEnumerator("navigator:browser");
     let closeWinPromises = [];
-    while (windowsEnum.hasMoreElements()) {
-      var currentWindow = windowsEnum.getNext();
+    for (let currentWindow of Services.wm.getEnumerator("navigator:browser")) {
       if (currentWindow != window) {
         closeWinPromises.push(BrowserTestUtils.closeWindow(currentWindow));
       }
     }
 
     Promise.all(closeWinPromises).then(() => {
       let currentTest = tests.shift();
       info("running " + currentTest.name);
--- a/browser/components/sessionstore/test/browser_636279.js
+++ b/browser/components/sessionstore/test/browser_636279.js
@@ -45,20 +45,17 @@ function test() {
     ss.setBrowserState(JSON.stringify(state));
   }, {once: true});
 
   ss.setBrowserState(JSON.stringify(statePinned));
 }
 
 function countTabs() {
   let needsRestore = 0, isRestoring = 0;
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-
-  while (windowsEnum.hasMoreElements()) {
-    let window = windowsEnum.getNext();
+  for (let window of Services.wm.getEnumerator("navigator:browser")) {
     if (window.closed)
       continue;
 
     for (let i = 0; i < window.gBrowser.tabs.length; i++) {
       let browserState = ss.getInternalObjectState(window.gBrowser.tabs[i].linkedBrowser);
       if (browserState == TAB_STATE_RESTORING)
         isRestoring++;
       else if (browserState == TAB_STATE_NEEDS_RESTORE)
--- a/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
+++ b/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
@@ -131,40 +131,35 @@ add_task(async function run_test() {
   Services.cookies.removeAll();
 
   // Open a new window.
   let win = await promiseNewWindowLoaded();
 
   // Restore window with session cookies that have no originAttributes.
   await setWindowState(win, SESSION_DATA, true);
 
-  let enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
-  let cookie;
   let cookieCount = 0;
-  while (enumerator.hasMoreElements()) {
-    cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (var cookie of Services.cookies.getCookiesFromHost(TEST_HOST, {})) {
     cookieCount++;
   }
 
   // Check that the cookie is restored successfully.
   is(cookieCount, 1, "expected one cookie");
   is(cookie.name, COOKIE.name, "cookie name successfully restored");
   is(cookie.value, COOKIE.value, "cookie value successfully restored");
   is(cookie.path, COOKIE.path, "cookie path successfully restored");
 
   // Clear cookies.
   Services.cookies.removeAll();
 
   // Restore window with session cookies that have originAttributes within.
   await setWindowState(win, SESSION_DATA_OA, true);
 
-  enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
   cookieCount = 0;
-  while (enumerator.hasMoreElements()) {
-    cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (cookie of Services.cookies.getCookiesFromHost(TEST_HOST, {})) {
     cookieCount++;
   }
 
   // Check that the cookie is restored successfully.
   is(cookieCount, 1, "expected one cookie");
   is(cookie.name, COOKIE.name, "cookie name successfully restored");
   is(cookie.value, COOKIE.value, "cookie value successfully restored");
   is(cookie.path, COOKIE.path, "cookie path successfully restored");
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -313,19 +313,17 @@ function promiseWindowLoaded(aWindow) {
 }
 
 var gUniqueCounter = 0;
 function r() {
   return Date.now() + "-" + (++gUniqueCounter);
 }
 
 function* BrowserWindowIterator() {
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-  while (windowsEnum.hasMoreElements()) {
-    let currentWindow = windowsEnum.getNext();
+  for (let currentWindow of Services.wm.getEnumerator("navigator:browser")) {
     if (!currentWindow.closed) {
       yield currentWindow;
     }
   }
 }
 
 var gWebProgressListener = {
   _callback: null,
--- a/browser/components/translation/test/browser_translation_exceptions.js
+++ b/browser/components/translation/test/browser_translation_exceptions.js
@@ -38,20 +38,17 @@ function test() {
 
 function getLanguageExceptions() {
   let langs = Services.prefs.getCharPref(kLanguagesPref);
   return langs ? langs.split(",") : [];
 }
 
 function getDomainExceptions() {
   let results = [];
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
-
+  for (let perm of Services.perms.enumerator) {
     if (perm.type == "translate" &&
         perm.capability == Services.perms.DENY_ACTION)
       results.push(perm.principal);
   }
 
   return results;
 }
 
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -647,19 +647,17 @@ var UITour = {
   },
 
   observe(aSubject, aTopic, aData) {
     log.debug("observe: aTopic =", aTopic);
     switch (aTopic) {
       // The browser message manager is disconnected when the <browser> is
       // destroyed and we want to teardown at that point.
       case "message-manager-close": {
-        let winEnum = Services.wm.getEnumerator("navigator:browser");
-        while (winEnum.hasMoreElements()) {
-          let window = winEnum.getNext();
+        for (let window of Services.wm.getEnumerator("navigator:browser")) {
           if (window.closed)
             continue;
 
           let tourBrowsers = this.tourBrowsersByWindow.get(window);
           if (!tourBrowsers)
             continue;
 
           for (let browser of tourBrowsers) {
@@ -1678,19 +1676,17 @@ var UITour = {
           }
         }
         reject("selectSearchEngine could not find engine with given ID");
       });
     });
   },
 
   notify(eventName, params) {
-    let winEnum = Services.wm.getEnumerator("navigator:browser");
-    while (winEnum.hasMoreElements()) {
-      let window = winEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       if (window.closed)
         continue;
 
       let openTourBrowsers = this.tourBrowsersByWindow.get(window);
       if (!openTourBrowsers)
         continue;
 
       for (let browser of openTourBrowsers) {
--- a/browser/components/uitour/test/browser_UITour_modalDialog.js
+++ b/browser/components/uitour/test/browser_UITour_modalDialog.js
@@ -42,34 +42,26 @@ var observer = SpecialPowers.wrapCallbac
     }
 });
 
 function getDialogDoc() {
   // Find the <browser> which contains notifyWindow, by looking
   // through all the open windows and all the <browsers> in each.
 
   // var enumerator = wm.getEnumerator("navigator:browser");
-  var enumerator = Services.wm.getXULWindowEnumerator(null);
-
-  while (enumerator.hasMoreElements()) {
-    var win = enumerator.getNext();
-    var windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
-
-    var containedDocShells = windowDocShell.getDocShellEnumerator(
-                                      Ci.nsIDocShellTreeItem.typeChrome,
-                                      Ci.nsIDocShell.ENUMERATE_FORWARDS);
-    while (containedDocShells.hasMoreElements()) {
+  for (let {docShell} of Services.wm.getEnumerator(null)) {
+    var containedDocShells = docShell.getDocShellEnumerator(
+                                      docShell.typeChrome,
+                                      docShell.ENUMERATE_FORWARDS);
+    for (let childDocShell of containedDocShells) {
         // Get the corresponding document for this docshell
-        var childDocShell = containedDocShells.getNext();
         // We don't want it if it's not done loading.
         if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
           continue;
-        var childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
-                                    .contentViewer
-                                    .DOMDocument;
+        var childDoc = childDocShell.contentViewer.DOMDocument;
 
         // ok(true, "Got window: " + childDoc.location.href);
         if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
           return childDoc;
     }
   }
 
   return null;
--- a/browser/extensions/formautofill/api.js
+++ b/browser/extensions/formautofill/api.js
@@ -123,21 +123,18 @@ this.formautofill = class extends Extens
   onShutdown() {
     resProto.setSubstitution(RESOURCE_HOST, null);
 
     this.chromeHandle.destruct();
     this.chromeHandle = null;
 
     Services.mm.removeMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
 
-    let enumerator = Services.wm.getEnumerator("navigator:browser");
-    while (enumerator.hasMoreElements()) {
-      let win = enumerator.getNext();
-      let domWindow = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
-      let cachedStyleSheets = CACHED_STYLESHEETS.get(domWindow);
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      let cachedStyleSheets = CACHED_STYLESHEETS.get(win);
 
       if (!cachedStyleSheets) {
         continue;
       }
 
       while (cachedStyleSheets.length !== 0) {
         cachedStyleSheets.pop().remove();
       }
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -31,16 +31,17 @@ do_get_profile();
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
 // ================================================
 
 const EXTENSION_ID = "formautofill@mozilla.org";
 
 AddonTestUtils.init(this);
+AddonTestUtils.overrideCertDB();
 
 async function loadExtension() {
   AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   await AddonTestUtils.promiseStartupManager();
 
   let extensionPath = Services.dirsvc.get("GreD", Ci.nsIFile);
   extensionPath.append("browser");
   extensionPath.append("features");
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -94,19 +94,17 @@ function getDOMWindow(aChannel, aPrincip
   return win;
 }
 
 function getLocalizedStrings(path) {
   var stringBundle =
     Services.strings.createBundle("chrome://pdf.js/locale/" + path);
 
   var map = {};
-  var enumerator = stringBundle.getSimpleEnumeration();
-  while (enumerator.hasMoreElements()) {
-    var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+  for (let string of stringBundle.getSimpleEnumeration()) {
     var key = string.key, property = "textContent";
     var i = key.lastIndexOf(".");
     if (i >= 0) {
       property = key.substring(i + 1);
       key = key.substring(0, i);
     }
     if (!(key in map)) {
       map[key] = {};
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -533,14 +533,11 @@ function shutdown(data, reason) {
 }
 
 function install() {
 }
 
 function uninstall() {
 }
 
-function* browserWindows() {
-  let windows = Services.wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements()) {
-    yield windows.getNext();
-  }
+function browserWindows() {
+  return Services.wm.getEnumerator("navigator:browser");
 }
--- a/browser/extensions/pocket/content/main.js
+++ b/browser/extensions/pocket/content/main.js
@@ -525,19 +525,17 @@ var pktUI = (function() {
                 }
             });
         });
 
         var _initL10NMessageId = "initL10N";
         pktUIMessaging.addMessageListener(iframe, _initL10NMessageId, function(panelId, data) {
             var strings = {};
             var bundle = Services.strings.createBundle("chrome://pocket/locale/pocket.properties");
-            var e = bundle.getSimpleEnumeration();
-            while (e.hasMoreElements()) {
-                var str = e.getNext().QueryInterface(Ci.nsIPropertyElement);
+            for (let str of bundle.getSimpleEnumeration()) {
                 if (str.key in data) {
                     strings[str.key] = bundle.formatStringFromName(str.key, data[str.key], data[str.key].length);
                 } else {
                     strings[str.key] = str.value;
                 }
             }
             pktUIMessaging.sendResponseMessageToPanel(panelId, _initL10NMessageId, { strings });
         });
@@ -563,19 +561,17 @@ var pktUI = (function() {
         if (!PrivateBrowsingUtils.isWindowPrivate(recentWindow) ||
             PrivateBrowsingUtils.permanentPrivateBrowsing) {
           recentWindow.openWebLinkIn(url, "tab", {
             triggeringPrincipal: aTriggeringPrincipal
           });
           return;
         }
 
-        let windows = Services.wm.getEnumerator("navigator:browser");
-        while (windows.hasMoreElements()) {
-          let win = windows.getNext();
+        for (let win of Services.wm.getEnumerator("navigator:browser")) {
           if (!PrivateBrowsingUtils.isWindowPrivate(win)) {
             win.openWebLinkIn(url, "tab", {
               triggeringPrincipal: aTriggeringPrincipal
             });
             return;
           }
         }
 
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -150,20 +150,18 @@ var pktApi = (function() {
      * Auth
      */
 
     /*
      *  All cookies from the Pocket domain
      *  The return format: { cookieName:cookieValue, cookieName:cookieValue, ... }
     */
     function getCookiesFromPocket() {
-        var pocketCookies = Services.cookies.getCookiesFromHost(pocketSiteHost, {});
         var cookies = {};
-        while (pocketCookies.hasMoreElements()) {
-            var cookie = pocketCookies.getNext().QueryInterface(Ci.nsICookie2);
+        for (let cookie of Services.cookies.getCookiesFromHost(pocketSiteHost, {})) {
             cookies[cookie.name] = cookie.value;
         }
         return cookies;
     }
 
     /**
      * Returns access token or undefined if no logged in user was found
      * @return {string | undefined} Access token for logged in user user
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -90,19 +90,17 @@ const URLBAR_SELECTED_RESULT_METHODS = {
 
 
 const MINIMUM_TAB_COUNT_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes, in ms
 
 function getOpenTabsAndWinsCounts() {
   let tabCount = 0;
   let winCount = 0;
 
-  let browserEnum = Services.wm.getEnumerator("navigator:browser");
-  while (browserEnum.hasMoreElements()) {
-    let win = browserEnum.getNext();
+  for (let win of Services.wm.getEnumerator("navigator:browser")) {
     winCount++;
     tabCount += win.gBrowser.tabs.length;
   }
 
   return { tabCount, winCount };
 }
 
 function getTabCount() {
@@ -654,19 +652,18 @@ let BrowserUsageTelemetry = {
    * windows.
    */
   _setupAfterRestore() {
     // Make sure to catch new chrome windows and subsession splits.
     Services.obs.addObserver(this, DOMWINDOW_OPENED_TOPIC, true);
     Services.obs.addObserver(this, TELEMETRY_SUBSESSIONSPLIT_TOPIC, true);
 
     // Attach the tabopen handlers to the existing Windows.
-    let browserEnum = Services.wm.getEnumerator("navigator:browser");
-    while (browserEnum.hasMoreElements()) {
-      this._registerWindow(browserEnum.getNext());
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      this._registerWindow(win);
     }
 
     // Get the initial tab and windows max counts.
     const counts = getOpenTabsAndWinsCounts();
     Services.telemetry.scalarSetMaximum(MAX_TAB_COUNT_SCALAR_NAME, counts.tabCount);
     Services.telemetry.scalarSetMaximum(MAX_WINDOW_COUNT_SCALAR_NAME, counts.winCount);
   },
 
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -164,29 +164,25 @@ var WindowHelper = {
       AppConstants.platform != "macosx" && AppConstants.platform != "win";
 
     if (broken_wm_z_order) {
       let win = Services.wm.getMostRecentWindow("navigator:browser");
 
       // if we're lucky, this isn't a popup, and we can just return this
       if (win && !isSuitableBrowserWindow(win)) {
         win = null;
-        let windowList = Services.wm.getEnumerator("navigator:browser");
         // this is oldest to newest, so this gets a bit ugly
-        while (windowList.hasMoreElements()) {
-          let nextWin = windowList.getNext();
+        for (let nextWin of Services.wm.getEnumerator("navigator:browser")) {
           if (isSuitableBrowserWindow(nextWin))
             win = nextWin;
         }
       }
       return win;
     }
-    let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
+    for (let win of Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true)) {
       if (isSuitableBrowserWindow(win))
         return win;
     }
     return null;
   }
 };
 
 this.BrowserWindowTracker = {
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -453,19 +453,17 @@ var TabCrashHandler = {
       this.prefs.setCharPref("email", "");
     }
 
     this.childMap.set(childID, null); // Avoid resubmission.
     this.removeSubmitCheckboxesForSameCrash(childID);
   },
 
   removeSubmitCheckboxesForSameCrash(childID) {
-    let enumerator = Services.wm.getEnumerator("navigator:browser");
-    while (enumerator.hasMoreElements()) {
-      let window = enumerator.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       if (!window.gMultiProcessBrowser)
         continue;
 
       for (let browser of window.gBrowser.browsers) {
         if (browser.isRemoteBrowser)
           continue;
 
         let doc = browser.contentDocument;
@@ -1064,19 +1062,17 @@ var PluginCrashReporter = {
     }, () => {
       this.broadcastState(runID, "failed");
     });
 
     this.crashReports.delete(runID);
   },
 
   broadcastState(runID, state) {
-    let enumerator = Services.wm.getEnumerator("navigator:browser");
-    while (enumerator.hasMoreElements()) {
-      let window = enumerator.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       let mm = window.messageManager;
       mm.broadcastAsyncMessage("BrowserPlugins:CrashReportSubmitted",
                                { runID, state });
     }
   },
 
   hasCrashReport(runID) {
     return this.crashReports.has(runID);
--- a/browser/modules/PageActions.jsm
+++ b/browser/modules/PageActions.jsm
@@ -1199,20 +1199,17 @@ function browserPageActions(obj) {
  *        need two separate cases, one where a window is given and another where
  *        it isn't.
  */
 function* allBrowserWindows(browserWindow = null) {
   if (browserWindow) {
     yield browserWindow;
     return;
   }
-  let windows = Services.wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements()) {
-    yield windows.getNext();
-  }
+  yield* Services.wm.getEnumerator("navigator:browser");
 }
 
 /**
  * A generator function for BrowserPageActions objects in all open windows.
  *
  * @param browserWindow (DOM window, optional)
  *        If given, then the BrowserPageActions for only this window will be
  *        yielded.
--- a/browser/modules/ProcessHangMonitor.jsm
+++ b/browser/modules/ProcessHangMonitor.jsm
@@ -386,19 +386,17 @@ var ProcessHangMonitor = {
     // If it turns out we have no windows (this can happen on macOS),
     // we have no opportunity to ask the user whether or not they want
     // to stop the hang or wait, so we'll opt for stopping the hang.
     if (!e.hasMoreElements()) {
       this.stopAllHangs();
       return;
     }
 
-    while (e.hasMoreElements()) {
-      let win = e.getNext();
-
+    for (let win of e) {
       this.updateWindow(win);
 
       // Only listen for these events if there are active hang reports.
       if (this._activeReports.size) {
         this.trackWindow(win);
       } else {
         this.untrackWindow(win);
       }
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -346,19 +346,17 @@ var Sanitizer = {
 
     formdata: {
       async clear(range) {
         let seenException;
         let refObj = {};
         TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj);
         try {
           // Clear undo history of all search bars.
-          let windows = Services.wm.getEnumerator("navigator:browser");
-          while (windows.hasMoreElements()) {
-            let currentWindow = windows.getNext();
+          for (let currentWindow of Services.wm.getEnumerator("navigator:browser")) {
             let currentDocument = currentWindow.document;
 
             // searchBar.textbox may not exist due to the search bar binding
             // not having been constructed yet if the search bar is in the
             // overflow or menu panel. It won't have a value or edit history in
             // that case.
             let searchBar = currentDocument.getElementById("searchbar");
             if (searchBar && searchBar.textbox)
@@ -461,20 +459,18 @@ var Sanitizer = {
         // browser console, etc.
 
         // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
         // dialogs
         let existingWindow = Services.appShell.hiddenDOMWindow;
         let startDate = existingWindow.performance.now();
 
         // First check if all these windows are OK with being closed:
-        let windowEnumerator = Services.wm.getEnumerator("navigator:browser");
         let windowList = [];
-        while (windowEnumerator.hasMoreElements()) {
-          let someWin = windowEnumerator.getNext();
+        for (let someWin of Services.wm.getEnumerator("navigator:browser")) {
           windowList.push(someWin);
           // If someone says "no" to a beforeunload prompt, we abort here:
           if (!this._canCloseWindow(someWin)) {
             this._resetAllWindowClosures(windowList);
             throw new Error("Sanitize could not close windows: cancelled by user");
           }
 
           // ...however, beforeunload prompts spin the event loop, and so the code here won't get
@@ -685,19 +681,17 @@ async function sanitizeOnShutdown(progre
   // use QuotaManager storage but don't have a specific permission set to
   // ACCEPT_NORMALLY need to be wiped.  Second, the set of origins that have
   // the permission explicitly set to ACCEPT_SESSION need to be wiped.  There
   // are also other ways to think about and accomplish this, but this is what
   // the logic below currently does!
   await sanitizeSessionPrincipals();
 
   // Let's see if we have to forget some particular site.
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  for (let permission of Services.perms.enumerator) {
     if (permission.type == "cookie" && permission.capability == Ci.nsICookiePermission.ACCESS_SESSION) {
       await sanitizeSessionPrincipal(permission.principal);
     }
   }
 
   if (Sanitizer.shouldSanitizeNewTabContainer) {
     sanitizeNewTabSegregation();
     removePendingSanitization("newtab-container");
--- a/browser/modules/SavantShieldStudy.jsm
+++ b/browser/modules/SavantShieldStudy.jsm
@@ -454,19 +454,17 @@ class WindowWatcher {
     }
 
     this._isActive = true;
     this._loadCallback = loadCallback;
     this._unloadCallback = unloadCallback;
     this._errorCallback = errorCallback;
 
     // Add loadCallback to existing windows
-    const windows = Services.wm.getEnumerator("navigator:browser");
-    while (windows.hasMoreElements()) {
-      const win = windows.getNext();
+    for (const win of Services.wm.getEnumerator("navigator:browser")) {
       try {
         this._loadCallback(win);
       } catch (ex) {
         this._errorCallback(`WindowWatcher code loading callback failed: ${ ex }`);
       }
     }
 
     // Add loadCallback to future windows
@@ -475,19 +473,17 @@ class WindowWatcher {
   }
 
   uninit() {
     if (!this._isActive) {
       this._errorCallback("Called uninit, but WindowWatcher was already uninited");
       return;
     }
 
-    const windows = Services.wm.getEnumerator("navigator:browser");
-    while (windows.hasMoreElements()) {
-      const win = windows.getNext();
+    for (const win of Services.wm.getEnumerator("navigator:browser")) {
       try {
         this._unloadCallback(win);
       } catch (ex) {
         this._errorCallback(`WindowWatcher code unloading callback failed: ${ ex }`);
       }
     }
 
     Services.ww.unregisterNotification(this);
--- a/browser/modules/SiteDataManager.jsm
+++ b/browser/modules/SiteDataManager.jsm
@@ -168,19 +168,17 @@ var SiteDataManager = {
       //      After the bug 742822 and 1286798 landed, localStorage usage will be included.
       //      So currently only get indexedDB usage.
       this._quotaUsageRequest = this._qms.getUsage(onUsageResult);
     });
     return this._getQuotaUsagePromise;
   },
 
   _getAllCookies() {
-    let cookiesEnum = Services.cookies.enumerator;
-    while (cookiesEnum.hasMoreElements()) {
-      let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
+    for (let cookie of Services.cookies.enumerator) {
       let site = this._getOrInsertSite(cookie.rawHost);
       site.cookies.push(cookie);
       if (site.lastAccessed < cookie.lastAccessed) {
         site.lastAccessed = cookie.lastAccessed;
       }
     }
   },
 
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -724,19 +724,17 @@ var AeroPeek = {
     this.cacheLifespan = this.prefs.getIntPref(CACHE_EXPIRATION_TIME_PREF_NAME);
 
     this.maxpreviews = this.prefs.getIntPref(DISABLE_THRESHOLD_PREF_NAME);
 
     // If the user toggled us on/off while the browser was already up
     // (rather than this code running on startup because the pref was
     // already set to true), we must initialize previews for open windows:
     if (this.initialized) {
-      let browserWindows = Services.wm.getEnumerator("navigator:browser");
-      while (browserWindows.hasMoreElements()) {
-        let win = browserWindows.getNext();
+      for (let win of Services.wm.getEnumerator("navigator:browser")) {
         if (!win.closed) {
           this.onOpenWindow(win);
         }
       }
     }
   },
 
   disable() {
--- a/browser/modules/test/browser/head.js
+++ b/browser/modules/test/browser/head.js
@@ -164,17 +164,17 @@ function checkEvents(events, expectedEve
  *
  * @param browser (<xul:browser>)
  *        The browser that we'll create a nsIContentPermissionRequest
  *        for.
  * @returns A nsIContentPermissionRequest-ish object.
  */
 function makeMockPermissionRequest(browser) {
   let type = {
-    options: [],
+    options: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIArray),
     QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPermissionType]),
   };
   let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
   types.appendElement(type);
   let result = {
     types,
     principal: browser.contentPrincipal,
     requester: null,
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -1085,19 +1085,17 @@ function updateIndicators(data, target) 
     }
 
     indicators.showGlobalIndicator = data.showGlobalIndicator;
     indicators.showCameraIndicator = data.showCameraIndicator;
     indicators.showMicrophoneIndicator = data.showMicrophoneIndicator;
     indicators.showScreenSharingIndicator = data.showScreenSharingIndicator;
   }
 
-  let browserWindowEnum = Services.wm.getEnumerator("navigator:browser");
-  while (browserWindowEnum.hasMoreElements()) {
-    let chromeWin = browserWindowEnum.getNext();
+  for (let chromeWin of Services.wm.getEnumerator("navigator:browser")) {
     if (webrtcUI.showGlobalIndicator) {
       showOrCreateMenuForWindow(chromeWin);
     } else {
       let doc = chromeWin.document;
       let existingMenu = doc.getElementById("tabSharingMenu");
       if (existingMenu) {
         existingMenu.hidden = true;
       }
--- a/build/autoconf/frameptr.m4
+++ b/build/autoconf/frameptr.m4
@@ -25,20 +25,22 @@ AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [
     *-mingw32*)
       MOZ_ENABLE_FRAME_PTR="-Oy-"
       MOZ_DISABLE_FRAME_PTR="-Oy"
     ;;
     esac
   fi
 
   # If we are debugging, profiling, using sanitizers, or on win32 we want a
-  # frame pointer.
+  # frame pointer.  It is not required to enable frame pointers on AArch64
+  # Windows, but we enable it for compatibility with ETW.
   if test -z "$MOZ_OPTIMIZE" -o \
           -n "$MOZ_PROFILING" -o \
           -n "$MOZ_DEBUG" -o \
           -n "$MOZ_MSAN" -o \
           -n "$MOZ_ASAN" -o \
-          "$OS_ARCH:$CPU_ARCH" = "WINNT:x86"; then
+          "$OS_ARCH:$CPU_ARCH" = "WINNT:x86" -o \
+	  "$OS_ARCH:$CPU_ARCH" = "WINNT:aarch64"; then
     MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR"
   else
     MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR"
   fi
 ])
--- a/build/build-clang/clang-7-pre-linux64.json
+++ b/build/build-clang/clang-7-pre-linux64.json
@@ -1,23 +1,22 @@
 {
-    "llvm_revision": "338869",
+    "llvm_revision": "340494",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc1",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc1",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/rc1",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc1",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc1",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc1",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/rc2",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc2",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc2",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc2",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
     "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "patches": [
       "find_symbolizer_linux.patch",
-      "rename_gcov_flush.patch",
-      "r339636.patch"
+      "rename_gcov_flush.patch"
     ]
 }
--- a/build/build-clang/clang-7-pre-mingw.json
+++ b/build/build-clang/clang-7-pre-mingw.json
@@ -1,21 +1,18 @@
 {
-    "llvm_revision": "338869",
+    "llvm_revision": "340494",
     "stages": "3",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc1",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc1",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/rc1",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc1",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc1",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc1",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/rc2",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/rc2",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/rc2",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/rc2",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/rc2",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_700/rc2",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/builds/worker/workspace/build/src/gcc",
     "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
-    "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
-    "patches": [
-      "r339636.patch"
-    ]
+    "as": "/builds/worker/workspace/build/src/gcc/bin/gcc"
 }
--- a/build/unix/mozconfig.lto
+++ b/build/unix/mozconfig.lto
@@ -12,10 +12,13 @@ export CXX="$topsrcdir/clang/bin/clang++
 
 # Use a newer binutils, from the tooltool gcc package, if it's there
 if [ -e "$topsrcdir/gcc/bin/ld" ]; then
     export CC="$CC -B $topsrcdir/gcc/bin"
     export CXX="$CXX -B $topsrcdir/gcc/bin"
 fi
 
 ac_add_options --enable-lto
+# Until it's either made the default or we figure a way to remove the
+# copy locations that LTO induces in non-PIE executables.
+ac_add_options --enable-pie
 
 . "$topsrcdir/build/unix/mozconfig.stdcxx"
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -212,17 +212,23 @@ function evaluateExpression(expression) 
 function getMappedExpression(expression) {
   return async function ({
     dispatch,
     getState,
     client,
     sourceMaps
   }) {
     const mappings = (0, _selectors.getSelectedScopeMappings)(getState());
-    const bindings = (0, _selectors.getSelectedFrameBindings)(getState());
+    const bindings = (0, _selectors.getSelectedFrameBindings)(getState()); // We bail early if we do not need to map the expression. This is important
+    // because mapping an expression can be slow if the parser worker is
+    // busy doing other work.
+    //
+    // 1. there are no mappings - we do not need to map original expressions
+    // 2. does not contain `await` - we do not need to map top level awaits
+    // 3. does not contain `=` - we do not need to map assignments
 
-    if (!mappings && !bindings && !expression.includes("await")) {
+    if (!mappings && !expression.match(/(await|=)/)) {
       return expression;
     }
 
     return parser.mapExpression(expression, mappings, bindings || [], _prefs.features.mapExpressionBindings, _prefs.features.mapAwaitExpression);
   };
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -405,22 +405,30 @@ function getSelectedFrameBindings(state)
   return frameBindings;
 }
 
 function getFrameScope(state, sourceId, frameId) {
   return getOriginalFrameScope(state, sourceId, frameId) || getGeneratedFrameScope(state, frameId);
 }
 
 function getSelectedScope(state) {
-  const sourceRecord = (0, _sources.getSelectedSource)(state);
+  const source = (0, _sources.getSelectedSource)(state);
   const frameId = getSelectedFrameId(state);
-  const {
-    scope
-  } = getFrameScope(state, sourceRecord && sourceRecord.id, frameId) || {};
-  return scope || null;
+
+  if (!source) {
+    return null;
+  }
+
+  const frameScope = getFrameScope(state, source.id, frameId);
+
+  if (!frameScope) {
+    return null;
+  }
+
+  return frameScope.scope || null;
 }
 
 function getSelectedScopeMappings(state) {
   const frameId = getSelectedFrameId(state);
 
   if (!frameId) {
     return null;
   }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -44,17 +44,17 @@ add_task(async function() {
   await addBreakpoint(dbg, "times2", 2);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   await waitForSelectedSource(dbg, "times2");
 
   info(`Test previewing in the original location`);
   await assertPreviews(dbg, [
-    { line: 2, column: 10, result: 4, expression: "x;" }
+    { line: 2, column: 10, result: 4, expression: "x" }
   ]);
 
   info(`Test previewing in the generated location`);
   await dbg.actions.jumpToMappedSelectedLocation();
   await waitForSelectedSource(dbg, "bundle.js");
   await assertPreviews(dbg, [
     { line: 70, column: 11, result: 4, expression: "x" }
   ]);
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -738,15 +738,13 @@ gDevTools.on("toolbox-destroyed", gDevTo
 
 Services.obs.addObserver(gDevToolsBrowser, "quit-application");
 Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
 // Watch for module loader unload. Fires when the tools are reloaded.
 Services.obs.addObserver(gDevToolsBrowser, "devtools:loader:destroy");
 
 // Fake end of browser window load event for all already opened windows
 // that is already fully loaded.
-const enumerator = Services.wm.getEnumerator(gDevTools.chromeWindowType);
-while (enumerator.hasMoreElements()) {
-  const win = enumerator.getNext();
+for (const win of Services.wm.getEnumerator(gDevTools.chromeWindowType)) {
   if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {
     gDevToolsBrowser._registerBrowserWindow(win);
   }
 }
--- a/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
@@ -45,22 +45,17 @@ function toolRegistered(toolId) {
     ok(menuitem, "menu item of new tool added to every browser window");
   }
 
   // then unregister it
   testUnregister();
 }
 
 function getAllBrowserWindows() {
-  const wins = [];
-  const enumerator = Services.wm.getEnumerator("navigator:browser");
-  while (enumerator.hasMoreElements()) {
-    wins.push(enumerator.getNext());
-  }
-  return wins;
+  return Array.from(Services.wm.getEnumerator("navigator:browser"));
 }
 
 function testUnregister() {
   gDevTools.once("tool-unregistered", toolUnregistered);
 
   gDevTools.unregisterTool("testTool");
 }
 
--- a/devtools/client/framework/test/browser_toolbox_tools_per_toolbox_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_tools_per_toolbox_registration.js
@@ -92,22 +92,17 @@ function testToolRegistered() {
   // Test that the tool is built once selected and then test its unregistering.
   info("select per-toolbox tool in the opened toolbox.");
   gDevTools.showToolbox(target, TOOL_ID)
            .then(waitForToolInstanceBuild)
            .then(testUnregister);
 }
 
 function getAllBrowserWindows() {
-  const wins = [];
-  const enumerator = Services.wm.getEnumerator("navigator:browser");
-  while (enumerator.hasMoreElements()) {
-    wins.push(enumerator.getNext());
-  }
-  return wins;
+  return Array.from(Services.wm.getEnumerator("navigator:browser"));
 }
 
 function testUnregister() {
   info("remove per-toolbox tool in the opened toolbox.");
   toolbox.removeAdditionalTool(TOOL_ID);
 
   Promise.all([
     waitForToolInstanceDestroyed
--- a/devtools/client/scratchpad/scratchpad-manager.jsm
+++ b/devtools/client/scratchpad/scratchpad-manager.jsm
@@ -82,19 +82,17 @@ this.ScratchpadManager = {
     }
 
     // We need to clone objects we get from Scratchpad instances
     // because such (cross-window) objects have a property 'parent'
     // that holds on to a ChromeWindow instance. This means that
     // such objects are not primitive-values-only anymore so they
     // can leak.
 
-    const enumerator = Services.wm.getEnumerator("devtools:scratchpad");
-    while (enumerator.hasMoreElements()) {
-      const win = enumerator.getNext();
+    for (const win of Services.wm.getEnumerator("devtools:scratchpad")) {
       if (!win.closed && win.Scratchpad.initialized) {
         this._scratchpads.push(clone(win.Scratchpad.getState()));
       }
     }
   },
 
   /**
    * Open a new scratchpad window with an optional initial state.
--- a/devtools/client/shared/view-source.js
+++ b/devtools/client/shared/view-source.js
@@ -127,21 +127,17 @@ exports.viewSourceInDebugger = async fun
  *
  * @param {string} sourceURL
  * @param {number} sourceLine
  *
  * @return {Promise}
  */
 exports.viewSourceInScratchpad = async function(sourceURL, sourceLine) {
   // Check for matching top level scratchpad window.
-  const wins = Services.wm.getEnumerator("devtools:scratchpad");
-
-  while (wins.hasMoreElements()) {
-    const win = wins.getNext();
-
+  for (const win of Services.wm.getEnumerator("devtools:scratchpad")) {
     if (!win.closed && win.Scratchpad.uniqueName === sourceURL) {
       win.focus();
       win.Scratchpad.editor.setCursor({ line: sourceLine, ch: 0 });
       return;
     }
   }
 
   // For scratchpads within toolbox
--- a/devtools/server/actors/accessibility.js
+++ b/devtools/server/actors/accessibility.js
@@ -20,17 +20,16 @@ const {
 const { isXUL } = require("devtools/server/actors/highlighters/utils/markup");
 const { isWindowIncluded } = require("devtools/shared/layout/utils");
 const { CustomHighlighterActor, register } =
   require("devtools/server/actors/highlighters");
 const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
 
 const nsIAccessibleEvent = Ci.nsIAccessibleEvent;
 const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent;
-const nsIPropertyElement = Ci.nsIPropertyElement;
 const nsIAccessibleRole = Ci.nsIAccessibleRole;
 
 const {
   EVENT_TEXT_CHANGED,
   EVENT_TEXT_INSERTED,
   EVENT_TEXT_REMOVED,
   EVENT_ACCELERATOR_CHANGE,
   EVENT_ACTION_CHANGE,
@@ -317,20 +316,17 @@ const AccessibleActor = ActorClassWithSp
   },
 
   get attributes() {
     if (this.isDefunct || !this.rawAccessible.attributes) {
       return {};
     }
 
     const attributes = {};
-    const attrsEnum = this.rawAccessible.attributes.enumerate();
-    while (attrsEnum.hasMoreElements()) {
-      const { key, value } = attrsEnum.getNext().QueryInterface(
-        nsIPropertyElement);
+    for (const { key, value } of this.rawAccessible.attributes.enumerate()) {
       attributes[key] = value;
     }
 
     return attributes;
   },
 
   get bounds() {
     if (this.isDefunct) {
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -181,19 +181,17 @@ var WalkerActor = protocol.ActorClassWit
   },
 
   /**
    * Callback for eventListenerService.addListenerChangeListener
    * @param nsISimpleEnumerator changesEnum
    *    enumerator of nsIEventListenerChange
    */
   _onEventListenerChange: function(changesEnum) {
-    const changes = changesEnum.enumerate();
-    while (changes.hasMoreElements()) {
-      const current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange);
+    for (const current of changesEnum.enumerate(Ci.nsIEventListenerChange)) {
       const target = current.target;
 
       if (this._refMap.has(target)) {
         const actor = this.getNode(target);
         const mutation = {
           type: "events",
           target: actor.actorID,
           hasEventListeners: actor._hasEventListeners
--- a/devtools/server/actors/replay/graphics.js
+++ b/devtools/server/actors/replay/graphics.js
@@ -46,19 +46,17 @@ function updateWindow(window, buffer, wi
 }
 
 // Entry point for when we have some new graphics data from the child process
 // to draw.
 // eslint-disable-next-line no-unused-vars
 function Update(buffer, width, height) {
   try {
     // Paint to all windows we can find. Hopefully there is only one.
-    const windowEnumerator = Services.ww.getWindowEnumerator();
-    while (windowEnumerator.hasMoreElements()) {
-      const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
+    for (const window of Services.ww.getWindowEnumerator()) {
       updateWindow(window, buffer, width, height);
     }
   } catch (e) {
     dump("Middleman Graphics Update Exception: " + e + "\n");
   }
 }
 
 // eslint-disable-next-line no-unused-vars
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -805,26 +805,18 @@ var cookieHelpers = {
   getCookiesFromHost(host, originAttributes) {
     // Local files have no host.
     if (host.startsWith("file:///")) {
       host = "";
     }
 
     host = trimHttpHttpsPort(host);
 
-    const cookies = Services.cookies.getCookiesFromHost(host, originAttributes);
-    const store = [];
-
-    while (cookies.hasMoreElements()) {
-      const cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
-
-      store.push(cookie);
-    }
-
-    return store;
+    return Array.from(
+      Services.cookies.getCookiesFromHost(host, originAttributes));
   },
 
   /**
    * Apply the results of a cookie edit.
    *
    * @param {Object} data
    *        An object in the following format:
    *        {
@@ -849,21 +841,19 @@ var cookieHelpers = {
    */
   editCookie(data) {
     let {field, oldValue, newValue} = data;
     const origName = field === "name" ? oldValue : data.items.name;
     const origHost = field === "host" ? oldValue : data.items.host;
     const origPath = field === "path" ? oldValue : data.items.path;
     let cookie = null;
 
-    const enumerator =
-      Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
-
-    while (enumerator.hasMoreElements()) {
-      const nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+    const cookies = Services.cookies.getCookiesFromHost(origHost,
+                                                        data.originAttributes || {});
+    for (const nsiCookie of cookies) {
       if (nsiCookie.name === origName &&
           nsiCookie.host === origHost &&
           nsiCookie.path === origPath) {
         cookie = {
           host: nsiCookie.host,
           path: nsiCookie.path,
           name: nsiCookie.name,
           value: nsiCookie.value,
@@ -951,21 +941,19 @@ var cookieHelpers = {
         return matchHost == null;
       }
       if (cookieHost.startsWith(".")) {
         return ("." + matchHost).endsWith(cookieHost);
       }
       return cookieHost == host;
     }
 
-    const enumerator =
-      Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
-
-    while (enumerator.hasMoreElements()) {
-      const cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+    const cookies = Services.cookies.getCookiesFromHost(host,
+                                                        opts.originAttributes || {});
+    for (const cookie of cookies) {
       if (hostMatches(cookie.host, host) &&
           (!opts.name || cookie.name === opts.name) &&
           (!opts.domain || cookie.host === opts.domain) &&
           (!opts.path || cookie.path === opts.path) &&
           (!opts.session || (!cookie.expires && !cookie.maxAge))) {
         Services.cookies.remove(
           cookie.host,
           cookie.name,
--- a/devtools/server/actors/targets/addon.js
+++ b/devtools/server/actors/targets/addon.js
@@ -180,30 +180,24 @@ AddonTargetActor.prototype = {
     return this._addon.reload()
       .then(() => {
         // send an empty response
         return {};
       });
   },
 
   preNest: function() {
-    const e = Services.wm.getEnumerator(null);
-    while (e.hasMoreElements()) {
-      const win = e.getNext();
-      const windowUtils = win.windowUtils;
+    for (const {windowUtils} of Services.wm.getEnumerator(null)) {
       windowUtils.suppressEventHandling(true);
       windowUtils.suspendTimeouts();
     }
   },
 
   postNest: function() {
-    const e = Services.wm.getEnumerator(null);
-    while (e.hasMoreElements()) {
-      const win = e.getNext();
-      const windowUtils = win.windowUtils;
+    for (const {windowUtils} of Services.wm.getEnumerator(null)) {
       windowUtils.resumeTimeouts();
       windowUtils.suppressEventHandling(false);
     }
   },
 
   /**
    * Return true if the given global is associated with this addon and should be
    * added as a debuggee, false otherwise.
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -72,19 +72,17 @@ function getDocShellChromeEventHandler(d
 
 function getChildDocShells(parentDocShell) {
   const docShellsEnum = parentDocShell.getDocShellEnumerator(
     Ci.nsIDocShellTreeItem.typeAll,
     Ci.nsIDocShell.ENUMERATE_FORWARDS
   );
 
   const docShells = [];
-  while (docShellsEnum.hasMoreElements()) {
-    const docShell = docShellsEnum.getNext();
-    docShell.QueryInterface(Ci.nsIDocShell);
+  for (const docShell of docShellsEnum) {
     docShell.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIWebProgress);
     docShells.push(docShell);
   }
   return docShells;
 }
 
 exports.getChildDocShells = getChildDocShells;
--- a/devtools/server/actors/targets/content-process.js
+++ b/devtools/server/actors/targets/content-process.js
@@ -33,24 +33,18 @@ function ContentProcessTargetActor(conne
   // Use a see-everything debugger
   this.makeDebugger = makeDebugger.bind(null, {
     findDebuggees: dbg => dbg.findAllGlobals(),
     shouldAddNewGlobalAsDebuggee: global => true
   });
 
   const sandboxPrototype = {
     get tabs() {
-      const tabs = [];
-      const windowEnumerator = Services.ww.getWindowEnumerator();
-      while (windowEnumerator.hasMoreElements()) {
-        const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
-        const tabChildGlobal = window.docShell.messageManager;
-        tabs.push(tabChildGlobal);
-      }
-      return tabs;
+      return Array.from(Services.ww.getWindowEnumerator(),
+                        win => win.docShell.messageManager);
     },
   };
 
   // Scope into which the webconsole executes:
   // A sandbox with chrome privileges with a `tabs` getter.
   const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
     .createInstance(Ci.nsIPrincipal);
   const sandbox = Cu.Sandbox(systemPrincipal, {
--- a/devtools/server/actors/targets/parent-process.js
+++ b/devtools/server/actors/targets/parent-process.js
@@ -88,20 +88,17 @@ parentProcessTargetPrototype.isRootActor
 /**
  * Getter for the list of all docshells in this targetActor
  * @return {Array}
  */
 Object.defineProperty(parentProcessTargetPrototype, "docShells", {
   get: function() {
     // Iterate over all top-level windows and all their docshells.
     let docShells = [];
-    const e = Services.ww.getWindowEnumerator();
-    while (e.hasMoreElements()) {
-      const window = e.getNext();
-      const docShell = window.docShell;
+    for (const {docShell} of Services.ww.getWindowEnumerator()) {
       docShells = docShells.concat(getChildDocShells(docShell));
     }
 
     return docShells;
   }
 });
 
 parentProcessTargetPrototype.observe = function(subject, topic, data) {
@@ -126,20 +123,17 @@ parentProcessTargetPrototype._attach = f
 
   BrowsingContextTargetActor.prototype._attach.call(this);
 
   // Listen for any new/destroyed chrome docshell
   Services.obs.addObserver(this, "chrome-webnavigation-create");
   Services.obs.addObserver(this, "chrome-webnavigation-destroy");
 
   // Iterate over all top-level windows.
-  const e = Services.ww.getWindowEnumerator();
-  while (e.hasMoreElements()) {
-    const window = e.getNext();
-    const docShell = window.docShell;
+  for (const {docShell} of Services.ww.getWindowEnumerator()) {
     if (docShell == this.docShell) {
       continue;
     }
     this._progressListener.watch(docShell);
   }
   return undefined;
 };
 
@@ -147,20 +141,17 @@ parentProcessTargetPrototype._detach = f
   if (!this.attached) {
     return false;
   }
 
   Services.obs.removeObserver(this, "chrome-webnavigation-create");
   Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
 
   // Iterate over all top-level windows.
-  const e = Services.ww.getWindowEnumerator();
-  while (e.hasMoreElements()) {
-    const window = e.getNext();
-    const docShell = window.docShell;
+  for (const {docShell} of Services.ww.getWindowEnumerator()) {
     if (docShell == this.docShell) {
       continue;
     }
     this._progressListener.unwatch(docShell);
   }
 
   BrowsingContextTargetActor.prototype._detach.call(this);
   return undefined;
@@ -168,34 +159,28 @@ parentProcessTargetPrototype._detach = f
 
 /* ThreadActor hooks. */
 
 /**
  * Prepare to enter a nested event loop by disabling debuggee events.
  */
 parentProcessTargetPrototype.preNest = function() {
   // Disable events in all open windows.
-  const e = Services.wm.getEnumerator(null);
-  while (e.hasMoreElements()) {
-    const win = e.getNext();
-    const windowUtils = win.windowUtils;
+  for (const {windowUtils} of Services.wm.getEnumerator(null)) {
     windowUtils.suppressEventHandling(true);
     windowUtils.suspendTimeouts();
   }
 };
 
 /**
  * Prepare to exit a nested event loop by enabling debuggee events.
  */
 parentProcessTargetPrototype.postNest = function(nestData) {
   // Enable events in all open windows.
-  const e = Services.wm.getEnumerator(null);
-  while (e.hasMoreElements()) {
-    const win = e.getNext();
-    const windowUtils = win.windowUtils;
+  for (const {windowUtils} of Services.wm.getEnumerator(null)) {
     windowUtils.resumeTimeouts();
     windowUtils.suppressEventHandling(false);
   }
 };
 
 exports.parentProcessTargetPrototype = parentProcessTargetPrototype;
 exports.ParentProcessTargetActor =
   ActorClassWithSpec(parentProcessTargetSpec, parentProcessTargetPrototype);
--- a/devtools/server/actors/targets/webextension.js
+++ b/devtools/server/actors/targets/webextension.js
@@ -183,20 +183,17 @@ webExtensionTargetPrototype._destroyFall
   }
 };
 
 // Discovery an extension page to use as a default target window.
 // NOTE: This currently fail to discovery an extension page running in a
 // windowless browser when running in non-oop mode, and the background page
 // is set later using _onNewExtensionWindow.
 webExtensionTargetPrototype._searchForExtensionWindow = function() {
-  const e = Services.ww.getWindowEnumerator(null);
-  while (e.hasMoreElements()) {
-    const window = e.getNext();
-
+  for (const window of Services.ww.getWindowEnumerator(null)) {
     if (window.document.nodePrincipal.addonId == this.id) {
       return window;
     }
   }
 
   return undefined;
 };
 
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -20,42 +20,28 @@ loader.lazyRequireGetter(this, "ServiceW
 loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
 loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 /**
  * Browser-specific actors.
  */
 
 /**
- * Yield all windows of type |windowType|, from the oldest window to the
- * youngest, using nsIWindowMediator::getEnumerator. We're usually
- * interested in "navigator:browser" windows.
- */
-function* allAppShellDOMWindows(windowType) {
-  const e = Services.wm.getEnumerator(windowType);
-  while (e.hasMoreElements()) {
-    yield e.getNext();
-  }
-}
-
-exports.allAppShellDOMWindows = allAppShellDOMWindows;
-
-/**
  * Retrieve the window type of the top-level window |window|.
  */
 function appShellDOMWindowType(window) {
   /* This is what nsIWindowMediator's enumerator checks. */
   return window.document.documentElement.getAttribute("windowtype");
 }
 
 /**
  * Send Debugger:Shutdown events to all "navigator:browser" windows.
  */
 function sendShutdownEvent() {
-  for (const win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
+  for (const win of Services.wm.getEnumerator(DebuggerServer.chromeWindowType)) {
     const evt = win.document.createEvent("Event");
     evt.initEvent("Debugger:Shutdown", true, false);
     win.document.documentElement.dispatchEvent(evt);
   }
 }
 
 exports.sendShutdownEvent = sendShutdownEvent;
 
@@ -223,17 +209,17 @@ BrowserTabList.prototype._getSelectedBro
 };
 
 /**
  * Produces an iterable (in this case a generator) to enumerate all available
  * browser tabs.
  */
 BrowserTabList.prototype._getBrowsers = function* () {
   // Iterate over all navigator:browser XUL windows.
-  for (const win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
+  for (const win of Services.wm.getEnumerator(DebuggerServer.chromeWindowType)) {
     // For each tab in this XUL window, ensure that we have an actor for
     // it, reusing existing actors where possible.
     for (const browser of this._getChildren(win)) {
       yield browser;
     }
   }
 };
 
@@ -483,17 +469,17 @@ BrowserTabList.prototype._checkListening
  *    already listening for those events.
  * @param eventNames array of strings
  *    An array of event names.
  */
 BrowserTabList.prototype._listenForEventsIf =
   function(shouldListen, guard, eventNames) {
     if (!shouldListen !== !this[guard]) {
       const op = shouldListen ? "addEventListener" : "removeEventListener";
-      for (const win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
+      for (const win of Services.wm.getEnumerator(DebuggerServer.chromeWindowType)) {
         for (const name of eventNames) {
           win[op](name, this, false);
         }
       }
       this[guard] = shouldListen;
     }
   };
 
@@ -507,17 +493,17 @@ BrowserTabList.prototype._listenForEvent
  *    already listening for those messages.
  * @param aMessageNames array of strings
  *    An array of message names.
  */
 BrowserTabList.prototype._listenForMessagesIf =
   function(shouldListen, guard, messageNames) {
     if (!shouldListen !== !this[guard]) {
       const op = shouldListen ? "addMessageListener" : "removeMessageListener";
-      for (const win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
+      for (const win of Services.wm.getEnumerator(DebuggerServer.chromeWindowType)) {
         for (const name of messageNames) {
           win.messageManager[op](name, this);
         }
       }
       this[guard] = shouldListen;
     }
   };
 
--- a/devtools/server/actors/worker/worker-list.js
+++ b/devtools/server/actors/worker/worker-list.js
@@ -48,19 +48,17 @@ function WorkerTargetActorList(conn, opt
   this.onRegister = this.onRegister.bind(this);
   this.onUnregister = this.onUnregister.bind(this);
 }
 
 WorkerTargetActorList.prototype = {
   getList() {
     // Create a set of debuggers.
     const dbgs = new Set();
-    const e = wdm.getWorkerDebuggerEnumerator();
-    while (e.hasMoreElements()) {
-      const dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger);
+    for (const dbg of wdm.getWorkerDebuggerEnumerator()) {
       if (matchWorkerDebugger(dbg, this._options)) {
         dbgs.add(dbg);
       }
     }
 
     // Delete each actor for which we don't have a debugger.
     for (const [dbg, ] of this._actors) {
       if (!dbgs.has(dbg)) {
--- a/devtools/server/performance/timeline.js
+++ b/devtools/server/performance/timeline.js
@@ -70,39 +70,33 @@ Timeline.prototype = {
    * the targetActor was switched to a child frame. This is because for now,
    * paint markers are only recorded at parent frame level so switching the
    * timeline to a child frame would hide all paint markers.
    * See https://bugzilla.mozilla.org/show_bug.cgi?id=1050773#c14
    * @return {Array}
    */
   get docShells() {
     let originalDocShell;
-    const docShells = [];
 
     if (this.targetActor.isRootActor) {
       originalDocShell = this.targetActor.docShell;
     } else {
       originalDocShell = this.targetActor.originalDocShell;
     }
 
     if (!originalDocShell) {
-      return docShells;
+      return [];
     }
 
     const docShellsEnum = originalDocShell.getDocShellEnumerator(
       Ci.nsIDocShellTreeItem.typeAll,
       Ci.nsIDocShell.ENUMERATE_FORWARDS
     );
 
-    while (docShellsEnum.hasMoreElements()) {
-      const docShell = docShellsEnum.getNext();
-      docShells.push(docShell.QueryInterface(Ci.nsIDocShell));
-    }
-
-    return docShells;
+    return Array.from(docShellsEnum);
   },
 
   /**
    * At regular intervals, pop the markers from the docshell, and forward
    * markers, memory, tick and frames events, if any.
    */
   _pullTimelineData: function() {
     const docShells = this.docShells;
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -170,19 +170,17 @@ async function getSystemInfo() {
 }
 
 function getProfileLocation() {
   // In child processes, we cannot access the profile location.
   try {
     const profd = Services.dirsvc.get("ProfD", Ci.nsIFile);
     const profservice = Cc["@mozilla.org/toolkit/profile-service;1"]
                         .getService(Ci.nsIToolkitProfileService);
-    const profiles = profservice.profiles;
-    while (profiles.hasMoreElements()) {
-      const profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
+    for (const profile of profservice.profiles) {
       if (profile.rootDir.path == profd.path) {
         return profile.name;
       }
     }
 
     return profd.leafName;
   } catch (e) {
     return "";
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -495,19 +495,17 @@ DevToolsStartup.prototype = {
     item.hidden = Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF);
   },
 
   /**
    * Loop on all windows and update the hidden attribute of the "enable DevTools" menu
    * item.
    */
   onEnabledPrefChanged() {
-    const enumerator = Services.wm.getEnumerator("navigator:browser");
-    while (enumerator.hasMoreElements()) {
-      const window = enumerator.getNext();
+    for (const window of Services.wm.getEnumerator("navigator:browser")) {
       if (window.gBrowserInit && window.gBrowserInit.delayedStartupFinished) {
         this.updateDevToolsMenuItems(window);
       }
     }
   },
 
   /**
    * Check if the user is a DevTools user by looking at our selfxss pref.
--- a/docshell/base/nsDocShellEnumerator.cpp
+++ b/docshell/base/nsDocShellEnumerator.cpp
@@ -16,18 +16,16 @@ nsDocShellEnumerator::nsDocShellEnumerat
   , mEnumerationDirection(aEnumerationDirection)
 {
 }
 
 nsDocShellEnumerator::~nsDocShellEnumerator()
 {
 }
 
-NS_IMPL_ISUPPORTS(nsDocShellEnumerator, nsISimpleEnumerator)
-
 NS_IMETHODIMP
 nsDocShellEnumerator::GetNext(nsISupports** aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = nullptr;
 
   nsresult rv = EnsureDocShellArray();
   if (NS_FAILED(rv)) {
--- a/docshell/base/nsDocShellEnumerator.h
+++ b/docshell/base/nsDocShellEnumerator.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDocShellEnumerator_h___
 #define nsDocShellEnumerator_h___
 
-#include "nsISimpleEnumerator.h"
+#include "nsSimpleEnumerator.h"
 #include "nsTArray.h"
 #include "nsIWeakReferenceUtils.h"
 
 class nsIDocShellTreeItem;
 
 /*
 // {13cbc281-35ae-11d5-be5b-bde0edece43c}
 #define NS_DOCSHELL_FORWARDS_ENUMERATOR_CID  \
@@ -24,36 +24,35 @@ class nsIDocShellTreeItem;
 // {13cbc282-35ae-11d5-be5b-bde0edece43c}
 #define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CID  \
 { 0x13cbc282, 0x35ae, 0x11d5, { 0xbe, 0x5b, 0xbd, 0xe0, 0xed, 0xec, 0xe4, 0x3c } }
 
 #define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CONTRACTID \
 "@mozilla.org/docshell/enumerator-backwards;1"
 */
 
-class nsDocShellEnumerator : public nsISimpleEnumerator
+class nsDocShellEnumerator : public nsSimpleEnumerator
 {
 protected:
   enum
   {
     enumerateForwards,
     enumerateBackwards
   };
 
   virtual ~nsDocShellEnumerator();
 
 public:
   explicit nsDocShellEnumerator(int32_t aEnumerationDirection);
 
-  // nsISupports
-  NS_DECL_ISUPPORTS
-
   // nsISimpleEnumerator
   NS_DECL_NSISIMPLEENUMERATOR
 
+  const nsID& DefaultInterface() override { return NS_GET_IID(nsIDocShell); }
+
 public:
   nsresult GetEnumerationRootItem(nsIDocShellTreeItem** aEnumerationRootItem);
   nsresult SetEnumerationRootItem(nsIDocShellTreeItem* aEnumerationRootItem);
 
   nsresult GetEnumDocShellType(int32_t* aEnumerationItemType);
   nsresult SetEnumDocShellType(int32_t aEnumerationItemType);
 
   nsresult First();
--- a/docshell/shistory/nsSHistory.h
+++ b/docshell/shistory/nsSHistory.h
@@ -6,19 +6,19 @@
 
 #ifndef nsSHistory_h
 #define nsSHistory_h
 
 #include "nsCOMPtr.h"
 #include "nsExpirationTracker.h"
 #include "nsISHistory.h"
 #include "nsISHistoryInternal.h"
-#include "nsISimpleEnumerator.h"
 #include "nsIWebNavigation.h"
 #include "nsSHEntryShared.h"
+#include "nsSimpleEnumerator.h"
 #include "nsTObserverArray.h"
 #include "nsWeakReference.h"
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
 
 class nsIDocShell;
 class nsDocShell;
--- a/docshell/test/browser/file_bug1328501_framescript.js
+++ b/docshell/test/browser/file_bug1328501_framescript.js
@@ -26,15 +26,10 @@ addEventListener("unload", e => {
 });
 
 function getChildDocShells() {
   let docShellsEnum = docShell.getDocShellEnumerator(
     Ci.nsIDocShellTreeItem.typeAll,
     Ci.nsIDocShell.ENUMERATE_FORWARDS
   );
 
-  let docShells = [];
-  while (docShellsEnum.hasMoreElements()) {
-    let ds = docShellsEnum.getNext();
-    docShells.push(ds);
-  }
-  return docShells;
+  return Array.from(docShellsEnum);
 }
--- a/docshell/test/navigation/NavigationUtils.js
+++ b/docshell/test/navigation/NavigationUtils.js
@@ -97,22 +97,20 @@ function isInaccessible(wnd, message) {
 // Functions that require UniversalXPConnect privilege
 ///////////////////////////////////////////////////////////////////////////
 
 function xpcEnumerateContentWindows(callback) {
 
   var Ci = SpecialPowers.Ci;
   var ww = SpecialPowers.Cc["@mozilla.org/embedcomp/window-watcher;1"]
                         .getService(Ci.nsIWindowWatcher);
-  var enumerator = ww.getWindowEnumerator();
 
   var contentWindows = [];
 
-  while (enumerator.hasMoreElements()) {
-    var win = enumerator.getNext();
+  for (let win of ww.getWindowEnumerator()) {
     if (win.isChromeWindow) {
       var docshellTreeNode = win.docShell;
       var childCount = docshellTreeNode.childCount;
       for (var i = 0; i < childCount; ++i) {
         var childTreeNode = docshellTreeNode.getChildAt(i);
 
         // we're only interested in content docshells
         if (SpecialPowers.unwrap(childTreeNode.itemType) != Ci.nsIDocShellTreeItem.typeContent)
--- a/dom/base/ContentFrameMessageManager.h
+++ b/dom/base/ContentFrameMessageManager.h
@@ -11,26 +11,32 @@
 #include "mozilla/dom/MessageManagerGlobal.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "nsContentUtils.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
+#define NS_CONTENTFRAMEMESSAGEMANAGER_IID \
+{ 0x97e192a6, 0xab7a, 0x4c8f, \
+  { 0xb7, 0xdd, 0xf7, 0xec, 0x36, 0x38, 0x71, 0xb5 } }
+
 /**
  * Base class for implementing the WebIDL ContentFrameMessageManager class.
  */
 class ContentFrameMessageManager : public DOMEventTargetHelper,
                                    public MessageManagerGlobal
 {
 public:
   using DOMEventTargetHelper::AddRef;
   using DOMEventTargetHelper::Release;
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTFRAMEMESSAGEMANAGER_IID)
+
   virtual already_AddRefed<nsPIDOMWindowOuter> GetContent(ErrorResult& aError) = 0;
   virtual already_AddRefed<nsIDocShell> GetDocShell(ErrorResult& aError) = 0;
   virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() = 0;
   virtual uint64_t ChromeOuterWindowID() = 0;
 
   nsFrameMessageManager* GetMessageManager()
   {
     return mMessageManager;
@@ -45,12 +51,14 @@ public:
 
 protected:
   explicit ContentFrameMessageManager(nsFrameMessageManager* aMessageManager)
     : DOMEventTargetHelper(xpc::NativeGlobal(xpc::PrivilegedJunkScope()))
     , MessageManagerGlobal(aMessageManager)
   {}
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(ContentFrameMessageManager, NS_CONTENTFRAMEMESSAGEMANAGER_IID)
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ContentFrameMessageManager_h
--- a/dom/base/InProcessTabChildMessageManager.cpp
+++ b/dom/base/InProcessTabChildMessageManager.cpp
@@ -142,16 +142,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   tmp->nsMessageManagerScriptExecutor::Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InProcessTabChildMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager)
+  NS_INTERFACE_MAP_ENTRY(ContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(InProcessTabChildMessageManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(InProcessTabChildMessageManager, DOMEventTargetHelper)
 
 JSObject*
 InProcessTabChildMessageManager::WrapObject(JSContext* aCx,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2933,17 +2933,23 @@ NS_IMETHODIMP nsJSArgArray::QueryElement
   return NS_ERROR_NO_INTERFACE;
 }
 
 NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
+NS_IMETHODIMP nsJSArgArray::ScriptedEnumerate(nsIJSIID* aElemIID, uint8_t aArgc,
+                                              nsISimpleEnumerator** aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsJSArgArray::EnumerateImpl(const nsID& aEntryIID, nsISimpleEnumerator **_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 // The factory function
 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc,
                          const JS::Value* argv, nsIJSArgArray **aArray)
 {
--- a/dom/browser-element/mochitest/browserElement_ExecuteScript.js
+++ b/dom/browser-element/mochitest/browserElement_ExecuteScript.js
@@ -86,17 +86,17 @@ function runTest() {
       `, {url});
     }).then(bail, (error) => {
       is(error.message, 'Value returned (resolve) by promise is not a valid JSON object', `scriptId: ${scriptId++}`);
       return iframe.executeScript('window.btoa("a")', {url})
     }, bail).then(rv => {
       ok(c(rv, 'YQ=='), `scriptId: ${scriptId++}`);
       return iframe.executeScript('window.wrappedJSObject.btoa("a")', {url})
     }, bail).then(bail, (error) => {
-      is(error.message, 'TypeError: window.wrappedJSObject is undefined', `scriptId: ${scriptId++}`);
+      is(error.message, `TypeError: window.wrappedJSObject is undefined, can't access property "btoa" of it`, `scriptId: ${scriptId++}`);
       return iframe.executeScript('42', {})
     }).then(bail, error => {
       is(error.name, 'InvalidAccessError', `scriptId: ${scriptId++}`);
       return iframe.executeScript('42');
     }).then(bail, error => {
       is(error.name, 'InvalidAccessError', `scriptId: ${scriptId++}`);
       return iframe.executeScript('43', { url: 'http://foo.com' });
     }).then(bail, (error) => {
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/IteratorResult.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+/**
+ * A dictionary which represents the result of a call to a next() method on a
+ * JS iterator object.
+ */
+dictionary IteratorResult {
+  required boolean done;
+  any value;
+};
--- a/dom/chrome-webidl/WebExtensionContentScript.webidl
+++ b/dom/chrome-webidl/WebExtensionContentScript.webidl
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 interface LoadInfo;
 interface URI;
 interface WindowProxy;
 
+typedef (MatchPatternSet or sequence<DOMString>) MatchPatternSetOrStringSequence;
+typedef (MatchGlob or DOMString) MatchGlobOrString;
+
 [Constructor(MozDocumentMatcherInit options), ChromeOnly, Exposed=System]
 interface MozDocumentMatcher {
   /**
    * Returns true if the script's match and exclude patterns match the given
    * URI, without reference to attributes such as `allFrames`.
    */
   boolean matchesURI(URI uri);
 
@@ -89,23 +92,23 @@ interface MozDocumentMatcher {
 
 dictionary MozDocumentMatcherInit {
   boolean allFrames = false;
 
   boolean matchAboutBlank = false;
 
   unsigned long long? frameID = null;
 
-  required MatchPatternSet matches;
+  required MatchPatternSetOrStringSequence matches;
 
-  MatchPatternSet? excludeMatches = null;
+  MatchPatternSetOrStringSequence? excludeMatches = null;
 
-  sequence<MatchGlob>? includeGlobs = null;
+  sequence<MatchGlobOrString>? includeGlobs = null;
 
-  sequence<MatchGlob>? excludeGlobs = null;
+  sequence<MatchGlobOrString>? excludeGlobs = null;
 
   boolean hasActiveTabPermission = false;
 };
 
 /**
  * Describes the earliest point in the load cycle at which a script should
  * run.
  */
--- a/dom/chrome-webidl/WebExtensionPolicy.webidl
+++ b/dom/chrome-webidl/WebExtensionPolicy.webidl
@@ -130,16 +130,22 @@ interface WebExtensionPolicy {
 
   /**
    * Unregister a content script.
    */
   [Throws]
   void unregisterContentScript(WebExtensionContentScript script);
 
   /**
+   * Injects the extension's content script into all existing matching windows.
+   */
+  [Throws]
+  void injectContentScripts();
+
+  /**
    * Returns the list of currently active extension policies.
    */
   static sequence<WebExtensionPolicy> getActiveExtensions();
 
   /**
    * Returns the currently-active policy for the extension with the given ID,
    * or null if no policy is active for that ID.
    */
@@ -171,20 +177,20 @@ dictionary WebExtensionInit {
   required ByteString mozExtensionHostname;
 
   required DOMString baseURL;
 
   DOMString name = "";
 
   required WebExtensionLocalizeCallback localizeCallback;
 
-  required MatchPatternSet allowedOrigins;
+  required MatchPatternSetOrStringSequence allowedOrigins;
 
   sequence<DOMString> permissions = [];
 
-  sequence<MatchGlob> webAccessibleResources = [];
+  sequence<MatchGlobOrString> webAccessibleResources = [];
 
   sequence<WebExtensionContentScriptInit> contentScripts = [];
 
   DOMString? contentSecurityPolicy = null;
 
   sequence<DOMString>? backgroundScripts = null;
 };
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -29,16 +29,17 @@ PREPROCESSED_WEBIDL_FILES = [
     'ChromeUtils.webidl',
 ]
 
 WEBIDL_FILES = [
     'ChannelWrapper.webidl',
     'DominatorTree.webidl',
     'HeapSnapshot.webidl',
     'InspectorUtils.webidl',
+    'IteratorResult.webidl',
     'MatchGlob.webidl',
     'MatchPattern.webidl',
     'MessageManager.webidl',
     'MozDocumentObserver.webidl',
     'MozSharedMap.webidl',
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
--- a/dom/commandhandler/nsCommandGroup.cpp
+++ b/dom/commandhandler/nsCommandGroup.cpp
@@ -2,48 +2,49 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
-#include "nsISimpleEnumerator.h"
+#include "nsSimpleEnumerator.h"
 #include "nsXPCOM.h"
 #include "nsSupportsPrimitives.h"
 #include "nsIComponentManager.h"
 #include "nsCommandGroup.h"
 #include "nsIControllerCommand.h"
 #include "nsCRT.h"
 
-class nsGroupsEnumerator : public nsISimpleEnumerator
+class nsGroupsEnumerator : public nsSimpleEnumerator
 {
 public:
   explicit nsGroupsEnumerator(
     nsControllerCommandGroup::GroupsHashtable& aInHashTable);
 
-  NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsISupportsCString);
+  }
+
 protected:
-  virtual ~nsGroupsEnumerator();
+  ~nsGroupsEnumerator() override;
 
   nsresult Initialize();
 
 protected:
   nsControllerCommandGroup::GroupsHashtable& mHashTable;
   int32_t mIndex;
   const char** mGroupNames;  // array of pointers to char16_t* in the hash table
   bool mInitted;
 };
 
-/* Implementation file */
-NS_IMPL_ISUPPORTS(nsGroupsEnumerator, nsISimpleEnumerator)
-
 nsGroupsEnumerator::nsGroupsEnumerator(
       nsControllerCommandGroup::GroupsHashtable& aInHashTable)
   : mHashTable(aInHashTable)
   , mIndex(-1)
   , mGroupNames(nullptr)
   , mInitted(false)
 {
 }
@@ -120,43 +121,45 @@ nsGroupsEnumerator::Initialize()
     mIndex++;
   }
 
   mIndex = -1;
   mInitted = true;
   return NS_OK;
 }
 
-class nsNamedGroupEnumerator : public nsISimpleEnumerator
+class nsNamedGroupEnumerator : public nsSimpleEnumerator
 {
 public:
   explicit nsNamedGroupEnumerator(nsTArray<nsCString>* aInArray);
 
-  NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsISupportsCString);
+  }
+
 protected:
-  virtual ~nsNamedGroupEnumerator();
+  ~nsNamedGroupEnumerator() override;
 
   nsTArray<nsCString>* mGroupArray;
   int32_t mIndex;
 };
 
 nsNamedGroupEnumerator::nsNamedGroupEnumerator(nsTArray<nsCString>* aInArray)
   : mGroupArray(aInArray)
   , mIndex(-1)
 {
 }
 
 nsNamedGroupEnumerator::~nsNamedGroupEnumerator()
 {
 }
 
-NS_IMPL_ISUPPORTS(nsNamedGroupEnumerator, nsISimpleEnumerator)
-
 NS_IMETHODIMP
 nsNamedGroupEnumerator::HasMoreElements(bool* aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
   int32_t arrayLen = mGroupArray ? mGroupArray->Length() : 0;
   *aResult = (mIndex < arrayLen - 1);
   return NS_OK;
--- a/dom/html/test/file_cookiemanager.js
+++ b/dom/html/test/file_cookiemanager.js
@@ -1,16 +1,14 @@
 addMessageListener("getCookieFromManager", ({ host, path }) => {
   let cm = Cc["@mozilla.org/cookiemanager;1"]
              .getService(Ci.nsICookieManager);
   let values = [];
   path = path.substring(0, path.lastIndexOf("/") + 1);
-  let e = cm.enumerator;
-  while (e.hasMoreElements()) {
-    let cookie = e.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of cm.enumerator) {
     if (!cookie) {
       break;
     }
     if (host != cookie.host || path != cookie.path) {
       continue;
     }
     values.push(cookie.name + "=" + cookie.value);
   }
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -17119,17 +17119,17 @@ FileManager::InitDirectory(nsIFile* aDir
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (NS_WARN_IF(!isDirectory)) {
       return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsISimpleEnumerator> entries;
+    nsCOMPtr<nsIDirectoryEnumerator> entries;
     rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     bool hasElements;
     rv = entries->HasMoreElements(&hasElements);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -85,16 +85,17 @@
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "mozilla/HangDetails.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIStringBundle.h"
 #include "nsIWorkerDebuggerManager.h"
+#include "nsGeolocation.h"
 
 #if !defined(XP_WIN)
 #include "mozilla/Omnijar.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ChildProfilerController.h"
 #endif
@@ -2621,30 +2622,30 @@ ContentChild::RecvUpdateSharedData(const
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition)
 {
-  nsCOMPtr<nsIGeolocationUpdate> gs =
-    do_GetService("@mozilla.org/geolocation/service;1");
+  RefPtr<nsGeolocationService> gs =
+    nsGeolocationService::GetGeolocationService();
   if (!gs) {
     return IPC_OK();
   }
   gs->Update(aPosition);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvGeolocationError(const uint16_t& errorCode)
 {
-  nsCOMPtr<nsIGeolocationUpdate> gs =
-    do_GetService("@mozilla.org/geolocation/service;1");
+  RefPtr<nsGeolocationService> gs =
+    nsGeolocationService::GetGeolocationService();
   if (!gs) {
     return IPC_OK();
   }
   gs->NotifyError(errorCode);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3558,16 +3558,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChildMessageManager,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
+  NS_INTERFACE_MAP_ENTRY(ContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(TabChildMessageManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TabChildMessageManager, DOMEventTargetHelper)
 
 JSObject*
 TabChildMessageManager::WrapObject(JSContext* aCx,
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -13,17 +13,17 @@
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
 #include "nsClassHashtable.h"
 
 #define CHROMIUM_CDM_API_BACKWARD_COMPAT "chromium-cdm8-host4"
 #define CHROMIUM_CDM_API "chromium-cdm9-host4"
 
 class nsIFile;
-class nsISimpleEnumerator;
+class nsIDirectoryEnumerator;
 
 namespace mozilla {
 
 template<typename T>
 struct DestroyPolicy
 {
   void operator()(T* aGMPObject) const {
     aGMPObject->Destroy();
@@ -57,17 +57,17 @@ public:
   };
 
   DirectoryEnumerator(nsIFile* aPath, Mode aMode);
 
   already_AddRefed<nsIFile> Next();
 
 private:
   Mode mMode;
-  nsCOMPtr<nsISimpleEnumerator> mIter;
+  nsCOMPtr<nsIDirectoryEnumerator> mIter;
 };
 
 class GMPInfoFileParser {
 public:
   bool Init(nsIFile* aFile);
   bool Contains(const nsCString& aKey) const;
   nsCString Get(const nsCString& aKey) const;
 private:
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -3,40 +3,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ClearOnShutdown.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
 #include "BasicCardPayment.h"
+#include "nsSimpleEnumerator.h"
 
 namespace mozilla {
 namespace dom {
 
 StaticRefPtr<PaymentRequestService> gPaymentService;
 
 namespace {
 
-class PaymentRequestEnumerator final : public nsISimpleEnumerator
+class PaymentRequestEnumerator final : public nsSimpleEnumerator
 {
 public:
-  NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
   PaymentRequestEnumerator()
     : mIndex(0)
   {}
+
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsIPaymentRequest);
+  }
+
 private:
-  ~PaymentRequestEnumerator() = default;
+  ~PaymentRequestEnumerator() override = default;
   uint32_t mIndex;
 };
 
-NS_IMPL_ISUPPORTS(PaymentRequestEnumerator, nsISimpleEnumerator)
-
 NS_IMETHODIMP
 PaymentRequestEnumerator::HasMoreElements(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = false;
   if (NS_WARN_IF(!gPaymentService)) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/payments/test/CleanupChromeScript.js
+++ b/dom/payments/test/CleanupChromeScript.js
@@ -23,30 +23,26 @@ addMessageListener("cleanup-check", func
   }
   sendAsyncMessage("cleanup-check-complete");
 });
 
 var setPaymentNums = 0;
 
 addMessageListener("payment-num-set", function() {
   setPaymentNums = 0;
-  const paymentEnum = paymentSrv.enumerate();
-  while (paymentEnum.hasMoreElements()) {
+  for (let payment of paymentSrv.enumerate()) {
     setPaymentNums = setPaymentNums + 1;
-    paymentEnum.getNext();
   }
   sendAsyncMessage("payment-num-set-complete");
 });
 
 addMessageListener("payment-num-check", function(expectedNumPayments) {
-  const paymentEnum = paymentSrv.enumerate();
   let numPayments = 0;
-  while (paymentEnum.hasMoreElements()) {
+  for (let payment of paymentSrv.enumerate()) {
     numPayments = numPayments + 1;
-    paymentEnum.getNext();
   }
   if (numPayments !== expectedNumPayments + setPaymentNums) {
     emitTestFail("Expected '" + expectedNumPayments +
                  "' PaymentRequests in PaymentRequestService" + ", but got '" +
                  numPayments + "'.");
   } else {
     emitTestPass("Got expected '" + numPayments +
                  "' PaymentRequests in PaymentRequestService.");
--- a/dom/payments/test/ConstructorChromeScript.js
+++ b/dom/payments/test/ConstructorChromeScript.js
@@ -348,69 +348,65 @@ function checkNonBasicCardRequest(payReq
   }
 }
 
 function checkSimplestRequestHandler() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
-  while (paymentEnum.hasMoreElements()) {
-    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
       break;
     }
     checkSimplestRequest(payRequest);
   }
   paymentSrv.cleanup();
   sendAsyncMessage("check-complete");
 }
 
 function checkComplexRequestHandler() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
-  while (paymentEnum.hasMoreElements()) {
-    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
       break;
     }
     checkComplexRequest(payRequest);
   }
   paymentSrv.cleanup();
   sendAsyncMessage("check-complete");
 }
 
 function checkNonBasicCardRequestHandler() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
-  while (paymentEnum.hasMoreElements()) {
-    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
       break;
     }
     checkNonBasicCardRequest(payRequest);
   }
   paymentSrv.cleanup();
   sendAsyncMessage("check-complete");
 }
 
 function checkMultipleRequestsHandler () {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
-  while (paymentEnum.hasMoreElements()) {
-    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
       break;
     }
     if (payRequest.paymentDetails.id == "payment details") {
       checkComplexRequest(payRequest);
     } else {
       checkSimplestRequest(payRequest);
@@ -420,18 +416,17 @@ function checkMultipleRequestsHandler ()
   sendAsyncMessage("check-complete");
 }
 
 function checkCrossOriginTopLevelPrincipalHandler() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
-  while (paymentEnum.hasMoreElements()) {
-    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
       break;
     }
     if (payRequest.topLevelPrincipal.origin != "https://example.com") {
       emitTestFail("Top level principal's origin should be 'https://example.com', but got '"
                    + payRequest.topLevelPrincipal.origin + "'.");
     }
--- a/dom/payments/test/CurrencyAmountValidationChromeScript.js
+++ b/dom/payments/test/CurrencyAmountValidationChromeScript.js
@@ -31,20 +31,17 @@ const InvalidDetailsUIService = {
 
 function checkLowerCaseCurrency() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     const msg =
       "PaymentRequestService should have at least one payment request.";
     sendAsyncMessage("test-fail", msg);
   }
-  while (paymentEnum.hasMoreElements()) {
-    const payRequest = paymentEnum
-      .getNext()
-      .QueryInterface(Ci.nsIPaymentRequest);
+  for (let payRequest of paymentEnum) {
     if (!payRequest) {
       sendAsyncMessage("test-fail", "Fail to get existing payment request.");
       break;
     }
     const { currency } = payRequest.paymentDetails.totalItem.amount;
     if (currency != "USD") {
       const msg =
         "Currency of PaymentItem total should be 'USD', but got ${currency}";
--- a/dom/payments/test/browser_payment_in_different_tabs.js
+++ b/dom/payments/test/browser_payment_in_different_tabs.js
@@ -12,25 +12,23 @@ add_task(async () => {
   };
   for (let i = 0; i < TABS_TO_OPEN; i++) {
     const tab = await BrowserTestUtils.openNewForegroundTab(options);
     tabs.push(tab);
   }
   const paymentSrv = Cc[
     "@mozilla.org/dom/payments/payment-request-service;1"
   ].getService(Ci.nsIPaymentRequestService);
-  ok(paymentSrv, "Fail to get PaymentRequestService.");
   const paymentEnum = paymentSrv.enumerate();
   ok(
     paymentEnum.hasMoreElements(),
     "PaymentRequestService should have at least one payment request."
   );
   const payments = new Set();
-  while (paymentEnum.hasMoreElements()) {
-    const payment = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+  for (let payment of paymentEnum) {
     ok(payment, "Fail to get existing payment request.");
     checkSimplePayment(payment);
     payments.add(payment);
   }
   is(payments.size, TABS_TO_OPEN, `Should be ${TABS_TO_OPEN} unique objects.`);
   tabs.forEach(async tab => {
     await TestUtils.waitForTick();
     BrowserTestUtils.removeTab(tab);
--- a/dom/plugins/base/nsPluginDirServiceProvider.cpp
+++ b/dom/plugins/base/nsPluginDirServiceProvider.cpp
@@ -57,17 +57,17 @@ nsPluginDirServiceProvider::GetPLIDDirec
   NS_ENSURE_ARG_POINTER(aEnumerator);
   *aEnumerator = nullptr;
 
   nsCOMArray<nsIFile> dirs;
 
   GetPLIDDirectoriesWithRootKey(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, dirs);
   GetPLIDDirectoriesWithRootKey(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, dirs);
 
-  return NS_NewArrayEnumerator(aEnumerator, dirs);
+  return NS_NewArrayEnumerator(aEnumerator, dirs, NS_GET_IID(nsIFile));
 }
 
 nsresult
 nsPluginDirServiceProvider::GetPLIDDirectoriesWithRootKey(uint32_t aKey, nsCOMArray<nsIFile> &aDirs)
 {
   nsCOMPtr<nsIWindowsRegKey> regKey =
     do_CreateInstance("@mozilla.org/windows-registry-key;1");
   NS_ENSURE_TRUE(regKey, NS_ERROR_FAILURE);
--- a/dom/plugins/test/unit/head_plugins.js
+++ b/dom/plugins/test/unit/head_plugins.js
@@ -11,19 +11,17 @@ const gIsLinux = mozinfo.os == "linux";
 const gDirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 
 function allow_all_plugins() {
   Services.prefs.setBoolPref("plugin.load_flash_only", false);
 }
 
 // Finds the test plugin library
 function get_test_plugin(secondplugin=false) {
-  var pluginEnum = gDirSvc.get("APluginsDL", Ci.nsISimpleEnumerator);
-  while (pluginEnum.hasMoreElements()) {
-    let dir = pluginEnum.getNext().QueryInterface(Ci.nsIFile);
+  for (let dir of gDirSvc.get("APluginsDL", Ci.nsISimpleEnumerator)) {
     let name = get_platform_specific_plugin_name(secondplugin);
     let plugin = dir.clone();
     plugin.append(name);
     if (plugin.exists()) {
       plugin.normalize();
       return plugin;
     }
   }
@@ -95,19 +93,17 @@ function get_platform_specific_plugin_su
   else if (gIsOSX) return ".plugin";
   else if (gIsLinux) return ".so";
   else return null;
 }
 
 function get_test_plugin_no_symlink() {
   let dirSvc = Cc["@mozilla.org/file/directory_service;1"]
                 .getService(Ci.nsIProperties);
-  let pluginEnum = dirSvc.get("APluginsDL", Ci.nsISimpleEnumerator);
-  while (pluginEnum.hasMoreElements()) {
-    let dir = pluginEnum.getNext().QueryInterface(Ci.nsIFile);
+  for (let dir of dirSvc.get("APluginsDL", Ci.nsISimpleEnumerator)) {
     let plugin = dir.clone();
     plugin.append(get_platform_specific_plugin_name());
     if (plugin.exists()) {
       return plugin;
     }
   }
   return null;
 }
new file mode 100644
--- /dev/null
+++ b/dom/promise/Promise-inl.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_Promise_inl_h
+#define mozilla_dom_Promise_inl_h
+
+#include "mozilla/TupleCycleCollection.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+
+namespace mozilla {
+namespace dom {
+
+class PromiseNativeThenHandlerBase : public PromiseNativeHandler
+{
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(PromiseNativeThenHandlerBase)
+
+  PromiseNativeThenHandlerBase(Promise& aPromise)
+    : mPromise(&aPromise)
+  {}
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+protected:
+  virtual ~PromiseNativeThenHandlerBase() = default;
+
+  virtual already_AddRefed<Promise>
+  CallResolveCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) = 0;
+
+  virtual void Traverse(nsCycleCollectionTraversalCallback&) = 0;
+  virtual void Unlink() = 0;
+
+  RefPtr<Promise> mPromise;
+};
+
+namespace {
+
+template <typename T,
+          bool = IsRefcounted<typename RemovePointer<T>::Type>::value,
+          bool = (IsConvertible<T, nsISupports*>::value ||
+                  IsConvertible<T*, nsISupports*>::value)>
+struct StorageTypeHelper
+{
+  using Type = T;
+};
+
+template <typename T>
+struct StorageTypeHelper<T, true, true>
+{
+  using Type = nsCOMPtr<T>;
+};
+
+template <typename T>
+struct StorageTypeHelper<nsCOMPtr<T>, true, true>
+{
+  using Type = nsCOMPtr<T>;
+};
+
+template <typename T>
+struct StorageTypeHelper<T*, true, false>
+{
+  using Type = RefPtr<T>;
+};
+
+template <template <typename> class SmartPtr, typename T>
+struct StorageTypeHelper<SmartPtr<T>, true, false>
+  : EnableIf<IsConvertible<SmartPtr<T>, T*>::value,
+             RefPtr<T>>
+{
+};
+
+template <typename T>
+using StorageType = typename StorageTypeHelper<typename Decay<T>::Type>::Type;
+
+using ::ImplCycleCollectionUnlink;
+
+template <typename Callback, typename... Args>
+class NativeThenHandler final : public PromiseNativeThenHandlerBase
+{
+public:
+  NativeThenHandler(Promise& aPromise, Callback&& aOnResolve,
+                    Args&&... aArgs)
+    : PromiseNativeThenHandlerBase(aPromise)
+    , mOnResolve(std::forward<Callback>(aOnResolve))
+    , mArgs(std::forward<Args>(aArgs)...)
+  {}
+
+protected:
+  void Traverse(nsCycleCollectionTraversalCallback& cb) override
+  {
+    auto* tmp = this;
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgs)
+  }
+
+  void Unlink() override
+  {
+    auto* tmp = this;
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgs)
+  }
+
+  already_AddRefed<Promise>
+  CallResolveCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    return CallCallback(aCx, mOnResolve, aValue);
+  }
+
+  template <size_t... Indices>
+  already_AddRefed<Promise>
+  CallCallback(JSContext* aCx, const Callback& aHandler, JS::Handle<JS::Value> aValue,
+               std::index_sequence<Indices...>)
+  {
+    return mOnResolve(aCx, aValue, std::forward<Args>(Get<Indices>(mArgs))...);
+  }
+
+  already_AddRefed<Promise>
+  CallCallback(JSContext* aCx, const Callback& aHandler, JS::Handle<JS::Value> aValue)
+  {
+    return CallCallback(aCx, aHandler, aValue, std::index_sequence_for<Args...>{});
+  }
+
+  Callback mOnResolve;
+
+  Tuple<StorageType<Args>...> mArgs;
+};
+
+} // anonymous namespace
+
+template <typename Callback, typename... Args>
+typename EnableIf<
+  Promise::IsHandlerCallback<Callback, Args...>::value,
+  Result<RefPtr<Promise>, nsresult>>::Type
+Promise::ThenWithCycleCollectedArgs(Callback&& aOnResolve, Args&&... aArgs)
+{
+  using HandlerType = NativeThenHandler<Callback, Args...>;
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
+  if (rv.Failed()) {
+    return Err(rv.StealNSResult());
+  }
+
+  auto* handler = new (fallible) HandlerType(
+    *promise, std::forward<Callback>(aOnResolve),
+    std::forward<Args>(aArgs)...);
+
+  if (!handler) {
+    return Err(NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  AppendNativeHandler(handler);
+  return std::move(promise);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Promise_inl_h
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1,22 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/Promise-inl.h"
 
 #include "js/Debug.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/ResultExtensions.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/WorkerPrivate.h"
@@ -222,16 +224,58 @@ Promise::Then(JSContext* aCx,
     aRv.NoteJSContextException(aCx);
     return;
   }
 
   aRetval.setObject(*retval);
 }
 
 void
+PromiseNativeThenHandlerBase::ResolvedCallback(JSContext* aCx,
+                                               JS::Handle<JS::Value> aValue)
+{
+  RefPtr<Promise> promise = CallResolveCallback(aCx, aValue);
+  mPromise->MaybeResolve(promise);
+}
+
+void
+PromiseNativeThenHandlerBase::RejectedCallback(JSContext* aCx,
+                                               JS::Handle<JS::Value> aValue)
+{
+  mPromise->MaybeReject(aCx, aValue);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(PromiseNativeThenHandlerBase)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PromiseNativeThenHandlerBase)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
+  tmp->Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PromiseNativeThenHandlerBase)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
+  tmp->Unlink();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseNativeThenHandlerBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseNativeThenHandlerBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseNativeThenHandlerBase)
+
+Result<RefPtr<Promise>, nsresult>
+Promise::ThenWithoutCycleCollection(
+  const std::function<already_AddRefed<Promise>(JSContext* aCx,
+                                                JS::HandleValue aValue)>& aCallback)
+{
+  return ThenWithCycleCollectedArgs(aCallback);
+}
+
+void
 Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
 {
   AutoJSAPI jsapi;
   if (!jsapi.Init(mGlobal)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
   JSContext* cx = jsapi.cx();
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -20,16 +20,17 @@
 #include "nsWrapperCache.h"
 #include "nsAutoPtr.h"
 #include "js/TypeDecls.h"
 #include "jspubtd.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
+
 namespace dom {
 
 class AnyCallback;
 class MediaStreamError;
 class PromiseInit;
 class PromiseNativeHandler;
 class PromiseDebugging;
 
@@ -137,16 +138,50 @@ public:
   Then(JSContext* aCx,
        // aCalleeGlobal may not be in the compartment of aCx, when called over
        // Xrays.
        JS::Handle<JSObject*> aCalleeGlobal,
        AnyCallback* aResolveCallback, AnyCallback* aRejectCallback,
        JS::MutableHandle<JS::Value> aRetval,
        ErrorResult& aRv);
 
+  template <typename Callback, typename... Args>
+  using IsHandlerCallback =
+      IsSame<already_AddRefed<Promise>,
+             decltype(DeclVal<Callback>()(
+                (JSContext*)(nullptr),
+                DeclVal<JS::Handle<JS::Value>>(),
+                DeclVal<Args>()...))>;
+
+  // Similar to the JavaScript Then() function. Accepts a single lambda function
+  // argument, which it attaches as a native resolution handler, and returns a
+  // new promise which resolves with that handler's return value, or propagates
+  // any rejections from this promise.
+  //
+  // Any additional arguments passed after the callback function are stored and
+  // passed as additional arguments to the function when it is called. These
+  // values will participate in cycle collection for the promise handler, and
+  // therefore may safely form reference cycles with the promise chain.
+  //
+  // Any strong references required by the callback should be passed in this
+  // manner, rather than using lambda capture, lambda captures do not support
+  // cycle collection, and can easily lead to leaks.
+  //
+  // Does not currently support rejection handlers.
+  template <typename Callback, typename... Args>
+  typename EnableIf<
+    IsHandlerCallback<Callback, Args...>::value,
+    Result<RefPtr<Promise>, nsresult>>::Type
+  ThenWithCycleCollectedArgs(Callback&& aOnResolve, Args&&... aArgs);
+
+  Result<RefPtr<Promise>, nsresult>
+  ThenWithoutCycleCollection(
+    const std::function<already_AddRefed<Promise>(JSContext*,
+                                                  JS::HandleValue)>& aCallback);
+
   JSObject* PromiseObj() const
   {
     return mPromiseObj;
   }
 
   void AppendNativeHandler(PromiseNativeHandler* aRunnable);
 
   JSObject* GlobalJSObject() const;
--- a/dom/promise/moz.build
+++ b/dom/promise/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 EXPORTS.mozilla.dom += [
+    'Promise-inl.h',
     'Promise.h',
     'PromiseDebugging.h',
     'PromiseNativeHandler.h',
     'PromiseWorkerProxy.h',
 ]
 
 UNIFIED_SOURCES += [
     'Promise.cpp',
--- a/dom/push/PushRecord.jsm
+++ b/dom/push/PushRecord.jsm
@@ -177,19 +177,17 @@ PushRecord.prototype = {
     }
     // Places records times in microseconds.
     let lastVisit = rows[0].getResultByName("lastVisit");
 
     return lastVisit / 1000;
   },
 
   isTabOpen() {
-    let windows = Services.wm.getEnumerator("navigator:browser");
-    while (windows.hasMoreElements()) {
-      let window = windows.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       if (window.closed || PrivateBrowsingUtils.isWindowPrivate(window)) {
         continue;
       }
       // `gBrowser` on Desktop; `BrowserApp` on Fennec.
       let tabs = window.gBrowser ? window.gBrowser.tabContainer.children :
                  window.BrowserApp.tabs;
       for (let tab of tabs) {
         // `linkedBrowser` on Desktop; `browser` on Fennec.
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -4878,17 +4878,17 @@ QuotaManager::MaybeRemoveLocalStorageDir
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsISimpleEnumerator> entries;
+  nsCOMPtr<nsIDirectoryEnumerator> entries;
   rv = defaultStorageDir->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!entries) {
     return NS_OK;
   }
--- a/dom/u2f/tests/frame_appid_facet_insecure.html
+++ b/dom/u2f/tests/frame_appid_facet_insecure.html
@@ -28,17 +28,17 @@ async function doTests() {
     local_ok(err == "ReferenceError: u2f is not defined", "calling u2f should have thrown from an insecure origin");
   }
 
   try {
     window.u2f.register(null, [], [], function(res) {
       local_ok(false, "Callbacks should not be called.");
     });
   } catch (err) {
-    local_ok(err == "TypeError: window.u2f is undefined", "accessing window.u2f should have thrown from an insecure origin");
+    local_ok(err.toString().includes("TypeError: window.u2f is undefined"), "accessing window.u2f should have thrown from an insecure origin");
   }
 
   try {
     await promiseU2FRegister(null, [{
       version: version,
       challenge: bytesToBase64UrlSafe(challenge),
     }], [], function(res){
       local_ok(false, "Shouldn't have gotten here on an insecure origin");
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerDebuggerManager.h"
 
-#include "nsISimpleEnumerator.h"
+#include "nsSimpleEnumerator.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 
 #include "WorkerDebugger.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
@@ -71,37 +71,39 @@ private:
     return NS_OK;
   }
 };
 
 static StaticRefPtr<WorkerDebuggerManager> gWorkerDebuggerManager;
 
 } /* anonymous namespace */
 
-class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
+class WorkerDebuggerEnumerator final : public nsSimpleEnumerator
 {
   nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
   uint32_t mIndex;
 
 public:
   explicit WorkerDebuggerEnumerator(
                              const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
   : mDebuggers(aDebuggers), mIndex(0)
   {
   }
 
-  NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsIWorkerDebugger);
+  }
+
 private:
-  ~WorkerDebuggerEnumerator() {}
+  ~WorkerDebuggerEnumerator() override = default;
 };
 
-NS_IMPL_ISUPPORTS(WorkerDebuggerEnumerator, nsISimpleEnumerator);
-
 NS_IMETHODIMP
 WorkerDebuggerEnumerator::HasMoreElements(bool* aResult)
 {
   *aResult = mIndex < mDebuggers.Length();
   return NS_OK;
 };
 
 NS_IMETHODIMP
--- a/dom/workers/test/dom_worker_helper.js
+++ b/dom/workers/test/dom_worker_helper.js
@@ -33,22 +33,18 @@ function assertThrows(fun, message) {
   try {
     fun();
   } catch (e) {
     throws = true;
   }
   ok(throws, message);
 }
 
-function* generateDebuggers() {
-  let e = wdm.getWorkerDebuggerEnumerator();
-  while (e.hasMoreElements()) {
-    let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger);
-    yield dbg;
-  }
+function generateDebuggers() {
+  return wdm.getWorkerDebuggerEnumerator();
 }
 
 function findDebugger(url) {
   for (let dbg of generateDebuggers()) {
     if (dbg.url === url) {
       return dbg;
     }
   }
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -2606,17 +2606,17 @@ NS_IMETHODIMP nsPermissionManager::GetEn
                              permEntry.mExpireTime);
       if (NS_WARN_IF(!permission)) {
         continue;
       }
       array.AppendObject(permission);
     }
   }
 
-  return NS_NewArrayEnumerator(aEnum, array);
+  return NS_NewArrayEnumerator(aEnum, array, NS_GET_IID(nsIPermission));
 }
 
 NS_IMETHODIMP nsPermissionManager::GetAllForURI(nsIURI* aURI, nsISimpleEnumerator **aEnum)
 {
   nsCOMPtr<nsIPrincipal> principal;
   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2655,17 +2655,17 @@ nsPermissionManager::GetAllForPrincipal(
                              permEntry.mExpireTime);
       if (NS_WARN_IF(!permission)) {
         continue;
       }
       array.AppendObject(permission);
     }
   }
 
-  return NS_NewArrayEnumerator(aEnum, array);
+  return NS_NewArrayEnumerator(aEnum, array, NS_GET_IID(nsIPermission));
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
   if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
     // The profile is about to change,
--- a/extensions/cookie/test/file_chromecommon.js
+++ b/extensions/cookie/test/file_chromecommon.js
@@ -1,13 +1,13 @@
 let cs = Cc["@mozilla.org/cookiemanager;1"]
            .getService(Ci.nsICookieManager);
 
 addMessageListener("getCookieCountAndClear", () => {
   let count = 0;
-  for (let list = cs.enumerator; list.hasMoreElements(); list.getNext())
+  for (let cookie of cs.enumerator)
     ++count;
   cs.removeAll();
 
   sendAsyncMessage("getCookieCountAndClear:return", { count });
 });
 
 cs.removeAll();
--- a/extensions/cookie/test/file_testloadflags_chromescript.js
+++ b/extensions/cookie/test/file_testloadflags_chromescript.js
@@ -57,19 +57,17 @@ obs.prototype = {
 
     this.os.removeObserver(this, "http-on-modify-request");
     this.os = null;
   }
 }
 
 function getCookieCount(cs) {
   let count = 0;
-  let list = cs.enumerator;
-  while (list.hasMoreElements()) {
-    let cookie = list.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of cs.enumerator) {
     info("cookie: " + cookie);
     info("cookie host " + cookie.host + " path " + cookie.path + " name " + cookie.name +
          " value " + cookie.value + " isSecure " + cookie.isSecure + " expires " + cookie.expires);
     ++count;
   }
 
   return count;
 }
--- a/extensions/cookie/test/unit/head_cookies.js
+++ b/extensions/cookie/test/unit/head_cookies.js
@@ -118,18 +118,18 @@ function do_set_cookies(uri, channel, se
   Assert.equal(Services.cookiemgr.countCookiesFromHost(uri.host), expected[2]);
   // with channel, from http
   Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel);
   Assert.equal(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]);
 }
 
 function do_count_enumerator(enumerator) {
   let i = 0;
-  while (enumerator.hasMoreElements()) {
-    enumerator.getNext();
+  for (let cookie of enumerator) {
+    void cookie;
     ++i;
   }
   return i;
 }
 
 function do_count_cookies() {
   return do_count_enumerator(Services.cookiemgr.enumerator);
 }
--- a/extensions/cookie/test/unit/test_bug526789.js
+++ b/extensions/cookie/test/unit/test_bug526789.js
@@ -180,20 +180,17 @@ function run_test() {
   testTrailingDotCookie("http://foo.com", "foo.com");
 
   cm.removeAll();
 }
 
 function getCookieCount() {
   var count = 0;
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
-  var enumerator = cm.enumerator;
-  while (enumerator.hasMoreElements()) {
-    if (!(enumerator.getNext() instanceof Ci.nsICookie2))
-      throw new Error("not a cookie");
+  for (let cookie of cm.enumerator) {
     ++count;
   }
   return count;
 }
 
 function testDomainCookie(uriString, domain) {
   var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
--- a/extensions/cookie/test/unit/test_domain_eviction.js
+++ b/extensions/cookie/test/unit/test_domain_eviction.js
@@ -51,20 +51,17 @@ function* do_run_test()
   // Wait a while, to make sure the first batch of cookies is older than
   // the second (timer resolution varies on different platforms).
   do_timeout(100, continue_test);
   yield;
 
   setCookies("tasty.horse.radish", 50, futureExpiry);
   Assert.equal(countCookies("horse.radish", "horse.radish"), 50);
 
-  let enumerator = Services.cookiemgr.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
-
+  for (let cookie of Services.cookiemgr.enumerator) {
     if (cookie.host == "horse.radish")
       do_throw("cookies not evicted by lastAccessed order");
   }
 
   // Test that expired cookies for a domain are evicted before live ones.
   let shortExpiry = Math.floor(Date.now() / 1000 + 2);
   setCookies("captchart.com", 49, futureExpiry);
   Services.cookiemgr.add("captchart.com", "", "test100", "eviction",
@@ -72,19 +69,17 @@ function* do_run_test()
   do_timeout(2100, continue_test);
   yield;
 
   Assert.equal(countCookies("captchart.com", "captchart.com"), 50);
   Services.cookiemgr.add("captchart.com", "", "test200", "eviction",
     false, false, false, futureExpiry, {});
   Assert.equal(countCookies("captchart.com", "captchart.com"), 50);
 
-  enumerator = Services.cookiemgr.getCookiesFromHost("captchart.com", {});
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+  for (let cookie of Services.cookiemgr.getCookiesFromHost("captchart.com", {})) {
     Assert.ok(cookie.expiry == futureExpiry);
   }
 
   do_finish_generator_test(test_generator);
 }
 
 // set 'aNumber' cookies with host 'aHost', with distinct names.
 function
@@ -99,39 +94,32 @@ setCookies(aHost, aNumber, aExpiry)
 // independent interface methods on nsICookieManager:
 // 1) 'enumerator', an enumerator of all cookies;
 // 2) 'countCookiesFromHost', which returns the number of cookies within the
 //    base domain of 'aHost',
 // 3) 'getCookiesFromHost', which returns an enumerator of 2).
 function
 countCookies(aBaseDomain, aHost)
 {
-  let enumerator = Services.cookiemgr.enumerator;
-
   // count how many cookies are within domain 'aBaseDomain' using the cookie
   // enumerator.
   let cookies = [];
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
-
+  for (let cookie of Services.cookiemgr.enumerator) {
     if (cookie.host.length >= aBaseDomain.length &&
         cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain)
       cookies.push(cookie);
   }
 
   // confirm the count using countCookiesFromHost and getCookiesFromHost.
   let result = cookies.length;
   Assert.equal(Services.cookiemgr.countCookiesFromHost(aBaseDomain),
     cookies.length);
   Assert.equal(Services.cookiemgr.countCookiesFromHost(aHost), cookies.length);
 
-  enumerator = Services.cookiemgr.getCookiesFromHost(aHost, {});
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
-
+  for (let cookie of Services.cookiemgr.getCookiesFromHost(aHost, {})) {
     if (cookie.host.length >= aBaseDomain.length &&
         cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain) {
       let found = false;
       for (let i = 0; i < cookies.length; ++i) {
         if (cookies[i].host == cookie.host && cookies[i].name == cookie.name) {
           found = true;
           cookies.splice(i, 1);
           break;
--- a/extensions/cookie/test/unit/test_eviction.js
+++ b/extensions/cookie/test/unit/test_eviction.js
@@ -226,21 +226,18 @@ function get_creationTime(i)
 }
 
 // Test that 'aNumberToExpect' cookies remain after purging is complete, and
 // that the cookies that remain consist of the set expected given the number of
 // of older and newer cookies -- eviction should occur by order of lastAccessed
 // time, if both the limit on total cookies (maxNumber + 10%) and the purge age
 // + 10% are exceeded.
 function check_remaining_cookies(aNumberTotal, aNumberOld, aNumberToExpect) {
-  var enumerator = Services.cookiemgr.enumerator;
-
   let i = 0;
-  while (enumerator.hasMoreElements()) {
-    var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+  for (let cookie of Services.cookiemgr.enumerator) {
     ++i;
 
     if (aNumberTotal != aNumberToExpect) {
       // make sure the cookie is one of the batch we expect was purged.
       var hostNumber = new Number(cookie.rawHost.split(".")[1]);
       if (hostNumber < (aNumberOld - aNumberToExpect)) break;
     }
   }
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js
+++ b/extensions/cookie/test/unit/test_permmanager_defaults.js
@@ -210,19 +210,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;
-  let e = Services.perms.enumerator;
-  while (e.hasMoreElements()) {
-    let perm = e.getNext().QueryInterface(Ci.nsIPermission);
+  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/cookie/test/unit/test_permmanager_migrate_4-7.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
@@ -154,19 +154,17 @@ add_task(async function test() {
 
   let found = expected.map((it) => 0);
 
   // Add some places to the places database
   await PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory"));
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory"));
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -171,19 +171,17 @@ add_task(function test() {
     ["https://127.0.0.1", "A", 1, 0, 0],
     ["http://263.123.555.676", "A", 1, 0, 0],
     ["https://263.123.555.676", "A", 1, 0, 0],
   ];
 
   let found = expected.map((it) => 0);
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
@@ -213,19 +213,17 @@ add_task(async function test() {
 
   let found = expected.map((it) => 0);
 
   // Add some places to the places database
   await PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory"));
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory"));
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
@@ -95,19 +95,17 @@ add_task(function test() {
 
     ["http://127.0.0.1", "B", 2, 0, 0, 0],
     ["http://localhost", "B", 2, 0, 0, 0],
   ];
 
   let found = expected.map((it) => 0);
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
@@ -213,19 +213,17 @@ add_task(async function test() {
 
   let found = expected.map((it) => 0);
 
   // Add some places to the places database
   await PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory"));
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory"));
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
@@ -89,19 +89,17 @@ add_task(function test() {
     ["https://foo.com", "A", 2, 0, 0, 0],
     ["http://foo.com", "A", 2, 0, 0, 0],
     ["http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0]
   ];
 
   let found = expected.map((it) => 0);
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
@@ -193,19 +193,17 @@ add_task(async function test() {
 
   // Add some places to the places database
   await PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory"));
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory"));
   await PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080"));
   await PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080"));
 
   // Force initialization of the nsPermissionManager
-  let enumerator = Services.perms.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+  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] &&
           permission.expireTime == it[4]) {
--- a/gfx/2d/CaptureCommandList.h
+++ b/gfx/2d/CaptureCommandList.h
@@ -8,17 +8,16 @@
 #define mozilla_gfx_2d_CaptureCommandList_h
 
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include <vector>
 
 #include "DrawCommand.h"
 #include "Logging.h"
-#include "RwAssert.h"
 
 namespace mozilla {
 namespace gfx {
 
 class CaptureCommandList
 {
 public:
   CaptureCommandList()
@@ -28,39 +27,36 @@ public:
     : mStorage(std::move(aOther.mStorage))
     , mLastCommand(aOther.mLastCommand)
   {
     aOther.mLastCommand = nullptr;
   }
   ~CaptureCommandList();
 
   CaptureCommandList& operator=(CaptureCommandList&& aOther) {
-    RwAssert::Writer lock(mAssert);
     mStorage = std::move(aOther.mStorage);
     mLastCommand = aOther.mLastCommand;
     aOther.mLastCommand = nullptr;
     return *this;
   }
 
   template <typename T>
   T* Append() {
-    RwAssert::Writer lock(mAssert);
     size_t oldSize = mStorage.size();
     mStorage.resize(mStorage.size() + sizeof(T) + sizeof(uint32_t));
     uint8_t* nextDrawLocation = &mStorage.front() + oldSize;
     *(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t);
     T* newCommand = reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t));
     mLastCommand = newCommand;
     return newCommand;
   }
 
   template <typename T>
   T* ReuseOrAppend() {
     { // Scope lock
-      RwAssert::Writer lock(mAssert);
       if (mLastCommand != nullptr &&
         mLastCommand->GetType() == T::Type) {
         return reinterpret_cast<T*>(mLastCommand);
       }
     }
     return Append<T>();
   }
 
@@ -82,26 +78,21 @@ public:
   class iterator
   {
   public:
     explicit iterator(CaptureCommandList& aParent)
      : mParent(aParent),
        mCurrent(nullptr),
        mEnd(nullptr)
     {
-      mParent.mAssert.BeginReading();
       if (!mParent.mStorage.empty()) {
         mCurrent = &mParent.mStorage.front();
         mEnd = mCurrent + mParent.mStorage.size();
       }
     }
-    ~iterator()
-    {
-      mParent.mAssert.EndReading();
-    }
 
     bool Done() const {
       return mCurrent >= mEnd;
     }
     void Next() {
       MOZ_ASSERT(!Done());
       mCurrent += *reinterpret_cast<uint32_t*>(mCurrent);
     }
@@ -127,15 +118,14 @@ public:
 
 private:
   CaptureCommandList(const CaptureCommandList& aOther) = delete;
   void operator =(const CaptureCommandList& aOther) = delete;
 
 private:
   std::vector<uint8_t> mStorage;
   DrawingCommand* mLastCommand;
-  RwAssert mAssert;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_gfx_2d_CaptureCommandList_h
deleted file mode 100644
--- a/gfx/2d/RwAssert.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_gfx_2d_RwAssert_h
-#define mozilla_gfx_2d_RwAssert_h
-
-#include "mozilla/Attributes.h"
-#include "mozilla/Mutex.h"
-
-namespace mozilla {
-namespace gfx {
-
-class RwAssert {
-public:
-  RwAssert()
-    : mLock("RwAssert::mLock")
-    , mReaders(0)
-    , mWriting(false)
-  { }
-
-  ~RwAssert()
-  {
-    MOZ_RELEASE_ASSERT(!mReaders && !mWriting);
-  }
-
-  class MOZ_RAII Writer {
-  public:
-    MOZ_IMPLICIT Writer(RwAssert& aAssert)
-      : mAssert(aAssert)
-    {
-      mAssert.BeginWriting();
-    }
-    ~Writer()
-    {
-      mAssert.EndWriting();
-    }
-
-  private:
-    RwAssert& mAssert;
-  };
-
-  class MOZ_RAII Reader {
-  public:
-    MOZ_IMPLICIT Reader(RwAssert& aAssert)
-      : mAssert(aAssert)
-    {
-      mAssert.BeginReading();
-    }
-    ~Reader()
-    {
-      mAssert.EndReading();
-    }
-
-  private:
-    RwAssert& mAssert;
-  };
-
-  void BeginWriting()
-  {
-    MutexAutoLock lock(mLock);
-    MOZ_RELEASE_ASSERT(!mReaders && !mWriting);
-    mWriting = true;
-  }
-
-  void EndWriting()
-  {
-    MutexAutoLock lock(mLock);
-    MOZ_ASSERT(mWriting);
-    mWriting = false;
-  }
-
-  void BeginReading()
-  {
-    MutexAutoLock lock(mLock);
-    MOZ_RELEASE_ASSERT(!mWriting);
-    mReaders += 1;
-  }
-
-  void EndReading()
-  {
-    MutexAutoLock lock(mLock);
-    MOZ_ASSERT(mReaders > 0);
-    mReaders -= 1;
-  }
-
-private:
-  Mutex mLock;
-  uint32_t mReaders;
-  bool mWriting;
-};
-
-} // namespace gfx
-} // namespace mozilla
-
-#endif // mozilla_gfx_2d_RwAssert_h
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -49,35 +49,49 @@ ScaledFont::GetDefaultAAMode()
   }
 
   return AntialiasMode::DEFAULT;
 }
 
 ScaledFontBase::~ScaledFontBase()
 {
 #ifdef USE_SKIA
-  SkSafeUnref(mTypeface);
+  SkSafeUnref<SkTypeface>(mTypeface);
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_scaled_font_destroy(mScaledFont);
 #endif
 }
 
 ScaledFontBase::ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont,
                                Float aSize)
   : ScaledFont(aUnscaledFont)
+#ifdef USE_SKIA
+  , mTypeface(nullptr)
+#endif
+#ifdef USE_CAIRO_SCALED_FONT
+  , mScaledFont(nullptr)
+#endif
   , mSize(aSize)
 {
+}
+
 #ifdef USE_SKIA
-  mTypeface = nullptr;
+SkTypeface*
+ScaledFontBase::GetSkTypeface()
+{
+  if (!mTypeface) {
+    SkTypeface* typeface = CreateSkTypeface();
+    if (!mTypeface.compareExchange(nullptr, typeface)) {
+        SkSafeUnref(typeface);
+    }
+  }
+  return mTypeface;
+}
 #endif
-#ifdef USE_CAIRO_SCALED_FONT
-  mScaledFont = nullptr;
-#endif
-}
 
 #ifdef USE_CAIRO_SCALED_FONT
 bool
 ScaledFontBase::PopulateCairoScaledFont()
 {
   cairo_font_face_t* cairoFontFace = GetCairoFontFace();
   if (!cairoFontFace) {
     return false;
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -37,29 +37,30 @@ public:
 
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint) override;
 
   virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) override;
 
   virtual Float GetSize() const override { return mSize; }
 
 #ifdef USE_SKIA
-  virtual SkTypeface* GetSkTypeface() { return mTypeface; }
+  SkTypeface* GetSkTypeface();
 #endif
 
 #ifdef USE_CAIRO_SCALED_FONT
   bool PopulateCairoScaledFont();
   virtual cairo_scaled_font_t* GetCairoScaledFont() override { return mScaledFont; }
   virtual void SetCairoScaledFont(cairo_scaled_font_t* font) override;
 #endif
 
 protected:
   friend class DrawTargetSkia;
 #ifdef USE_SKIA
-  SkTypeface* mTypeface;
+  Atomic<SkTypeface*> mTypeface;
+  virtual SkTypeface* CreateSkTypeface() { return nullptr; }
   SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   // Overridders should ensure the cairo_font_face_t has been addrefed.
   virtual cairo_font_face_t* GetCairoFontFace() { return nullptr; }
   cairo_scaled_font_t* mScaledFont;
 #endif
   Float mSize;
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -167,39 +167,36 @@ ScaledFontDWrite::GetPathForGlyphs(const
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 
 #ifdef USE_SKIA
 SkTypeface*
-ScaledFontDWrite::GetSkTypeface()
+ScaledFontDWrite::CreateSkTypeface()
 {
-  if (!mTypeface) {
-    RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
-    if (!factory) {
-      return nullptr;
-    }
+  RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
+  if (!factory) {
+    return nullptr;
+  }
 
-    Float gamma = mGamma;
-    // Skia doesn't support a gamma value outside of 0-4, so default to 2.2
-    if (gamma < 0.0f || gamma > 4.0f) {
-      gamma = 2.2f;
-    }
+  Float gamma = mGamma;
+  // Skia doesn't support a gamma value outside of 0-4, so default to 2.2
+  if (gamma < 0.0f || gamma > 4.0f) {
+    gamma = 2.2f;
+  }
 
-    Float contrast = mContrast;
-    // Skia doesn't support a contrast value outside of 0-1, so default to 1.0
-    if (contrast < 0.0f || contrast > 1.0f) {
-      contrast = 1.0f;
-    }
+  Float contrast = mContrast;
+  // Skia doesn't support a contrast value outside of 0-1, so default to 1.0
+  if (contrast < 0.0f || contrast > 1.0f) {
+    contrast = 1.0f;
+  }
 
-    mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode, gamma, contrast);
-  }
-  return mTypeface;
+  return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle, mForceGDIMode, gamma, contrast);
 }
 #endif
 
 void
 ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
 {
   BackendType backendType = aBuilder->GetBackendType();
   if (backendType != BackendType::DIRECT2D && backendType != BackendType::DIRECT2D1_1) {
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -62,17 +62,17 @@ public:
                                 std::vector<FontVariation>* aOutVariations) override;
 
   AntialiasMode GetDefaultAAMode() override;
 
   bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
   bool ForceGDIMode() { return mForceGDIMode; }
 
 #ifdef USE_SKIA
-  SkTypeface* GetSkTypeface() override;
+  SkTypeface* CreateSkTypeface() override;
   SkFontStyle mStyle;
 #endif
 
   RefPtr<IDWriteFontFace> mFontFace;
   bool mUseEmbeddedBitmap;
   bool mForceGDIMode;
   // DrawTargetD2D1 requires the IDWriteRenderingParams,
   // but we also separately need to store the gamma and contrast
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -38,23 +38,19 @@ ScaledFontFontconfig::ScaledFontFontconf
 }
 
 ScaledFontFontconfig::~ScaledFontFontconfig()
 {
   FcPatternDestroy(mPattern);
 }
 
 #ifdef USE_SKIA
-SkTypeface* ScaledFontFontconfig::GetSkTypeface()
+SkTypeface* ScaledFontFontconfig::CreateSkTypeface()
 {
-  if (!mTypeface) {
-    mTypeface = SkCreateTypefaceFromCairoFTFontWithFontconfig(mScaledFont, mPattern);
-  }
-
-  return mTypeface;
+  return SkCreateTypefaceFromCairoFTFontWithFontconfig(mScaledFont, mPattern);
 }
 #endif
 
 ScaledFontFontconfig::InstanceData::InstanceData(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern)
   : mFlags(0)
   , mHintStyle(FC_HINT_NONE)
   , mSubpixelOrder(FC_RGBA_UNKNOWN)
   , mLcdFilter(FC_LCD_LEGACY)
--- a/gfx/2d/ScaledFontFontconfig.h
+++ b/gfx/2d/ScaledFontFontconfig.h
@@ -23,17 +23,17 @@ public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontFontconfig, override)
   ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
                        const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
   ~ScaledFontFontconfig();
 
   FontType GetType() const override { return FontType::FONTCONFIG; }
 
 #ifdef USE_SKIA
-  SkTypeface* GetSkTypeface() override;
+  SkTypeface* CreateSkTypeface() override;
 #endif
 
   bool CanSerialize() override { return true; }
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   bool GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions>* aOutOptions,
                                 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
--- a/gfx/2d/ScaledFontFreeType.cpp
+++ b/gfx/2d/ScaledFontFreeType.cpp
@@ -30,23 +30,19 @@ ScaledFontFreeType::ScaledFontFreeType(c
                                        Float aSize)
   : ScaledFontBase(aUnscaledFont, aSize)
   , mFace(aFace)
 {
   SetCairoScaledFont(aScaledFont);
 }
 
 #ifdef USE_SKIA
-SkTypeface* ScaledFontFreeType::GetSkTypeface()
+SkTypeface* ScaledFontFreeType::CreateSkTypeface()
 {
-  if (!mTypeface) {
-    mTypeface = SkCreateTypefaceFromCairoFTFont(mScaledFont);
-  }
-
-  return mTypeface;
+  return SkCreateTypefaceFromCairoFTFont(mScaledFont);
 }
 #endif
 
 bool
 ScaledFontFreeType::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
 {
   std::vector<FontVariation> variations;
   if (HasVariationSettings()) {
--- a/gfx/2d/ScaledFontFreeType.h
+++ b/gfx/2d/ScaledFontFreeType.h
@@ -22,17 +22,17 @@ public:
   ScaledFontFreeType(cairo_scaled_font_t* aScaledFont,
                      FT_Face aFace,
                      const RefPtr<UnscaledFont>& aUnscaledFont,
                      Float aSize);
 
   FontType GetType() const override { return FontType::FREETYPE; }
 
 #ifdef USE_SKIA
-  virtual SkTypeface* GetSkTypeface() override;
+  virtual SkTypeface* CreateSkTypeface() override;
 #endif
 
   bool CanSerialize() override { return true; }
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   bool GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions>* aOutOptions,
                                 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -172,31 +172,29 @@ ScaledFontMac::~ScaledFontMac()
 {
   if (mCTFont) {
     CFRelease(mCTFont);
   }
   CGFontRelease(mFont);
 }
 
 #ifdef USE_SKIA
-SkTypeface* ScaledFontMac::GetSkTypeface()
+SkTypeface* ScaledFontMac::CreateSkTypeface()
 {
-  if (!mTypeface) {
-    if (mCTFont) {
-      mTypeface = SkCreateTypefaceFromCTFont(mCTFont);
-    } else {
-      auto unscaledMac = static_cast<UnscaledFontMac*>(GetUnscaledFont().get());
-      bool dataFont = unscaledMac->IsDataFont();
-      CTFontRef fontFace =
-        CreateCTFontFromCGFontWithVariations(mFont, mSize, !dataFont);
-      mTypeface = SkCreateTypefaceFromCTFont(fontFace);
-      CFRelease(fontFace);
-    }
+  if (mCTFont) {
+    return SkCreateTypefaceFromCTFont(mCTFont);
+  } else {
+    auto unscaledMac = static_cast<UnscaledFontMac*>(GetUnscaledFont().get());
+    bool dataFont = unscaledMac->IsDataFont();
+    CTFontRef fontFace =
+      CreateCTFontFromCGFontWithVariations(mFont, mSize, !dataFont);
+    SkTypeface* typeface = SkCreateTypefaceFromCTFont(fontFace);
+    CFRelease(fontFace);
+    return typeface;
   }
-  return mTypeface;
 }
 #endif
 
 // private API here are the public options on OS X
 // CTFontCreatePathForGlyph
 // ATSUGlyphGetCubicPaths
 // we've used this in cairo sucessfully for some time.
 // Note: cairo dlsyms it. We could do that but maybe it's
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -29,17 +29,17 @@ public:
                 bool aOwnsFont = false,
                 const Color &aFontSmoothingBackgroundColor = Color(),
                 bool aUseFontSmoothing = true,
                 bool aApplySyntheticBold = false);
   ~ScaledFontMac();
 
   FontType GetType() const override { return FontType::MAC; }
 #ifdef USE_SKIA
-  SkTypeface* GetSkTypeface() override;
+  SkTypeface* CreateSkTypeface() override;
 #endif
   already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) override;
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   bool GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions>* aOutOptions,
                                 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
                                 std::vector<FontVariation>* aOutVariations) override;
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -117,22 +117,19 @@ UnscaledFontGDI::CreateScaledFont(Float 
 
 AntialiasMode
 ScaledFontWin::GetDefaultAAMode()
 {
   return GetSystemDefaultAAMode();
 }
 
 #ifdef USE_SKIA
-SkTypeface* ScaledFontWin::GetSkTypeface()
+SkTypeface* ScaledFontWin::CreateSkTypeface()
 {
-  if (!mTypeface) {
-    mTypeface = SkCreateTypefaceFromLOGFONT(mLogFont);
-  }
-  return mTypeface;
+  return SkCreateTypefaceFromLOGFONT(mLogFont);
 }
 #endif
 
 #ifdef USE_CAIRO_SCALED_FONT
 cairo_font_face_t*
 ScaledFontWin::GetCairoFontFace()
 {
   if (mLogFont.lfFaceName[0] == 0) {
--- a/gfx/2d/ScaledFontWin.h
+++ b/gfx/2d/ScaledFontWin.h
@@ -23,17 +23,17 @@ public:
 
   FontType GetType() const override { return FontType::GDI; }
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   AntialiasMode GetDefaultAAMode() override;
 
 #ifdef USE_SKIA
-  SkTypeface* GetSkTypeface() override;
+  SkTypeface* CreateSkTypeface() override;
 #endif
 
 protected:
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_font_face_t* GetCairoFontFace() override;
 #endif
 
 private:
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -1891,19 +1891,19 @@ public:
 private:
     BundledFontFileEnumerator() = delete;
     BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
     BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) = delete;
     virtual ~BundledFontFileEnumerator() {}
 
     RefPtr<IDWriteFactory>      mFactory;
 
-    nsCOMPtr<nsIFile>             mFontDir;
-    nsCOMPtr<nsISimpleEnumerator> mEntries;
-    nsCOMPtr<nsISupports>         mCurrent;
+    nsCOMPtr<nsIFile>                mFontDir;
+    nsCOMPtr<nsIDirectoryEnumerator> mEntries;
+    nsCOMPtr<nsISupports>            mCurrent;
 };
 
 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory *aFactory,
                                                      nsIFile        *aFontDir)
     : mFactory(aFactory)
     , mFontDir(aFontDir)
 {
     mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -597,16 +597,17 @@ WebRenderDebugPrefChangeCallback(const c
   GFX_WEBRENDER_DEBUG(".gpu-time-queries",   1 << 3)
   GFX_WEBRENDER_DEBUG(".gpu-sample-queries", 1 << 4)
   GFX_WEBRENDER_DEBUG(".disable-batching",   1 << 5)
   GFX_WEBRENDER_DEBUG(".epochs",             1 << 6)
   GFX_WEBRENDER_DEBUG(".compact-profiler",   1 << 7)
   GFX_WEBRENDER_DEBUG(".echo-driver-messages", 1 << 8)
   GFX_WEBRENDER_DEBUG(".new-frame-indicator", 1 << 9)
   GFX_WEBRENDER_DEBUG(".new-scene-indicator", 1 << 10)
+  GFX_WEBRENDER_DEBUG(".show-overdraw", 1 << 11)
 #undef GFX_WEBRENDER_DEBUG
 
   gfx::gfxVars::SetWebRenderDebugFlags(flags);
 }
 
 
 #if defined(USE_SKIA)
 static uint32_t GetSkiaGlyphCacheSize()
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -2,24 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
 use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
 use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, LayoutTransform};
 use border::{ensure_no_corner_overlap};
 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
-use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex};
+use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
 use ellipse::Ellipse;
 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
 use gpu_types::BoxShadowStretchMode;
 use prim_store::{BrushClipMaskKind, ClipData, ImageMaskData};
 use render_task::to_cache_size;
 use resource_cache::{ImageRequest, ResourceCache};
-use spatial_node::SpatialNode;
 use std::u32;
 use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers};
 
 /*
 
  Module Overview
 
  There are a number of data structures involved in the clip module:
@@ -444,28 +443,29 @@ impl ClipStore {
     // The main interface other code uses. Given a local primitive, positioning
     // information, and a clip chain id, build an optimized clip chain instance.
     pub fn build_clip_chain_instance(
         &mut self,
         clip_chain_id: ClipChainId,
         local_prim_rect: LayoutRect,
         local_prim_clip_rect: LayoutRect,
         spatial_node_index: SpatialNodeIndex,
-        spatial_nodes: &[SpatialNode],
+        clip_scroll_tree: &ClipScrollTree,
         gpu_cache: &mut GpuCache,
         resource_cache: &mut ResourceCache,
         device_pixel_scale: DevicePixelScale,
     ) -> Option<ClipChainInstance> {
         // Trivial check to see if the primitive is clipped out by the
         // local clip rect of the primitive itself.
         let mut local_bounding_rect = match local_prim_rect.intersection(&local_prim_clip_rect) {
             Some(rect) => rect,
             None => return None,
         };
         let mut current_local_clip_rect = local_prim_clip_rect;
+        let spatial_nodes = &clip_scroll_tree.spatial_nodes;
 
         // Walk the clip chain to build local rects, and collect the
         // smallest possible local clip area.
 
         self.clip_node_info.clear();
         let ref_spatial_node = &spatial_nodes[spatial_node_index.0];
         let mut current_clip_chain_id = clip_chain_id;
 
@@ -484,36 +484,26 @@ impl ClipStore {
                 // systems of the primitive and clip node.
                 let conversion = if spatial_node_index == clip_node.spatial_node_index {
                     Some(ClipSpaceConversion::Local)
                 } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id {
                     let offset = clip_spatial_node.coordinate_system_relative_offset -
                                  ref_spatial_node.coordinate_system_relative_offset;
                     Some(ClipSpaceConversion::Offset(offset))
                 } else {
-                    // TODO(gw): We still have issues with clip nodes and primitives where
-                    //           there is a perspective transform. We intend to fix these
-                    //           cases as a follow up.
-                    let relative_transform = ref_spatial_node
-                        .world_content_transform
-                        .to_transform()
-                        .inverse()
-                        .map(|inv| {
-                            inv.pre_mul(&clip_spatial_node.world_content_transform.to_transform())
-                        });
-                    let inv_relative_transform = relative_transform
-                        .and_then(|rt| rt.inverse());
-                    match (relative_transform, inv_relative_transform) {
-                        (Some(relative_transform), Some(inv_relative_transform)) => {
-                            Some(ClipSpaceConversion::Transform(relative_transform, inv_relative_transform))
-                        }
-                        _ => {
-                            None
-                        }
-                    }
+                    let xf = clip_scroll_tree.get_relative_transform(
+                        clip_node.spatial_node_index,
+                        spatial_node_index,
+                    );
+
+                    xf.and_then(|xf| {
+                        xf.inverse().map(|inv| {
+                            ClipSpaceConversion::Transform(xf, inv)
+                        })
+                    })
                 };
 
                 // If we can convert spaces, try to reduce the size of the region
                 // requested, and cache the conversion information for the next step.
                 if let Some(conversion) = conversion {
                     if let Some(clip_rect) = clip_node.item.get_local_clip_rect() {
                         let clip_rect = conversion.transform_to_prim_space(&clip_rect);
                         if let Some(clip_rect) = clip_rect {
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -5,16 +5,17 @@
 use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D, LayoutVector3D};
 use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation};
 use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint};
 use clip::{ClipStore};
 use gpu_types::TransformPalette;
 use internal_types::{FastHashMap, FastHashSet};
 use print_tree::{PrintTree, PrintTreePrinter};
 use scene::SceneProperties;
+use smallvec::SmallVec;
 use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo};
 use util::LayoutToWorldFastTransform;
 
 pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
 
 /// An id that identifies coordinate systems in the ClipScrollTree. Each
 /// coordinate system has an id and those ids will be shared when the coordinates
 /// system are the same or are in the same axis-aligned space. This allows
@@ -107,45 +108,64 @@ impl ClipScrollTree {
     }
 
     /// Calculate the relative transform from `ref_node_index`
     /// to `target_node_index`. It's assumed that `ref_node_index`
     /// is a parent of `target_node_index`. This method will
     /// panic if that invariant isn't true!
     pub fn get_relative_transform(
         &self,
-        ref_node_index: SpatialNodeIndex,
-        target_node_index: SpatialNodeIndex,
-    ) -> LayoutTransform {
-        let ref_node = &self.spatial_nodes[ref_node_index.0];
-        let target_node = &self.spatial_nodes[target_node_index.0];
+        from_node_index: SpatialNodeIndex,
+        to_node_index: SpatialNodeIndex,
+    ) -> Option<LayoutTransform> {
+        let from_node = &self.spatial_nodes[from_node_index.0];
+        let to_node = &self.spatial_nodes[to_node_index.0];
+
+        let (child, parent, inverse) = if from_node_index.0 > to_node_index.0 {
+            (from_node, to_node, false)
+        } else {
+            (to_node, from_node, true)
+        };
 
-        let mut offset = LayoutVector3D::new(
-            target_node.coordinate_system_relative_offset.x,
-            target_node.coordinate_system_relative_offset.y,
+        let mut coordinate_system_id = child.coordinate_system_id;
+        let mut nodes: SmallVec<[_; 16]> = SmallVec::new();
+
+        while coordinate_system_id != parent.coordinate_system_id {
+            nodes.push(coordinate_system_id);
+            let coord_system = &self.coord_systems[coordinate_system_id.0 as usize];
+            coordinate_system_id = coord_system.parent.expect("invalid parent!");
+        }
+
+        nodes.reverse();
+
+        let mut transform = LayoutTransform::create_translation(
+            -parent.coordinate_system_relative_offset.x,
+            -parent.coordinate_system_relative_offset.y,
             0.0,
         );
-        let mut transform = LayoutTransform::identity();
 
-        // Walk up the tree of coordinate systems, accumulating each
-        // relative transform.
-        let mut current_coordinate_system_id = target_node.coordinate_system_id;
-        while current_coordinate_system_id != ref_node.coordinate_system_id {
-            let coord_system = &self.coord_systems[current_coordinate_system_id.0 as usize];
-
-            let relative_transform = coord_system
-                .transform
-                .post_translate(offset);
-            transform = transform.pre_mul(&relative_transform);
-
-            offset = coord_system.offset;
-            current_coordinate_system_id = coord_system.parent.expect("invalid parent!");
+        for node in nodes {
+            let coord_system = &self.coord_systems[node.0 as usize];
+            transform = transform.pre_translate(coord_system.offset)
+                                 .pre_mul(&coord_system.transform);
         }
 
-        transform
+        let transform = transform.post_translate(
+            LayoutVector3D::new(
+                child.coordinate_system_relative_offset.x,
+                child.coordinate_system_relative_offset.y,
+                0.0,
+            )
+        );
+
+        if inverse {
+            transform.inverse()
+        } else {
+            Some(transform)
+        }
     }
 
     /// The root reference frame, which is the true root of the ClipScrollTree. Initially
     /// this ID is not valid, which is indicated by ```spatial_nodes``` being empty.
     pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.spatial_nodes.is_empty());
         ROOT_REFERENCE_FRAME_INDEX
@@ -437,8 +457,224 @@ impl ClipScrollTree {
     }
 
     pub fn print_with<T: PrintTreePrinter>(&self, clip_store: &ClipStore, pt: &mut T) {
         if !self.spatial_nodes.is_empty() {
             self.print_node(self.root_reference_frame_index(), pt, clip_store);
         }
     }
 }
+
+#[cfg(test)]
+fn add_reference_frame(
+    cst: &mut ClipScrollTree,
+    parent: Option<SpatialNodeIndex>,
+    transform: LayoutTransform,
+    origin_in_parent_reference_frame: LayoutVector2D,
+) -> SpatialNodeIndex {
+    cst.add_reference_frame(
+        parent,
+        Some(PropertyBinding::Value(transform)),
+        None,
+        origin_in_parent_reference_frame,
+        PipelineId::dummy(),
+    )
+}
+
+#[cfg(test)]
+fn test_pt(
+    px: f32,
+    py: f32,
+    cst: &ClipScrollTree,
+    from: SpatialNodeIndex,
+    to: SpatialNodeIndex,
+    expected_x: f32,
+    expected_y: f32,
+) {
+    use euclid::approxeq::ApproxEq;
+    const EPSILON: f32 = 0.0001;
+
+    let p = LayoutPoint::new(px, py);
+    let m = cst.get_relative_transform(from, to).unwrap();
+    let pt = m.transform_point2d(&p).unwrap();
+    assert!(pt.x.approx_eq_eps(&expected_x, &EPSILON) &&
+            pt.y.approx_eq_eps(&expected_y, &EPSILON),
+            "p: {:?} -> {:?}\nm={:?}",
+            p, pt, m,
+            );
+}
+
+#[test]
+fn test_cst_simple_translation() {
+    // Basic translations only
+
+    let mut cst = ClipScrollTree::new();
+
+    let root = add_reference_frame(
+        &mut cst,
+        None,
+        LayoutTransform::identity(),
+        LayoutVector2D::zero(),
+    );
+
+    let child1 = add_reference_frame(
+        &mut cst,
+        Some(root),
+        LayoutTransform::create_translation(100.0, 0.0, 0.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child2 = add_reference_frame(
+        &mut cst,
+        Some(child1),
+        LayoutTransform::create_translation(0.0, 50.0, 0.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child3 = add_reference_frame(
+        &mut cst,
+        Some(child2),
+        LayoutTransform::create_translation(200.0, 200.0, 0.0),
+        LayoutVector2D::zero(),
+    );
+
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+
+    test_pt(100.0, 100.0, &cst, child1, root, 200.0, 100.0);
+    test_pt(100.0, 100.0, &cst, root, child1, 0.0, 100.0);
+    test_pt(100.0, 100.0, &cst, child2, root, 200.0, 150.0);
+    test_pt(100.0, 100.0, &cst, root, child2, 0.0, 50.0);
+    test_pt(100.0, 100.0, &cst, child2, child1, 100.0, 150.0);
+    test_pt(100.0, 100.0, &cst, child1, child2, 100.0, 50.0);
+    test_pt(100.0, 100.0, &cst, child3, root, 400.0, 350.0);
+}
+
+#[test]
+fn test_cst_simple_scale() {
+    // Basic scale only
+
+    let mut cst = ClipScrollTree::new();
+
+    let root = add_reference_frame(
+        &mut cst,
+        None,
+        LayoutTransform::identity(),
+        LayoutVector2D::zero(),
+    );
+
+    let child1 = add_reference_frame(
+        &mut cst,
+        Some(root),
+        LayoutTransform::create_scale(4.0, 1.0, 1.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child2 = add_reference_frame(
+        &mut cst,
+        Some(child1),
+        LayoutTransform::create_scale(1.0, 2.0, 1.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child3 = add_reference_frame(
+        &mut cst,
+        Some(child2),
+        LayoutTransform::create_scale(2.0, 2.0, 1.0),
+        LayoutVector2D::zero(),
+    );
+
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+
+    test_pt(100.0, 100.0, &cst, child1, root, 400.0, 100.0);
+    test_pt(100.0, 100.0, &cst, root, child1, 25.0, 100.0);
+    test_pt(100.0, 100.0, &cst, child2, root, 400.0, 200.0);
+    test_pt(100.0, 100.0, &cst, root, child2, 25.0, 50.0);
+    test_pt(100.0, 100.0, &cst, child3, root, 800.0, 400.0);
+    test_pt(100.0, 100.0, &cst, child2, child1, 100.0, 200.0);
+    test_pt(100.0, 100.0, &cst, child1, child2, 100.0, 50.0);
+    test_pt(100.0, 100.0, &cst, child3, child1, 200.0, 400.0);
+    test_pt(100.0, 100.0, &cst, child1, child3, 50.0, 25.0);
+}
+
+#[test]
+fn test_cst_scale_translation() {
+    // Scale + translation
+
+    let mut cst = ClipScrollTree::new();
+
+    let root = add_reference_frame(
+        &mut cst,
+        None,
+        LayoutTransform::identity(),
+        LayoutVector2D::zero(),
+    );
+
+    let child1 = add_reference_frame(
+        &mut cst,
+        Some(root),
+        LayoutTransform::create_translation(100.0, 50.0, 0.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child2 = add_reference_frame(
+        &mut cst,
+        Some(child1),
+        LayoutTransform::create_scale(2.0, 4.0, 1.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child3 = add_reference_frame(
+        &mut cst,
+        Some(child2),
+        LayoutTransform::create_translation(200.0, -100.0, 0.0),
+        LayoutVector2D::zero(),
+    );
+
+    let child4 = add_reference_frame(
+        &mut cst,
+        Some(child3),
+        LayoutTransform::create_scale(3.0, 2.0, 1.0),
+        LayoutVector2D::zero(),
+    );
+
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+
+    test_pt(100.0, 100.0, &cst, child1, root, 200.0, 150.0);
+    test_pt(100.0, 100.0, &cst, child2, root, 300.0, 450.0);
+    test_pt(100.0, 100.0, &cst, root, child1, 0.0, 50.0);
+    test_pt(100.0, 100.0, &cst, root, child2, 0.0, 12.5);
+    test_pt(100.0, 100.0, &cst, child4, root, 1100.0, 450.0);
+    test_pt(1100.0, 450.0, &cst, root, child4, 100.0, 100.0);
+
+    test_pt(0.0, 0.0, &cst, child4, child1, 400.0, -400.0);
+    test_pt(100.0, 100.0, &cst, child4, child1, 1000.0, 400.0);
+    test_pt(100.0, 100.0, &cst, child2, child1, 200.0, 400.0);
+    test_pt(200.0, 400.0, &cst, child1, child2, 100.0, 100.0);
+
+    test_pt(100.0, 100.0, &cst, child3, child1, 400.0, 300.0);
+    test_pt(400.0, 300.0, &cst, child1, child3, 100.0, 100.0);
+}
+
+#[test]
+fn test_cst_translation_rotate() {
+    // Rotation + translation
+    use euclid::Angle;
+
+    let mut cst = ClipScrollTree::new();
+
+    let root = add_reference_frame(
+        &mut cst,
+        None,
+        LayoutTransform::identity(),
+        LayoutVector2D::zero(),
+    );
+
+    let child1 = add_reference_frame(
+        &mut cst,
+        Some(root),
+        LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::degrees(90.0)),
+        LayoutVector2D::zero(),
+    );
+
+    cst.update_tree(WorldPoint::zero(), &SceneProperties::new());
+
+    test_pt(100.0, 0.0, &cst, child1, root, 0.0, -100.0);
+}
--- a/gfx/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/webrender/src/glyph_rasterizer/mod.rs
@@ -10,17 +10,17 @@ use api::{LayoutPoint, LayoutToWorldTran
 use app_units::Au;
 use euclid::approxeq::ApproxEq;
 use internal_types::ResourceCacheError;
 use platform::font::FontContext;
 use rayon::ThreadPool;
 use std::cmp;
 use std::hash::{Hash, Hasher};
 use std::mem;
-use std::sync::{Arc, Mutex, MutexGuard};
+use std::sync::{Arc, Condvar, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 
 #[cfg(feature = "pathfinder")]
 mod pathfinder;
 #[cfg(feature = "pathfinder")]
 use self::pathfinder::create_pathfinder_font_context;
 #[cfg(feature = "pathfinder")]
 pub use self::pathfinder::{ThreadSafePathfinderFontContext, NativeFontHandleWrapper};
@@ -446,16 +446,18 @@ pub struct FontContexts {
     // This worker should be accessed by threads that don't belong to the thread pool
     // (in theory that's only the render backend thread so no contention expected either).
     shared_context: Mutex<FontContext>,
     #[cfg(feature = "pathfinder")]
     pathfinder_context: Box<ThreadSafePathfinderFontContext>,
     // Stored here as a convenience to get the current thread index.
     #[allow(dead_code)]
     workers: Arc<ThreadPool>,
+    locked_mutex: Mutex<bool>,
+    locked_cond: Condvar,
 }
 
 impl FontContexts {
 
     /// Get access to any particular font context.
     ///
     /// The id is ```Some(i)``` where i is an index between 0 and num_worker_contexts
     /// for font contexts associated to the thread pool, and None for the shared
@@ -473,16 +475,56 @@ impl FontContexts {
     }
 
     // number of contexts associated to workers
     pub fn num_worker_contexts(&self) -> usize {
         self.worker_contexts.len()
     }
 }
 
+pub trait ForEach<T> {
+    fn for_each<F: Fn(MutexGuard<T>) + Send + 'static>(&self, f: F);
+}
+
+impl ForEach<FontContext> for Arc<FontContexts> {
+    fn for_each<F: Fn(MutexGuard<FontContext>) + Send + 'static>(&self, f: F) {
+        // Reset the locked condition.
+        let mut locked = self.locked_mutex.lock().unwrap();
+        *locked = false;
+
+        // Arc that can be safely moved into a spawn closure.
+        let font_contexts = self.clone();
+        // Spawn a new thread on which to run the for-each off the main thread.
+        self.workers.spawn(move || {
+            // Lock the shared and worker contexts up front.
+            let mut locks = Vec::with_capacity(font_contexts.num_worker_contexts() + 1);
+            locks.push(font_contexts.lock_shared_context());
+            for i in 0 .. font_contexts.num_worker_contexts() {
+                locks.push(font_contexts.lock_context(Some(i)));
+            }
+
+            // Signal the locked condition now that all contexts are locked.
+            *font_contexts.locked_mutex.lock().unwrap() = true;
+            font_contexts.locked_cond.notify_all();
+
+            // Now that everything is locked, proceed to processing each locked context.
+            for context in locks {
+                f(context);
+            }
+        });
+
+        // Wait for locked condition before resuming. Safe to proceed thereafter
+        // since any other thread that needs to use a FontContext will try to lock
+        // it first.
+        while !*locked {
+            locked = self.locked_cond.wait(locked).unwrap();
+        }
+    }
+}
+
 pub struct GlyphRasterizer {
     #[allow(dead_code)]
     workers: Arc<ThreadPool>,
     font_contexts: Arc<FontContexts>,
 
     // Maintain a set of glyphs that have been requested this
     // frame. This ensures the glyph thread won't rasterize
     // the same glyph more than once in a frame. This is required
@@ -522,51 +564,38 @@ impl GlyphRasterizer {
         }
 
         let font_context = FontContexts {
                 worker_contexts: contexts,
                 shared_context: Mutex::new(shared_context),
                 #[cfg(feature = "pathfinder")]
                 pathfinder_context: create_pathfinder_font_context()?,
                 workers: Arc::clone(&workers),
+                locked_mutex: Mutex::new(false),
+                locked_cond: Condvar::new(),
         };
 
         Ok(GlyphRasterizer {
             font_contexts: Arc::new(font_context),
             pending_glyphs: 0,
             glyph_rx,
             glyph_tx,
             workers,
             fonts_to_remove: Vec::new(),
             next_gpu_glyph_cache_key: GpuGlyphCacheKey(0),
         })
     }
 
     pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
-        let font_contexts = Arc::clone(&self.font_contexts);
-        // It's important to synchronously add the font for the shared context because
-        // we use it to check that fonts have been properly added when requesting glyphs.
-        font_contexts
-            .lock_shared_context()
-            .add_font(&font_key, &template);
-
-        // TODO: this locks each font context while adding the font data, probably not a big deal,
-        // but if there is contention on this lock we could easily have a queue of per-context
-        // operations to add and delete fonts, and have these queues lazily processed by each worker
-        // before rendering a glyph.
-        // We can also move this into a worker to free up some cycles in the calling (render backend)
-        // thread.
-        for i in 0 .. font_contexts.num_worker_contexts() {
-            font_contexts
-                .lock_context(Some(i))
-                .add_font(&font_key, &template);
-        }
-
         #[cfg(feature = "pathfinder")]
         self.add_font_to_pathfinder(&font_key, &template);
+
+        self.font_contexts.for_each(move |mut context| {
+            context.add_font(&font_key, &template);
+        });
     }
 
     pub fn delete_font(&mut self, font_key: FontKey) {
         self.fonts_to_remove.push(font_key);
     }
 
     pub fn prepare_font(&self, font: &mut FontInstance) {
         FontContext::prepare_font(font);
@@ -594,28 +623,20 @@ impl GlyphRasterizer {
             .get_glyph_index(font_key, ch)
     }
 
     fn remove_dead_fonts(&mut self) {
         if self.fonts_to_remove.is_empty() {
             return
         }
 
-        let font_contexts = Arc::clone(&self.font_contexts);
         let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new());
-
-        self.workers.spawn(move || {
+        self.font_contexts.for_each(move |mut context| {
             for font_key in &fonts_to_remove {
-                font_contexts.lock_shared_context().delete_font(font_key);
-            }
-            for i in 0 .. font_contexts.num_worker_contexts() {
-                let mut context = font_contexts.lock_context(Some(i));
-                for font_key in &fonts_to_remove {
-                    context.delete_font(font_key);
-                }
+                context.delete_font(font_key);
             }
         });
     }
 
     #[cfg(feature = "replay")]
     pub fn reset(&mut self) {
         //TODO: any signals need to be sent to the workers?
         self.pending_glyphs = 0;
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -157,19 +157,19 @@ impl LocalRectBuilder {
             self.kind = if self.ref_spatial_node_index == target_node_index {
                 CoordinateSpaceMapping::Local
             } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
                 let offset = target_spatial_node.coordinate_system_relative_offset -
                              ref_spatial_node.coordinate_system_relative_offset;
                 CoordinateSpaceMapping::Offset(offset)
             } else {
                 let transform = clip_scroll_tree.get_relative_transform(
+                    target_node_index,
                     self.ref_spatial_node_index,
-                    target_node_index,
-                );
+                ).expect("bug: should have already been culled");
                 CoordinateSpaceMapping::Transform(transform)
             };
         }
     }
 
     pub fn accumulate(&mut self, rect: &LayoutRect) {
         match self.kind {
             CoordinateSpaceMapping::Local => {
@@ -1615,17 +1615,17 @@ impl PrimitiveStore {
 
         let clip_chain = frame_state
             .clip_store
             .build_clip_chain_instance(
                 prim.metadata.clip_chain_id,
                 local_rect,
                 prim.metadata.local_clip_rect,
                 prim_context.spatial_node_index,
-                &frame_context.clip_scroll_tree.spatial_nodes,
+                &frame_context.clip_scroll_tree,
                 frame_state.gpu_cache,
                 frame_state.resource_cache,
                 frame_context.device_pixel_scale,
             );
 
         let clip_chain = match clip_chain {
             Some(clip_chain) => clip_chain,
             None => {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-e70bae07664def86aefd11c86dac818ab7ea64ea
+93997662842b6d8bafbdb3dde79009c930db66ca
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.6.0 */
+/* Generated with cbindgen:0.6.2 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
 
@@ -235,19 +235,19 @@ struct NormalizedCoordinates;
 struct Renderer;
 
 // Offset in number of tiles.
 struct Tiles;
 
 // A Transaction is a group of commands to apply atomically to a document.
 //
 // This mechanism ensures that:
-//  - no other message can be interleaved between two commands that need to be applied together.
-//  - no redundant work is performed if two commands in the same transaction cause the scene or
-//    the frame to be rebuilt.
+// - no other message can be interleaved between two commands that need to be applied together.
+// - no redundant work is performed if two commands in the same transaction cause the scene or
+// the frame to be rebuilt.
 struct Transaction;
 
 // The default unit.
 struct UnknownUnit;
 
 template<typename T>
 struct Vec;
 
@@ -710,18 +710,17 @@ union GlyphRasterSpace {
     Sentinel /* this must be last for serialization purposes. */
   };
 
   struct Local_Body {
     Tag tag;
     float _0;
 
     bool operator==(const Local_Body& aOther) const {
-      return tag == aOther.tag &&
-             _0 == aOther._0;
+      return _0 == aOther._0;
     }
   };
 
   struct {
     Tag tag;
   };
   Local_Body local;
 
@@ -740,16 +739,26 @@ union GlyphRasterSpace {
 
   bool IsLocal() const {
     return tag == Tag::Local;
   }
 
   bool IsScreen() const {
     return tag == Tag::Screen;
   }
+
+  bool operator==(const GlyphRasterSpace& aOther) const {
+    if (tag != aOther.tag) {
+      return false;
+    }
+    switch (tag) {
+      case Tag::Local: return local == aOther.local;
+      default: return true;
+    }
+  }
 };
 
 struct FontInstanceKey {
   IdNamespace mNamespace;
   uint32_t mHandle;
 
   bool operator==(const FontInstanceKey& aOther) const {
     return mNamespace == aOther.mNamespace &&
@@ -967,23 +976,16 @@ struct WrOpacityProperty {
   bool operator==(const WrOpacityProperty& aOther) const {
     return id == aOther.id &&
            opacity == aOther.opacity;
   }
 };
 
 extern "C" {
 
-/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
- * To generate this file:
- *   1. Get the latest cbindgen using `cargo install --force cbindgen`
- *      a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
- *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
- */
-
 extern void AddFontData(WrFontKey aKey,
                         const uint8_t *aData,
                         uintptr_t aSize,
                         uint32_t aIndex,
                         const ArcVecU8 *aVec);
 
 extern void AddNativeFontHandle(WrFontKey aKey,
                                 void *aHandle,
@@ -1291,17 +1293,17 @@ void wr_dp_push_image(WrState *aState,
                       LayoutRect aBounds,
                       LayoutRect aClip,
                       bool aIsBackfaceVisible,
                       LayoutSize aStretchSize,
                       LayoutSize aTileSpacing,
                       ImageRendering aImageRendering,
                       WrImageKey aKey,
                       bool aPremultipliedAlpha,
-                      ColorF color)
+                      ColorF aColor)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_line(WrState *aState,
                      const LayoutRect *aClip,
                      bool aIsBackfaceVisible,
                      const LayoutRect *aBounds,
                      float aWavyLineThickness,
@@ -1758,15 +1760,8 @@ bool wr_window_new(WrWindowId aWindowId,
                    Renderer **aOutRenderer,
                    uint32_t *aOutMaxTextureSize)
 WR_FUNC;
 
 } // extern "C"
 
 } // namespace wr
 } // namespace mozilla
-
-/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
- * To generate this file:
- *   1. Get the latest cbindgen using `cargo install --force cbindgen`
- *      a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
- *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
- */
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -21,16 +21,17 @@
 #include "nsIObserverService.h"
 #include "nsCOMArray.h"
 #include "nsTextFormatter.h"
 #include "nsIErrorService.h"
 #include "nsICategoryManager.h"
 #include "nsContentUtils.h"
 #include "nsPersistentProperties.h"
 #include "nsQueryObject.h"
+#include "nsSimpleEnumerator.h"
 #include "nsStringStream.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/URLPreloader.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ipc/SharedStringMap.h"
 
@@ -256,36 +257,39 @@ private:
 
   Maybe<FileDescriptor> mMapFile;
   size_t mMapSize;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(SharedStringBundle, SHAREDSTRINGBUNDLE_IID)
 
 
-class StringMapEnumerator final : public nsISimpleEnumerator
+class StringMapEnumerator final : public nsSimpleEnumerator
 {
-  NS_DECL_ISUPPORTS
+public:
   NS_DECL_NSISIMPLEENUMERATOR
 
   explicit StringMapEnumerator(SharedStringMap* aStringMap)
     : mStringMap(aStringMap)
   {}
 
+  const nsID& DefaultInterface() override
+  {
+    return NS_GET_IID(nsIPropertyElement);
+  }
+
 protected:
   virtual ~StringMapEnumerator() = default;
 
 private:
   RefPtr<SharedStringMap> mStringMap;
 
   uint32_t mIndex = 0;
 };
 
-NS_IMPL_ISUPPORTS(StringMapEnumerator, nsISimpleEnumerator)
-
 template <typename T, typename... Args>
 already_AddRefed<T>
 MakeBundle(Args... args)
 {
   return nsStringBundleBase::Create<T>(args...);
 }
 
 template <typename T, typename... Args>
--- a/js/src/jit-test/tests/basic/bug827104.js
+++ b/js/src/jit-test/tests/basic/bug827104.js
@@ -5,9 +5,9 @@ function f() {
     }
     a[i][0] = 0;
 }
 
 var e;
 try {
     f();
 } catch (error) {e = error;}
-assertEq(e.toString(), 'TypeError: a[i] is undefined');
\ No newline at end of file
+assertEq(e.toString(), `TypeError: a[i] is undefined, can't access property 0 of it`);
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -13,17 +13,17 @@ function check_one(expected, f, err) {
         assertEq(s.slice(11, -err.length), expected);
     }
     if (!failed)
         throw new Error("didn't fail");
 }
 ieval = eval;
 function check(expr, expected=expr, testStrict=true) {
     var end, err;
-    for ([end, err] of [[".random_prop", " is undefined"], ["()", " is not a function"]]) {
+    for ([end, err] of [[".random_prop", ` is undefined, can't access property \"random_prop" of it`], ["()", " is not a function"]]) {
         var statement = "o = {};" + expr + end, f;
         var cases = [
             // Global scope
             function () {
                 ieval("var o, undef;\n" + statement);
             },
             // Function scope
             Function("o", "undef", statement),
@@ -97,17 +97,17 @@ check("o[(~(o + 1))]");
 check("o[(+ (o + 1))]");
 check("o[(- (o + 1))]");
 
 // A few one off tests
 check_one("6", (function () { 6() }), " is not a function");
 check_one("4", (function() { (4||eval)(); }), " is not a function");
 check_one("0", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
 check_one("[...][Symbol.iterator](...).next(...).value",
-          function () { ieval("{ let x; var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
+          function () { ieval("{ let x; var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined, can't access property Symbol.iterator of it");
 check_one("(void 1)", function() { (void 1)(); }, " is not a function");
 check_one("(void o[1])", function() { var o = []; (void o[1])() }, " is not a function");
 
 check_one("(typeof 1)", function() { (typeof 1)(); }, " is not a function");
 check_one("(typeof o[1])", function() { var o = []; (typeof o[1])() }, " is not a function");
 
 check_one("(delete foo)",
           function() { (delete foo)(); },
--- a/js/src/jit-test/tests/basic/iterable-error-messages.js
+++ b/js/src/jit-test/tests/basic/iterable-error-messages.js
@@ -10,30 +10,30 @@ function assertThrowsMsg(f, msg) {
 
 // For-of
 function testForOf(val) {
     for (var x of val) {}
 }
 for (v of [{}, Math, new Proxy({}, {})]) {
     assertThrowsMsg(() => testForOf(v), "val is not iterable");
 }
-assertThrowsMsg(() => testForOf(null), "val is null");
+assertThrowsMsg(() => testForOf(null), "val is null, can't access property Symbol.iterator of it");
 assertThrowsMsg(() => { for (var x of () => 1) {}}, "() => 1 is not iterable");
 
 // Destructuring
 function testDestr(val) {
     var [a, b] = val;
 }
 for (v of [{}, Math, new Proxy({}, {})]) {
     assertThrowsMsg(() => testDestr(v), "val is not iterable");
 }
-assertThrowsMsg(() => testDestr(null), "val is null");
+assertThrowsMsg(() => testDestr(null), "val is null, can't access property Symbol.iterator of it");
 assertThrowsMsg(() => { [a, b] = () => 1; }, "() => 1 is not iterable");
 
 // Spread
 function testSpread(val) {
     [...val];
 }
 for (v of [{}, Math, new Proxy({}, {})]) {
     assertThrowsMsg(() => testSpread(v), "val is not iterable");
 }
-assertThrowsMsg(() => testSpread(null), "val is null");
+assertThrowsMsg(() => testSpread(null), "val is null, can't access property Symbol.iterator of it");
 assertThrowsMsg(() => { [...() => 1]; }, "() => 1 is not iterable");
--- a/js/src/jit-test/tests/basic/testBug604210.js
+++ b/js/src/jit-test/tests/basic/testBug604210.js
@@ -1,11 +1,11 @@
 function f() {
     var msg = '';
     try {
         var x = undefined;
         print(x.foo);
     } catch (e) {
         msg = '' + e;
     }
-    assertEq(msg, "TypeError: x is undefined");
+    assertEq(msg, `TypeError: x is undefined, can't access property "foo" of it`);
 }
 f();
--- a/js/src/jit-test/tests/debug/bug1275001.js
+++ b/js/src/jit-test/tests/debug/bug1275001.js
@@ -12,17 +12,17 @@ function check_one(expected, f, err) {
     } catch (ex) {
         s = ex.toString()
         assertEq(s.slice(11, -err.length), expected)
     }
 }
 ieval = eval
 function check(expr, expected = expr) {
     var end, err
-    for ([end, err] of[[".random_prop", " is undefined" ]]) 
+    for ([end, err] of[[".random_prop", ` is undefined, can't access property \"random_prop" of it` ]]) 
          statement = "o = {};" + expr + end;
          cases = [
             function() { return ieval("var undef;" + statement); },
             Function(statement)
         ]
         for (f of cases) 
             check_one(expected, f, err)
 }
--- a/js/src/jit-test/tests/ion/bug913749.js
+++ b/js/src/jit-test/tests/ion/bug913749.js
@@ -10,12 +10,12 @@ this.toSource();
 
 y = undefined;
 
 for (var i = 0; i < 3; i++) {
     try {
 	x.toString();
 	assertEq(0, 1);
     } catch (e) {
-	assertEq(e.message === "y is undefined" ||
+	assertEq(e.message === `y is undefined, can't access property "length" of it` ||
 		 e.message === "undefined has no properties", true);
     }
 }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1875,17 +1875,17 @@ DoSetElemFallback(JSContext* cx, Baselin
 
     MOZ_ASSERT(op == JSOP_SETELEM ||
                op == JSOP_STRICTSETELEM ||
                op == JSOP_INITELEM ||
                op == JSOP_INITHIDDENELEM ||
                op == JSOP_INITELEM_ARRAY ||
                op == JSOP_INITELEM_INC);
 
-    RootedObject obj(cx, ToObjectFromStack(cx, objv));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, objv, index));
     if (!obj)
         return false;
 
     RootedShape oldShape(cx, obj->maybeShape());
     RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
     if (!oldGroup)
         return false;
 
@@ -2764,17 +2764,17 @@ DoSetPropFallback(JSContext* cx, Baselin
 
     RootedPropertyName name(cx);
     if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL)
         name = EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc);
     else
         name = script->getName(pc);
     RootedId id(cx, NameToId(name));
 
-    RootedObject obj(cx, ToObjectFromStack(cx, lhs));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, lhs, id));
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->maybeShape());
     RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
     if (!oldGroup)
         return false;
 
     if (obj->is<UnboxedPlainObject>()) {
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -71,23 +71,52 @@ void
 OutOfLineBailout::accept(CodeGeneratorARM64* codegen)
 {
     codegen->visitOutOfLineBailout(this);
 }
 
 void
 CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test)
 {
-    MOZ_CRASH("visitTestIAndBranch");
+    Register input = ToRegister(test->input());
+    MBasicBlock* mirTrue = test->ifTrue();
+    MBasicBlock* mirFalse = test->ifFalse();
+
+    masm.test32(input, input);
+
+    // Jump to the True block if NonZero.
+    // Jump to the False block if Zero.
+    if (isNextBlock(mirFalse->lir())) {
+        jumpToBlock(mirTrue, Assembler::NonZero);
+    } else {
+        jumpToBlock(mirFalse, Assembler::Zero);
+        if (!isNextBlock(mirTrue->lir()))
+            jumpToBlock(mirTrue);
+    }
 }
 
 void
 CodeGenerator::visitCompare(LCompare* comp)
 {
-    MOZ_CRASH("visitCompare");
+    const MCompare* mir = comp->mir();
+    const MCompare::CompareType type = mir->compareType();
+    const Assembler::Condition cond = JSOpToCondition(type, comp->jsop());
+    const Register leftreg = ToRegister(comp->getOperand(0));
+    const LAllocation* right = comp->getOperand(1);
+    const Register defreg = ToRegister(comp->getDef(0));
+
+    if (type == MCompare::Compare_Object || type == MCompare::Compare_Symbol) {
+        masm.cmpPtrSet(cond, leftreg, ToRegister(right), defreg);
+        return;
+    }
+
+    if (right->isConstant())
+        masm.cmp32Set(cond, leftreg, Imm32(ToInt32(right)), defreg);
+    else
+        masm.cmp32Set(cond, leftreg, ToRegister(right), defreg);
 }
 
 void
 CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp)
 {
     const MCompare* mir = comp->cmpMir();
     const MCompare::CompareType type = mir->compareType();
     const LAllocation* left = comp->left();
@@ -204,22 +233,22 @@ toWRegister(const T* a)
 // FIXME: Uh, is this a static function? It looks like it is...
 template <typename T>
 ARMRegister
 toXRegister(const T* a)
 {
     return ARMRegister(ToRegister(a), 64);
 }
 
-js::jit::Operand
+Operand
 toWOperand(const LAllocation* a)
 {
     if (a->isConstant())
-        return js::jit::Operand(ToInt32(a));
-    return js::jit::Operand(toWRegister(a));
+        return Operand(ToInt32(a));
+    return Operand(toWRegister(a));
 }
 
 vixl::CPURegister
 ToCPURegister(const LAllocation* a, Scalar::Type type)
 {
     if (a->isFloatReg() && type == Scalar::Float64)
         return ARMFPRegister(ToFloatRegister(a), 64);
     if (a->isFloatReg() && type == Scalar::Float32)
@@ -251,30 +280,112 @@ CodeGenerator::visitAddI(LAddI* ins)
     } else {
         masm.Add(toWRegister(dest), toWRegister(lhs), toWOperand(rhs));
     }
 }
 
 void
 CodeGenerator::visitSubI(LSubI* ins)
 {
-    MOZ_CRASH("visitSubI");
+    const LAllocation* lhs = ins->getOperand(0);
+    const LAllocation* rhs = ins->getOperand(1);
+    const LDefinition* dest = ins->getDef(0);
+
+    // Platforms with three-operand arithmetic ops don't need recovery.
+    MOZ_ASSERT(!ins->recoversInput());
+
+    if (ins->snapshot()) {
+        masm.Subs(toWRegister(dest), toWRegister(lhs), toWOperand(rhs));
+        bailoutIf(Assembler::Overflow, ins->snapshot());
+    } else {
+        masm.Sub(toWRegister(dest), toWRegister(lhs), toWOperand(rhs));
+    }
 }
 
 void
 CodeGenerator::visitMulI(LMulI* ins)
 {
-    MOZ_CRASH("visitMulI");
+    const LAllocation* lhs = ins->getOperand(0);
+    const LAllocation* rhs = ins->getOperand(1);
+    const LDefinition* dest = ins->getDef(0);
+    MMul* mul = ins->mir();
+    MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
+
+    Register lhsreg = ToRegister(lhs);
+
+    if (rhs->isConstant()) {
+        // Bailout on -0.0.
+        int32_t constant = ToInt32(rhs);
+        if (mul->canBeNegativeZero() && constant <= 0) {
+            Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
+            masm.Cmp(toWRegister(lhs), Operand(0));
+            bailoutIf(bailoutCond, ins->snapshot());
+        }
+
+        switch (constant) {
+          case -1:
+            masm.neg32(lhsreg);
+            break;
+          case 0:
+            masm.Mov(ARMRegister(lhsreg, 32), wzr);
+            return; // escape overflow check;
+          case 1:
+            // nop
+            return; // escape overflow check;
+          case 2:
+            masm.add32(lhsreg, lhsreg);
+            break;
+          default:
+            // Use shift if cannot overflow and constant is a power of 2
+            if (!mul->canOverflow() && constant > 0) {
+                int32_t shift = FloorLog2(constant);
+                if ((1 << shift) == constant) {
+                    masm.lshift32(Imm32(shift), lhsreg);
+                    return;
+                }
+            }
+
+            // Otherwise, just multiply.
+            Label bailout;
+            Label* onZero = mul->canBeNegativeZero() ? &bailout : nullptr;
+            Label* onOverflow = mul->canOverflow() ? &bailout : nullptr;
+
+            vixl::UseScratchRegisterScope temps(&masm.asVIXL());
+            const Register scratch = temps.AcquireW().asUnsized();
+
+            masm.move32(Imm32(constant), scratch);
+            masm.mul32(lhsreg, scratch, ToRegister(dest), onOverflow, onZero);
+            if (onZero || onOverflow)
+                bailoutFrom(&bailout, ins->snapshot());
+            return; // escape overflow check;
+        }
+
+        // Overflow check.
+        if (mul->canOverflow())
+            bailoutIf(Assembler::Overflow, ins->snapshot());
+    } else {
+        Register rhsreg = ToRegister(rhs);
+
+        Label bailout;
+        // TODO: x64 (but not other platforms) have an OOL path for onZero.
+        Label* onZero = mul->canBeNegativeZero() ? &bailout : nullptr;
+        Label* onOverflow = mul->canOverflow() ? &bailout : nullptr;
+
+        masm.mul32(lhsreg, rhsreg, ToRegister(dest), onOverflow, onZero);
+        if (onZero || onOverflow)
+            bailoutFrom(&bailout, ins->snapshot());
+    }
 }
 
 
 void
 CodeGenerator::visitDivI(LDivI* ins)
 {
     MOZ_CRASH("visitDivI");
+
 }
 
 void
 CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins)
 {
     MOZ_CRASH("CodeGenerator::visitDivPowTwoI");
 }
 
@@ -289,35 +400,88 @@ void
 CodeGenerator::visitModI(LModI* ins)
 {
     MOZ_CRASH("visitModI");
 }
 
 void
 CodeGenerator::visitModPowTwoI(LModPowTwoI* ins)
 {
-    MOZ_CRASH("visitModPowTwoI");
+    Register lhs = ToRegister(ins->getOperand(0));
+    ARMRegister lhsw = toWRegister(ins->getOperand(0));
+    ARMRegister outw = toWRegister(ins->output());
+
+    int32_t shift = ins->shift();
+    bool canBeNegative = !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend();
+
+    Label negative;
+    if (canBeNegative) {
+        // Switch based on sign of the lhs.
+        // Positive numbers are just a bitmask.
+        masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
+    }
+
+    masm.And(outw, lhsw, Operand((uint32_t(1) << shift) - 1));
+
+    if (canBeNegative) {
+        Label done;
+        masm.jump(&done);
+
+        // Negative numbers need a negate, bitmask, negate.
+        masm.bind(&negative);
+        masm.Neg(outw, Operand(lhsw));
+        masm.And(outw, outw, Operand((uint32_t(1) << shift) - 1));
+
+        // Since a%b has the same sign as b, and a is negative in this branch,
+        // an answer of 0 means the correct result is actually -0. Bail out.
+        if (!ins->mir()->isTruncated()) {
+            masm.Negs(outw, Operand(outw));
+            bailoutIf(Assembler::Zero, ins->snapshot());
+        } else {
+            masm.Neg(outw, Operand(outw));
+        }
+
+        masm.bind(&done);
+    }
 }
 
 void
 CodeGenerator::visitModMaskI(LModMaskI* ins)
 {
     MOZ_CRASH("CodeGenerator::visitModMaskI");
 }
 
 void
 CodeGenerator::visitBitNotI(LBitNotI* ins)
 {
-    MOZ_CRASH("visitBitNotI");
+    const LAllocation* input = ins->getOperand(0);
+    const LDefinition* output = ins->getDef(0);
+    masm.Mvn(toWRegister(output), toWOperand(input));
 }
 
 void
 CodeGenerator::visitBitOpI(LBitOpI* ins)
 {
-    MOZ_CRASH("visitBitOpI");
+    const ARMRegister lhs = toWRegister(ins->getOperand(0));
+    const Operand rhs = toWOperand(ins->getOperand(1));
+    const ARMRegister dest = toWRegister(ins->getDef(0));
+
+    switch (ins->bitop()) {
+      case JSOP_BITOR:
+        masm.Orr(dest, lhs, rhs);
+        break;
+      case JSOP_BITXOR:
+        masm.Eor(dest, lhs, rhs);
+        break;
+      case JSOP_BITAND:
+        masm.And(dest, lhs, rhs);
+        break;
+      default:
+        MOZ_CRASH("unexpected binary opcode");
+    }
 }
 
 void
 CodeGenerator::visitShiftI(LShiftI* ins)
 {
     MOZ_CRASH("visitShiftI");
 }
 
@@ -331,17 +495,21 @@ void
 CodeGenerator::visitPowHalfD(LPowHalfD* ins)
 {
     MOZ_CRASH("visitPowHalfD");
 }
 
 MoveOperand
 CodeGeneratorARM64::toMoveOperand(const LAllocation a) const
 {
-    MOZ_CRASH("toMoveOperand");
+    if (a.isGeneralReg())
+        return MoveOperand(ToRegister(a));
+    if (a.isFloatReg())
+        return MoveOperand(ToFloatRegister(a));
+    return MoveOperand(AsRegister(masm.getStackPointer()), ToStackOffset(a));
 }
 
 class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorARM64>
 {
     MTableSwitch* mir_;
     Vector<CodeLabel, 8, JitAllocPolicy> codeLabels_;
 
     void accept(CodeGeneratorARM64* codegen) override {
@@ -388,35 +556,55 @@ void
 CodeGenerator::visitMathF(LMathF* math)
 {
     MOZ_CRASH("visitMathF");
 }
 
 void
 CodeGenerator::visitFloor(LFloor* lir)
 {
-    MOZ_CRASH("visitFloor");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label bailout;
+    masm.floor(input, output, &bailout);
+    bailoutFrom(&bailout, lir->snapshot());
 }
 
 void
 CodeGenerator::visitFloorF(LFloorF* lir)
 {
-    MOZ_CRASH("visitFloorF");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label bailout;
+    masm.floorf(input, output, &bailout);
+    bailoutFrom(&bailout, lir->snapshot());
 }
 
 void
 CodeGenerator::visitCeil(LCeil* lir)
 {
-    MOZ_CRASH("visitCeil");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label bailout;
+    masm.ceil(input, output, &bailout);
+    bailoutFrom(&bailout, lir->snapshot());
 }
 
 void
 CodeGenerator::visitCeilF(LCeilF* lir)
 {
-    MOZ_CRASH("visitCeilF");
+    FloatRegister input = ToFloatRegister(lir->input());
+    Register output = ToRegister(lir->output());
+
+    Label bailout;
+    masm.ceilf(input, output, &bailout);
+    bailoutFrom(&bailout, lir->snapshot());
 }
 
 void
 CodeGenerator::visitRound(LRound* lir)
 {
     MOZ_CRASH("visitRound");
 }
 
@@ -436,17 +624,19 @@ void
 CodeGenerator::visitTruncF(LTruncF* lir)
 {
     MOZ_CRASH("visitTruncF");
 }
 
 void
 CodeGenerator::visitClzI(LClzI* lir)
 {
-    MOZ_CRASH("visitClzI");
+    ARMRegister input = toWRegister(lir->input());
+    ARMRegister output = toWRegister(lir->output());
+    masm.Clz(output, input);
 }
 
 void
 CodeGenerator::visitCtzI(LCtzI* lir)
 {
     MOZ_CRASH("visitCtzI");
 }
 
@@ -498,17 +688,18 @@ ValueOperand
 CodeGeneratorARM64::ToTempValue(LInstruction* ins, size_t pos)
 {
     MOZ_CRASH("CodeGeneratorARM64::ToTempValue");
 }
 
 void
 CodeGenerator::visitValue(LValue* value)
 {
-    MOZ_CRASH("visitValue");
+    ValueOperand result = ToOutValue(value);
+    masm.moveValue(value->value(), result);
 }
 
 void
 CodeGenerator::visitBox(LBox* box)
 {
     const LAllocation* in = box->getOperand(0);
     ValueOperand result = ToOutValue(box);
 
@@ -578,23 +769,25 @@ CodeGenerator::visitUnbox(LUnbox* unbox)
       default:
         MOZ_CRASH("Given MIRType cannot be unboxed.");
     }
 }
 
 void
 CodeGenerator::visitDouble(LDouble* ins)
 {
-    MOZ_CRASH("visitDouble");
+    ARMFPRegister output(ToFloatRegister(ins->getDef(0)), 64);
+    masm.Fmov(output, ins->getDouble());
 }
 
 void
 CodeGenerator::visitFloat32(LFloat32* ins)
 {
-    MOZ_CRASH("visitFloat32");
+    ARMFPRegister output(ToFloatRegister(ins->getDef(0)), 32);
+    masm.Fmov(output, ins->getFloat());
 }
 
 void
 CodeGeneratorARM64::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
 {
     MOZ_CRASH("splitTagForTest");
 }
 
@@ -608,35 +801,59 @@ void
 CodeGenerator::visitTestFAndBranch(LTestFAndBranch* test)
 {
     MOZ_CRASH("visitTestFAndBranch");
 }
 
 void
 CodeGenerator::visitCompareD(LCompareD* comp)
 {
-    MOZ_CRASH("visitCompareD");
+    const FloatRegister left = ToFloatRegister(comp->left());
+    const FloatRegister right = ToFloatRegister(comp->right());
+    ARMRegister output = toWRegister(comp->output());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+
+    masm.compareDouble(cond, left, right);
+    masm.cset(output, Assembler::ConditionFromDoubleCondition(cond));
 }
 
 void
 CodeGenerator::visitCompareF(LCompareF* comp)
 {
-    MOZ_CRASH("visitCompareF");
+    const FloatRegister left = ToFloatRegister(comp->left());
+    const FloatRegister right = ToFloatRegister(comp->right());
+    ARMRegister output = toWRegister(comp->output());
+    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
+
+    masm.compareFloat(cond, left, right);
+    masm.cset(output, Assembler::ConditionFromDoubleCondition(cond));
 }
 
 void
 CodeGenerator::visitCompareDAndBranch(LCompareDAndBranch* comp)
 {
-    MOZ_CRASH("visitCompareDAndBranch");
+    const FloatRegister left = ToFloatRegister(comp->left());
+    const FloatRegister right = ToFloatRegister(comp->right());
+    Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
+    Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
+
+    masm.compareDouble(doubleCond, left, right);
+    emitBranch(cond, comp->ifTrue(), comp->ifFalse());
 }
 
 void
 CodeGenerator::visitCompareFAndBranch(LCompareFAndBranch* comp)
 {
-    MOZ_CRASH("visitCompareFAndBranch");
+    const FloatRegister left = ToFloatRegister(comp->left());
+    const FloatRegister right = ToFloatRegister(comp->right());
+    Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
+    Assembler::Condition cond = Assembler::ConditionFromDoubleCondition(doubleCond);
+
+    masm.compareFloat(doubleCond, left, right);
+    emitBranch(cond, comp->ifTrue(), comp->ifFalse());
 }
 
 void
 CodeGenerator::visitCompareB(LCompareB* lir)
 {
     MOZ_CRASH("visitCompareB");
 }
 
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -77,17 +77,20 @@ LIRGenerator::visitUnbox(MUnbox* unbox)
     LUnboxBase* lir;
     if (IsFloatingPointType(unbox->type())) {
         lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
     } else if (unbox->fallible()) {
         // If the unbox is fallible, load the Value in a register first to
         // avoid multiple loads.
         lir = new(alloc()) LUnbox(useRegisterAtStart(box));
     } else {
-        lir = new(alloc()) LUnbox(useAtStart(box));
+        // FIXME: It should be possible to useAtStart() here, but the DEBUG
+        // code in CodeGenerator::visitUnbox() needs to handle non-Register
+        // cases. ARM64 doesn't have an Operand type.
+        lir = new(alloc()) LUnbox(useRegisterAtStart(box));
     }
 
     if (unbox->fallible())
         assignSnapshot(lir, unbox->bailoutKind());
 
     define(lir, unbox);
 }
 
@@ -173,31 +176,33 @@ LIRGeneratorARM64::lowerForBitAndAndBran
                                          MDefinition* lhs, MDefinition* rhs)
 {
     MOZ_CRASH("lowerForBitAndAndBranch");
 }
 
 void
 LIRGeneratorARM64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
 {
-    MOZ_CRASH("defineUntypedPhi");
+    defineTypedPhi(phi, lirIndex);
 }
 
 void
 LIRGeneratorARM64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
                                         LBlock* block, size_t lirIndex)
 {
-    MOZ_CRASH("lowerUntypedPhiInput");
+    lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
 }
 
 void
 LIRGeneratorARM64::lowerForShift(LInstructionHelper<1, 2, 0>* ins,
                                  MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
 {
-    MOZ_CRASH("lowerForShift");
+    ins->setOperand(0, useRegister(lhs));
+    ins->setOperand(1, useRegisterOrConstant(rhs));
+    define(ins, mir);
 }
 
 void
 LIRGeneratorARM64::lowerDivI(MDiv* div)
 {
     MOZ_CRASH("lowerDivI");
 }
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -49,16 +49,18 @@ MSG_DEF(JSMSG_READ_ONLY,               1
 MSG_DEF(JSMSG_CANT_DELETE,             1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
 MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY,     0, JSEXN_TYPEERR, "can't delete non-configurable array element")
 MSG_DEF(JSMSG_NOT_FUNCTION,            1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         1, JSEXN_TYPEERR, "{0} is not a constructor")
 MSG_DEF(JSMSG_CANT_CONVERT_TO,         2, JSEXN_TYPEERR, "can't convert {0} to {1}")
 MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
 MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
 MSG_DEF(JSMSG_NO_PROPERTIES,           1, JSEXN_TYPEERR, "{0} has no properties")
+MSG_DEF(JSMSG_PROPERTY_FAIL,           2, JSEXN_TYPEERR, "can't access property {0} of {1}")
+MSG_DEF(JSMSG_PROPERTY_FAIL_EXPR,      3, JSEXN_TYPEERR, "{0} is {1}, can't access property {2} of it")
 MSG_DEF(JSMSG_BAD_REGEXP_FLAG,         1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
 MSG_DEF(JSMSG_INVALID_DATA_VIEW_LENGTH, 0, JSEXN_RANGEERR, "invalid data view length")
 MSG_DEF(JSMSG_OFFSET_LARGER_THAN_FILESIZE, 0, JSEXN_RANGEERR, "offset is larger than filesize")
 MSG_DEF(JSMSG_OFFSET_OUT_OF_BUFFER,    0, JSEXN_RANGEERR, "start offset is outside the bounds of the buffer")
 MSG_DEF(JSMSG_OFFSET_OUT_OF_DATAVIEW,  0, JSEXN_RANGEERR, "offset is outside the bounds of the DataView")
 MSG_DEF(JSMSG_SPREAD_TOO_LARGE,        0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
 MSG_DEF(JSMSG_BAD_WEAKMAP_KEY,         0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
 MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER,    1, JSEXN_TYPEERR, "invalid {0} usage")
--- a/js/src/jsapi-tests/testErrorInterceptor.cpp
+++ b/js/src/jsapi-tests/testErrorInterceptor.cpp
@@ -47,17 +47,17 @@ BEGIN_TEST(testErrorInterceptor)
     };
     // With the simpleInterceptor, we should end up with the following error:
     const char* TO_STRING[] = {
         "Error: I am an Error\0",
         "TypeError: I am a TypeError\0",
         "ReferenceError: I am a ReferenceError\0",
         "SyntaxError: I am a SyntaxError\0",
         "5\0",
-        "TypeError: undefined has no properties\0",
+        "TypeError: can't access property 0 of undefined\0",
         "ReferenceError: foo is not defined\0",
         "SyntaxError: expected expression, got end of script\0",
     };
     MOZ_ASSERT(mozilla::ArrayLength(SAMPLES) == mozilla::ArrayLength(TO_STRING));
 
 
     // Save original callback.
     JSErrorInterceptor* original = JS_GetErrorInterceptorCallback(cx->runtime());
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -480,16 +480,23 @@ if test "$GNU_CC"; then
 
     AC_MSG_CHECKING([for -z relro option to ld])
     _SAVE_LDFLAGS=$LDFLAGS
     LDFLAGS="$LDFLAGS -Wl,-z,relro"
     AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
                   AC_MSG_RESULT([no])
                   LDFLAGS=$_SAVE_LDFLAGS)
 
+    AC_MSG_CHECKING([for -z nocopyreloc option to ld])
+    _SAVE_LDFLAGS=$LDFLAGS
+    LDFLAGS="$LDFLAGS -Wl,-z,nocopyreloc"
+    AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
+                  AC_MSG_RESULT([no])
+                  LDFLAGS=$_SAVE_LDFLAGS)
+
     AC_MSG_CHECKING([for --build-id option to ld])
     _SAVE_LDFLAGS=$LDFLAGS
     LDFLAGS="$LDFLAGS -Wl,--build-id"
     AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
                   AC_MSG_RESULT([no])
                   LDFLAGS=$_SAVE_LDFLAGS)
 
     _DEFINES_CFLAGS="-include $jsconfdefs -DMOZILLA_CLIENT"
--- a/js/src/tests/non262/extensions/regress-353116.js
+++ b/js/src/tests/non262/extensions/regress-353116.js
@@ -14,57 +14,57 @@ var expect = '';
 test();
 //-----------------------------------------------------------------------------
 
 function test()
 {
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
-  expect = 'TypeError: undefined has no properties';
+  expect = `TypeError: can't access property "y" of undefined`;
   actual = 'No Error';
 
   try
   {
     undefined.y;
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary);
 
-  expect = 'TypeError: null has no properties';
+  expect = `TypeError: can't access property "y" of null`;
   actual = 'No Error';
 
   try
   {
     null.y;
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary);
 
-  expect = 'TypeError: x is undefined';
+  expect = `TypeError: x is undefined, can't access property "y" of it`;
   actual = 'No Error';
 
   try
   {
     x = undefined; 
     x.y;
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary);
 
-  expect = 'TypeError: x is null';
+  expect = `TypeError: x is null, can't access property "y" of it`;
   actual = 'No Error';
 
   try
   {
     x = null; 
     x.y;
   }
   catch(ex)
--- a/js/src/tests/non262/regress/regress-469625-03.js
+++ b/js/src/tests/non262/regress/regress-469625-03.js
@@ -20,17 +20,17 @@ function test()
 {
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   function f(x) {
     var [a, b, [c0, c1]] = [x, x, x];
   }
 
-  expect = `TypeError: [...][Symbol.iterator](...).next(...).value is null`;
+  expect = `TypeError: [...][Symbol.iterator](...).next(...).value is null, can't access property Symbol.iterator of it`;
   actual = 'No Error';
   try
   {
     f(null);
   }
   catch(ex)
   {
     actual = ex + '';
--- a/js/src/tests/non262/regress/regress-469758.js
+++ b/js/src/tests/non262/regress/regress-469758.js
@@ -4,11 +4,11 @@
 var err;
 try {
     {let i=1}
     {let j=1; [][j][2]}
 } catch (e) {
     err = e;
 }
 assertEq(err instanceof TypeError, true);
-assertEq(err.message, "[][j] is undefined");
+assertEq(err.message, "[][j] is undefined, can't access property 2 of it");
 
 reportCompare(0, 0, 'ok');
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -533,17 +533,17 @@ GetObjectElementOperation(JSContext* cx,
 
 static MOZ_ALWAYS_INLINE bool
 GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver,
                              HandleValue key, MutableHandleValue res)
 {
     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     // FIXME: Bug 1234324 We shouldn't be boxing here.
-    RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
+    RootedObject boxed(cx, ToObjectFromStackForPropertyAccess(cx, receiver, key));
     if (!boxed)
         return false;
 
     do {
         uint32_t index;
         if (IsDefinitelyIndex(key, &index)) {
             if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
                 break;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -258,17 +258,17 @@ GetImportOperation(JSContext* cx, Interp
     return FetchName<GetNameMode::Normal>(cx, env, pobj, name, prop, vp);
 }
 
 static bool
 SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, HandleValue rval)
 {
     MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);
 
-    RootedObject obj(cx, ToObjectFromStack(cx, lval));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, lval, id));
     if (!obj)
         return false;
 
     ObjectOpResult result;
     return SetProperty(cx, obj, id, rval, lval, result) &&
            result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP);
 }
 
@@ -1513,20 +1513,20 @@ HandleError(JSContext* cx, InterpreterRe
 #define PUSH_SYMBOL(s)           REGS.sp++->setSymbol(s)
 #define PUSH_STRING(s)           do { REGS.sp++->setString(s); cx->debugOnlyCheck(REGS.sp[-1]); } while (0)
 #define PUSH_OBJECT(obj)         do { REGS.sp++->setObject(obj); cx->debugOnlyCheck(REGS.sp[-1]); } while (0)
 #define PUSH_OBJECT_OR_NULL(obj) do { REGS.sp++->setObjectOrNull(obj); cx->debugOnlyCheck(REGS.sp[-1]); } while (0)
 #define PUSH_MAGIC(magic)        REGS.sp++->setMagic(magic)
 #define POP_COPY_TO(v)           (v) = *--REGS.sp
 #define POP_RETURN_VALUE()       REGS.fp()->setReturnValue(*--REGS.sp)
 
-#define FETCH_OBJECT(cx, n, obj)                                              \
+#define FETCH_OBJECT(cx, n, obj, key)                                         \
     JS_BEGIN_MACRO                                                            \
         HandleValue val = REGS.stackHandleAt(n);                              \
-        obj = ToObjectFromStack((cx), (val));                                 \
+        obj = ToObjectFromStackForPropertyAccess((cx), (val), (key));         \
         if (!obj)                                                             \
             goto error;                                                       \
     JS_END_MACRO
 
 /*
  * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
  * remain distinct for the decompiler.
  */
@@ -2819,17 +2819,17 @@ END_CASE(JSOP_DELNAME)
 
 CASE(JSOP_DELPROP)
 CASE(JSOP_STRICTDELPROP)
 {
     static_assert(JSOP_DELPROP_LENGTH == JSOP_STRICTDELPROP_LENGTH,
                   "delprop and strictdelprop must be the same size");
     ReservedRooted<jsid> id(&rootId0, NameToId(script->getName(REGS.pc)));
     ReservedRooted<JSObject*> obj(&rootObject0);
-    FETCH_OBJECT(cx, -1, obj);
+    FETCH_OBJECT(cx, -1, obj, id);
 
     ObjectOpResult result;
     if (!DeleteProperty(cx, obj, id, result))
         goto error;
     if (!result && JSOp(*REGS.pc) == JSOP_STRICTDELPROP) {
         result.reportError(cx, obj, id);
         goto error;
     }
@@ -2840,19 +2840,18 @@ END_CASE(JSOP_DELPROP)
 
 CASE(JSOP_DELELEM)
 CASE(JSOP_STRICTDELELEM)
 {
     static_assert(JSOP_DELELEM_LENGTH == JSOP_STRICTDELELEM_LENGTH,
                   "delelem and strictdelelem must be the same size");
     /* Fetch the left part and resolve it to a non-null object. */
     ReservedRooted<JSObject*> obj(&rootObject0);
-    FETCH_OBJECT(cx, -2, obj);
-
     ReservedRooted<Value> propval(&rootValue0, REGS.sp[-1]);
+    FETCH_OBJECT(cx, -2, obj, propval);
 
     ObjectOpResult result;
     ReservedRooted<jsid> id(&rootId0);
     if (!ToPropertyKey(cx, propval, &id))
         goto error;
     if (!DeleteProperty(cx, obj, id, result))
         goto error;
     if (!result && JSOp(*REGS.pc) == JSOP_STRICTDELELEM) {
@@ -3106,17 +3105,17 @@ END_CASE(JSOP_GETELEM_SUPER)
 
 CASE(JSOP_SETELEM)
 CASE(JSOP_STRICTSETELEM)
 {
     static_assert(JSOP_SETELEM_LENGTH == JSOP_STRICTSETELEM_LENGTH,
                   "setelem and strictsetelem must be the same size");
     HandleValue receiver = REGS.stackHandleAt(-3);
     ReservedRooted<JSObject*> obj(&rootObject0);
-    obj = ToObjectFromStack(cx, receiver);
+    obj = ToObjectFromStackForPropertyAccess(cx, receiver, REGS.stackHandleAt(-2));
     if (!obj)
         goto error;
     ReservedRooted<jsid> id(&rootId0);
     FETCH_ELEMENT_ID(-2, id);
     HandleValue value = REGS.stackHandleAt(-1);
     if (!SetObjectElementOperation(cx, obj, id, value, receiver, *REGS.pc == JSOP_STRICTSETELEM))
         goto error;
     REGS.sp[-3] = value;
@@ -4609,17 +4608,17 @@ js::GetProperty(JSContext* cx, HandleVal
         if (!proto)
             return false;
 
         if (GetPropertyPure(cx, proto, NameToId(name), vp.address()))
             return true;
     }
 
     RootedValue receiver(cx, v);
-    RootedObject obj(cx, ToObjectFromStack(cx, v));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, v, name));
     if (!obj)
         return false;
 
     return GetProperty(cx, obj, receiver, name, vp);
 }
 
 JSObject*
 js::Lambda(JSContext* cx, HandleFunction fun, HandleObject parent)
@@ -4754,17 +4753,17 @@ js::GetAndClearException(JSContext* cx, 
     // Allow interrupting deeply nested exception handling.
     return CheckForInterrupt(cx);
 }
 
 template <bool strict>
 bool
 js::DeletePropertyJit(JSContext* cx, HandleValue v, HandlePropertyName name, bool* bp)
 {
-    RootedObject obj(cx, ToObjectFromStack(cx, v));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, v, name));
     if (!obj)
         return false;
 
     RootedId id(cx, NameToId(name));
     ObjectOpResult result;
     if (!DeleteProperty(cx, obj, id, result))
         return false;
 
@@ -4782,17 +4781,17 @@ template bool js::DeletePropertyJit<true
                                            bool* bp);
 template bool js::DeletePropertyJit<false>(JSContext* cx, HandleValue val, HandlePropertyName name,
                                            bool* bp);
 
 template <bool strict>
 bool
 js::DeleteElementJit(JSContext* cx, HandleValue val, HandleValue index, bool* bp)
 {
-    RootedObject obj(cx, ToObjectFromStack(cx, val));
+    RootedObject obj(cx, ToObjectFromStackForPropertyAccess(cx, val, index));
     if (!obj)
         return false;
 
     RootedId id(cx);
     if (!ToPropertyKey(cx, index, &id))
         return false;
     ObjectOpResult result;
     if (!DeleteProperty(cx, obj, id, result))
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -905,37 +905,88 @@ js::ReportIsNotDefined(JSContext* cx, Ha
 void
 js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
     ReportIsNotDefined(cx, id);
 }
 
 void
-js::ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v)
+js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, bool reportScanStack)
 {
     MOZ_ASSERT(v.isNullOrUndefined());
 
-    UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, nullptr);
+    if (!reportScanStack) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
+                                  v.isNull() ? "null" : "undefined", "object");
+        return;
+    }
+
+    UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
                                    bytes.get());
     } else if (v.isUndefined()) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                    bytes.get(), js_undefined_str);
     } else {
         MOZ_ASSERT(v.isNull());
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                    bytes.get(), js_null_str);
     }
 }
 
+char*
+EncodeIdAsLatin1(JSContext* cx, HandleId id, JSAutoByteString& bytes)
+{
+    RootedValue idVal(cx, IdToValue(id));
+    RootedString idStr(cx, ValueToSource(cx, idVal));
+    if (!idStr)
+        return nullptr;
+
+    return bytes.encodeLatin1(cx, idStr);
+}
+
+void
+js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
+                                             bool reportScanStack)
+{
+    MOZ_ASSERT(v.isNullOrUndefined());
+
+    JSAutoByteString keyBytes;
+    if (!EncodeIdAsLatin1(cx, key, keyBytes))
+        return;
+
+    if (!reportScanStack) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                   keyBytes.ptr(),
+                                   v.isUndefined() ? js_undefined_str : js_null_str);
+        return;
+    }
+
+    UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
+    if (!bytes)
+        return;
+
+    if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                   keyBytes.ptr(), bytes.get());
+    } else if (v.isUndefined()) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                   bytes.get(), js_undefined_str, keyBytes.ptr());
+    } else {
+        MOZ_ASSERT(v.isNull());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                   bytes.get(), js_null_str, keyBytes.ptr());
+    }
+}
+
 void
 js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
 {
     char argbuf[11];
     UniqueChars bytes;
 
     SprintfLiteral(argbuf, "%u", arg);
     if (IsFunctionObject(v)) {
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -1102,17 +1102,20 @@ ReportIsNotDefined(JSContext* cx, Handle
 
 extern void
 ReportIsNotDefined(JSContext* cx, HandleId id);
 
 /*
  * Report an attempt to access the property of a null or undefined value (v).
  */
 extern void
-ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v);
+ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, bool reportScanStack);
+extern void
+ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
+                                         bool reportScanStack);
 
 extern void
 ReportMissingArg(JSContext* cx, js::HandleValue v, unsigned arg);
 
 /*
  * Report error using js_DecompileValueGenerator(cx, spindex, v, fallback) as
  * the first argument for the error message. If the error message has less
  * then 3 arguments, use null for arg1 or arg2.
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -3235,21 +3235,69 @@ js::PrimitiveToObject(JSContext* cx, con
  */
 JSObject*
 js::ToObjectSlow(JSContext* cx, JS::HandleValue val, bool reportScanStack)
 {
     MOZ_ASSERT(!val.isMagic());
     MOZ_ASSERT(!val.isObject());
 
     if (val.isNullOrUndefined()) {
-        if (reportScanStack) {
-            ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val);
+        ReportIsNullOrUndefinedForPropertyAccess(cx, val, reportScanStack);
+        return nullptr;
+    }
+
+    return PrimitiveToObject(cx, val);
+}
+
+JSObject*
+js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleId key,
+                                  bool reportScanStack)
+{
+    MOZ_ASSERT(!val.isMagic());
+    MOZ_ASSERT(!val.isObject());
+
+    if (val.isNullOrUndefined()) {
+        ReportIsNullOrUndefinedForPropertyAccess(cx, val, key, reportScanStack);
+        return nullptr;
+    }
+
+    return PrimitiveToObject(cx, val);
+}
+
+JSObject*
+js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandlePropertyName key,
+                                  bool reportScanStack)
+{
+    MOZ_ASSERT(!val.isMagic());
+    MOZ_ASSERT(!val.isObject());
+
+    if (val.isNullOrUndefined()) {
+        RootedId keyId(cx, NameToId(key));
+        ReportIsNullOrUndefinedForPropertyAccess(cx, val, keyId, reportScanStack);
+        return nullptr;
+    }
+
+    return PrimitiveToObject(cx, val);
+}
+
+JSObject*
+js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleValue keyValue,
+                                  bool reportScanStack)
+{
+    MOZ_ASSERT(!val.isMagic());
+    MOZ_ASSERT(!val.isObject());
+
+    if (val.isNullOrUndefined()) {
+        RootedId key(cx);
+        if (keyValue.isPrimitive()) {
+            if (!ValueToId<CanGC>(cx, keyValue, &key))
+                return nullptr;
+            ReportIsNullOrUndefinedForPropertyAccess(cx, val, key, reportScanStack);
         } else {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
-                                      val.isNull() ? "null" : "undefined", "object");
+            ReportIsNullOrUndefinedForPropertyAccess(cx, val, reportScanStack);
         }
         return nullptr;
     }
 
     return PrimitiveToObject(cx, val);
 }
 
 Value
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -1201,16 +1201,48 @@ namespace js {
 MOZ_ALWAYS_INLINE JSObject*
 ToObjectFromStack(JSContext* cx, HandleValue vp)
 {
     if (vp.isObject())
         return &vp.toObject();
     return js::ToObjectSlow(cx, vp, true);
 }
 
+JSObject*
+ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleId key,
+                              bool reportScanStack);
+JSObject*
+ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandlePropertyName key,
+                              bool reportScanStack);
+JSObject*
+ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, HandleValue keyValue,
+                              bool reportScanStack);
+
+MOZ_ALWAYS_INLINE JSObject*
+ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleId key)
+{
+    if (vp.isObject())
+        return &vp.toObject();
+    return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
+}
+MOZ_ALWAYS_INLINE JSObject*
+ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandlePropertyName key)
+{
+    if (vp.isObject())
+        return &vp.toObject();
+    return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
+}
+MOZ_ALWAYS_INLINE JSObject*
+ToObjectFromStackForPropertyAccess(JSContext* cx, HandleValue vp, HandleValue key)
+{
+    if (vp.isObject())
+        return &vp.toObject();
+    return js::ToObjectSlowForPropertyAccess(cx, vp, key, true);
+}
+
 template<XDRMode mode>
 XDRResult
 XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
 
 /*
  * Report a TypeError: "so-and-so is not an object".
  * Using NotNullObject is usually less code.
  */
--- a/js/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -404,27 +404,16 @@ var XPCOMUtils = {
       Services.prefs.addObserver(aPreference, observer, true);
 
       defineGetter(lazyGetter);
       return lazyGetter();
     });
   },
 
   /**
-   * Helper which iterates over a nsISimpleEnumerator.
-   * @param e The nsISimpleEnumerator to iterate over.
-   * @param i The expected interface for each element.
-   */
-  IterSimpleEnumerator: function* XPCU_IterSimpleEnumerator(e, i)
-  {
-    while (e.hasMoreElements())
-      yield e.getNext().QueryInterface(i);
-  },
-
-  /**
    * Helper which iterates over a string enumerator.
    * @param e The string enumerator (nsIUTF8StringEnumerator or
    *          nsIStringEnumerator) over which to iterate.
    */
   IterStringEnumerator: function* XPCU_IterStringEnumerator(e)
   {
     while (e.hasMore())
       yield e.getNext();
@@ -432,17 +421,17 @@ var XPCOMUtils = {
 
   /**
    * Helper which iterates over the entries in a category.
    * @param aCategory The name of the category over which to iterate.
    */
   enumerateCategoryEntries: function* XPCOMUtils_enumerateCategoryEntries(aCategory)
   {
     let category = this.categoryManager.enumerateCategory(aCategory);
-    for (let entry of this.IterSimpleEnumerator(category, Ci.nsISupportsCString)) {
+    for (let entry of category) {
       yield [entry.data, this.categoryManager.getCategoryEntry(aCategory, entry.data)];
     }
   },
 
   /**
    * Returns an nsIFactory for |component|.
    */
   _getFactory: function XPCOMUtils__getFactory(component) {
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1508,28 +1508,28 @@ XPCShellDirProvider::GetFiles(const char
         file->AppendNative(NS_LITERAL_CSTRING("chrome"));
         dirs.AppendObject(file);
 
         nsresult rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR,
                                              getter_AddRefs(file));
         if (NS_SUCCEEDED(rv))
             dirs.AppendObject(file);
 
-        return NS_NewArrayEnumerator(result, dirs);
+        return NS_NewArrayEnumerator(result, dirs, NS_GET_IID(nsIFile));
     } else if (!strcmp(prop, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
         nsCOMArray<nsIFile> dirs;
         nsCOMPtr<nsIFile> appDir;
         bool exists;
         if (mAppDir &&
             NS_SUCCEEDED(mAppDir->Clone(getter_AddRefs(appDir))) &&
             NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("defaults"))) &&
             NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("preferences"))) &&
             NS_SUCCEEDED(appDir->Exists(&exists)) && exists) {
             dirs.AppendObject(appDir);
-            return NS_NewArrayEnumerator(result, dirs);
+            return NS_NewArrayEnumerator(result, dirs, NS_GET_IID(nsIFile));
         }
         return NS_ERROR_FAILURE;
     } else if (!strcmp(prop, NS_APP_PLUGINS_DIR_LIST)) {
         nsCOMArray<nsIFile> dirs;
         // Add the test plugin location passed in by the caller or through
         // runxpcshelltests.
         if (mPluginDir) {
             dirs.AppendObject(mPluginDir);
@@ -1544,12 +1544,12 @@ XPCShellDirProvider::GetFiles(const char
                 if (NS_SUCCEEDED(mGREDir->Clone(getter_AddRefs(file)))) {
                     file->AppendNative(NS_LITERAL_CSTRING("plugins"));
                     if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
                         dirs.AppendObject(file);
                     }
                 }
             }
         }
-        return NS_NewArrayEnumerator(result, dirs);
+        return NS_NewArrayEnumerator(result, dirs, NS_GET_IID(nsIFile));
     }
     return NS_ERROR_FAILURE;
 }
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -393,17 +393,17 @@ nsXPCWrappedJSClass::BuildPropertyEnumer
 
         nsCOMPtr<nsIProperty> property =
             new xpcProperty(autoStr.get(), (uint32_t)autoStr.Length(), value);
 
         if (!propertyArray.AppendObject(property))
             return NS_ERROR_FAILURE;
     }
 
-    return NS_NewArrayEnumerator(aEnumerate, propertyArray);
+    return NS_NewArrayEnumerator(aEnumerate, propertyArray, NS_GET_IID(nsIProperty));
 }
 
 /***************************************************************************/
 
 NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty)
 
 xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen,
                          nsIVariant* aValue)
@@ -938,17 +938,16 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
 {
     Value* sp = nullptr;
     Value* argv = nullptr;
     uint8_t i;
     nsresult retval = NS_ERROR_FAILURE;
     bool success;
     bool readyToDoTheCall = false;
     nsID  param_iid;
-    const char* name = info->GetName();
     bool foundDependentParam;
 
     // We're about to call into script via an XPCWrappedJS, so we need an
     // AutoEntryScript. This is probably Gecko-specific at this point, and
     // definitely will be when we turn off XPConnect for the web.
     RootedObject obj(RootingCx(), wrapper->GetJSObject());
     nsIGlobalObject* nativeGlobal = NativeGlobal(js::UncheckedUnwrap(obj));
     AutoEntryScript aes(nativeGlobal, "XPCWrappedJS method call",
@@ -957,16 +956,29 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     if (!ccx.IsValid())
         return retval;
 
     JSContext* cx = ccx.GetJSContext();
 
     if (!cx || !IsReflectable(methodIndex))
         return NS_ERROR_FAILURE;
 
+    JS::RootedId id(cx);
+    const char* name;
+    nsAutoCString symbolName;
+    if (info->IsSymbol()) {
+        info->GetSymbolDescription(cx, symbolName);
+        name = symbolName.get();
+        id = SYMBOL_TO_JSID(info->GetSymbol(cx));
+    } else {
+        name = info->GetName();
+        if (!AtomizeAndPinJSString(cx, id.get(), name))
+            return NS_ERROR_FAILURE;
+    }
+
     // We passed the unwrapped object's global to AutoEntryScript so we now need
     // to enter the realm corresponding with the (maybe wrapper) object.
     RootedObject scope(cx, wrapper->GetJSObjectGlobal());
     JSAutoRealm ar(cx, scope);
 
     // [optional_argc] has a different calling convention, which we don't
     // support for JS-implemented components.
     if (info->WantsOptArgc()) {
@@ -1025,17 +1037,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
         // In the normal (non-function) case we just lookup the property by
         // name and as long as the object has such a named property we go ahead
         // and try to make the call. If it turns out the named property is not
         // a callable object then the JS engine will throw an error and we'll
         // pass this along to the caller as an exception/result code.
 
         fval = ObjectValue(*obj);
         if (!isFunction || JS_TypeOfValue(ccx, fval) != JSTYPE_FUNCTION) {
-            if (!JS_GetProperty(cx, obj, name, &fval))
+            if (!JS_GetPropertyById(cx, obj, id, &fval))
                 goto pre_call_clean_up;
             // XXX We really want to factor out the error reporting better and
             // specifically report the failure to find a function with this name.
             // This is what we do below if the property is found but is not a
             // function. We just need to factor better so we can get to that
             // reporting path from here.
 
             thisObj = obj;
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -82,17 +82,23 @@ XPCNativeMember::Resolve(XPCCallContext&
             argc-- ;
 
         callback = XPC_WN_CallMethod;
     } else {
         argc = 0;
         callback = XPC_WN_GetterSetter;
     }
 
-    JSFunction* fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
+    JSFunction* fun;
+    jsid name = GetName();
+    if (JSID_IS_STRING(name)) {
+        fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
+    } else {
+        fun = js::NewFunctionWithReserved(ccx, callback, argc, 0, nullptr);
+    }
     if (!fun)
         return false;
 
     JSObject* funobj = JS_GetFunctionObject(fun);
     if (!funobj)
         return false;
 
     js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_NATIVE_MEMBER_SLOT,
@@ -287,23 +293,28 @@ XPCNativeInterface::NewInstance(const ns
 
         // don't reflect Addref or Release
         if (i == 1 || i == 2)
             continue;
 
         if (!XPCConvert::IsMethodReflectable(*info))
             continue;
 
-        str = JS_AtomizeAndPinString(cx, info->GetName());
-        if (!str) {
-            NS_ERROR("bad method name");
-            failed = true;
-            break;
+        jsid name;
+        if (info->IsSymbol()) {
+            name = SYMBOL_TO_JSID(info->GetSymbol(cx));
+        } else {
+            str = JS_AtomizeAndPinString(cx, info->GetName());
+            if (!str) {
+                NS_ERROR("bad method name");
+                failed = true;
+                break;
+            }
+            name = INTERNED_STRING_TO_JSID(cx, str);
         }
-        jsid name = INTERNED_STRING_TO_JSID(cx, str);
 
         if (info->IsSetter()) {
             MOZ_ASSERT(realTotalCount,"bad setter");
             // Note: ASSUMES Getter/Setter pairs are next to each other
             // This is a rule of the typelib spec.
             cur = &members[realTotalCount-1];
             MOZ_ASSERT(cur->GetName() == name,"bad setter");
             MOZ_ASSERT(cur->IsReadOnlyAttribute(),"bad setter");
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -424,27 +424,22 @@ static nsresult                         
     return rv;                                                                \
 }                                                                             \
 
 #define NS_GEOLOCATION_CID \
   { 0x1E1C3FF, 0x94A, 0xD048, { 0x44, 0xB4, 0x62, 0xD2, 0x9C, 0x7B, 0x4F, 0x39 } }
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Geolocation, Geolocation::NonWindowSingleton)
 
-#define NS_GEOLOCATION_SERVICE_CID \
-  { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
-
 #define NS_AUDIOCHANNEL_SERVICE_CID \
   { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }}
 
 #define NS_WEBSOCKETEVENT_SERVICE_CID \
   { 0x31689828, 0xda66, 0x49a6, { 0x87, 0x0c, 0xdf, 0x62, 0xb8, 0x3f, 0xe7, 0x89 }}
 
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
-
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreate)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WebSocketEventService, WebSocketEventService::GetOrCreate)
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 NS_GENERIC_FACTORY_CONSTRUCTOR(FakeSpeechRecognitionService)
 #endif
 
@@ -547,17 +542,16 @@ NS_DEFINE_NAMED_CID(NOTIFICATIONTELEMETR
 NS_DEFINE_NAMED_CID(PUSHNOTIFIER_CID);
 NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNELAGENT_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCOMMANDTABLE_CID);
-NS_DEFINE_NAMED_CID(NS_GEOLOCATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_CID);
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNEL_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_WEBSOCKETEVENT_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_FOCUSMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_CONTENTSECURITYMANAGER_CID);
 NS_DEFINE_NAMED_CID(CSPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_CSPCONTEXT_CID);
 NS_DEFINE_NAMED_CID(NS_MIXEDCONTENTBLOCKER_CID);
@@ -785,17 +779,16 @@ static const mozilla::Module::CIDEntry k
   { &kPUSHNOTIFIER_CID, false, nullptr, PushNotifierConstructor },
   { &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor },
   { &kNS_AUDIOCHANNELAGENT_CID, true, nullptr, AudioChannelAgentConstructor },
   { &kNS_HTMLEDITOR_CID, false, nullptr, HTMLEditorConstructor },
   { &kNS_EDITORCONTROLLER_CID, false, nullptr, EditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor },
   { &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor },
   { &kNS_EDITINGCOMMANDTABLE_CID, false, nullptr, nsEditingCommandTableConstructor },
-  { &kNS_GEOLOCATION_SERVICE_CID, false, nullptr, nsGeolocationServiceConstructor },
   { &kNS_GEOLOCATION_CID, false, nullptr, GeolocationConstructor },
   { &kNS_AUDIOCHANNEL_SERVICE_CID, false, nullptr, AudioChannelServiceConstructor },
   { &kNS_WEBSOCKETEVENT_SERVICE_CID, false, nullptr, WebSocketEventServiceConstructor },
   { &kNS_FOCUSMANAGER_CID, false, nullptr, CreateFocusManager },
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
   { &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID, false, nullptr, FakeSpeechRecognitionServiceConstructor },
 #endif
 #ifdef MOZ_WEBSPEECH
@@ -886,17 +879,16 @@ static const mozilla::Module::ContractID
   { STORAGE_ACTIVITY_SERVICE_CONTRACTID, &kSTORAGEACTIVITYSERVICE_CID },
   { NOTIFICATIONTELEMETRYSERVICE_CONTRACTID, &kNOTIFICATIONTELEMETRYSERVICE_CID },
   { PUSHNOTIFIER_CONTRACTID, &kPUSHNOTIFIER_CID },
   { WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID },
   { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
-  { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID },
   { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
   { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID },
   { "@mozilla.org/websocketevent/service;1", &kNS_WEBSOCKETEVENT_SERVICE_CID },
   { "@mozilla.org/focus-manager;1", &kNS_FOCUSMANAGER_CID },
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
   { NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake", &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID },
 #endif
 #ifdef MOZ_WEBSPEECH
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -268,16 +268,23 @@ nsFieldSetFrame::PaintBorder(
     // We want to avoid drawing our border under the legend, so clip out the
     // legend while drawing our border.  We don't want to use mLegendRect here,
     // because we do want to draw our border under the legend's inline-start and
     // -end margins.  And we use GetNormalRect(), not GetRect(), because we do
     // not want relative positioning applied to the legend to change how our
     // border looks.
     nsRect legendRect = legend->GetNormalRect() + aPt;
 
+    // Make sure we clip all of the border in case the legend is smaller.
+    nscoord borderTopWidth = GetUsedBorder().top;
+    if (legendRect.height < borderTopWidth) {
+      legendRect.height = borderTopWidth;
+      legendRect.y = aPt.y;
+    }
+
     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
     // We set up a clip path which has our rect clockwise and the legend rect
     // counterclockwise, with FILL_WINDING as the fill rule.  That will allow us
     // to paint within our rect but outside the legend rect.  For "our rect" we
     // use our visual overflow rect (relative to ourselves, so it's not affected
     // by transforms), because we can have borders sticking outside our border
     // box (e.g. due to border-image-outset).
     RefPtr<PathBuilder> pathBuilder =
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1861,17 +1861,17 @@ pref(layout.css.moz-document.content.ena
 == 1059498-3.html 1059498-1-ref.html
 == 1062108-1.html 1062108-1-ref.html
 == 1062792-1.html 1062792-1-ref.html
 == 1062963-floatmanager-reflow.html 1062963-floatmanager-reflow-ref.html
 test-pref(dom.webcomponents.shadowdom.enabled,true) == 1066554-1.html 1066554-1-ref.html
 == 1069716-1.html 1069716-1-ref.html
 == 1078262-1.html about:blank
 test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html
-== 1081185-1.html 1081185-1-ref.html
+fails-if(webrender) == 1081185-1.html 1081185-1-ref.html
 == 1097437-1.html 1097437-1-ref.html
 == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test
 == 1105137-1.html 1105137-1-ref.html
 fuzzy-if(d2d,0-36,0-304) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&d2d,0-139,0-701) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
 == 1111753-1.html about:blank
 == 1114526-1.html 1114526-1-ref.html
 fuzzy-if(skiaContent,0-1,0-800000) == 1119117-1a.html 1119117-1-ref.html
 fuzzy-if(skiaContent,0-1,0-800000) == 1119117-1b.html 1119117-1-ref.html
--- a/layout/tools/reftest/bootstrap.js
+++ b/layout/tools/reftest/bootstrap.js
@@ -19,19 +19,17 @@ function processTerminated() {
 
 var WindowListener = {
   onOpenWindow: function(xulWin) {
     Services.wm.removeListener(WindowListener);
 
     let win = xulWin.docShell.domWindow;
     win.addEventListener("load", function listener() {
       // Load into any existing windows.
-      let windows = Services.wm.getEnumerator("navigator:browser");
-      while (windows.hasMoreElements()) {
-        win = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+      for (win of Services.wm.getEnumerator("navigator:browser")) {
         break;
       }
 
       ChromeUtils.import("chrome://reftest/content/reftest.jsm");
       win.addEventListener("pageshow", function() {
         // Add setTimeout here because windows.innerWidth/Height are not set yet.
         win.setTimeout(function() {OnRefTestLoad(win);}, 0);
       }, {once: true});
--- a/mfbt/Tuple.h
+++ b/mfbt/Tuple.h
@@ -95,16 +95,19 @@ struct TupleImpl;
  * of an empty tuple).
  */
 template<std::size_t Index>
 struct TupleImpl<Index> {
   bool operator==(const TupleImpl<Index>& aOther) const
   {
     return true;
   }
+
+  template <typename F>
+  void ForEach(const F& aFunc) {}
 };
 
 /*
  * One node of the recursive inheritance hierarchy. It stores the element at
  * index 'Index' of a tuple, of type 'HeadT', and inherits from the nodes
  * that store the remaining elements, of types 'TailT...'.
  */
 template<std::size_t Index, typename HeadT, typename... TailT>
@@ -186,16 +189,37 @@ struct TupleImpl<Index, HeadT, TailT...>
     Head(*this) = std::move(Head(aOther));
     Tail(*this) = std::move(Tail(aOther));
     return *this;
   }
   bool operator==(const TupleImpl& aOther) const
   {
     return Head(*this) == Head(aOther) && Tail(*this) == Tail(aOther);
   }
+
+  template <typename F>
+  void ForEach(const F& aFunc) const &
+  {
+    aFunc(Head(*this));
+    Tail(*this).ForEach(aFunc);
+  }
+
+  template <typename F>
+  void ForEach(const F& aFunc) &
+  {
+    aFunc(Head(*this));
+    Tail(*this).ForEach(aFunc);
+  }
+
+  template <typename F>
+  void ForEach(const F& aFunc) &&
+  {
+    aFunc(std::move(Head(*this)));
+    std::move(Tail(*this)).ForEach(aFunc);
+  }
 private:
   HeadT mHead;  // The element stored at this index in the tuple.
 };
 
 } // namespace detail
 
 /**
  * Tuple is a class that stores zero or more objects, whose types are specified
@@ -416,16 +440,60 @@ auto Get(Tuple<Elements...>&& aTuple)
     -> decltype(std::move(mozilla::Get<Index>(aTuple)))
 {
   // We need a 'mozilla::' qualification here to avoid
   // name lookup only finding the current function.
   return std::move(mozilla::Get<Index>(aTuple));
 }
 
 /**
+ * Helpers which call a function for each member of the tuple in turn. This will
+ * typically be used with a lambda function with an `auto&` argument:
+ *
+ *   Tuple<Foo*, Bar*, SmartPtr<Baz>> tuple{a, b, c};
+ *
+ *   ForEach(tuple, [](auto& aElem) {
+ *     aElem = nullptr;
+ *   });
+ */
+
+template <typename F>
+inline void
+ForEach(const Tuple<>& aTuple, const F& aFunc)
+{
+}
+
+template <typename F>
+inline void
+ForEach(Tuple<>& aTuple, const F& aFunc)
+{
+}
+
+template <typename F, typename... Elements>
+void
+ForEach(const Tuple<Elements...>& aTuple, const F& aFunc)
+{
+  aTuple.ForEach(aTuple, aFunc);
+}
+
+template <typename F, typename... Elements>
+void
+ForEach(Tuple<Elements...>& aTuple, const F& aFunc)
+{
+  aTuple.ForEach(aFunc);
+}
+
+template <typename F, typename... Elements>
+void
+ForEach(Tuple<Elements...>&& aTuple, const F& aFunc)
+{
+  std::forward<Tuple<Elements...>>(aTuple).ForEach(aFunc);
+}
+
+/**
  * A convenience function for constructing a tuple out of a sequence of
  * values without specifying the type of the tuple.
  * The type of the tuple is deduced from the types of its elements.
  *
  * Example:
  *
  * auto tuple = MakeTuple(42, 0.5f, 'c');  // has type Tuple<int, float, char>
  */
--- a/mobile/android/components/DirectoryProvider.js
+++ b/mobile/android/components/DirectoryProvider.js
@@ -135,16 +135,19 @@ DirectoryProvider.prototype = {
     if (prop != NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)
       return null;
 
     let result = [];
     this._appendDistroSearchDirs(result);
 
     return {
       QueryInterface: ChromeUtils.generateQI([Ci.nsISimpleEnumerator]),
+      [Symbol.iterator]() {
+        return result.values();
+      },
       hasMoreElements: function() {
         return result.length > 0;
       },
       getNext: function() {
         return result.shift();
       }
     };
   },
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -1175,19 +1175,17 @@ SessionStore.prototype = {
           tabData.extData = browser.__SS_extdata;
         }
         winData.tabs.push(tabData);
       }
     }
   },
 
   _forEachBrowserWindow: function ss_forEachBrowserWindow(aFunc) {
-    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-    while (windowsEnum.hasMoreElements()) {
-      let window = windowsEnum.getNext();
+    for (let window of Services.wm.getEnumerator("navigator:browser")) {
       if (window.__SSID && !window.closed) {
         aFunc.call(this, window);
       }
     }
   },
 
   /**
    * Writes the session state to a disk file, while doing some telemetry and notification
--- a/mobile/android/components/extensions/ext-browsingData.js
+++ b/mobile/android/components/extensions/ext-browsingData.js
@@ -18,20 +18,17 @@ const clearCookies = async function(opti
   let cookieMgr = Services.cookies;
   let yieldCounter = 0;
   const YIELD_PERIOD = 10;
 
   if (options.since) {
     // Convert it to microseconds
     let since =  options.since * 1000;
     // Iterate through the cookies and delete any created after our cutoff.
-    let cookiesEnum = cookieMgr.enumerator;
-    while (cookiesEnum.hasMoreElements()) {
-      let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
-
+    for (let cookie of cookieMgr.enumerator) {
       if (cookie.creationTime >= since) {
         // This cookie was created after our cutoff, clear it.
         cookieMgr.remove(cookie.host, cookie.name, cookie.path,
                          false, cookie.originAttributes);
 
         if (++yieldCounter % YIELD_PERIOD == 0) {
           await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
         }
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
@@ -1,27 +1,35 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.geckoview.test
 
+import android.app.assist.AssistStructure
+import android.os.Build
 import org.mozilla.geckoview.GeckoResult
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.IgnoreCrash
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDevToolsAPI
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
 import org.mozilla.geckoview.test.util.Callbacks
 import org.mozilla.geckoview.test.util.UiThreadUtils
 
 import android.os.Looper
 import android.support.test.filters.MediumTest
+import android.support.test.filters.SdkSuppress
 import android.support.test.runner.AndroidJUnit4
+import android.text.InputType
+import android.util.SparseArray
+import android.view.View
+import android.view.ViewStructure
+import android.widget.EditText
 import org.hamcrest.Matchers.*
 import org.junit.Assume.assumeThat
 import org.junit.Test
 import org.junit.runner.RunWith
 
 import kotlin.concurrent.thread
 
 @RunWith(AndroidJUnit4::class)
@@ -179,9 +187,232 @@ class ContentDelegateTest : BaseSessionT
             Looper.loop()
         }
 
         worker.join(sessionRule.timeoutMillis)
         if (worker.isAlive) {
             throw UiThreadUtils.TimeoutException("Timed out")
         }
     }
+
+    val ViewNode by lazy {
+        AssistStructure.ViewNode::class.java.getDeclaredConstructor().apply { isAccessible = true }
+    }
+
+    val ViewNodeBuilder by lazy {
+        Class.forName("android.app.assist.AssistStructure\$ViewNodeBuilder")
+                .getDeclaredConstructor(AssistStructure::class.java,
+                                        AssistStructure.ViewNode::class.java,
+                                        Boolean::class.javaPrimitiveType)
+                .apply { isAccessible = true }
+    }
+
+    // TextInputDelegateTest is parameterized, so we put this test under ContentDelegateTest.
+    @SdkSuppress(minSdkVersion = 23)
+    @WithDevToolsAPI
+    @Test fun autofill() {
+        // Test parts of the Oreo auto-fill API; there is another autofill test in
+        // SessionAccessibility for a11y auto-fill support.
+        mainSession.loadTestPath(FORMS_HTML_PATH)
+        // Wait for the auto-fill nodes to populate.
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            // For the root document and the iframe document, each has a form group and
+            // a group for inputs outside of forms, so the total count is 4.
+            @AssertCalled(count = 4)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+            }
+        })
+
+        val autoFills = mapOf(
+                "#user1" to "bar", "#user2" to "bar") +
+                if (Build.VERSION.SDK_INT >= 26) mapOf(
+                        "#pass1" to "baz", "#pass2" to "baz", "#email1" to "a@b.c",
+                        "#number1" to "24", "#tel1" to "42")
+                else mapOf(
+                        "#pass1" to "bar", "#pass2" to "bar", "#email1" to "bar",
+                        "#number1" to "", "#tel1" to "bar")
+
+        // Set up promises to monitor the values changing.
+        val promises = autoFills.flatMap { entry ->
+            // Repeat each test with both the top document and the iframe document.
+            arrayOf("document", "$('#iframe').contentDocument").map { doc ->
+                mainSession.evaluateJS("""new Promise(resolve =>
+                $doc.querySelector('${entry.key}').addEventListener(
+                    'input', event => resolve([event.target.value, '${entry.value}']),
+                    { once: true }))""").asJSPromise()
+            }
+        }
+
+        val rootNode = ViewNode.newInstance()
+        val rootStructure = ViewNodeBuilder.newInstance(AssistStructure(), rootNode,
+                /* async */ false) as ViewStructure
+        val autoFillValues = SparseArray<CharSequence>()
+
+        // Perform auto-fill and return number of auto-fills performed.
+        fun checkAutoFillChild(child: AssistStructure.ViewNode) {
+            // Seal the node info instance so we can perform actions on it.
+            if (child.childCount > 0) {
+                for (i in 0 until child.childCount) {
+                    checkAutoFillChild(child.getChildAt(i))
+                }
+            }
+
+            if (child === rootNode) {
+                return
+            }
+
+            assertThat("ID should be valid", child.id, not(equalTo(View.NO_ID)))
+
+            if (Build.VERSION.SDK_INT >= 26) {
+                assertThat("Should have HTML tag",
+                           child.htmlInfo.tag, not(isEmptyOrNullString()))
+                assertThat("Web domain should match",
+                           child.webDomain, equalTo("android"))
+            }
+
+            if (EditText::class.java.name == child.className) {
+                assertThat("Input should be enabled", child.isEnabled, equalTo(true))
+                assertThat("Input should be focusable",
+                           child.isFocusable, equalTo(true))
+                assertThat("Input should be visible",
+                           child.visibility, equalTo(View.VISIBLE))
+
+                if (Build.VERSION.SDK_INT < 26) {
+                    autoFillValues.append(child.id, "bar")
+                    return
+                }
+
+                val htmlInfo = child.htmlInfo
+                assertThat("Should have HTML tag", htmlInfo.tag, equalTo("input"))
+                assertThat("Should have ID attribute",
+                           htmlInfo.attributes.map { it.first }, hasItem("id"))
+
+                assertThat("Autofill type should match",
+                           child.autofillType, equalTo(View.AUTOFILL_TYPE_TEXT))
+
+                assertThat("Autofill hints should match", child.autofillHints, equalTo(
+                        when (child.inputType) {
+                            InputType.TYPE_CLASS_TEXT or
+                                    InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD ->
+                                arrayOf(View.AUTOFILL_HINT_PASSWORD)
+                            InputType.TYPE_CLASS_TEXT or
+                                    InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ->
+                                arrayOf(View.AUTOFILL_HINT_EMAIL_ADDRESS)
+                            InputType.TYPE_CLASS_PHONE -> arrayOf(View.AUTOFILL_HINT_PHONE)
+                            else -> null
+                        }))
+
+                autoFillValues.append(child.id, when (child.inputType) {
+                    InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD -> "baz"
+                    InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS -> "a@b.c"
+                    InputType.TYPE_CLASS_NUMBER -> "24"
+                    InputType.TYPE_CLASS_PHONE -> "42"
+                    else -> "bar"
+                })
+            }
+        }
+
+        mainSession.textInput.onProvideAutofillVirtualStructure(rootStructure, 0)
+        checkAutoFillChild(rootNode)
+        mainSession.textInput.autofill(autoFillValues)
+
+        // Wait on the promises and check for correct values.
+        for ((actual, expected) in promises.map { it.value.asJSList<String>() }) {
+            assertThat("Auto-filled value must match", actual, equalTo(expected))
+        }
+    }
+
+    // TextInputDelegateTest is parameterized, so we put this test under ContentDelegateTest.
+    @SdkSuppress(minSdkVersion = 23)
+    @WithDevToolsAPI
+    @WithDisplay(width = 100, height = 100)
+    @Test fun autoFill_navigation() {
+
+        fun countAutoFillNodes(cond: (AssistStructure.ViewNode) -> Boolean =
+                                       { it.className == "android.widget.EditText" },
+                               root: AssistStructure.ViewNode? = null): Int {
+            val node = if (root !== null) root else ViewNode.newInstance().also {
+                // Fill the nodes first.
+                val structure = ViewNodeBuilder.newInstance(
+                        AssistStructure(), it, /* async */ false) as ViewStructure
+                mainSession.textInput.onProvideAutofillVirtualStructure(structure, 0)
+            }
+            return (if (cond(node)) 1 else 0) +
+                    (if (node.childCount > 0) (0 until node.childCount).sumBy {
+                        countAutoFillNodes(cond, node.getChildAt(it)) } else 0)
+        }
+
+        // Wait for the accessibility nodes to populate.
+        mainSession.loadTestPath(FORMS_HTML_PATH)
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            @AssertCalled(count = 4)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+                assertThat("Should be starting auto-fill", notification, equalTo(forEachCall(
+                        GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_STARTED,
+                        GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_VIEW_ADDED)))
+                assertThat("ID should be valid", virtualId, not(equalTo(View.NO_ID)))
+            }
+        })
+        assertThat("Initial auto-fill count should match",
+                   countAutoFillNodes(), equalTo(14))
+
+        // Now wait for the nodes to clear.
+        mainSession.loadTestPath(HELLO_HTML_PATH)
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            @AssertCalled(count = 1)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+                assertThat("Should be canceling auto-fill",
+                           notification,
+                           equalTo(GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_CANCELED))
+                assertThat("ID should be valid", virtualId, equalTo(View.NO_ID))
+            }
+        })
+        assertThat("Should not have auto-fill fields",
+                   countAutoFillNodes(), equalTo(0))
+
+        // Now wait for the nodes to reappear.
+        mainSession.waitForPageStop()
+        mainSession.goBack()
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            @AssertCalled(count = 4)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+                assertThat("Should be starting auto-fill", notification, equalTo(forEachCall(
+                        GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_STARTED,
+                        GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_VIEW_ADDED)))
+                assertThat("ID should be valid", virtualId, not(equalTo(View.NO_ID)))
+            }
+        })
+        assertThat("Should have auto-fill fields again",
+                   countAutoFillNodes(), equalTo(14))
+        assertThat("Should not have focused field",
+                   countAutoFillNodes({ it.isFocused }), equalTo(0))
+
+        mainSession.evaluateJS("$('#pass1').focus()")
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            @AssertCalled(count = 1)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+                assertThat("Should be entering auto-fill view",
+                           notification,
+                           equalTo(GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_VIEW_ENTERED))
+                assertThat("ID should be valid", virtualId, not(equalTo(View.NO_ID)))
+            }
+        })
+        assertThat("Should have one focused field",
+                   countAutoFillNodes({ it.isFocused }), equalTo(1))
+        // The focused field, its siblings, and its parent should be visible.
+        assertThat("Should have at least six visible fields",
+                   countAutoFillNodes({ node -> node.width > 0 && node.height > 0 }),
+                   greaterThanOrEqualTo(6))
+
+        mainSession.evaluateJS("$('#pass1').blur()")
+        sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
+            @AssertCalled(count = 1)
+            override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
+                assertThat("Should be exiting auto-fill view",
+                           notification,
+                           equalTo(GeckoSession.TextInputDelegate.AUTO_FILL_NOTIFY_VIEW_EXITED))
+                assertThat("ID should be valid", virtualId, not(equalTo(View.NO_ID)))
+            }
+        })
+        assertThat("Should not have focused field",
+                   countAutoFillNodes({ it.isFocused }), equalTo(0))
+    }
 }
--- a/mobile/android/modules/geckoview/GeckoViewUtils.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewUtils.jsm
@@ -340,19 +340,17 @@ var GeckoViewUtils = {
 
   getActiveDispatcherAndWindow: function() {
     let win = Services.focus.activeWindow;
     let dispatcher = this.getDispatcherForWindow(win);
     if (dispatcher) {
       return [dispatcher, win];
     }
 
-    let iter = Services.wm.getEnumerator(/* windowType */ null);
-    while (iter.hasMoreElements()) {
-      win = iter.getNext().QueryInterface(Ci.nsIDOMWindow);
+    for (let win of Services.wm.getEnumerator(/* windowType */ null)) {
       dispatcher = this.getDispatcherForWindow(win);
       if (dispatcher) {
         return [dispatcher, win];
       }
     }
     return [null, null];
   },
 
--- a/mobile/android/tests/browser/robocop/testFilePicker.js
+++ b/mobile/android/tests/browser/robocop/testFilePicker.js
@@ -19,30 +19,26 @@ add_test(function filepicker_open() {
   fp.appendFilters(Ci.nsIFilePicker.filterAll);
   fp.filterIndex = 0;
 
   let fpCallback = function(result) {
     if (result == Ci.nsIFilePicker.returnOK || result == Ci.nsIFilePicker.returnReplace) {
       do_print("File: " + fp.file.path);
       is(fp.file.path, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file!");
 
-      let files = fp.files;
-      while (files.hasMoreElements()) {
-        let file = files.getNext().QueryInterface(Ci.nsIFile);
+      for (let file of fp.files) {
         do_print("File: " + file.path);
         is(file.path, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file from array!");
       }
 
       let file = fp.domFileOrDirectory;
       do_print("DOMFile: " + file.mozFullPath);
       is(file.mozFullPath, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian DOM File!");
 
-      let e = fp.domFileOrDirectoryEnumerator;
-      while (e.hasMoreElements()) {
-        let domFile = e.getNext();
+      for (let domFile of fp.domFileOrDirectoryEnumerator) {
         do_print("DOMFile: " + domFile.mozFullPath);
         is(domFile.mozFullPath, "/mnt/sdcard/my-favorite-martian.png", "Retrieve the right martian file from domFileOrDirectoryEnumerator array!");
       }
 
       do_test_finished();
 
       run_next_test();
     }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -919,16 +919,17 @@ pref("gfx.webrender.debug.profiler", fal
 pref("gfx.webrender.debug.gpu-time-queries", false);
 pref("gfx.webrender.debug.gpu-sample-queries", false);
 pref("gfx.webrender.debug.disable-batching", false);
 pref("gfx.webrender.debug.epochs", false);
 pref("gfx.webrender.debug.compact-profiler", false);
 pref("gfx.webrender.debug.echo-driver-messages", false);
 pref("gfx.webrender.debug.new-frame-indicator", false);
 pref("gfx.webrender.debug.new-scene-indicator", false);
+pref("gfx.webrender.debug.show-overdraw", false);
 pref("gfx.webrender.dl.dump-parent", false);
 pref("gfx.webrender.dl.dump-content", false);
 
 pref("accessibility.browsewithcaret", false);
 pref("accessibility.warn_on_browsewithcaret", true);
 
 pref("accessibility.browsewithcaret_shortcut.enabled", true);
 
--- a/mozglue/misc/StackWalk.cpp
+++ b/mozglue/misc/StackWalk.cpp
@@ -202,36 +202,40 @@ WalkStackMain64(struct WalkStackData* aD
       ::RtlCaptureContext(context);
     } else if (!GetThreadContext(aData->thread, context)) {
       return;
     }
   } else {
     context = aData->context;
   }
 
-#if defined(_M_IX86) || defined(_M_IA64)
+#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_ARM64)
   // Setup initial stack frame to walk from.
   STACKFRAME64 frame64;
   memset(&frame64, 0, sizeof(frame64));
 #ifdef _M_IX86
   frame64.AddrPC.Offset    = context->Eip;
   frame64.AddrStack.Offset = context->Esp;
   frame64.AddrFrame.Offset = context->Ebp;
 #elif defined _M_IA64
   frame64.AddrPC.Offset    = context->StIIP;
   frame64.AddrStack.Offset = context->SP;
   frame64.AddrFrame.Offset = context->RsBSP;
+#elif defined _M_ARM64
+  frame64.AddrPC.Offset    = context->Pc;
+  frame64.AddrStack.Offset = context->Sp;
+  frame64.AddrFrame.Offset = context->Fp;
 #endif
   frame64.AddrPC.Mode      = AddrModeFlat;
   frame64.AddrStack.Mode   = AddrModeFlat;
   frame64.AddrFrame.Mode   = AddrModeFlat;
   frame64.AddrReturn.Mode  = AddrModeFlat;
 #endif
 
-#ifdef _WIN64
+#ifdef _M_AMD64
   // If there are any active suppressions, then at least one thread (we don't
   // know which) is holding a lock that can deadlock RtlVirtualUnwind. Since
   // that thread may be the one that we're trying to unwind, we can't proceed.
   //
   // But if there are no suppressions, then our target thread can't be holding
   // a lock, and it's safe to proceed. By virtue of being suspended, the target
   // thread can't acquire any new locks during the unwind process, so we only
   // need to do this check once. After that, sStackWalkSuppressions can be
@@ -249,25 +253,27 @@ WalkStackMain64(struct WalkStackData* aD
   // Skip our own stack walking frames.
   int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
 
   // Now walk the stack.
   while (true) {
     DWORD64 addr;
     DWORD64 spaddr;
 
-#if defined(_M_IX86) || defined(_M_IA64)
+#if defined(_M_IX86) || defined(_M_IA64) || defined(_M_ARM64)
     // 32-bit frame unwinding.
     // Debug routines are not threadsafe, so grab the lock.
     EnterCriticalSection(&gDbgHelpCS);
     BOOL ok = StackWalk64(
 #if defined _M_IA64
       IMAGE_FILE_MACHINE_IA64,
 #elif defined _M_IX86
       IMAGE_FILE_MACHINE_I386,
+#elif defined _M_ARM64
+      IMAGE_FILE_MACHINE_ARM64,
 #endif
       aData->process,
       aData->thread,
       &frame64,
       context,
       nullptr,
       SymFunctionTableAccess64, // function table access routine
       SymGetModuleBase64,       // module base routine
--- a/mozglue/misc/StackWalk.h
+++ b/mozglue/misc/StackWalk.h
@@ -42,17 +42,18 @@ typedef void
  * May skip some stack frames due to compiler optimizations or code
  * generation.
  */
 MFBT_API void
 MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
              uint32_t aMaxFrames, void* aClosure);
 
 #if defined(_WIN32) && \
-    (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64))
+    (defined(_M_IX86) || defined(_M_AMD64) || \
+     defined(_M_IA64) || defined(_M_ARM64))
 
 #include <windows.h>
 
 #define MOZ_STACKWALK_SUPPORTS_WINDOWS 1
 
 /**
  * Like MozStackWalk, but walks the stack for another thread.
  * Call aCallback for each stack frame on the current thread, from
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -649,17 +649,17 @@ nsLoadGroup::GetRequests(nsISimpleEnumer
     nsCOMArray<nsIRequest> requests;
     requests.SetCapacity(mRequests.EntryCount());
 
     for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
       auto e = static_cast<RequestMapEntry*>(iter.Get());
       requests.AppendObject(e->mKey);
     }
 
-    return NS_NewArrayEnumerator(aRequests, requests);
+    return NS_NewArrayEnumerator(aRequests, requests, NS_GET_IID(nsIRequest));
 }
 
 NS_IMETHODIMP
 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver)
 {
     mObserver = do_GetWeakReference(aObserver);
     return NS_OK;
 }
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -3849,17 +3849,17 @@ CacheFileIOManager::GetDoomedFile(nsIFil
 
 nsresult
 CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
 {
   MOZ_ASSERT(mIOThread->IsCurrentThread());
 
   nsresult rv;
 
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  nsCOMPtr<nsIDirectoryEnumerator> enumerator;
   rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMoreElements = false;
   rv = enumerator->HasMoreElements(&hasMoreElements);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *_retval = !hasMoreElements;
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2488,17 +2488,17 @@ nsCookieService::GetEnumerator(nsISimple
   nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
     const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
     for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
       cookieList.AppendObject(cookies[i]);
     }
   }
 
-  return NS_NewArrayEnumerator(aEnumerator, cookieList);
+  return NS_NewArrayEnumerator(aEnumerator, cookieList, NS_GET_IID(nsICookie2));
 }
 
 NS_IMETHODIMP
 nsCookieService::GetSessionEnumerator(nsISimpleEnumerator **aEnumerator)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
@@ -2513,17 +2513,17 @@ nsCookieService::GetSessionEnumerator(ns
       nsCookie* cookie = cookies[i];
       // Filter out non-session cookies.
       if (cookie->IsSession()) {
         cookieList.AppendObject(cookie);
       }
     }
   }
 
-  return NS_NewArrayEnumerator(aEnumerator, cookieList);
+  return NS_NewArrayEnumerator(aEnumerator, cookieList, NS_GET_IID(nsICookie2));
 }
 
 static nsresult
 InitializeOriginAttributes(OriginAttributes* aAttrs,
                            JS::HandleValue aOriginAttributes,
                            JSContext* aCx,
                            uint8_t aArgc,
                            const char16_t* aAPI,
@@ -4923,17 +4923,17 @@ nsCookieService::GetCookiesFromHost(cons
     return NS_NewEmptyEnumerator(aEnumerator);
 
   nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
     cookieList.AppendObject(cookies[i]);
   }
 
-  return NS_NewArrayEnumerator(aEnumerator, cookieList);
+  return NS_NewArrayEnumerator(aEnumerator, cookieList, NS_GET_IID(nsICookie2));
 }
 
 NS_IMETHODIMP
 nsCookieService::GetCookiesWithOriginAttributes(const nsAString&    aPattern,
                                                 const nsACString&   aHost,
                                                 nsISimpleEnumerator **aEnumerator)
 {
   mozilla::OriginAttributesPattern pattern;
@@ -4982,17 +4982,17 @@ nsCookieService::GetCookiesWithOriginAtt
 
     const nsCookieEntry::ArrayType& entryCookies = entry->GetCookies();
 
     for (nsCookieEntry::IndexType i = 0; i < entryCookies.Length(); ++i) {
       cookies.AppendObject(entryCookies[i]);
     }
   }
 
-  return NS_NewArrayEnumerator(aEnumerator, cookies);
+  return NS_NewArrayEnumerator(aEnumerator, cookies, NS_GET_IID(nsICookie2));
 }
 
 NS_IMETHODIMP
 nsCookieService::RemoveCookiesWithOriginAttributes(const nsAString& aPattern,
                                                    const nsACString& aHost)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
--- a/netwerk/cookie/test/browser/browser_originattributes.js
+++ b/netwerk/cookie/test/browser/browser_originattributes.js
@@ -79,18 +79,17 @@ async function checkCookies(expectedValu
     }
 
   }
 }
 
 function getCookiesFromManager(userContextId) {
   let cookies = {};
   let enumerator = cm.getCookiesWithOriginAttributes(JSON.stringify({userContextId}));
-  while (enumerator.hasMoreElements()) {
-    let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+  for (let cookie of enumerator) {
     cookies[cookie.name] = cookie.value;
   }
   return cookies;
 }
 
 async function getCookiesFromJS(userContextId) {
   let {tab, browser} = await openTabInUserContext(TEST_URL, userContextId);
 
--- a/netwerk/cookie/test/unit/test_bug1267910.js
+++ b/netwerk/cookie/test/unit/test_bug1267910.js
@@ -50,22 +50,20 @@ function checkCookie(cookie, cookieObj) 
     } else {
       equal(cookie[prop], cookieObj[prop], "Check cookie: " + prop);
     }
   }
 }
 
 function countCookies(enumerator) {
   let cnt = 0;
-
-  while (enumerator.hasMoreElements()) {
+  for (let cookie of enumerator) {
+    void cookie;
     cnt++;
-    enumerator.getNext();
   }
-
   return cnt;
 }
 
 function run_test() {
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // Enable user context id
--- a/netwerk/cookie/test/unit/test_eviction.js
+++ b/netwerk/cookie/test/unit/test_eviction.js
@@ -218,20 +218,18 @@ async function test_basic_eviction(base_
     // or have one that matches subdomains.
     // They will therefore be evicted from oldest to newest if all new cookies added share
     // similar characteristics.
 }
 
 // Verify that the given cookie names exist, and are ordered from least to most recently accessed
 function verifyCookies(names, uri) {
     Assert.equal(cm.countCookiesFromHost(uri.host), names.length);
-    let cookies = cm.getCookiesFromHost(uri.host, {});
     let actual_cookies = [];
-    while (cookies.hasMoreElements()) {
-        let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
+    for (let cookie of cm.getCookiesFromHost(uri.host, {})) {
         actual_cookies.push(cookie);
     }
     if (names.length != actual_cookies.length) {
         let left = names.filter(function(n) {
             return actual_cookies.findIndex(function(c) {
                 return c.name == n;
             }) == -1;
         });
--- a/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
+++ b/netwerk/dns/mdns/libmdns/MulticastDNSAndroid.jsm
@@ -141,19 +141,17 @@ ServiceManager.prototype = {
 
 // make an object from nsIPropertyBag2
 function parsePropertyBag2(bag) {
   if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
     throw new TypeError("Not a property bag");
   }
 
   let attributes = [];
-  let enumerator = bag.enumerator;
-  while (enumerator.hasMoreElements()) {
-    let name = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
+  for (let {name} of bag.enumerator) {
     let value = bag.getPropertyAsACString(name);
     attributes.push({
       "name": name,
       "value": value
     });
   }
 
   return attributes;
--- a/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
+++ b/netwerk/dns/mdns/libmdns/fallback/MulticastDNS.jsm
@@ -833,19 +833,17 @@ function _parseServiceDomainName(service
 
 /**
  * @private
  */
 function _propertyBagToObject(propBag) {
   let result = {};
   if (propBag.QueryInterface) {
     propBag.QueryInterface(Ci.nsIPropertyBag2);
-    let propEnum = propBag.enumerator;
-    while (propEnum.hasMoreElements()) {
-      let prop = propEnum.getNext().QueryInterface(Ci.nsIProperty);
+    for (let prop of propBag.enumerator) {
       result[prop.name] = prop.value.toString();
     }
   } else {
     for (let name in propBag) {
       result[name] = propBag[name].toString();
     }
   }
   return result;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -448,16 +448,17 @@ protected:
   void AddCookiesToRequest();
   virtual MOZ_MUST_USE nsresult
   SetupReplacementChannel(nsIURI *, nsIChannel *, bool preserveMethod,
                           uint32_t redirectFlags);
 
   // bundle calling OMR observers and marking flag into one function
   inline void CallOnModifyRequestObservers() {
     gHttpHandler->OnModifyRequest(this);
+    MOZ_ASSERT(!mRequestObserversCalled);
     mRequestObserversCalled = true;
   }
 
   // Helper function to simplify getting notification callbacks.
   template <class T>
   void GetCallback(nsCOMPtr<T> &aResult)
   {
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7057,16 +7057,18 @@ nsHttpChannel::GetRequestMethod(nsACStri
 // nsHttpChannel::nsIRequestObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
 {
     nsresult rv;
 
+    MOZ_ASSERT(mRequestObserversCalled);
+
     AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
 
     if (!(mCanceled || NS_FAILED(mStatus)) && !WRONG_RACING_RESPONSE_SOURCE(request)) {
         // capture the request's status, so our consumers will know ASAP of any
         // connection failures, etc - bug 93581
         nsresult status;
         request->GetStatus(&status);
         mStatus = status;
@@ -8246,16 +8248,20 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnec
 
     MOZ_ASSERT(!mTransaction, "should not have a transaction");
     nsresult rv;
 
     // toggle mIsPending to allow nsIObserver implementations to modify
     // the request headers (bug 95044).
     mIsPending = false;
 
+    // Reset mRequestObserversCalled because we've probably called the request
+    // observers once already.
+    mRequestObserversCalled = false;
+
     // fetch cookies, and add them to the request header.
     // the server response could have included cookies that must be sent with
     // this authentication attempt (bug 84794).
     // TODO: save cookies from auth response and send them here (bug 572151).
     AddCookiesToRequest();
 
     // notify "http-on-modify-request" observers
     CallOnModifyRequestObservers();
--- a/netwerk/test/httpserver/httpd.js
+++ b/netwerk/test/httpserver/httpd.js
@@ -5125,16 +5125,19 @@ nsSimpleEnumerator.prototype =
   },
   getNext: function()
   {
     if (!this.hasMoreElements())
       throw Components.Exception("", Cr.NS_ERROR_NOT_AVAILABLE);
 
     return this._items[this._nextIndex++];
   },
+  [Symbol.iterator]() {
+    return this._items.values();
+  },
   QueryInterface: function(aIID)
   {
     if (Ci.nsISimpleEnumerator.equals(aIID) ||
         Ci.nsISupports.equals(aIID))
       return this;
 
     throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
   }
--- a/netwerk/test/unit/test_bug411952.js
+++ b/netwerk/test/unit/test_bug411952.js
@@ -3,19 +3,18 @@ function run_test() {
     var cm = Cc["@mozilla.org/cookiemanager;1"].
                getService(Ci.nsICookieManager);
     Assert.notEqual(cm, null, "Retrieving the cookie manager failed");
 
     const time = (new Date("Jan 1, 2030")).getTime() / 1000;
     cm.add("example.com", "/", "C", "V", false, true, false, time, {});
     const now = Math.floor((new Date()).getTime() / 1000);
 
-    var enumerator = cm.enumerator, found = false;
-    while (enumerator.hasMoreElements()) {
-      var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+    var found = false;
+    for (let cookie of cm.enumerator) {
       if (cookie.host == "example.com" &&
           cookie.path == "/" &&
           cookie.name == "C") {
         Assert.ok("creationTime" in cookie,
           "creationTime attribute is not accessible on the cookie");
         var creationTime = Math.floor(cookie.creationTime / 1000000);
         // allow the times to slip by one second at most,
         // which should be fine under normal circumstances.
--- a/netwerk/test/unit/test_network_activity.js
+++ b/netwerk/test/unit/test_network_activity.js
@@ -39,20 +39,17 @@ function doTest() {
     ok(results[0].rx > 0 || results[0].tx > 0);
     httpserver.stop(do_test_finished);
 }
 
 function run_test() {
   // setting up an observer
   let networkActivity = function(subject, topic, value) {
     subject.QueryInterface(Ci.nsIMutableArray);
-    let enumerator = subject.enumerate();
-    while (enumerator.hasMoreElements()) {
-        let data = enumerator.getNext();
-        data.QueryInterface(Ci.nsINetworkActivityData);
+    for (let data of subject.enumerate()) {
         results.push(data);
     }
   };
 
   Services.obs.addObserver(networkActivity, 'network-activity');
 
   // send events every 100ms
   prefs.setIntPref("network.activity.intervalMilliseconds", 100);
--- a/netwerk/test/unit/test_permmgr.js
+++ b/netwerk/test/unit/test_permmgr.js
@@ -56,23 +56,17 @@ function run_test() {
     let uri = ioService.newURI(results[i][0]);
     let principal = secMan.createCodebasePrincipal(uri, {});
 
     Assert.equal(pm.testPermissionFromPrincipal(principal, results[i][1]), results[i][2]);
     Assert.equal(pm.testExactPermissionFromPrincipal(principal, results[i][1]), results[i][3]);
   }
 
   // test the enumerator ...
-  var j = 0;
-  var perms = new Array();
-  var enumerator = pm.enumerator;
-  while (enumerator.hasMoreElements()) {
-    perms[j] = enumerator.getNext().QueryInterface(Ci.nsIPermission);
-    ++j;
-  }
+  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 ...
--- a/old-configure.in
+++ b/old-configure.in
@@ -540,16 +540,23 @@ if test "$GNU_CC"; then
 
     AC_MSG_CHECKING([for -z relro option to ld])
     _SAVE_LDFLAGS=$LDFLAGS
     LDFLAGS="$LDFLAGS -Wl,-z,relro"
     AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
                   AC_MSG_RESULT([no])
                   LDFLAGS=$_SAVE_LDFLAGS)
 
+    AC_MSG_CHECKING([for -z nocopyreloc option to ld])
+    _SAVE_LDFLAGS=$LDFLAGS
+    LDFLAGS="$LDFLAGS -Wl,-z,nocopyreloc"
+    AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
+                  AC_MSG_RESULT([no])
+                  LDFLAGS=$_SAVE_LDFLAGS)
+
     AC_MSG_CHECKING([for --build-id option to ld])
     _SAVE_LDFLAGS=$LDFLAGS
     LDFLAGS="$LDFLAGS -Wl,--build-id"
     AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
                   AC_MSG_RESULT([no])
                   LDFLAGS=$_SAVE_LDFLAGS)
 
     AC_MSG_CHECKING([for --ignore-unresolved-symbol option to ld])
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -35,19 +35,17 @@ function doPrompt(msg) {
   Services.prompt.alert(window, null, msg);
 }
 
 function doConfirm(msg) {
   return Services.prompt.confirm(window, null, msg);
 }
 
 function RefreshDeviceList() {
-  let modules = secmoddb.listModules();
-  for (let module of XPCOMUtils.IterSimpleEnumerator(modules,
-                                                     Ci.nsIPKCS11Module)) {
+  for (let module of secmoddb.listModules()) {
     let slots = module.listSlots();
     AddModule(module, slots);
   }
 
   // Set the text on the FIPS button.
   SetFIPSButton();
 }
 
@@ -76,17 +74,17 @@ function AddModule(module, slots) {
   var tree = document.getElementById("device_list");
   var item  = document.createElement("treeitem");
   var row  = document.createElement("treerow");
   var cell = document.createElement("treecell");
   cell.setAttribute("label", module.name);
   row.appendChild(cell);
   item.appendChild(row);
   var parent = document.createElement("treechildren");
-  for (let slot of XPCOMUtils.IterSimpleEnumerator(slots, Ci.nsIPKCS11Slot)) {
+  for (let slot of slots) {
     var child_item = document.createElement("treeitem");
     var child_row = document.createElement("treerow");
     var child_cell = document.createElement("treecell");
     child_cell.setAttribute("label", slot.name);
     child_row.appendChild(child_cell);
     child_item.appendChild(child_row);
     child_item.setAttribute("pk11kind", "slot");
     // 'slot' is an attribute on any HTML element, hence 'slotObject' instead.
--- a/security/manager/pki/resources/content/pippki.js
+++ b/security/manager/pki/resources/content/pippki.js
@@ -283,19 +283,13 @@ function getBestChain(results) {
  *        A numerical value corresponding to a usage. See `certificateUsages`.
  * @returns {Array} An array of `nsIX509Cert` representing the verified
  *          certificate chain for the given usage, or null if there is none.
  */
 function getChainForUsage(results, usage) {
   for (let result of results) {
     if (certificateUsages[result.usageString] == usage &&
         result.errorCode == PRErrorCodeSuccess) {
-      let array = [];
-      let enumerator = result.chain.getEnumerator();
-      while (enumerator.hasMoreElements()) {
-        let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
-        array.push(cert);
-      }
-      return array;
+      return Array.from(result.chain.getEnumerator());
     }
   }
   return null;
 }
--- a/security/manager/ssl/PKCS11ModuleDB.cpp
+++ b/security/manager/ssl/PKCS11ModuleDB.cpp
@@ -187,17 +187,17 @@ PKCS11ModuleDB::ListModules(nsISimpleEnu
        list = list->next) {
     nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
     nsresult rv = array->AppendElement(module);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
-  return array->Enumerate(_retval);
+  return array->Enumerate(_retval, NS_GET_IID(nsIPKCS11Module));
 }
 
 NS_IMETHODIMP
 PKCS11ModuleDB::GetCanToggleFIPS(bool* aCanToggleFIPS)
 {
   NS_ENSURE_ARG_POINTER(aCanToggleFIPS);
 
   *aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -1215,18 +1215,16 @@ nsNSSCertList::GetRootCertificate(/* out
   // Duplicates the certificate
   aRoot = nsNSSCertificate::Create(rootNode->cert);
   if (!aRoot) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(nsNSSCertListEnumerator, nsISimpleEnumerator)
-
 nsNSSCertListEnumerator::nsNSSCertListEnumerator(
   const UniqueCERTCertList& certList)
 {
   MOZ_ASSERT(certList);
   mCertList = nsNSSCertList::DupCertList(certList);
 }
 
 NS_IMETHODIMP
--- a/security/manager/ssl/nsNSSCertificate.h
+++ b/security/manager/ssl/nsNSSCertificate.h
@@ -10,20 +10,20 @@
 #include <vector>
 
 #include "ScopedNSSTypes.h"
 #include "certt.h"
 #include "nsCOMPtr.h"
 #include "nsIASN1Object.h"
 #include "nsIClassInfo.h"
 #include "nsISerializable.h"
-#include "nsISimpleEnumerator.h"
 #include "nsIX509Cert.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509CertList.h"
+#include "nsSimpleEnumerator.h"
 #include "nsStringFwd.h"
 
 namespace mozilla { namespace pkix { class DERArray; } }
 
 class nsINSSComponent;
 class nsIASN1Sequence;
 
 class nsNSSCertificate final : public nsIX509Cert
@@ -118,22 +118,23 @@ private:
    virtual ~nsNSSCertList() {}
 
    mozilla::UniqueCERTCertList mCertList;
 
    nsNSSCertList(const nsNSSCertList&) = delete;
    void operator=(const nsNSSCertList&) = delete;
 };
 
-class nsNSSCertListEnumerator : public nsISimpleEnumerator
+class nsNSSCertListEnumerator : public nsSimpleEnumerator
 {
 public:
-   NS_DECL_THREADSAFE_ISUPPORTS
    NS_DECL_NSISIMPLEENUMERATOR
 
+   const nsID& DefaultInterface() override { return NS_GET_IID(nsIX509Cert); }
+
    explicit nsNSSCertListEnumerator(const mozilla::UniqueCERTCertList& certList);
 private:
    virtual ~nsNSSCertListEnumerator() {}
 
    mozilla::UniqueCERTCertList mCertList;
 
    nsNSSCertListEnumerator(const nsNSSCertListEnumerator&) = delete;
    void operator=(const nsNSSCertListEnumerator&) = delete;
--- a/security/manager/ssl/nsPKCS11Slot.cpp
+++ b/security/manager/ssl/nsPKCS11Slot.cpp
@@ -285,10 +285,10 @@ nsPKCS11Module::ListSlots(nsISimpleEnume
       nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(mModule->slots[i]);
       rv = array->AppendElement(slot);
       if (NS_FAILED(rv)) {
         return rv;
       }
     }
   }
 
-  return array->Enumerate(_retval);
+  return array->Enumerate(_retval, NS_GET_IID(nsIPKCS11Slot));
 }
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -468,17 +468,17 @@ SiteHPKPState::GetSha256Keys(nsISimpleEn
     nsresult rv = variant->SetAsAUTF8String(key);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (!keys.AppendObject(variant)) {
       return NS_ERROR_FAILURE;
     }
   }
-  return NS_NewArrayEnumerator(aSha256Keys, keys);
+  return NS_NewArrayEnumerator(aSha256Keys, keys, NS_GET_IID(nsIVariant));
 }
 
 NS_IMETHODIMP
 SiteHPKPState::GetOriginAttributes(JSContext* aCx,
   JS::MutableHandle<JS::Value> aOriginAttributes)
 {
   if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
     return NS_ERROR_FAILURE;
@@ -1890,17 +1890,17 @@ nsSiteSecurityService::Enumerate(uint32_
         break;
       default:
         MOZ_ASSERT_UNREACHABLE("SSS:Enumerate got invalid type");
     }
 
     states.AppendObject(state);
   }
 
-  NS_NewArrayEnumerator(aEnumerator, states);
+  NS_NewArrayEnumerator(aEnumerator, states, NS_GET_IID(nsISiteSecurityState));
   return NS_OK;
 }
 
 //------------------------------------------------------------
 // nsSiteSecurityService::nsIObserver
 //------------------------------------------------------------
 
 NS_IMETHODIMP
--- a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
@@ -85,19 +85,17 @@ function checkDialogContents(win, notBef
                "Issued by: OU=Profile Guided Optimization,O=Mozilla Testing," +
                "CN=Temporary Certificate Authority",
                "Actual and expected issuer should be equal");
   Assert.equal(tokenName, "Stored on: Software Security Device",
                "Actual and expected token name should be equal");
 }
 
 function findCertByCommonName(commonName) {
-  let certEnumerator = certDB.getCerts().getEnumerator();
-  while (certEnumerator.hasMoreElements()) {
-    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of certDB.getCerts().getEnumerator()) {
     if (cert.commonName == commonName) {
       return cert;
     }
   }
   return null;
 }
 
 add_task(async function setup() {
--- a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
@@ -12,19 +12,17 @@ const gCertDB = Cc["@mozilla.org/securit
                   .getService(Ci.nsIX509CertDB);
 
 const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
 const CERT_COMMON_NAME = "test_cert_from_windows";
 const TEST_CERT_PASSWORD = "黒い";
 const TEST_OUTPUT_PASSWORD = "other password";
 
 function findCertByCommonName(commonName) {
-  let certEnumerator = gCertDB.getCerts().getEnumerator();
-  while (certEnumerator.hasMoreElements()) {
-    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of gCertDB.getCerts().getEnumerator()) {
     if (cert.commonName == commonName) {
       return cert;
     }
   }
   return null;
 }
 
 function run_test() {
--- a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
@@ -40,19 +40,17 @@ var gPrompt = {
 };
 
 const gPromptFactory = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptFactory]),
   getPrompt: (aWindow, aIID) => gPrompt,
 };
 
 function findCertByCommonName(commonName) {
-  let certEnumerator = gCertDB.getCerts().getEnumerator();
-  while (certEnumerator.hasMoreElements()) {
-    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of gCertDB.getCerts().getEnumerator()) {
     if (cert.commonName == commonName) {
       return cert;
     }
   }
   return null;
 }
 
 function run_test() {
--- a/security/manager/ssl/tests/unit/test_certDB_import.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import.js
@@ -60,19 +60,17 @@ function getCertAsByteArray(certPath) {
   for (let i = 0; i < certBytes.length; i++) {
     byteArray.push(certBytes.charCodeAt(i));
   }
 
   return byteArray;
 }
 
 function commonFindCertBy(propertyName, value) {
-  let certEnumerator = gCertDB.getCerts().getEnumerator();
-  while (certEnumerator.hasMoreElements()) {
-    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of gCertDB.getCerts().getEnumerator()) {
     if (cert[propertyName] == value) {
       return cert;
     }
   }
   return null;
 }
 
 function findCertByCommonName(commonName) {
--- a/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
@@ -69,19 +69,17 @@ let gTestcases = [
     successExpected: true,
     errorCode: Ci.nsIX509CertDB.Success,
     checkCertExist: false,
   },
 ];
 
 function doesCertExist(commonName) {
   let allCerts = gCertDB.getCerts();
-  let enumerator = allCerts.getEnumerator();
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of allCerts.getEnumerator()) {
     if (cert.isBuiltInRoot) {
       continue;
     }
     if (cert.commonName == commonName) {
       return true;
     }
   }
 
--- a/security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js
+++ b/security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js
@@ -77,19 +77,17 @@ function getCertAsByteArray(certPath) {
   for (let i = 0; i < certBytes.length; i++) {
     byteArray.push(certBytes.charCodeAt(i));
   }
 
   return byteArray;
 }
 
 function findCertByCommonName(commonName) {
-  let certEnumerator = gCertDB.getCerts().getEnumerator();
-  while (certEnumerator.hasMoreElements()) {
-    let cert = certEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of gCertDB.getCerts().getEnumerator()) {
     if (cert.commonName == commonName) {
       return cert;
     }
   }
   return null;
 }
 
 function run_test() {
--- a/security/manager/ssl/tests/unit/test_enterprise_roots.js
+++ b/security/manager/ssl/tests/unit/test_enterprise_roots.js
@@ -26,21 +26,19 @@ function check_no_enterprise_roots_impor
          "TLS server auth certificates");
     }
   }
 }
 
 function check_some_enterprise_roots_imported(certDB) {
   let enterpriseRoots = certDB.getEnterpriseRoots();
   notEqual(enterpriseRoots, null, "should have imported some enterprise roots");
-  let enumerator = enterpriseRoots.getEnumerator();
   let foundNonBuiltIn = false;
   let savedDBKey = null;
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of enterpriseRoots.getEnumerator()) {
     if (!cert.isBuiltInRoot && !savedDBKey) {
       foundNonBuiltIn = true;
       savedDBKey = cert.dbKey;
       info("saving dbKey from " + cert.commonName);
     }
   }
   ok(foundNonBuiltIn, "should have found non-built-in root");
   return savedDBKey;
--- a/security/manager/ssl/tests/unit/test_pkcs11_module.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_module.js
@@ -11,18 +11,17 @@ do_get_profile();
 
 const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
                     .getService(Ci.nsIPKCS11ModuleDB);
 
 function checkTestModuleNotPresent() {
   let modules = gModuleDB.listModules();
   ok(modules.hasMoreElements(),
      "One or more modules should be present with test module not present");
-  while (modules.hasMoreElements()) {
-    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+  for (let module of modules) {
     notEqual(module.name, "PKCS11 Test Module",
              "Non-test module name shouldn't equal 'PKCS11 Test Module'");
     ok(!(module.libName && module.libName.includes("pkcs11testmodule")),
        "Non-test module lib name should not include 'pkcs11testmodule'");
   }
 }
 
 /**
@@ -32,18 +31,17 @@ function checkTestModuleNotPresent() {
  * @returns {nsIPKCS11Module}
  *          The test module.
  */
 function checkTestModuleExists() {
   let modules = gModuleDB.listModules();
   ok(modules.hasMoreElements(),
      "One or more modules should be present with test module present");
   let testModule = null;
-  while (modules.hasMoreElements()) {
-    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+  for (let module of modules) {
     if (module.name == "PKCS11 Test Module") {
       testModule = module;
       break;
     }
   }
   notEqual(testModule, null, "Test module should have been found");
   notEqual(testModule.libName, null, "Test module lib name should not be null");
   ok(testModule.libName.includes(ctypes.libraryName("pkcs11testmodule")),
@@ -85,22 +83,18 @@ function run_test() {
 
   // Check that adding the test module makes it appear in the module list.
   loadPKCS11TestModule(true);
   checkModuleTelemetry(
     `${AppConstants.DLL_PREFIX}pkcs11testmodule${AppConstants.DLL_SUFFIX}`);
   let testModule = checkTestModuleExists();
 
   // Check that listing the slots for the test module works.
-  let slots = testModule.listSlots();
-  let testModuleSlotNames = [];
-  while (slots.hasMoreElements()) {
-    let slot = slots.getNext().QueryInterface(Ci.nsIPKCS11Slot);
-    testModuleSlotNames.push(slot.name);
-  }
+  let testModuleSlotNames = Array.from(testModule.listSlots(),
+                                       slot => slot.name);
   testModuleSlotNames.sort();
   const expectedSlotNames = ["Empty PKCS11 Slot", "Test PKCS11 Slot", "Test PKCS11 Slot 二"];
   deepEqual(testModuleSlotNames, expectedSlotNames,
             "Actual and expected slot names should be equal");
 
   // Check that deleting the test module makes it disappear from the module list.
   let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
                          .getService(Ci.nsIPKCS11ModuleDB);
--- a/security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js
@@ -25,18 +25,17 @@ function run_test() {
   throws(() => moduleDB.addModule("", libraryFile.path, 0, 0),
          /NS_ERROR_ILLEGAL_VALUE/,
          "Adding a module with an empty name should fail.");
 
   let bundle =
     Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
   let rootsModuleName = bundle.GetStringFromName("RootCertModuleName");
   let foundRootsModule = false;
-  for (let module of XPCOMUtils.IterSimpleEnumerator(moduleDB.listModules(),
-                                                     Ci.nsIPKCS11Module)) {
+  for (let module of moduleDB.listModules()) {
     if (module.name == rootsModuleName) {
       foundRootsModule = true;
       break;
     }
   }
   ok(foundRootsModule,
      "Should be able to find builtin roots module by localized name.");
 }
--- a/security/manager/ssl/tests/unit/test_pkcs11_slot.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_slot.js
@@ -4,28 +4,26 @@
 "use strict";
 
 // Tests the methods and attributes for interfacing with a PKCS #11 slot.
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 
 function find_slot_by_name(module, name) {
-  for (let slot of XPCOMUtils.IterSimpleEnumerator(module.listSlots(),
-                                                   Ci.nsIPKCS11Slot)) {
+  for (let slot of module.listSlots()) {
     if (slot.name == name) {
       return slot;
     }
   }
   return null;
 }
 
 function find_module_by_name(moduleDB, name) {
-  for (let slot of XPCOMUtils.IterSimpleEnumerator(moduleDB.listModules(),
-                                                   Ci.nsIPKCS11Module)) {
+  for (let slot of moduleDB.listModules()) {
     if (slot.name == name) {
       return slot;
     }
   }
   return null;
 }
 
 function run_test() {
--- a/security/manager/ssl/tests/unit/test_sss_enumerate.js
+++ b/security/manager/ssl/tests/unit/test_sss_enumerate.js
@@ -59,23 +59,17 @@ function insertEntries() {
     }
     sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, header,
                       sslStatus, 0,
                       Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
   }
 }
 
 function getEntries(type) {
-  let entryEnumerator = sss.enumerate(type);
-  let entries = [];
-  while (entryEnumerator.hasMoreElements()) {
-    let entry = entryEnumerator.getNext();
-    entries.push(entry.QueryInterface(Ci.nsISiteSecurityState));
-  }
-  return entries;
+  return Array.from(sss.enumerate(type));
 }
 
 function checkSiteSecurityStateAttrs(entries) {
   entries.sort((a, b) => a.expireTime - b.expireTime);
   equal(entries.length, TESTCASES.length,
         "Should get correct number of entries");
   for (let i = 0; i < TESTCASES.length; i++) {
     equal(entries[i].hostname, TESTCASES[i].hostname, "Hostnames should match");
@@ -87,21 +81,19 @@ function checkSiteSecurityStateAttrs(ent
     // There's a delay from our "now" and the "now" that the implementation uses.
     less(Math.abs(entries[i].expireTime - TESTCASES[i].expireTime), 60000,
          "ExpireTime should be within 60-second error");
   }
 }
 
 function checkSha256Keys(hpkpEntries) {
   for (let hpkpEntry of hpkpEntries) {
-    let enumerator = hpkpEntry.QueryInterface(Ci.nsISiteHPKPState).sha256Keys;
-    let keys = [];
-    while (enumerator.hasMoreElements()) {
-      keys.push(enumerator.getNext().QueryInterface(Ci.nsIVariant));
-    }
+    let keys = Array.from(hpkpEntry.QueryInterface(Ci.nsISiteHPKPState).sha256Keys,
+                          key => key.QueryInterface(Ci.nsIVariant));
+
     equal(keys.length, KEY_HASHES.length, "Should get correct number of keys");
     keys.sort();
     for (let i = 0; i < KEY_HASHES.length; i++) {
       equal(keys[i], KEY_HASHES[i], "Should get correct keys");
     }
   }
 }
 
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -370,21 +370,19 @@ function downloadAndParseChromePins(file
   });
   return [ chromeImportedPinsets, chromeImportedEntries ];
 }
 
 // Returns a pair of maps [certNameToSKD, certSKDToName] between cert
 // nicknames and digests of the SPKInfo for the mozilla trust store
 function loadNSSCertinfo(extraCertificates) {
   let allCerts = gCertDB.getCerts();
-  let enumerator = allCerts.getEnumerator();
   let certNameToSKD = {};
   let certSKDToName = {};
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+  for (let cert of allCerts.getEnumerator()) {
     if (!cert.isBuiltInRoot) {
       continue;
     }
     let name = cert.displayName;
     let SKD = cert.sha256SubjectPublicKeyInfoDigest;
     certNameToSKD[name] = SKD;
     certSKDToName[SKD] = name;
   }
--- a/security/manager/tools/genRootCAHashes.js
+++ b/security/manager/tools/genRootCAHashes.js
@@ -167,20 +167,17 @@ function getLabelForCert(cert) {
 
 // Fill in the gTrustAnchors list with trust anchors from the database.
 function insertTrustAnchorsFromDatabase() {
   // We only want CA certs for SSL
   const CERT_TYPE = Ci.nsIX509Cert.CA_CERT;
   const TRUST_TYPE = Ci.nsIX509CertDB.TRUSTED_SSL;
 
   // Iterate through the whole Cert DB
-  let enumerator = CertDb.getCerts().getEnumerator();
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
-
+  for (let cert of CertDb.getCerts().getEnumerator()) {
     // Find the certificate in our existing list. Do it here because we need to check if
     // it's untrusted too.
 
     // If this is a trusted cert
     if (CertDb.isCertTrusted(cert, CERT_TYPE, TRUST_TYPE)) {
       // Base64 encode the hex string
       let binaryFingerprint = CommonUtils.hexToBytes(stripColons(cert.sha256Fingerprint));
       let encodedFingerprint = btoa(binaryFingerprint);
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-d3f512d4f76e
+5bc69334e84f
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -271,18 +271,23 @@ TEST_P(TlsConnectGeneric, ConnectResumeC
 
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   PK11SymKey* hmac_key =
       PK11_ImportSymKey(slot.get(), CKM_SHA256_HMAC, PK11_OriginUnwrap,
                         CKA_SIGN, &key_item, nullptr);
   ASSERT_NE(nullptr, hmac_key);
   SSLInt_SetSelfEncryptMacKey(hmac_key);
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
-  ConnectExpectAlert(server_, illegal_parameter);
-  server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    ExpectResumption(RESUME_NONE);
+    Connect();
+  } else {
+    ConnectExpectAlert(server_, illegal_parameter);
+    server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+  }
 }
 
 // This callback switches out the "server" cert used on the server with
 // the "client" certificate, which should be the same type.
 static int32_t SwitchCertificates(TlsAgent* agent, const SECItem* srvNameArr,
                                   uint32_t srvNameArrSize) {
   bool ok = agent->ConfigServerCert("client");
   if (!ok) return SSL_SNI_SEND_ALERT;
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -1169,27 +1169,28 @@ ssl3_ProcessSessionTicketCommon(sslSocke
     }
 
     /* Decrypt the ticket. */
     rv = ssl_SelfEncryptUnprotect(ss, ticket->data, ticket->len,
                                   decryptedTicket.data,
                                   &decryptedTicket.len,
                                   decryptedTicket.len);
     if (rv != SECSuccess) {
-        SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
-
-        /* Fail with no ticket if we're not a recipient. Otherwise
-         * it's a hard failure. */
-        if (PORT_GetError() != SEC_ERROR_NOT_A_RECIPIENT) {
-            SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
-            return SECFailure;
+        /* Ignore decryption failure if we are doing TLS 1.3; that
+         * means the server rejects the client's resumption
+         * attempt. In TLS 1.2, however, it's a hard failure, unless
+         * it's just because we're not the recipient of the ticket. */
+        if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ||
+            PORT_GetError() == SEC_ERROR_NOT_A_RECIPIENT) {
+            SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
+            return SECSuccess;
         }
 
-        /* We didn't have the right key, so pretend we don't have a
-         * ticket. */
+        SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
+        goto loser;
     }
 
     rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket);
     if (rv != SECSuccess) {
         SSL3Statistics *ssl3stats;
 
         SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
                  SSL_GETPID(), ss->fd));
--- a/services/common/tests/unit/test_logmanager.js
+++ b/services/common/tests/unit/test_logmanager.js
@@ -222,20 +222,19 @@ add_task(async function test_logFileErro
   checkLogFile(null);
 
   lm.finalize();
 });
 
 function countLogFiles() {
   let logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
   let count = 0;
-  let entries = logsdir.directoryEntries;
-  while (entries.hasMoreElements()) {
+  for (let entry of logsdir.directoryEntries) {
+    void entry;
     count += 1;
-    entries.getNext();
   }
   return count;
 }
 
 // Test that removeAllLogs removes all log files.
 add_task(async function test_logFileError() {
   Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnError", true);
   Services.prefs.setBoolPref("log-manager.test.log.appender.file.logOnSuccess", true);
--- a/services/sync/modules/engines/tabs.js
+++ b/services/sync/modules/engines/tabs.js
@@ -130,19 +130,17 @@ TabStore.prototype = {
     return JSON.parse(SessionStore.getTabState(tab));
   },
 
   async getAllTabs(filter) {
     let filteredUrls = new RegExp(Svc.Prefs.get("engine.tabs.filteredUrls"), "i");