Merge mozilla-central to autoland. a=merge CLOSED TREE
authorTiberius Oros <toros@mozilla.com>
Fri, 24 Aug 2018 12:45:50 +0300
changeset 433270 067c58fa95f155a7cc2780c11ca76dc3dca5896d
parent 433269 1a83a81da7387171b49c73ed634f026e070ba83d (current diff)
parent 433262 190b827aaa2b5e6fb9af7a0defb238ccc35f8b9e (diff)
child 433271 5c420838bf5229f08145ac9a8c0f4b06cfbc2925
push id34503
push useraiakab@mozilla.com
push dateFri, 24 Aug 2018 22:07:45 +0000
treeherdermozilla-central@d2da77c319d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
gfx/2d/RwAssert.h
testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini
--- 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");
 
     let allTabs = [];
 
-    let winEnum = this.getWindowEnumerator();
-    while (winEnum.hasMoreElements()) {
-      let win = winEnum.getNext();
+    for (let win of this.getWindowEnumerator()) {
       if (this.shouldSkipWindow(win)) {
         continue;
       }
 
       for (let tab of win.gBrowser.tabs) {
         let tabState = this.getTabState(tab);
 
         // Make sure there are history entries to look at.
@@ -230,19 +228,18 @@ TabStore.prototype = {
     return record;
   },
 
   async getAllIDs() {
     // Don't report any tabs if all windows are in private browsing for
     // first syncs.
     let ids = {};
     let allWindowsArePrivate = false;
-    let wins = Services.wm.getEnumerator("navigator:browser");
-    while (wins.hasMoreElements()) {
-      if (PrivateBrowsingUtils.isWindowPrivate(wins.getNext())) {
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      if (PrivateBrowsingUtils.isWindowPrivate(win)) {
         // Ensure that at least there is a private window.
         allWindowsArePrivate = true;
       } else {
         // If there is a not private windown then finish and continue.
         allWindowsArePrivate = false;
         break;
       }
     }
@@ -315,27 +312,25 @@ TabTracker.prototype = {
     }
     if (window.gBrowser) {
       window.gBrowser.removeProgressListener(this);
     }
   },
 
   onStart() {
     Svc.Obs.add("domwindowopened", this.asyncObserver);
-    let wins = Services.wm.getEnumerator("navigator:browser");
-    while (wins.hasMoreElements()) {
-      this._registerListenersForWindow(wins.getNext());
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      this._registerListenersForWindow(win);
     }
   },
 
   onStop() {
     Svc.Obs.remove("domwindowopened", this.asyncObserver);
-    let wins = Services.wm.getEnumerator("navigator:browser");
-    while (wins.hasMoreElements()) {
-      this._unregisterListenersForWindow(wins.getNext());
+    for (let win of Services.wm.getEnumerator("navigator:browser")) {
+      this._unregisterListenersForWindow(win);
     }
   },
 
   async observe(subject, topic, data) {
     switch (topic) {
       case "domwindowopened":
         let onLoad = () => {
           subject.removeEventListener("load", onLoad);
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -236,24 +236,17 @@ function mockGetWindowEnumerator(url, nu
   elements.push({
     closed: false,
     mockIsPrivate: true,
     gBrowser: {
       tabs: [],
     },
   });
 
-  return {
-    hasMoreElements() {
-      return elements.length;
-    },
-    getNext() {
-      return elements.shift();
-    },
-  };
+  return elements.values();
 }
 
 // Helper function to get the sync telemetry and add the typically used test
 // engine names to its list of allowed engines.
 function get_sync_test_telemetry() {
   let ns = {};
   ChromeUtils.import("resource://services-sync/telemetry.js", ns);
   let testEngines = ["rotary", "steam", "sterling", "catapult"];
--- a/services/sync/tests/unit/test_tab_tracker.js