Merge mozilla-inbound to mozilla-central. a=merge
authorDorel Luca <dluca@mozilla.com>
Wed, 01 Aug 2018 12:51:56 +0300
changeset 429557 af6a7edf0069549543f2fba6a8ee3ea251b20829
parent 429498 2ef5c0e6dd2cdd667f3aaa9dae6332734664fb01 (current diff)
parent 429556 8ded8bf9b94ca03d8f6ef555af0e9c6c296e1afe (diff)
child 429558 89374090a8377286a2025e15c8063a0285920304
child 429569 f32e9ff50ebd4e63205491905087ab7dee11b506
child 429626 16c83bec0e2bbbe9cdce20d8c2b7d18ee11983a8
push id34366
push userdluca@mozilla.com
push dateWed, 01 Aug 2018 09:52:20 +0000
treeherdermozilla-central@af6a7edf0069 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
af6a7edf0069 / 63.0a1 / 20180801100116 / files
nightly linux64
af6a7edf0069 / 63.0a1 / 20180801100116 / files
nightly mac
af6a7edf0069 / 63.0a1 / 20180801100116 / files
nightly win32
af6a7edf0069 / 63.0a1 / 20180801100116 / files
nightly win64
af6a7edf0069 / 63.0a1 / 20180801100116 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
modules/libpref/init/all.js
testing/web-platform/meta/content-security-policy/media-src/media-src-7_1_2.sub.html.ini
testing/web-platform/meta/content-security-policy/media-src/media-src-7_2_2.sub.html.ini
testing/web-platform/meta/content-security-policy/media-src/media-src-blocked.sub.html.ini
--- a/accessible/xpcom/AccEventGen.py
+++ b/accessible/xpcom/AccEventGen.py
@@ -192,17 +192,17 @@ def writeAttributeGetter(fd, classname, 
     fd.write("  return NS_OK;\n")
     fd.write("}\n\n")
 
 
 def interfaces(iface):
     interfaces = []
     while iface.base:
         interfaces.append(iface)
-        iface = iface.idl.getName(iface.base, iface.location)
+        iface = iface.idl.getName(xpidl.TypeId(iface.base), iface.location)
     interfaces.append(iface)
     interfaces.reverse()
     return interfaces
 
 
 def allAttributes(iface):
     attributes = []
     for i in interfaces(iface):
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -41,16 +41,22 @@ let whitelist = [
    isFromDevTools: false},
   {sourceName: /\b(html|mathml|ua)\.css$/i,
    errorMessage: /Unknown property.*-moz-/i,
    isFromDevTools: false},
   // Reserved to UA sheets unless layout.css.overflow-clip-box.enabled flipped to true.
   {sourceName: /(?:res|gre-resources)\/forms\.css$/i,
    errorMessage: /Unknown property.*overflow-clip-box/i,
    isFromDevTools: false},
+  // The '-moz-menulist-button' value is only supported in chrome and UA sheets
+  // but forms.css is loaded as a document sheet by this test.
+  // Maybe bug 1261237 will fix this?
+  {sourceName: /(?:res|gre-resources)\/forms\.css$/i,
+   errorMessage: /Error in parsing value for \u2018-moz-appearance\u2019/iu,
+   isFromDevTools: false},
   // These variables are declared somewhere else, and error when we load the
   // files directly. They're all marked intermittent because their appearance
   // in the error console seems to not be consistent.
   {sourceName: /jsonview\/css\/general\.css$/i,
    intermittent: true,
    errorMessage: /Property contained reference to invalid variable.*color/i,
    isFromDevTools: true},
   {sourceName: /webide\/skin\/logs\.css$/i,
--- a/browser/components/sessionstore/test/browser_async_remove_tab.js
+++ b/browser/components/sessionstore/test/browser_async_remove_tab.js
@@ -61,40 +61,16 @@ function promiseNewLocationAndHistoryEnt
         try {
           shistory.removeSHistoryListener(listener);
         } catch (e) { /* Will most likely fail. */ }
       });
     });
   });
 }
 
-function promiseHistoryEntryReplacedNonRemote(browser) {
-  let {listeners} = promiseHistoryEntryReplacedNonRemote;
-
-  return new Promise(resolve => {
-    let shistory = browser.webNavigation.sessionHistory.legacySHistory;
-
-    let listener = {
-      OnHistoryReplaceEntry() {
-        shistory.removeSHistoryListener(this);
-        executeSoon(resolve);
-      },
-
-      QueryInterface: ChromeUtils.generateQI([
-        Ci.nsISHistoryListener,
-        Ci.nsISupportsWeakReference
-      ])
-    };
-
-    shistory.addSHistoryListener(listener);
-    listeners.set(browser, listener);
-  });
-}
-promiseHistoryEntryReplacedNonRemote.listeners = new WeakMap();
-
 add_task(async function dont_save_empty_tabs() {
   let {tab, r} = await createTabWithRandomValue("about:blank");
 
   // Remove the tab before the update arrives.
   let promise = promiseRemoveTabAndSessionState(tab);
 
   // No tab state worth saving.
   ok(!isValueInClosedData(r), "closed tab not saved");
@@ -163,18 +139,20 @@ add_task(async function save_worthy_tabs
   let {tab, r} = await createTabWithRandomValue("about:blank");
   let browser = tab.linkedBrowser;
   ok(browser.isRemoteBrowser, "browser is remote");
 
   // Replace about:blank with a non-remote entry.
   await BrowserTestUtils.loadURI(browser, "about:robots");
   ok(!browser.isRemoteBrowser, "browser is not remote anymore");
 
-  // Wait until the new entry replaces about:blank.
-  await promiseHistoryEntryReplacedNonRemote(browser);
+  // Switching remoteness caused a SessionRestore to begin, moving over history
+  // and initiating the load in the target process. Wait for the full restore
+  // and load to complete before trying to close the tab.
+  await promiseTabRestored(tab);
 
   // Remove the tab before the update arrives.
   let promise = promiseRemoveTabAndSessionState(tab);
 
   // No tab state worth saving (that we know about yet).
   ok(!isValueInClosedData(r), "closed tab not saved");
   await promise;
 
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -191,16 +191,17 @@ exports.CSS_PROPERTIES = {
       "-moz-mac-fullscreen-button",
       "-moz-mac-help-button",
       "-moz-mac-source-list",
       "-moz-mac-source-list-selection",
       "-moz-mac-vibrancy-dark",
       "-moz-mac-vibrancy-light",
       "-moz-mac-vibrant-titlebar-dark",
       "-moz-mac-vibrant-titlebar-light",
+      "-moz-menulist-button",
       "-moz-win-borderless-glass",
       "-moz-win-browsertabbar-toolbox",
       "-moz-win-communications-toolbox",
       "-moz-win-exclude-glass",
       "-moz-win-glass",
       "-moz-win-media-toolbox",
       "-moz-window-button-box",
       "-moz-window-button-box-maximized",
--- a/docshell/test/test_bug529119-2.html
+++ b/docshell/test/test_bug529119-2.html
@@ -55,20 +55,22 @@ function windowLoaded()
   window.setTimeout(function() {
     w.location.href = faultyURL;
   }, 0);
 
   pollForPage(true, function(succeeded) {
     ok(succeeded, "Waiting for error page succeeded");
     /* 3. now, while we are on the error page, navigate back */
     try {
-      SpecialPowers.wrap(w).back();
+      // We need the SpecialPowers bit, because this is a cross-origin window
+      // and we normally can't touch .history on those.
+      SpecialPowers.wrap(w).history.back();
     }
     catch(ex) {
-      ok(false, "w.back() threw " + ex);
+      ok(false, "w.history.back() threw " + ex);
     }
 
     pollForPage(false, function(succeeded) {
       ok(succeeded, "Waiting for original page succeeded");
       /* 4-finish, check we are back at the original page */
       isnot(SpecialPowers.wrap(w).location.href, faultyURL, "Is on an error page");
       is(SpecialPowers.wrap(w).location.href, workingURL, "Is not on the previous page");
       w.close();
--- a/docshell/test/test_bug669671.html
+++ b/docshell/test/test_bug669671.html
@@ -87,17 +87,17 @@ function* test()
 
   // Step 4 - Navigate away.  This should trigger another onChildLoad.
   popup.location = 'file_bug669671.sjs?navigated-1';
   yield undefined;
 
   // Step 5 - Go back.  This should result in another onload (because the file is
   // not in bfcache) and should be the fourth time we've requested the sjs file.
   checkPopupLoadCount();
-  SpecialPowers.wrap(popup).back();
+  popup.history.back();
   yield undefined;
 
   // This is the check which was failing before we fixed the bug.
   checkPopupLoadCount();
 
   popup.close();
 
   // Do the whole thing again, but with replaceState.
@@ -106,31 +106,31 @@ function* test()
   checkPopupLoadCount();
   popup.location = 'file_bug669671.sjs';
   yield undefined;
   checkPopupLoadCount();
   popup.history.replaceState('', '', '?replaced');
   popup.location = 'file_bug669671.sjs?navigated-2';
   yield undefined;
   checkPopupLoadCount();
-  SpecialPowers.wrap(popup).back();
+  popup.history.back();
   yield undefined;
   checkPopupLoadCount();
   popup.close();
 
   // Once more, with feeling.  Notice that we don't have to prime the cache
   // with an extra load here, because X and X#hash share the same cache entry.
   popup = window.open('file_bug669671.sjs?hash-test');
   yield undefined;
   var initialCount = checkPopupLoadCount();
   popup.history.replaceState('', '', '#hash');
   popup.location = 'file_bug669671.sjs?navigated-3';
   yield undefined;
   checkPopupLoadCount();
-  SpecialPowers.wrap(popup).back();
+  popup.history.back();
   yield undefined;
   is(popup.document.body.innerHTML, initialCount + '',
      'Load count (should be cached)');
   popup.close();
 
   SimpleTest.finish();
 }
 
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -921,53 +921,43 @@ nsGlobalWindowInner::nsGlobalWindowInner
   mIsInnerWindow = true;
 
   AssertIsOnMainThread();
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
-  if (aOuterWindow) {
-    // |this| is an inner window, add this inner window to the outer
-    // window list of inners.
-    PR_INSERT_AFTER(this, aOuterWindow);
-
-    mTimeoutManager =
-      MakeUnique<mozilla::dom::TimeoutManager>(*nsGlobalWindowInner::Cast(AsInner()));
-
-    mObserver = new nsGlobalWindowObserver(this);
-    if (mObserver) {
-      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-      if (os) {
-        // Watch for online/offline status changes so we can fire events. Use
-        // a strong reference.
-        os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
-                        false);
-
-        os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
-
-        if (aOuterWindow->IsTopLevelWindow()) {
-          os->AddObserver(mObserver, "clear-site-data-reload-needed", false);
-        }
-      }
-
-      Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
-
-      // Watch for storage notifications so we can fire storage events.
-      RefPtr<StorageNotifierService> sns =
-        StorageNotifierService::GetOrCreate();
-      if (sns) {
-        sns->Register(mObserver);
-      }
-    }
-  } else {
-    // |this| is an outer window. Outer windows start out frozen and
-    // remain frozen until they get an inner window.
-    MOZ_ASSERT(IsFrozen());
+  // add this inner window to the outer window list of inners.
+  PR_INSERT_AFTER(this, aOuterWindow);
+
+  mTimeoutManager = MakeUnique<dom::TimeoutManager>(*this);
+
+  mObserver = new nsGlobalWindowObserver(this);
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    // Watch for online/offline status changes so we can fire events. Use
+    // a strong reference.
+    os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
+                    false);
+
+    os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
+
+    if (aOuterWindow->IsTopLevelWindow()) {
+      os->AddObserver(mObserver, "clear-site-data-reload-needed", false);
+    }
+  }
+
+  Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
+
+  // Watch for storage notifications so we can fire storage events.
+  RefPtr<StorageNotifierService> sns =
+    StorageNotifierService::GetOrCreate();
+  if (sns) {
+    sns->Register(mObserver);
   }
 
   if (XRE_IsContentProcess()) {
     nsCOMPtr<nsIDocShell> docShell = GetDocShell();
     if (docShell) {
       mTabChild = docShell->GetTabChild();
     }
   }
@@ -2013,28 +2003,16 @@ nsGlobalWindowInner::DialogsAreBeingAbus
   }
 
   // Reset the abuse counter
   mDialogAbuseCount = 0;
 
   return false;
 }
 
-void
-nsGlobalWindowInner::DisableDialogs()
-{
-  FORWARD_TO_OUTER_VOID(DisableDialogs, ());
-}
-
-void
-nsGlobalWindowInner::EnableDialogs()
-{
-  FORWARD_TO_OUTER_VOID(EnableDialogs, ());
-}
-
 nsresult
 nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
   // Return early if there is nothing to do.
   switch (aVisitor.mEvent->mMessage) {
     case eResize:
     case eUnload:
     case eLoad:
@@ -2308,28 +2286,16 @@ nsPIDOMWindowInner::Suspend()
 
 void
 nsPIDOMWindowInner::Resume()
 {
   nsGlobalWindowInner::Cast(this)->Resume();
 }
 
 void
-nsPIDOMWindowInner::Freeze()
-{
-  nsGlobalWindowInner::Cast(this)->Freeze();
-}
-
-void
-nsPIDOMWindowInner::Thaw()
-{
-  nsGlobalWindowInner::Cast(this)->Thaw();
-}
-
-void
 nsPIDOMWindowInner::SyncStateFromParentWindow()
 {
   nsGlobalWindowInner::Cast(this)->SyncStateFromParentWindow();
 }
 
 Maybe<ClientInfo>
 nsPIDOMWindowInner::GetClientInfo() const
 {
@@ -2343,22 +2309,16 @@ nsPIDOMWindowInner::GetClientState() con
 }
 
 Maybe<ServiceWorkerDescriptor>
 nsPIDOMWindowInner::GetController() const
 {
   return nsGlobalWindowInner::Cast(this)->GetController();
 }
 
-RefPtr<mozilla::dom::ServiceWorker>
-nsPIDOMWindowInner::GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor)
-{
-  return nsGlobalWindowInner::Cast(this)->GetOrCreateServiceWorker(aDescriptor);
-}
-
 void
 nsPIDOMWindowInner::NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope)
 {
   nsGlobalWindowInner::Cast(this)->NoteCalledRegisterForServiceWorkerScope(aScope);
 }
 
 void
 nsPIDOMWindowInner::NoteDOMContentLoaded()
@@ -2748,26 +2708,16 @@ nsGlobalWindowInner::GetParent(ErrorResu
  */
 nsPIDOMWindowOuter*
 nsGlobalWindowInner::GetScriptableParent()
 {
   FORWARD_TO_OUTER(GetScriptableParent, (), nullptr);
 }
 
 /**
- * Behavies identically to GetScriptableParent extept that it returns null
- * if GetScriptableParent would return this window.
- */
-nsPIDOMWindowOuter*
-nsGlobalWindowInner::GetScriptableParentOrNull()
-{
-  FORWARD_TO_OUTER(GetScriptableParentOrNull, (), nullptr);
-}
-
-/**
  * GetScriptableTop is called when script reads window.top.
  *
  * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
  * boundaries.  If we encounter a window owned by an <iframe mozbrowser> while
  * walking up the window hierarchy, we'll stop and return that window.
  */
 nsPIDOMWindowOuter*
 nsGlobalWindowInner::GetScriptableTop()
@@ -3811,34 +3761,16 @@ nsGlobalWindowInner::Focus()
 
 void
 nsGlobalWindowInner::Blur(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(BlurOuter, (), aError, );
 }
 
 void
-nsGlobalWindowInner::Back(ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(BackOuter, (aError), aError, );
-}
-
-void
-nsGlobalWindowInner::Forward(ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(ForwardOuter, (aError), aError, );
-}
-
-void
-nsGlobalWindowInner::Home(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(HomeOuter, (aSubjectPrincipal, aError), aError, );
-}
-
-void
 nsGlobalWindowInner::Stop(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(StopOuter, (aError), aError, );
 }
 
 /* static */
 bool
 nsGlobalWindowInner::IsWindowPrintEnabled(JSContext*, JSObject*)
@@ -4186,22 +4118,16 @@ nsGlobalWindowInner::Close(ErrorResult& 
 }
 
 nsresult
 nsGlobalWindowInner::Close()
 {
   FORWARD_TO_OUTER(Close, (), NS_ERROR_UNEXPECTED);
 }
 
-void
-nsGlobalWindowInner::ReallyCloseWindow()
-{
-  FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ());
-}
-
 bool
 nsGlobalWindowInner::IsInModalState()
 {
   FORWARD_TO_OUTER(IsInModalState, (), false);
 }
 
 // static
 void
@@ -4881,36 +4807,16 @@ nsGlobalWindowInner::DispatchSyncPopStat
   event->SetTrusted(true);
   event->SetTarget(this);
 
   ErrorResult err;
   DispatchEvent(*event, err);
   return err.StealNSResult();
 }
 
-// Find an nsICanvasFrame under aFrame.  Only search the principal
-// child lists.  aFrame must be non-null.
-static nsCanvasFrame*
-FindCanvasFrame(nsIFrame* aFrame)
-{
-    nsCanvasFrame* canvasFrame = do_QueryFrame(aFrame);
-    if (canvasFrame) {
-        return canvasFrame;
-    }
-
-    for (nsIFrame* kid : aFrame->PrincipalChildList()) {
-        canvasFrame = FindCanvasFrame(kid);
-        if (canvasFrame) {
-            return canvasFrame;
-        }
-    }
-
-    return nullptr;
-}
-
 //-------------------------------------------------------
 // Tells the HTMLFrame/CanvasFrame that is now has focus
 void
 nsGlobalWindowInner::UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent)
 {
   // this is called from the inner window so use GetDocShell
   nsIDocShell* docShell = GetDocShell();
   if (!docShell)
@@ -4922,36 +4828,29 @@ nsGlobalWindowInner::UpdateCanvasFocus(b
     return;
 
   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   if (!presShell || !mDoc)
     return;
 
   Element *rootElement = mDoc->GetRootElement();
   if (rootElement) {
-      if ((mHasFocus || aFocusChanged) &&
-          (mFocusedElement == rootElement || aNewContent == rootElement)) {
-          nsIFrame* frame = rootElement->GetPrimaryFrame();
-          if (frame) {
-              frame = frame->GetParent();
-              nsCanvasFrame* canvasFrame = do_QueryFrame(frame);
-              if (canvasFrame) {
-                  canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
-              }
-          }
+    if ((mHasFocus || aFocusChanged) &&
+        (mFocusedElement == rootElement || aNewContent == rootElement)) {
+      nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
+      if (canvasFrame) {
+        canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
       }
+    }
   } else {
-      // Look for the frame the hard way
-      nsIFrame* frame = presShell->GetRootFrame();
-      if (frame) {
-          nsCanvasFrame* canvasFrame = FindCanvasFrame(frame);
-          if (canvasFrame) {
-              canvasFrame->SetHasFocus(false);
-          }
-      }
+    // XXXbz I would expect that there is never a canvasFrame in this case...
+    nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame();
+    if (canvasFrame) {
+      canvasFrame->SetHasFocus(false);
+    }
   }
 }
 
 already_AddRefed<nsICSSDeclaration>
 nsGlobalWindowInner::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
                                       ErrorResult& aError)
 {
   return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
@@ -8077,16 +7976,20 @@ nsPIDOMWindowInner::MaybeCreateDoc()
     nsCOMPtr<nsIDocument> document = docShell->GetDocument();
     Unused << document;
   }
 }
 
 void
 nsGlobalWindowInner::PropagateClearSiteDataReload(const nsACString& aOrigin)
 {
+  if (!IsCurrentInnerWindow()) {
+    return;
+  }
+
   nsIPrincipal* principal = GetPrincipal();
   if (!principal) {
     return;
   }
 
   nsAutoCString origin;
   nsresult rv = principal->GetOrigin(origin);
   NS_ENSURE_SUCCESS_VOID(rv);
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -270,19 +270,16 @@ public:
   {
     // Make sure this matches the casts we do in QueryInterface().
     return (nsGlobalWindowInner *)(mozilla::dom::EventTarget *)supports;
   }
 
   static already_AddRefed<nsGlobalWindowInner>
   Create(nsGlobalWindowOuter* aOuter, bool aIsChrome);
 
-  // callback for close event
-  void ReallyCloseWindow();
-
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsWrapperCache
   virtual JSObject *WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override
   {
     return GetWrapper();
   }
@@ -347,16 +344,24 @@ public:
   // Outer windows only.
   virtual bool IsTopLevelWindowActive() override;
 
   virtual PopupControlState GetPopupControlState() const override;
 
   void Suspend();
   void Resume();
   virtual bool IsSuspended() const override;
+
+  // Calling Freeze() on a window will automatically Suspend() it.  In
+  // addition, the window and its children are further treated as no longer
+  // suitable for interaction with the user.  For example, it may be marked
+  // non-visible, cannot be focused, etc.  All worker threads are also frozen
+  // bringing them to a complete stop.  A window can have Freeze() called
+  // multiple times and will only thaw after a matching number of Thaw()
+  // calls.
   void Freeze();
   void Thaw();
   virtual bool IsFrozen() const override;
   void SyncStateFromParentWindow();
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const override;
@@ -444,24 +449,16 @@ public:
   // DialogsAreBeingAbused must be called on the scriptable top inner window.
   //
   // nsGlobalWindowOuter::ShouldPromptToBlockDialogs is implemented in terms of
   // nsGlobalWindowInner::DialogsAreBeingAbused, and will get the scriptable top inner window
   // automatically.
   // Inner windows only.
   bool DialogsAreBeingAbused();
 
-  // These functions are used for controlling and determining whether dialogs
-  // (alert, prompt, confirm) are currently allowed in this window.  If you want
-  // to temporarily disable dialogs, please use TemporarilyDisableDialogs, not
-  // EnableDialogs/DisableDialogs, because correctly determining whether to
-  // re-enable dialogs is actually quite difficult.
-  void EnableDialogs();
-  void DisableDialogs();
-
   nsIScriptContext *GetContextInternal();
 
   nsGlobalWindowOuter *GetOuterWindowInternal() const;
 
   bool IsChromeWindow() const
   {
     return mIsChrome;
   }
@@ -671,17 +668,16 @@ public:
   nsPIDOMWindowOuter* GetOpenerWindow(mozilla::ErrorResult& aError);
   void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                  mozilla::ErrorResult& aError);
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
   void GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
   already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
   nsPIDOMWindowOuter* GetScriptableParent() override;
-  nsPIDOMWindowOuter* GetScriptableParentOrNull() override;
   mozilla::dom::Element*
   GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   mozilla::dom::Element* GetFrameElement() override;
   already_AddRefed<nsPIDOMWindowOuter>
   Open(const nsAString& aUrl,
        const nsAString& aName,
        const nsAString& aOptions,
@@ -882,19 +878,16 @@ public:
                              mozilla::ErrorResult& aError);
   int32_t GetScrollMinX(mozilla::ErrorResult& aError);
   int32_t GetScrollMinY(mozilla::ErrorResult& aError);
   int32_t GetScrollMaxX(mozilla::ErrorResult& aError);
   int32_t GetScrollMaxY(mozilla::ErrorResult& aError);
   bool GetFullScreen(mozilla::ErrorResult& aError);
   bool GetFullScreen() override;
   void SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError);
-  void Back(mozilla::ErrorResult& aError);
-  void Forward(mozilla::ErrorResult& aError);
-  void Home(nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aError);
   bool Find(const nsAString& aString, bool aCaseSensitive, bool aBackwards,
             bool aWrapAround, bool aWholeWord, bool aSearchInFrames,
             bool aShowDialog, mozilla::ErrorResult& aError);
   uint64_t GetMozPaintCount(mozilla::ErrorResult& aError);
 
   bool ShouldResistFingerprinting();
 
   already_AddRefed<nsPIDOMWindowOuter>
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -301,19 +301,16 @@ using mozilla::TimeStamp;
     ::mozilla::Unused << kungFuDeathGrip;                               \
     if (!mInnerWindow) {                                                \
       return err_rval;                                                  \
     }                                                                   \
   }                                                                     \
   return GetCurrentInnerWindowInternal()->method args;                  \
   PR_END_MACRO
 
-#define DEFAULT_HOME_PAGE "www.mozilla.org"
-#define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
-
 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
 
 static int32_t              gOpenPopupSpamCount               = 0;
 
 nsGlobalWindowOuter::OuterWindowByIdTable *nsGlobalWindowOuter::sOuterWindowsById = nullptr;
 
 // CIDs
 static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
@@ -849,23 +846,16 @@ nsGlobalWindowOuter::nsGlobalWindowOuter
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
   // |this| is an outer window. Outer windows start out frozen and
   // remain frozen until they get an inner window.
   MOZ_ASSERT(IsFrozen());
 
-  if (XRE_IsContentProcess()) {
-    nsCOMPtr<nsIDocShell> docShell = GetDocShell();
-    if (docShell) {
-      mTabChild = docShell->GetTabChild();
-    }
-  }
-
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   mSerial = nsContentUtils::InnerOrOuterWindowCreated();
 
 #ifdef DEBUG
   if (!PR_GetEnv("MOZ_QUIET")) {
@@ -1141,17 +1131,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
@@ -1169,17 +1158,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
 
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
@@ -1251,22 +1239,16 @@ nsGlobalWindowOuter::GetScriptContext()
 }
 
 JSObject *
 nsGlobalWindowOuter::GetGlobalJSObject()
 {
   return FastGetGlobalJSObject();
 }
 
-void
-nsGlobalWindowOuter::TraceGlobalJSObject(JSTracer* aTrc)
-{
-  TraceWrapper(aTrc, "active window global");
-}
-
 bool
 nsGlobalWindowOuter::WouldReuseInnerWindow(nsIDocument* aNewDocument)
 {
   // We reuse the inner window when:
   // a. We are currently at our original document.
   // b. At least one of the following conditions are true:
   // -- The new document is the same as the old document. This means that we're
   //    getting called from document.open().
@@ -2178,17 +2160,16 @@ nsGlobalWindowOuter::DetachFromDocShell(
   nsGlobalWindowInner *currentInner = GetCurrentInnerWindowInternal();
 
   if (currentInner) {
     NS_ASSERTION(mDoc, "Must have doc!");
 
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
-    mDocBaseURI = mDoc->GetDocBaseURI();
 
     // Release our document reference
     DropOuterWindowDocs();
   }
 
   ClearControllers();
 
   mChromeEventHandler = nullptr; // force release now
@@ -4865,91 +4846,16 @@ nsGlobalWindowOuter::BlurOuter()
       if (element == mDoc->GetRootElement()) {
         fm->ClearFocus(this);
       }
     }
   }
 }
 
 void
-nsGlobalWindowOuter::BackOuter(ErrorResult& aError)
-{
-  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
-  if (!webNav) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  aError = webNav->GoBack();
-}
-
-void
-nsGlobalWindowOuter::ForwardOuter(ErrorResult& aError)
-{
-  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
-  if (!webNav) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  aError = webNav->GoForward();
-}
-
-void
-nsGlobalWindowOuter::HomeOuter(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError)
-{
-  if (!mDocShell) {
-    return;
-  }
-
-  nsAutoString homeURL;
-  Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE, homeURL);
-
-  if (homeURL.IsEmpty()) {
-    // if all else fails, use this
-#ifdef DEBUG_seth
-    printf("all else failed.  using %s as the home page\n", DEFAULT_HOME_PAGE);
-#endif
-    homeURL = NS_LITERAL_STRING(DEFAULT_HOME_PAGE);
-  }
-
-#ifdef MOZ_PHOENIX
-  {
-    // Firefox lets the user specify multiple home pages to open in
-    // individual tabs by separating them with '|'. Since we don't
-    // have the machinery in place to easily open new tabs from here,
-    // simply truncate the homeURL at the first '|' character to
-    // prevent any possibilities of leaking the users list of home
-    // pages to the first home page.
-    //
-    // Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=221445 is
-    // fixed we can revisit this.
-    int32_t firstPipe = homeURL.FindChar('|');
-
-    if (firstPipe > 0) {
-      homeURL.Truncate(firstPipe);
-    }
-  }
-#endif
-
-  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
-  if (!webNav) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  aError = webNav->LoadURI(homeURL.get(),
-                           nsIWebNavigation::LOAD_FLAGS_NONE,
-                           nullptr,
-                           nullptr,
-                           nullptr,
-                           &aSubjectPrincipal);
-}
-
-void
 nsGlobalWindowOuter::StopOuter(ErrorResult& aError)
 {
   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
   if (webNav) {
     aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
   }
 }
 
@@ -6651,22 +6557,16 @@ nsGlobalWindowOuter::SetIsBackgroundInte
 {
   if (mIsBackground != aIsBackground) {
     TabGroup()->WindowChangedBackgroundStatus(aIsBackground);
   }
   mIsBackground = aIsBackground;
 }
 
 void
-nsGlobalWindowOuter::MaybeUpdateTouchState()
-{
-  FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
-}
-
-void
 nsGlobalWindowOuter::SetChromeEventHandler(EventTarget* aChromeEventHandler)
 {
   SetChromeEventHandlerInternal(aChromeEventHandler);
   // update the chrome event handler on all our inner windows
   RefPtr<nsGlobalWindowInner> inner;
   for (PRCList* node = PR_LIST_HEAD(this);
        node != this;
        node = PR_NEXT_LINK(inner)) {
@@ -7239,23 +7139,16 @@ nsGlobalWindowOuter::SecurityCheckURL(co
         CheckLoadURIFromScript(cx, uri))) {
     return NS_ERROR_FAILURE;
   }
 
   uri.forget(aURI);
   return NS_OK;
 }
 
-bool
-nsGlobalWindowOuter::IsPrivateBrowsing()
-{
-  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
-  return loadContext && loadContext->UsePrivateBrowsing();
-}
-
 void
 nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType)
 {
   if (mDoc) {
     mDoc->FlushPendingNotifications(aType);
   }
 }
 
@@ -7331,39 +7224,22 @@ nsGlobalWindowOuter::RestoreWindowState(
 
   inner->Thaw();
 
   holder->DidRestoreWindow();
 
   return NS_OK;
 }
 
-// XXX(nika): Can we remove these?
-void
-nsGlobalWindowOuter::EventListenerAdded(nsAtom* aType)
-{
-}
-
-void
-nsGlobalWindowOuter::EventListenerRemoved(nsAtom* aType)
-{
-}
-
 void
 nsGlobalWindowOuter::AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
 {
   aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
 }
 
-bool
-nsGlobalWindowOuter::UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDevices)
-{
-  FORWARD_TO_INNER(UpdateVRDisplays, (aDevices), false);
-}
-
 uint32_t
 nsGlobalWindowOuter::GetAutoActivateVRDisplayID()
 {
   uint32_t retVal = mAutoActivateVRDisplayID;
   mAutoActivateVRDisplayID = 0;
   return retVal;
 }
 
@@ -7733,22 +7609,16 @@ nsGlobalWindowOuter::Create(bool aIsChro
 
 nsIURI*
 nsPIDOMWindowOuter::GetDocumentURI() const
 {
   return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
 }
 
 
-nsIURI*
-nsPIDOMWindowOuter::GetDocBaseURI() const
-{
-  return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
-}
-
 void
 nsPIDOMWindowOuter::MaybeCreateDoc()
 {
   MOZ_ASSERT(!mDoc);
   if (nsIDocShell* docShell = GetDocShell()) {
     // Note that |document| here is the same thing as our mDoc, but we
     // don't have to explicitly set the member variable because the docshell
     // has already called SetNewDocument().
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -240,18 +240,16 @@ public:
   virtual JSObject* GetGlobalJSObject() override;
 
   // nsIScriptGlobalObject
   JSObject *FastGetGlobalJSObject() const
   {
     return GetWrapperPreserveColor();
   }
 
-  void TraceGlobalJSObject(JSTracer* aTrc);
-
   virtual nsresult EnsureScriptEnvironment() override;
 
   virtual nsIScriptContext *GetScriptContext() override;
 
   void PoisonOuterWindowProxy(JSObject *aObject);
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true) override;
 
@@ -340,39 +338,32 @@ public:
 
   virtual void EnterModalState() override;
   virtual void LeaveModalState() override;
 
   // Outer windows only.
   virtual bool CanClose() override;
   virtual void ForceClose() override;
 
-  virtual void MaybeUpdateTouchState() override;
-
   // Outer windows only.
   virtual bool DispatchCustomEvent(const nsAString& aEventName) override;
   bool DispatchResizeEvent(const mozilla::CSSIntSize& aSize);
 
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Outer windows only.
   nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen) final;
   void FullscreenWillChange(bool aIsFullscreen) final;
   void FinishFullscreenChange(bool aIsFullscreen) final;
   bool SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
                            nsIWidget* aWidget, nsIScreen* aScreen);
   bool FullScreen() const;
 
-  using EventTarget::EventListenerAdded;
-  virtual void EventListenerAdded(nsAtom* aType) override;
-  using EventTarget::EventListenerRemoved;
-  virtual void EventListenerRemoved(nsAtom* aType) override;
-
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
   already_AddRefed<nsPIDOMWindowOuter> IndexedGetterOuter(uint32_t aIndex);
 
   already_AddRefed<nsPIDOMWindowOuter> GetTop() override;
   nsPIDOMWindowOuter* GetScriptableTop() override;
   inline nsGlobalWindowOuter *GetTopInternal();
@@ -501,19 +492,16 @@ public:
 
   void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
 
   void AllowScriptsToClose()
   {
     mAllowScriptsToClose = true;
   }
 
-  // Update the VR displays for this window
-  bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
-
   // Outer windows only.
   uint32_t GetAutoActivateVRDisplayID();
   // Outer windows only.
   void SetAutoActivateVRDisplayID(uint32_t aAutoActivateVRDisplayID);
 
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
@@ -669,19 +657,16 @@ public:
   mozilla::dom::Element* GetRealFrameElementOuter();
   float GetMozInnerScreenXOuter(mozilla::dom::CallerType aCallerType);
   float GetMozInnerScreenYOuter(mozilla::dom::CallerType aCallerType);
   double GetDevicePixelRatioOuter(mozilla::dom::CallerType aCallerType);
   bool GetFullScreenOuter();
   bool GetFullScreen() override;
   void SetFullScreenOuter(bool aFullScreen, mozilla::ErrorResult& aError);
   nsresult SetFullScreen(bool aFullScreen) override;
-  void BackOuter(mozilla::ErrorResult& aError);
-  void ForwardOuter(mozilla::ErrorResult& aError);
-  void HomeOuter(nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aError);
   bool FindOuter(const nsAString& aString, bool aCaseSensitive, bool aBackwards,
                  bool aWrapAround, bool aWholeWord, bool aSearchInFrames,
                  bool aShowDialog, mozilla::ErrorResult& aError);
   uint64_t GetMozPaintCountOuter();
 
   bool ShouldResistFingerprinting();
 
   already_AddRefed<nsPIDOMWindowOuter>
@@ -910,17 +895,16 @@ private:
   static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
 
 public:
   // Helper Functions
   already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
   already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
   already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
   nsresult SecurityCheckURL(const char *aURL, nsIURI** aURI);
-  bool IsPrivateBrowsing();
 
   bool PopupWhitelisted();
   PopupControlState RevisePopupAbuseLevel(PopupControlState);
   void     FireAbuseEvents(const nsAString &aPopupURL,
                            const nsAString &aPopupWindowName,
                            const nsAString &aPopupWindowFeatures);
 
 private:
@@ -1113,18 +1097,16 @@ protected:
 
   RefPtr<nsDOMWindowList>     mFrames;
   RefPtr<nsDOMWindowUtils>      mWindowUtils;
   nsString                      mStatus;
 
   RefPtr<mozilla::dom::Storage> mLocalStorage;
 
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
-  // mTabChild is only ever populated in the content process.
-  nsCOMPtr<nsITabChild>  mTabChild;
 
   uint32_t mSerial;
 
 #ifdef DEBUG
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -275,26 +275,16 @@ public:
   // requestAnimationFrame, and events should not be fired. Suspending
   // a window also suspends its children and workers.  Workers may
   // continue to perform computations in the background.  A window
   // can have Suspend() called multiple times and will only resume after
   // a matching number of Resume() calls.
   void Suspend();
   void Resume();
 
-  // Calling Freeze() on a window will automatically Suspend() it.  In
-  // addition, the window and its children are further treated as no longer
-  // suitable for interaction with the user.  For example, it may be marked
-  // non-visible, cannot be focused, etc.  All worker threads are also frozen
-  // bringing them to a complete stop.  A window can have Freeze() called
-  // multiple times and will only thaw after a matching number of Thaw()
-  // calls.
-  void Freeze();
-  void Thaw();
-
   // Apply the parent window's suspend, freeze, and modal state to the current
   // window.
   void SyncStateFromParentWindow();
 
   /**
    * Increment active peer connection count.
    */
   void AddPeerConnection();
@@ -336,40 +326,31 @@ public:
   // Return true if there are any open WebSockets that could block
   // timeout-throttling.
   bool HasOpenWebSockets() const;
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
 
-  RefPtr<mozilla::dom::ServiceWorker>
-  GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor);
-
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   void NoteDOMContentLoaded();
 
   mozilla::dom::TabGroup* TabGroup();
 
   virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
 
   virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0;
 
   // XXX: This is called on inner windows
   virtual nsPIDOMWindowOuter* GetScriptableTop() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableParent() = 0;
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() = 0;
 
-  /**
-   * Behavies identically to GetScriptableParent extept that it returns null
-   * if GetScriptableParent would return this window.
-   */
-  virtual nsPIDOMWindowOuter* GetScriptableParentOrNull() = 0;
-
   mozilla::dom::EventTarget* GetChromeEventHandler() const
   {
     return mChromeEventHandler;
   }
 
   virtual nsresult RegisterIdleObserver(
     mozilla::dom::MozIdleObserver& aIdleObserver) = 0;
   virtual nsresult UnregisterIdleObserver(
@@ -857,17 +838,17 @@ public:
 
   virtual already_AddRefed<nsPIDOMWindowOuter> GetTop() = 0; // Outer only
   virtual already_AddRefed<nsPIDOMWindowOuter> GetParent() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableTop() = 0;
   virtual nsPIDOMWindowOuter* GetScriptableParent() = 0;
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() = 0;
 
   /**
-   * Behavies identically to GetScriptableParent extept that it returns null
+   * Behaves identically to GetScriptableParent except that it returns null
    * if GetScriptableParent would return this window.
    */
   virtual nsPIDOMWindowOuter* GetScriptableParentOrNull() = 0;
 
   virtual bool IsTopLevelWindowActive() = 0;
 
   virtual void SetActive(bool aActive)
   {
@@ -886,24 +867,21 @@ public:
   mozilla::dom::EventTarget* GetParentTarget()
   {
     if (!mParentTarget) {
       UpdateParentTarget();
     }
     return mParentTarget;
   }
 
-  virtual void MaybeUpdateTouchState() {}
-
   nsIDocument* GetExtantDoc() const
   {
     return mDoc;
   }
   nsIURI* GetDocumentURI() const;
-  nsIURI* GetDocBaseURI() const;
 
   nsIDocument* GetDoc()
   {
     if (!mDoc) {
       MaybeCreateDoc();
     }
     return mDoc;
   }
@@ -1192,17 +1170,16 @@ protected:
 
   // These two variables are special in that they're set to the same
   // value on both the outer window and the current inner window. Make
   // sure you keep them in sync!
   nsCOMPtr<mozilla::dom::EventTarget> mChromeEventHandler; // strong
   nsCOMPtr<nsIDocument> mDoc; // strong
   // Cache the URI when mDoc is cleared.
   nsCOMPtr<nsIURI> mDocumentURI; // strong
-  nsCOMPtr<nsIURI> mDocBaseURI; // strong
 
   nsCOMPtr<mozilla::dom::EventTarget> mParentTarget; // strong
 
   nsCOMPtr<mozilla::dom::Element> mFrameElement;
 
   // This reference is used by nsGlobalWindow.
   nsCOMPtr<nsIDocShell> mDocShell;
 
--- a/dom/events/DOMEventTargetHelper.cpp
+++ b/dom/events/DOMEventTargetHelper.cpp
@@ -290,30 +290,32 @@ DOMEventTargetHelper::IgnoreKeepAliveIfH
   MaybeUpdateKeepAlive();
 }
 
 void
 DOMEventTargetHelper::MaybeUpdateKeepAlive()
 {
   bool shouldBeKeptAlive = false;
 
-  if (!mKeepingAliveTypes.mAtoms.IsEmpty()) {
-    for (uint32_t i = 0; i < mKeepingAliveTypes.mAtoms.Length(); ++i) {
-      if (HasListenersFor(mKeepingAliveTypes.mAtoms[i])) {
-        shouldBeKeptAlive = true;
-        break;
+  if (NS_SUCCEEDED(CheckInnerWindowCorrectness())) {
+    if (!mKeepingAliveTypes.mAtoms.IsEmpty()) {
+      for (uint32_t i = 0; i < mKeepingAliveTypes.mAtoms.Length(); ++i) {
+        if (HasListenersFor(mKeepingAliveTypes.mAtoms[i])) {
+          shouldBeKeptAlive = true;
+          break;
+        }
       }
     }
-  }
 
-  if (!shouldBeKeptAlive && !mKeepingAliveTypes.mStrings.IsEmpty()) {
-    for (uint32_t i = 0; i < mKeepingAliveTypes.mStrings.Length(); ++i) {
-      if (HasListenersFor(mKeepingAliveTypes.mStrings[i])) {
-        shouldBeKeptAlive = true;
-        break;
+    if (!shouldBeKeptAlive && !mKeepingAliveTypes.mStrings.IsEmpty()) {
+      for (uint32_t i = 0; i < mKeepingAliveTypes.mStrings.Length(); ++i) {
+        if (HasListenersFor(mKeepingAliveTypes.mStrings[i])) {
+          shouldBeKeptAlive = true;
+          break;
+        }
       }
     }
   }
 
   if (shouldBeKeptAlive == mIsKeptAlive) {
     return;
   }
 
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -299,17 +299,18 @@ interface nsIContentSecurityPolicy : nsI
    * of a redirect. In this case, aOriginalURIIfRedirect must be the original
    * URL.
    */
   short shouldLoad(in nsContentPolicyType aContentType,
                    in nsIURI          aContentLocation,
                    in nsIURI          aRequestOrigin,
                    in nsISupports     aContext,
                    in ACString        aMimeTypeGuess,
-                   in nsIURI          aOriginalURIIfRedirect);
+                   in nsIURI          aOriginalURIIfRedirect,
+                   in bool            aSendViolationReports);
 
 %{ C++
 // nsIObserver topic to fire when the policy encounters a violation.
 #define CSP_VIOLATION_TOPIC "csp-on-violate-policy"
 %}
 
   /**
    * Returns the CSP in JSON notation.
--- a/dom/permission/PermissionStatus.cpp
+++ b/dom/permission/PermissionStatus.cpp
@@ -30,16 +30,17 @@ PermissionStatus::Create(nsPIDOMWindowIn
 }
 
 PermissionStatus::PermissionStatus(nsPIDOMWindowInner* aWindow,
                                    PermissionName aName)
   : DOMEventTargetHelper(aWindow)
   , mName(aName)
   , mState(PermissionState::Denied)
 {
+  KeepAliveIfHasListenersFor(NS_LITERAL_STRING("change"));
 }
 
 nsresult
 PermissionStatus::Init()
 {
   mObserver = PermissionObserver::GetInstance();
   if (NS_WARN_IF(!mObserver)) {
     return NS_ERROR_FAILURE;
@@ -120,10 +121,23 @@ PermissionStatus::PermissionChanged()
   UpdateState();
   if (mState != oldState) {
     RefPtr<AsyncEventDispatcher> eventDispatcher =
       new AsyncEventDispatcher(this, NS_LITERAL_STRING("change"), CanBubble::eNo);
     eventDispatcher->PostDOMEvent();
   }
 }
 
+void
+PermissionStatus::DisconnectFromOwner()
+{
+  IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("change"));
+
+  if (mObserver) {
+    mObserver->RemoveSink(this);
+    mObserver = nullptr;
+  }
+
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/permission/PermissionStatus.h
+++ b/dom/permission/PermissionStatus.h
@@ -28,16 +28,19 @@ public:
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   PermissionState State() const { return mState; }
 
   IMPL_EVENT_HANDLER(change)
 
+  void
+  DisconnectFromOwner() override;
+
 private:
   ~PermissionStatus();
 
   PermissionStatus(nsPIDOMWindowInner* aWindow, PermissionName aName);
 
   nsresult Init();
 
   nsresult UpdateState();
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -109,100 +109,50 @@ BlockedContentSourceToString(nsCSPContex
       break;
 
     case nsCSPContext::BlockedContentSource::eSelf:
       aString.AssignLiteral("self");
       break;
   }
 }
 
-/**
- * Creates a key for use in the ShouldLoad cache.
- * Looks like: <uri>!<nsIContentPolicy::LOAD_TYPE>
- */
-nsresult
-CreateCacheKey_Internal(nsIURI* aContentLocation,
-                        nsContentPolicyType aContentType,
-                        nsACString& outCacheKey)
-{
-  if (!aContentLocation) {
-    return NS_ERROR_FAILURE;
-  }
-
-  bool isDataScheme = false;
-  nsresult rv = aContentLocation->SchemeIs("data", &isDataScheme);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  outCacheKey.Truncate();
-  if (aContentType != nsIContentPolicy::TYPE_SCRIPT && isDataScheme) {
-    // For non-script data: URI, use ("data:", aContentType) as the cache key.
-    outCacheKey.AppendLiteral("data:");
-    outCacheKey.AppendInt(aContentType);
-    return NS_OK;
-  }
-
-  nsAutoCString spec;
-  rv = aContentLocation->GetSpec(spec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Don't cache for a URI longer than the cutoff size.
-  if (spec.Length() <= CSP_CACHE_URI_CUTOFF_SIZE) {
-    outCacheKey.Append(spec);
-    outCacheKey.AppendLiteral("!");
-    outCacheKey.AppendInt(aContentType);
-  }
-
-  return NS_OK;
-}
-
 /* =====  nsIContentSecurityPolicy impl ====== */
 
 NS_IMETHODIMP
 nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
                          nsIURI*             aContentLocation,
                          nsIURI*             aRequestOrigin,
                          nsISupports*        aRequestContext,
                          const nsACString&   aMimeTypeGuess,
                          nsIURI*             aOriginalURIIfRedirect,
+                         bool                aSendViolationReports,
                          int16_t*            outDecision)
 {
   if (CSPCONTEXTLOGENABLED()) {
     CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s",
                    aContentLocation->GetSpecOrDefault().get()));
     CSPCONTEXTLOG((">>>>                      aContentType: %d", aContentType));
   }
 
   bool isPreload = nsContentUtils::IsPreloadType(aContentType);
 
   // Since we know whether we are dealing with a preload, we have to convert
   // the internal policytype ot the external policy type before moving on.
   // We still need to know if this is a worker so child-src can handle that
   // case correctly.
   aContentType = nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(aContentType);
 
-  nsresult rv = NS_OK;
-
   // This ShouldLoad function is called from nsCSPService::ShouldLoad,
   // which already checked a number of things, including:
   // * aContentLocation is not null; we can consume this without further checks
   // * scheme is not a whitelisted scheme (about: chrome:, etc).
   // * CSP is enabled
   // * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
   // * Fast Path for Apps
 
-  nsAutoCString cacheKey;
-  rv = CreateCacheKey_Internal(aContentLocation, aContentType, cacheKey);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool isCached = mShouldLoadCache.Get(cacheKey, outDecision);
-  if (isCached && cacheKey.Length() > 0) {
-    // this is cached, use the cached value.
-    return NS_OK;
-  }
-
   // Default decision, CSP can revise it if there's a policy to enforce
   *outDecision = nsIContentPolicy::ACCEPT;
 
   // If the content type doesn't map to a CSP directive, there's nothing for
   // CSP to do.
   CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
   if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
     return NS_OK;
@@ -228,28 +178,23 @@ nsCSPContext::ShouldLoad(nsContentPolicy
 
   bool permitted = permitsInternal(dir,
                                    nullptr, // aTriggeringElement
                                    aContentLocation,
                                    aOriginalURIIfRedirect,
                                    nonce,
                                    isPreload,
                                    false,     // allow fallback to default-src
-                                   true,      // send violation reports
+                                   aSendViolationReports,
                                    true,     // send blocked URI in violation reports
                                    parserCreated);
 
   *outDecision = permitted ? nsIContentPolicy::ACCEPT
                            : nsIContentPolicy::REJECT_SERVER;
 
-  // Done looping, cache any relevant result
-  if (cacheKey.Length() > 0 && !isPreload) {
-    mShouldLoadCache.Put(cacheKey, *outDecision);
-  }
-
   if (CSPCONTEXTLOGENABLED()) {
     CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, "
                    "aContentLocation: %s",
                    *outDecision > 0 ? "load" : "deny",
                    aContentLocation->GetSpecOrDefault().get()));
   }
   return NS_OK;
 }
@@ -330,17 +275,16 @@ nsCSPContext::nsCSPContext()
 }
 
 nsCSPContext::~nsCSPContext()
 {
   CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     delete mPolicies[i];
   }
-  mShouldLoadCache.Clear();
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetPolicyString(uint32_t aIndex, nsAString& outStr)
 {
   if (aIndex >= mPolicies.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
@@ -425,18 +369,16 @@ nsCSPContext::AppendPolicy(const nsAStri
         mSelfURI->GetAsciiSpec(selfURIspec);
       }
       referrer = NS_ConvertUTF16toUTF8(mReferrer);
       CSPCONTEXTLOG(("nsCSPContext::AppendPolicy added UPGRADE_IF_INSECURE_DIRECTIVE self-uri=%s referrer=%s",
                      selfURIspec.get(), referrer.get()));
     }
 
     mPolicies.AppendElement(policy);
-    // reset cache since effective policy changes
-    mShouldLoadCache.Clear();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
                             bool* outAllowsEval)
 {
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsCSPContext_h___
 #define nsCSPContext_h___
 
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/SecurityPolicyViolationEvent.h"
 #include "mozilla/StaticPrefs.h"
-#include "nsDataHashtable.h"
 #include "nsIChannel.h"
 #include "nsIChannelEventSink.h"
 #include "nsIClassInfo.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsISerializable.h"
 #include "nsIStreamListener.h"
 #include "nsWeakReference.h"
@@ -167,17 +166,16 @@ class nsCSPContext : public nsIContentSe
                                uint32_t aViolatedPolicyIndex,
                                uint32_t aLineNumber,
                                uint32_t aColumnNumber);
 
     nsString                                   mReferrer;
     uint64_t                                   mInnerWindowID; // used for web console logging
     nsTArray<nsCSPPolicy*>                     mPolicies;
     nsCOMPtr<nsIURI>                           mSelfURI;
-    nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
     nsCOMPtr<nsILoadGroup>                     mCallingChannelLoadGroup;
     nsWeakPtr                                  mLoadingContext;
     // The CSP hangs off the principal, so let's store a raw pointer of the principal
     // to avoid memory leaks. Within the destructor of the principal we explicitly
     // set mLoadingPrincipal to null.
     nsIPrincipal*                              mLoadingPrincipal;
     nsCOMPtr<nsICSPEventListener>              mEventListener;
 
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -183,16 +183,17 @@ CSPService::ShouldLoad(nsIURI *aContentL
     if (preloadCsp) {
       // obtain the enforcement decision
       rv = preloadCsp->ShouldLoad(contentType,
                                   aContentLocation,
                                   requestOrigin,
                                   requestContext,
                                   aMimeTypeGuess,
                                   nullptr, // no redirect, aOriginal URL is null.
+                                  aLoadInfo->GetSendCSPViolationEvents(),
                                   aDecision);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // if the preload policy already denied the load, then there
       // is no point in checking the real policy
       if (NS_CP_REJECTED(*aDecision)) {
         return NS_OK;
       }
@@ -207,16 +208,17 @@ CSPService::ShouldLoad(nsIURI *aContentL
   if (csp) {
     // obtain the enforcement decision
     rv = csp->ShouldLoad(contentType,
                          aContentLocation,
                          requestOrigin,
                          requestContext,
                          aMimeTypeGuess,
                          nullptr, // no redirect, aOriginal URL is null.
+                         aLoadInfo->GetSendCSPViolationEvents(),
                          aDecision);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CSPService::ShouldProcess(nsIURI           *aContentLocation,
@@ -319,16 +321,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
     if (preloadCsp) {
       // Pass  originalURI to indicate the redirect
       preloadCsp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
                              newUri,         // nsIURI
                              nullptr,        // nsIURI
                              requestContext, // nsISupports
                              EmptyCString(), // ACString - MIME guess
                              originalUri,    // Original nsIURI
+                             true,           // aSendViolationReports
                              &aDecision);
 
       // if the preload policy already denied the load, then there
       // is no point in checking the real policy
       if (NS_CP_REJECTED(aDecision)) {
         autoCallback.DontCallback();
         oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
         return NS_BINDING_FAILED;
@@ -343,16 +346,17 @@ CSPService::AsyncOnChannelRedirect(nsICh
   if (csp) {
     // Pass  originalURI to indicate the redirect
     csp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
                     newUri,         // nsIURI
                     nullptr,        // nsIURI
                     requestContext, // nsISupports
                     EmptyCString(), // ACString - MIME guess
                     originalUri,    // Original nsIURI
+                    true,           // aSendViolationReports
                     &aDecision);
   }
 
   // if ShouldLoad doesn't accept the load, cancel the request
   if (!NS_CP_ACCEPTED(aDecision)) {
     autoCallback.DontCallback();
     oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     return NS_BINDING_FAILED;
--- a/dom/security/test/unit/test_csp_reports.js
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -147,17 +147,17 @@ function run_test() {
         }
       });
 
   makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false,
       function(csp) {
         // shouldLoad creates and sends out the report here.
         csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                       NetUtil.newURI("http://blocked.test/foo.js"),
-                      null, null, null, null);
+                      null, null, null, null, true);
       });
 
   // test that inline script violations cause a report in report-only policy
   makeTest(3, {"blocked-uri": "inline"}, true,
       function(csp) {
         let inlineOK = true;
         inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
                                        "", // aNonce
@@ -197,40 +197,40 @@ function run_test() {
   makeTest(5, {"blocked-uri": "data"}, false,
     function(csp) {
       var base64data =
         "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
         "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
                      NetUtil.newURI("data:image/png;base64," + base64data),
-                     null, null, null, null);
+                     null, null, null, null, true);
       });
 
   // test that only the uri's scheme is reported for globally unique identifiers
   makeTest(6, {"blocked-uri": "intent"}, false,
     function(csp) {
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
                      NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"),
-                     null, null, null, null);
+                     null, null, null, null, true);
       });
 
   // test fragment removal
   var selfSpec = REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js";
   makeTest(7, {"blocked-uri": selfSpec}, false,
     function(csp) {
       var uri = NetUtil
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                      NetUtil.newURI(selfSpec + "#bar"),
-                     null, null, null, null);
+                     null, null, null, null, true);
       });
 
   // test scheme of ftp:
   makeTest(8, {"blocked-uri": "ftp://blocked.test/profile.png"}, false,
     function(csp) {
       // shouldLoad creates and sends out the report here.
       csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                     NetUtil.newURI("ftp://blocked.test/profile.png"),
-                    null, null, null, null);
+                    null, null, null, null, true);
     });
 }
--- a/dom/svg/SVGMarkerElement.cpp
+++ b/dom/svg/SVGMarkerElement.cpp
@@ -9,17 +9,16 @@
 #include "nsGkAtoms.h"
 #include "nsCOMPtr.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 #include "nsError.h"
 #include "mozilla/dom/SVGAngle.h"
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/dom/SVGMarkerElement.h"
 #include "mozilla/dom/SVGMarkerElementBinding.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/FloatingPoint.h"
 #include "SVGContentUtils.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::dom::SVGMarkerElement_Binding;
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Marker)
@@ -64,21 +63,16 @@ nsSVGElement::AngleInfo SVGMarkerElement
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsresult
 nsSVGOrientType::SetBaseValue(uint16_t aValue,
                               nsSVGElement *aSVGElement)
 {
-  if (aValue == SVG_MARKER_ORIENT_AUTO_START_REVERSE &&
-      !SVGMarkerElement::MarkerImprovementsPrefEnabled()) {
-    return NS_ERROR_DOM_TYPE_ERR;
-  }
-
   if (aValue == SVG_MARKER_ORIENT_AUTO ||
       aValue == SVG_MARKER_ORIENT_ANGLE ||
       aValue == SVG_MARKER_ORIENT_AUTO_START_REVERSE) {
     SetBaseValue(aValue);
     aSVGElement->SetAttr(
       kNameSpaceID_None, nsGkAtoms::orient, nullptr,
       (aValue == SVG_MARKER_ORIENT_AUTO ?
         NS_LITERAL_STRING("auto") :
@@ -221,18 +215,17 @@ SVGMarkerElement::ParseAttribute(int32_t
   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::orient) {
     if (aValue.EqualsLiteral("auto")) {
       mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO);
       aResult.SetTo(aValue);
       mAngleAttributes[ORIENT].SetBaseValue(0.f, SVG_ANGLETYPE_UNSPECIFIED,
                                             this, false);
       return true;
     }
-    if (aValue.EqualsLiteral("auto-start-reverse") &&
-        MarkerImprovementsPrefEnabled()) {
+    if (aValue.EqualsLiteral("auto-start-reverse")) {
       mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO_START_REVERSE);
       aResult.SetTo(aValue);
       mAngleAttributes[ORIENT].SetBaseValue(0.f, SVG_ANGLETYPE_UNSPECIFIED,
                                             this, false);
       return true;
     }
     mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE);
   }
@@ -381,16 +374,10 @@ SVGMarkerElement::GetViewBoxTransform()
     TM.PostTranslate(-ref.x, -ref.y);
 
     mViewBoxToViewportTransform = new gfx::Matrix(TM);
   }
 
   return *mViewBoxToViewportTransform;
 }
 
-/* static */ bool
-SVGMarkerElement::MarkerImprovementsPrefEnabled()
-{
-  return Preferences::GetBool("svg.marker-improvements.enabled", false);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGMarkerElement.h
+++ b/dom/svg/SVGMarkerElement.h
@@ -122,19 +122,16 @@ public:
   nsSVGViewBoxRect GetViewBoxRect();
   gfx::Matrix GetViewBoxTransform();
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   nsSVGOrientType* GetOrientType() { return &mOrientType; }
 
-  // Returns the value of svg.marker-improvements.enabled.
-  static bool MarkerImprovementsPrefEnabled();
-
   // WebIDL
   already_AddRefed<SVGAnimatedRect> ViewBox();
   already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
   already_AddRefed<SVGAnimatedLength> RefX();
   already_AddRefed<SVGAnimatedLength> RefY();
   already_AddRefed<SVGAnimatedEnumeration> MarkerUnits();
   already_AddRefed<SVGAnimatedLength> MarkerWidth();
   already_AddRefed<SVGAnimatedLength> MarkerHeight();
--- a/dom/svg/test/test_dataTypes.html
+++ b/dom/svg/test/test_dataTypes.html
@@ -366,17 +366,13 @@ function runTests()
   is(marker.viewBox.animVal.x, 9, "viewBox.x animVal after re-setting attribute to same rect value");
   is(marker.viewBox.animVal.y, 10, "viewBox.y animVal after re-setting attribute to same rect value");
   is(marker.viewBox.animVal.width, 11, "viewBox.width animVal after re-setting attribute to same rect value");
   is(marker.viewBox.animVal.height, 12, "viewBox.height animVal after re-setting attribute to same rect value");
 
   SimpleTest.finish();
 }
 
-function runTestsWithPref() {
-  SpecialPowers.pushPrefEnv({ 'set': [['svg.marker-improvements.enabled', true]] }, runTests);
-}
-
-window.addEventListener("load", runTestsWithPref);
+window.addEventListener("load", runTests);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/svg/test/test_markerOrient.xhtml
+++ b/dom/svg/test/test_markerOrient.xhtml
@@ -83,21 +83,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     // testing condition 1 for orient set to "auto-start-reverse" using
     // setAttribute
     m.setAttribute("orient", "auto-start-reverse");
     testAutoStartReverseIsSet(m);
 
     SimpleTest.finish();
   }
 
-  function runTestsWithPref() {
-    SpecialPowers.pushPrefEnv({ 'set': [['svg.marker-improvements.enabled', true]] }, run);
-  }
-
-  window.addEventListener("load", runTestsWithPref);
+  window.addEventListener("load", run);
 
   ]]>
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=892372">Mozilla Bug 892372</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/tests/mochitest/chrome/window_focus.xul
+++ b/dom/tests/mochitest/chrome/window_focus.xul
@@ -1560,17 +1560,17 @@ function doFrameHistoryTests()
   gChildWindow.addEventListener("focus",
     function(event) {
       if (event.target == t20) {
         is(fm.focusedElement, t20, "focus restored after history back"); done();
     }
   }, true);
 
   // make sure that loading a new page and then going back maintains the focus
-  gChildWindow.location = "data:text/html,<script>window.onload=function() {setTimeout(function () {SpecialPowers.wrap(window).back();}, 0);}</script>";
+  gChildWindow.location = "data:text/html,<script>window.onload=function() {setTimeout(function () { history.back() }, 0);}</script>";
 }
 
 function addFrameSwitchingListeners(frame)
 {
   frame.contentWindow.addEventListener("focus", frameSwitchingEventOccured, false);
   frame.contentWindow.addEventListener("blur", frameSwitchingEventOccured, false);
   frame.contentDocument.addEventListener("focus", frameSwitchingEventOccured, false);
   frame.contentDocument.addEventListener("blur", frameSwitchingEventOccured, false);
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -268,20 +268,16 @@ partial interface Window {
      (i.e., the document width/height minus the scrollport width/height) */
   [ChromeOnly, Throws]  readonly attribute long   scrollMinX;
   [ChromeOnly, Throws]  readonly attribute long   scrollMinY;
   [Replaceable, Throws] readonly attribute long   scrollMaxX;
   [Replaceable, Throws] readonly attribute long   scrollMaxY;
 
   [Throws] attribute boolean fullScreen;
 
-  [Throws, ChromeOnly] void back();
-  [Throws, ChromeOnly] void forward();
-  [Throws, ChromeOnly, NeedsSubjectPrincipal] void home();
-
   // XXX Should this be in nsIDOMChromeWindow?
   void                      updateCommands(DOMString action,
                                            optional Selection? sel = null,
                                            optional short reason = 0);
 
   /* Find in page.
    * @param str: the search pattern
    * @param caseSensitive: is the search caseSensitive
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -945,16 +945,17 @@ pub extern "C" fn wr_window_new(window_i
                 }
             }
         },
         renderer_id: Some(window_id.0),
         upload_method,
         scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),
         sampler: Some(Box::new(SamplerCallback::new(window_id))),
         max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
+        clear_color: Some(ColorF::new(0.0, 0.0, 0.0, 0.0)),
         ..Default::default()
     };
 
     let notifier = Box::new(CppNotifier {
         window_id: window_id,
     });
     let (renderer, sender) = match Renderer::new(gl, notifier, opts) {
         Ok((renderer, sender)) => (renderer, sender),
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -611,17 +611,18 @@ ShouldRevalidateEntry(imgCacheEntry* aEn
   return bValidateEntry;
 }
 
 /* Call content policies on cached images that went through a redirect */
 static bool
 ShouldLoadCachedImage(imgRequest* aImgRequest,
                       nsISupports* aLoadingContext,
                       nsIPrincipal* aTriggeringPrincipal,
-                      nsContentPolicyType aPolicyType)
+                      nsContentPolicyType aPolicyType,
+                      bool aSendCSPViolationReports)
 {
   /* Call content policies on cached images - Bug 1082837
    * Cached images are keyed off of the first uri in a redirect chain.
    * Hence content policies don't get a chance to test the intermediate hops
    * or the final desitnation.  Here we test the final destination using
    * mFinalURI off of the imgRequest and passing it into content policies.
    * For Mixed Content Blocker, we do an additional check to determine if any
    * of the intermediary hops went through an insecure redirect with the
@@ -645,16 +646,18 @@ ShouldLoadCachedImage(imgRequest* aImgRe
 
   nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
     new LoadInfo(loadingPrincipal,
                  aTriggeringPrincipal,
                  requestingNode,
                  nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
                  aPolicyType);
 
+  secCheckLoadInfo->SetSendCSPViolationEvents(aSendCSPViolationReports);
+
   int16_t decision = nsIContentPolicy::REJECT_REQUEST;
   rv = NS_CheckContentLoadPolicy(contentLocation,
                                  secCheckLoadInfo,
                                  EmptyCString(), //mime guess
                                  &decision,
                                  nsContentUtils::GetContentPolicy());
   if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
     return false;
@@ -741,17 +744,18 @@ ValidateSecurityInfo(imgRequest* request
       otherprincipal->Equals(triggeringPrincipal, &equals);
       if (!equals) {
         return false;
       }
     }
   }
 
   // Content Policy Check on Cached Images
-  return ShouldLoadCachedImage(request, aCX, triggeringPrincipal, aPolicyType);
+  return ShouldLoadCachedImage(request, aCX, triggeringPrincipal, aPolicyType,
+                               /* aSendCSPViolationReports */ false);
 }
 
 static nsresult
 NewImageChannel(nsIChannel** aResult,
                 // If aForcePrincipalCheckForCacheEntry is true, then we will
                 // force a principal check even when not using CORS before
                 // assuming we have a cache hit on a cache entry that we
                 // create for this channel.  This is an out param that should
@@ -1792,17 +1796,18 @@ imgLoader::ValidateRequestWithNewChannel
                                          nsILoadGroup* aLoadGroup,
                                          imgINotificationObserver* aObserver,
                                          nsISupports* aCX,
                                          nsIDocument* aLoadingDocument,
                                          nsLoadFlags aLoadFlags,
                                          nsContentPolicyType aLoadPolicyType,
                                          imgRequestProxy** aProxyRequest,
                                          nsIPrincipal* aTriggeringPrincipal,
-                                         int32_t aCORSMode)
+                                         int32_t aCORSMode,
+                                         bool* aNewChannelCreated)
 {
   // now we need to insert a new channel request object inbetween the real
   // request and the proxy that basically delays loading the image until it
   // gets a 304 or figures out that this needs to be a new request
 
   nsresult rv;
 
   // If we're currently in the middle of validating this request, just hand
@@ -1848,16 +1853,20 @@ imgLoader::ValidateRequestWithNewChannel
                        aLoadPolicyType,
                        aTriggeringPrincipal,
                        aCX,
                        mRespectPrivacy);
   if (NS_FAILED(rv)) {
     return false;
   }
 
+  if (aNewChannelCreated) {
+    *aNewChannelCreated = true;
+  }
+
   RefPtr<imgRequestProxy> req;
   rv = CreateNewProxyForRequest(request, aLoadGroup, aLoadingDocument,
                                 aObserver, aLoadFlags, getter_AddRefs(req));
   if (NS_FAILED(rv)) {
     return false;
   }
 
   // Make sure that OnStatus/OnProgress calls have the right request set...
@@ -1912,16 +1921,17 @@ imgLoader::ValidateEntry(imgCacheEntry* 
                          ReferrerPolicy aReferrerPolicy,
                          nsILoadGroup* aLoadGroup,
                          imgINotificationObserver* aObserver,
                          nsISupports* aCX,
                          nsIDocument* aLoadingDocument,
                          nsLoadFlags aLoadFlags,
                          nsContentPolicyType aLoadPolicyType,
                          bool aCanMakeNewChannel,
+                         bool* aNewChannelCreated,
                          imgRequestProxy** aProxyRequest,
                          nsIPrincipal* aTriggeringPrincipal,
                          int32_t aCORSMode)
 {
   LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
 
   // If the expiration time is zero, then the request has not gotten far enough
   // to know when it will expire.
@@ -2031,17 +2041,17 @@ imgLoader::ValidateEntry(imgCacheEntry* 
               "imgLoader::ValidateRequest |cache hit| must validate");
 
     return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
                                          aReferrerURI, aReferrerPolicy,
                                          aLoadGroup, aObserver,
                                          aCX, aLoadingDocument,
                                          aLoadFlags, aLoadPolicyType,
                                          aProxyRequest, aTriggeringPrincipal,
-                                         aCORSMode);
+                                         aCORSMode, aNewChannelCreated);
   }
 
   return !validateRequest;
 }
 
 bool
 imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
 {
@@ -2336,20 +2346,22 @@ imgLoader::LoadImage(nsIURI* aURI,
   if (aTriggeringPrincipal) {
     attrs = aTriggeringPrincipal->OriginAttributesRef();
   }
   ImageCacheKey key(aURI, attrs, aLoadingDocument, rv);
   NS_ENSURE_SUCCESS(rv, rv);
   imgCacheTable& cache = GetCache(key);
 
   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
+    bool newChannelCreated = false;
     if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
                       aReferrerPolicy, aLoadGroup, aObserver, aLoadingDocument,
                       aLoadingDocument, requestFlags, aContentPolicyType, true,
-                      _retval, aTriggeringPrincipal, corsmode)) {
+                      &newChannelCreated, _retval, aTriggeringPrincipal,
+                      corsmode)) {
       request = entry->GetRequest();
 
       // If this entry has no proxies, its request has no reference to the
       // entry.
       if (entry->HasNoProxies()) {
         LOG_FUNC_WITH_PARAM(gImgLog,
           "imgLoader::LoadImage() adding proxyless entry", "uri", key.URI());
         MOZ_ASSERT(!request->HasCacheEntry(),
@@ -2358,16 +2370,28 @@ imgLoader::LoadImage(nsIURI* aURI,
 
         if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
           mCacheTracker->MarkUsed(entry);
         }
       }
 
       entry->Touch();
 
+      if (!newChannelCreated) {
+        // This is ugly but it's needed to report CSP violations. We have 3
+        // scenarios:
+        // - we don't have cache. We are not in this if() stmt. A new channel is
+        //   created and that triggers the CSP checks.
+        // - We have a cache entry and this is blocked by CSP directives.
+        DebugOnly<bool> shouldLoad =
+          ShouldLoadCachedImage(request, aLoadingDocument, aTriggeringPrincipal,
+                                aContentPolicyType,
+                                /* aSendCSPViolationReports */ true);
+        MOZ_ASSERT(shouldLoad);
+      }
     } else {
       // We can't use this entry. We'll try to load it off the network, and if
       // successful, overwrite the old entry in the cache with a new one.
       entry = nullptr;
     }
   }
 
   // Keep the channel in this scope, so we can adjust its notificationCallbacks
@@ -2608,17 +2632,17 @@ imgLoader::LoadImageWithChannel(nsIChann
       // if there is a loadInfo, use the right contentType, otherwise
       // default to the internal image type
       nsContentPolicyType policyType = loadInfo
         ? loadInfo->InternalContentPolicyType()
         : nsIContentPolicy::TYPE_INTERNAL_IMAGE;
 
       if (ValidateEntry(entry, uri, nullptr, nullptr, RP_Unset,
                         nullptr, aObserver, aCX, doc, requestFlags,
-                        policyType, false, nullptr,
+                        policyType, false, nullptr, nullptr,
                         nullptr, imgIRequest::CORS_NONE)) {
         request = entry->GetRequest();
       } else {
         nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(channel));
         bool bUseCacheCopy;
 
         if (cacheChan) {
           cacheChan->IsFromCache(&bUseCacheCopy);
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -408,33 +408,35 @@ private: // methods
                      nsIURI* aInitialDocumentURI, nsIURI* aReferrerURI,
                      ReferrerPolicy aReferrerPolicy,
                      nsILoadGroup* aLoadGroup,
                      imgINotificationObserver* aObserver, nsISupports* aCX,
                      nsIDocument* aLoadingDocument,
                      nsLoadFlags aLoadFlags,
                      nsContentPolicyType aContentPolicyType,
                      bool aCanMakeNewChannel,
+                     bool* aNewChannelCreated,
                      imgRequestProxy** aProxyRequest,
                      nsIPrincipal* aLoadingPrincipal,
                      int32_t aCORSMode);
 
   bool ValidateRequestWithNewChannel(imgRequest* request, nsIURI* aURI,
                                      nsIURI* aInitialDocumentURI,
                                      nsIURI* aReferrerURI,
                                      ReferrerPolicy aReferrerPolicy,
                                      nsILoadGroup* aLoadGroup,
                                      imgINotificationObserver* aObserver,
                                      nsISupports* aCX,
                                      nsIDocument* aLoadingDocument,
                                      nsLoadFlags aLoadFlags,
                                      nsContentPolicyType aContentPolicyType,
                                      imgRequestProxy** aProxyRequest,
                                      nsIPrincipal* aLoadingPrincipal,
-                                     int32_t aCORSMode);
+                                     int32_t aCORSMode,
+                                     bool* aNewChannelCreated);
 
   nsresult CreateNewProxyForRequest(imgRequest* aRequest,
                                     nsILoadGroup* aLoadGroup,
                                     nsIDocument* aLoadingDocument,
                                     imgINotificationObserver* aObserver,
                                     nsLoadFlags aLoadFlags,
                                     imgRequestProxy** _retval);
 
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -426,16 +426,17 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       aLoadInfo->GetOuterWindowID(),
       aLoadInfo->GetParentOuterWindowID(),
       aLoadInfo->GetTopOuterWindowID(),
       aLoadInfo->GetFrameOuterWindowID(),
       aLoadInfo->GetEnforceSecurity(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(),
       aLoadInfo->GetIsDocshellReload(),
+      aLoadInfo->GetSendCSPViolationEvents(),
       aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects,
       redirectChain,
       ancestorPrincipals,
       aLoadInfo->AncestorOuterWindowIDs(),
       ipcClientInfo,
       ipcReservedClientInfo,
       ipcInitialClientInfo,
@@ -582,16 +583,17 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           loadInfoArgs.outerWindowID(),
                           loadInfoArgs.parentOuterWindowID(),
                           loadInfoArgs.topOuterWindowID(),
                           loadInfoArgs.frameOuterWindowID(),
                           loadInfoArgs.enforceSecurity(),
                           loadInfoArgs.initialSecurityCheckDone(),
                           loadInfoArgs.isInThirdPartyContext(),
                           loadInfoArgs.isDocshellReload(),
+                          loadInfoArgs.sendCSPViolationEvents(),
                           loadInfoArgs.originAttributes(),
                           redirectChainIncludingInternalRedirects,
                           redirectChain,
                           std::move(ancestorPrincipals),
                           loadInfoArgs.ancestorOuterWindowIDs(),
                           loadInfoArgs.corsUnsafeHeaders(),
                           loadInfoArgs.forcePreflight(),
                           loadInfoArgs.isPreflight(),
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -261,16 +261,21 @@ def main(argv):
         options.exclude += [os.path.join('xdr', 'bug1186973.js')]                # Bug 1369785
         options.exclude += [os.path.join('xdr', 'relazify.js')]
         options.exclude += [os.path.join('basic', 'werror.js')]
 
         # Prevent code coverage test that expects coverage
         # to be off when it starts.
         options.exclude += [os.path.join('debug', 'Script-getOffsetsCoverage-02.js')]
 
+        # These tests expect functions to be parsed lazily, but lazy parsing
+        # is disabled on coverage build.
+        options.exclude += [os.path.join('debug', 'Debugger-findScripts-uncompleted-01.js')]
+        options.exclude += [os.path.join('debug', 'Debugger-findScripts-uncompleted-02.js')]
+
     if options.exclude_from:
         with open(options.exclude_from) as fh:
             for line in fh:
                 line_exclude = line.strip()
                 if not line_exclude.startswith("#") and len(line_exclude):
                     options.exclude.append(line_exclude)
 
     if options.exclude:
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -771,26 +771,24 @@ MacroAssembler::nurseryAllocateObject(Re
     if (nDynamicSlots >= Nursery::MaxNurseryBufferSize / sizeof(Value)) {
         jump(fail);
         return;
     }
 
     // No explicit check for nursery.isEnabled() is needed, as the comparison
     // with the nursery's end will always fail in such cases.
     CompileZone* zone = GetJitContext()->realm->zone();
-    int thingSize = int(gc::Arena::thingSize(allocKind));
-    int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
+    size_t thingSize = gc::Arena::thingSize(allocKind);
+    size_t totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
+    MOZ_ASSERT(totalSize < INT32_MAX);
     MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0);
-    void *ptrNurseryPosition = zone->addressOfNurseryPosition();
-    loadPtr(AbsoluteAddress(ptrNurseryPosition), result);
-    computeEffectiveAddress(Address(result, totalSize), temp);
-    const void *ptrNurseryCurrentEnd = zone->addressOfNurseryCurrentEnd();
-    branchPtr(Assembler::Below, AbsoluteAddress(ptrNurseryCurrentEnd), temp,
-        fail);
-    storePtr(temp, AbsoluteAddress(ptrNurseryPosition));
+
+    bumpPointerAllocate(result, temp, fail,
+        zone->addressOfNurseryPosition(),
+        zone->addressOfNurseryCurrentEnd(), totalSize, totalSize);
 
     if (nDynamicSlots) {
         computeEffectiveAddress(Address(result, thingSize), temp);
         storePtr(temp, Address(result, NativeObject::offsetOfSlots()));
     }
 }
 
 // Inlined version of FreeSpan::allocate. This does not fill in slots_.
@@ -957,33 +955,49 @@ MacroAssembler::nurseryAllocateString(Re
                                       Label* fail)
 {
     MOZ_ASSERT(IsNurseryAllocable(allocKind));
 
     // No explicit check for nursery.isEnabled() is needed, as the comparison
     // with the nursery's end will always fail in such cases.
 
     CompileZone* zone = GetJitContext()->realm->zone();
-    int thingSize = int(gc::Arena::thingSize(allocKind));
-    int totalSize = js::Nursery::stringHeaderSize() + thingSize;
+    size_t thingSize = gc::Arena::thingSize(allocKind);
+    size_t totalSize = js::Nursery::stringHeaderSize() + thingSize;
+    MOZ_ASSERT(totalSize < INT32_MAX,
+        "Nursery allocation too large");
     MOZ_ASSERT(totalSize % gc::CellAlignBytes == 0);
 
-    // The nursery position (allocation pointer) and the nursery end are stored
+    bumpPointerAllocate(result, temp, fail,
+        zone->addressOfStringNurseryPosition(),
+        zone->addressOfStringNurseryCurrentEnd(), totalSize, thingSize);
+    storePtr(ImmPtr(zone), Address(result, -js::Nursery::stringHeaderSize()));
+}
+
+void
+MacroAssembler::bumpPointerAllocate(Register result, Register temp, Label* fail,
+    void* posAddr, const void* curEndAddr, uint32_t totalSize, uint32_t size)
+{
+    // The position (allocation pointer) and the end pointer are stored
     // very close to each other -- specifically, easily within a 32 bit offset.
     // Use relative offsets between them, to avoid 64-bit immediate loads.
-    auto nurseryPosAddr = intptr_t(zone->addressOfStringNurseryPosition());
-    auto nurseryEndAddr = intptr_t(zone->addressOfStringNurseryCurrentEnd());
-
-    movePtr(ImmPtr(zone->addressOfNurseryPosition()), temp);
+    //
+    // I tried to optimise this further by using an extra register to avoid
+    // the final subtraction and hopefully get some more instruction
+    // parallelism, but it made no difference.
+    movePtr(ImmPtr(posAddr), temp);
     loadPtr(Address(temp, 0), result);
     addPtr(Imm32(totalSize), result);
-    branchPtr(Assembler::Below, Address(temp, nurseryEndAddr - nurseryPosAddr), result, fail);
+    CheckedInt<int32_t> endOffset = (CheckedInt<uintptr_t>(uintptr_t(curEndAddr)) -
+        CheckedInt<uintptr_t>(uintptr_t(posAddr))).toChecked<int32_t>();
+    MOZ_ASSERT(endOffset.isValid(),
+        "Position and end pointers must be nearby");
+    branchPtr(Assembler::Below, Address(temp, endOffset.value()), result, fail);
     storePtr(result, Address(temp, 0));
-    subPtr(Imm32(thingSize), result);
-    storePtr(ImmPtr(zone), Address(result, -js::Nursery::stringHeaderSize()));
+    subPtr(Imm32(size), result);
 }
 
 // Inlined equivalent of gc::AllocateString, jumping to fail if nursery
 // allocation requested but unsuccessful.
 void
 MacroAssembler::allocateString(Register result, Register temp, gc::AllocKind allocKind,
                                gc::InitialHeap initialHeap, Label* fail)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -2274,16 +2274,20 @@ class MacroAssembler : public MacroAssem
     }
 
     // Inline allocation.
   private:
     void checkAllocatorState(Label* fail);
     bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap);
     void nurseryAllocateObject(Register result, Register temp, gc::AllocKind allocKind,
                                size_t nDynamicSlots, Label* fail);
+    void bumpPointerAllocate(Register result, Register temp, Label* fail,
+        void* posAddr, const void* curEddAddr,
+        uint32_t totalSize, uint32_t size);
+
     void freeListAllocate(Register result, Register temp, gc::AllocKind allocKind, Label* fail);
     void allocateObject(Register result, Register temp, gc::AllocKind allocKind,
                         uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail);
     void nurseryAllocateString(Register result, Register temp, gc::AllocKind allocKind,
                                Label* fail);
     void allocateString(Register result, Register temp, gc::AllocKind allocKind,
                         gc::InitialHeap initialHeap, Label* fail);
     void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label* fail);
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -53,17 +53,17 @@ XPCConvert::IsMethodReflectable(const ns
         return false;
 
     for (int i = info.GetParamCount() - 1; i >= 0; i--) {
         const nsXPTParamInfo& param = info.GetParam(i);
         const nsXPTType& type = param.GetType();
 
         // Reflected methods can't use native types. All native types end up
         // getting tagged as void*, so this check is easy.
-        if (type.TagPart() == nsXPTType::T_VOID)
+        if (type.Tag() == nsXPTType::T_VOID)
             return false;
     }
     return true;
 }
 
 static JSObject*
 UnwrapNativeCPOW(nsISupports* wrapper)
 {
@@ -103,17 +103,17 @@ XPCConvert::NativeData2JS(MutableHandleV
                           uint32_t arrlen, nsresult* pErr)
 {
     MOZ_ASSERT(s, "bad param");
 
     AutoJSContext cx;
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
 
-    switch (type.TagPart()) {
+    switch (type.Tag()) {
     case nsXPTType::T_I8    :
         d.setInt32(*static_cast<const int8_t*>(s));
         return true;
     case nsXPTType::T_I16   :
         d.setInt32(*static_cast<const int16_t*>(s));
         return true;
     case nsXPTType::T_I32   :
         d.setInt32(*static_cast<const int32_t*>(s));
@@ -380,19 +380,26 @@ XPCConvert::NativeData2JS(MutableHandleV
         RootedObject jsobj(cx, promise->PromiseObj());
         if (!JS_WrapObject(cx, &jsobj)) {
             return false;
         }
         d.setObject(*jsobj);
         return true;
     }
 
+    case nsXPTType::T_LEGACY_ARRAY:
+        return NativeArray2JS(d, *static_cast<const void* const*>(s),
+                              type.ArrayElementType(), iid, arrlen, pErr);
+
     case nsXPTType::T_ARRAY:
-        return NativeArray2JS(d, static_cast<const void* const*>(s),
-                              type.ArrayElementType(), iid, arrlen, pErr);
+    {
+        auto* array = static_cast<const xpt::detail::UntypedTArray*>(s);
+        return NativeArray2JS(d, array->Elements(), type.ArrayElementType(),
+                              iid, array->Length(), pErr);
+    }
 
     default:
         NS_ERROR("bad type");
         return false;
     }
     return true;
 }
 
@@ -443,17 +450,17 @@ XPCConvert::JSData2Native(void* d, Handl
 
     AutoJSContext cx;
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
 
     bool sizeis = type.Tag() == TD_PSTRING_SIZE_IS ||
         type.Tag() == TD_PWSTRING_SIZE_IS;
 
-    switch (type.TagPart()) {
+    switch (type.Tag()) {
     case nsXPTType::T_I8     :
         return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
     case nsXPTType::T_I16    :
         return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
     case nsXPTType::T_I32    :
         return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
     case nsXPTType::T_I64    :
         return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d));
@@ -829,19 +836,80 @@ XPCConvert::JSData2Native(void* d, Handl
         bool ok = !err.Failed();
         if (pErr) {
             *pErr = err.StealNSResult();
         }
 
         return ok;
     }
 
+    case nsXPTType::T_LEGACY_ARRAY:
+    {
+        void** dest = (void**)d;
+        const nsXPTType& elty = type.ArrayElementType();
+
+        *dest = nullptr;
+
+        // FIXME: XPConnect historically has shortcut the JSArray2Native codepath in
+        // its caller if arrlen is 0, allowing arbitrary values to be passed as
+        // arrays and interpreted as the empty array (bug 1458987).
+        //
+        // NOTE: Once this is fixed, null/undefined should be allowed for arrays if
+        // arrlen is 0.
+        if (arrlen == 0) {
+            return true;
+        }
+
+        bool ok = JSArray2Native(s, elty, iid, pErr, [&] (uint32_t* aLength) -> void* {
+            // Check that we have enough elements in our array.
+            if (*aLength < arrlen) {
+                if (pErr)
+                    *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
+                return nullptr;
+            }
+            *aLength = arrlen;
+
+            // Allocate the backing buffer & return it.
+            *dest = moz_xmalloc(*aLength * elty.Stride());
+            if (!*dest) {
+                if (pErr)
+                    *pErr = NS_ERROR_OUT_OF_MEMORY;
+                return nullptr;
+            }
+            return *dest;
+        });
+
+        if (!ok && *dest) {
+            // An error occurred, free any allocated backing buffer.
+            free(*dest);
+            *dest = nullptr;
+        }
+        return ok;
+    }
+
     case nsXPTType::T_ARRAY:
-        return JSArray2Native((void**)d, s, arrlen,
-                              type.ArrayElementType(), iid, pErr);
+    {
+        auto* dest = (xpt::detail::UntypedTArray*)d;
+        const nsXPTType& elty = type.ArrayElementType();
+
+        bool ok = JSArray2Native(s, elty, iid, pErr, [&] (uint32_t* aLength) -> void* {
+            if (!dest->SetLength(elty, *aLength)) {
+                if (pErr)
+                    *pErr = NS_ERROR_OUT_OF_MEMORY;
+                return nullptr;
+            }
+            return dest->Elements();
+        });
+
+        if (!ok) {
+            // An error occurred, free any allocated backing buffer.
+            dest->Clear();
+        }
+        return ok;
+    }
 
     default:
         NS_ERROR("bad type");
         return false;
     }
     return true;
 }
 
@@ -1327,304 +1395,180 @@ XPCConvert::JSValToXPCException(MutableH
     }
     return NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 
 // array fun...
 
-static bool
-ValidArrayType(const nsXPTType& type)
-{
-    switch (type.Tag()) {
-        // These types aren't allowed to be in arrays.
-        case nsXPTType::T_VOID:
-        case nsXPTType::T_DOMSTRING:
-        case nsXPTType::T_UTF8STRING:
-        case nsXPTType::T_CSTRING:
-        case nsXPTType::T_ASTRING:
-        case nsXPTType::T_PSTRING_SIZE_IS:
-        case nsXPTType::T_PWSTRING_SIZE_IS:
-        case nsXPTType::T_ARRAY:
-            return false;
-        default:
-            return true;
-    }
-}
-
 // static
 bool
-XPCConvert::NativeArray2JS(MutableHandleValue d, const void* const* s,
+XPCConvert::NativeArray2JS(MutableHandleValue d, const void* buf,
                            const nsXPTType& type, const nsID* iid,
                            uint32_t count, nsresult* pErr)
 {
-    MOZ_ASSERT(s, "bad param");
+    MOZ_ASSERT(buf || count == 0, "Must have buf or 0 elements");
 
     AutoJSContext cx;
 
-    // XXX add support for putting chars in a string rather than an array
-
-    // XXX add support to indicate *which* array element was not convertable
-
     RootedObject array(cx, JS_NewArrayObject(cx, count));
     if (!array)
         return false;
 
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
 
-    if (!ValidArrayType(type))
-        return false;
-
     RootedValue current(cx, JS::NullValue());
     for (uint32_t i = 0; i < count; ++i) {
-        if (!NativeData2JS(&current, type.ElementPtr(*s, i), type, iid, 0, pErr) ||
+        if (!NativeData2JS(&current, type.ElementPtr(buf, i), type, iid, 0, pErr) ||
             !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE))
             return false;
     }
 
     if (pErr)
         *pErr = NS_OK;
     d.setObject(*array);
     return true;
 }
 
-
-// Fast conversion of typed arrays to native using memcpy.
-// No float or double canonicalization is done. Called by
-// JSarray2Native whenever a TypedArray is met. ArrayBuffers
-// are not accepted; create a properly typed array view on them
-// first. The element type of array must match the XPCOM
-// type in size, type and signedness exactly. As an exception,
-// Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
-// are not supported.
-
 // static
 bool
-XPCConvert::JSTypedArray2Native(void** d,
-                                JSObject* jsArray,
-                                uint32_t count,
-                                const nsXPTType& type,
-                                nsresult* pErr)
+XPCConvert::JSArray2Native(JS::HandleValue aJSVal,
+                           const nsXPTType& aEltType,
+                           const nsIID* aIID,
+                           nsresult* pErr,
+                           const ArrayAllocFixupLen& aAllocFixupLen)
 {
-    MOZ_ASSERT(jsArray, "bad param");
-    MOZ_ASSERT(d, "bad param");
-    MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array");
-
-    // Check the actual length of the input array against the
-    // given size_is.
-    uint32_t len = JS_GetTypedArrayLength(jsArray);
-    if (len < count) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
-
-        return false;
-    }
-
-    uint8_t expected;
-    switch (JS_GetArrayBufferViewType(jsArray)) {
-    case js::Scalar::Int8:
-        expected = nsXPTType::T_I8;
-        break;
-
-    case js::Scalar::Uint8:
-    case js::Scalar::Uint8Clamped:
-        expected = nsXPTType::T_U8;
-        break;
-
-    case js::Scalar::Int16:
-        expected = nsXPTType::T_I16;
-        break;
-
-    case js::Scalar::Uint16:
-        expected = nsXPTType::T_U16;
-        break;
-
-    case js::Scalar::Int32:
-        expected = nsXPTType::T_I32;
-        break;
-
-    case js::Scalar::Uint32:
-        expected = nsXPTType::T_U32;
-        break;
-
-    case js::Scalar::Float32:
-        expected = nsXPTType::T_FLOAT;
-        break;
-
-    case js::Scalar::Float64:
-        expected = nsXPTType::T_DOUBLE;
-        break;
+    // Wrap aAllocFixupLen to check length is within bounds & initialize the
+    // allocated memory if needed.
+    auto allocFixupLen = [&] (uint32_t* aLength) -> void* {
+        if (*aLength > (UINT32_MAX / aEltType.Stride())) {
+            return nullptr; // Byte length doesn't fit in uint32_t
+        }
 
-    // Yet another array type was defined? It is not supported yet...
-    default:
-        if (pErr)
-            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
-        return false;
-    }
-
-    // Check that the type we got is the type we expected.
-    if (expected != type.Tag()) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
-        return false;
-    }
-
-    // Check if the size would overflow uint32_t.
-    if (count > (UINT32_MAX / type.Stride())) {
-        if (pErr)
-            *pErr = NS_ERROR_OUT_OF_MEMORY;
-        return false;
-    }
-
-    // Get the backing memory buffer to copy out of.
-    JS::AutoCheckCannotGC nogc;
-    bool isShared;
-    void* buf = JS_GetArrayBufferViewData(jsArray, &isShared, nogc);
+        void* buf = aAllocFixupLen(aLength);
 
-    // Require opting in to shared memory - a future project.
-    if (isShared) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
-        return false;
-    }
-
-    // Allocate the buffer, and copy.
-    *d = moz_xmalloc(count * type.Stride());
-    if (!*d) {
-        if (pErr)
-            *pErr = NS_ERROR_OUT_OF_MEMORY;
-        return false;
-    }
-
-    memcpy(*d, buf, count * type.Stride());
-
-    if (pErr)
-        *pErr = NS_OK;
-
-    return true;
-}
-
-// static
-bool
-XPCConvert::JSArray2Native(void** d, HandleValue s,
-                           uint32_t count, const nsXPTType& type,
-                           const nsID* iid, nsresult* pErr)
-{
-    MOZ_ASSERT(d, "bad param");
+        // Ensure the buffer has valid values for each element. We can skip this
+        // for arithmetic types, as they do not require initialization.
+        if (buf && !aEltType.IsArithmetic()) {
+            for (uint32_t i = 0; i < *aLength; ++i) {
+                InitializeValue(aEltType, aEltType.ElementPtr(buf, i));
+            }
+        }
+        return buf;
+    };
 
     AutoJSContext cx;
 
-    // FIXME: XPConnect historically has shortcut the JSArray2Native codepath in
-    // its caller if count is 0, allowing arbitrary values to be passed as
-    // arrays and interpreted as the empty array (bug 1458987).
-    //
-    // NOTE: Once this is fixed, null/undefined should be allowed for arrays if
-    // count is 0.
-    if (count == 0) {
-        *d = nullptr;
-        return true;
+    // JSArray2Native only accepts objects (Array and TypedArray).
+    if (!aJSVal.isObject()) {
+        if (pErr)
+            *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
+        return false;
     }
-
-    // XXX add support for getting chars from strings
-
-    // XXX add support to indicate *which* array element was not convertable
+    RootedObject jsarray(cx, &aJSVal.toObject());
 
     if (pErr)
         *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
 
-    if (!ValidArrayType(type))
-        return false;
+    if (JS_IsTypedArrayObject(jsarray)) {
+        // Fast conversion of typed arrays to native using memcpy. No float or
+        // double canonicalization is done. ArrayBuffers are not accepted;
+        // create a properly typed array view on them first. The element type of
+        // array must match the XPCOM type in size, type and signedness exactly.
+        // As an exception, Uint8ClampedArray is allowed for arrays of uint8_t.
+        // DataViews are not supported.
 
-    if (s.isNullOrUndefined()) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
-        return false;
+        nsXPTTypeTag tag;
+        switch (JS_GetArrayBufferViewType(jsarray)) {
+            case js::Scalar::Int8:         tag = TD_INT8;   break;
+            case js::Scalar::Uint8:        tag = TD_UINT8;  break;
+            case js::Scalar::Uint8Clamped: tag = TD_UINT8;  break;
+            case js::Scalar::Int16:        tag = TD_INT16;  break;
+            case js::Scalar::Uint16:       tag = TD_UINT16; break;
+            case js::Scalar::Int32:        tag = TD_INT32;  break;
+            case js::Scalar::Uint32:       tag = TD_UINT32; break;
+            case js::Scalar::Float32:      tag = TD_FLOAT;  break;
+            case js::Scalar::Float64:      tag = TD_DOUBLE; break;
+            default:                       return false;
+        }
+        if (aEltType.Tag() != tag) {
+            return false;
+        }
+
+        // Allocate the backing buffer before getting the view data in case
+        // allocFixupLen can cause GCs.
+        uint32_t length = JS_GetTypedArrayLength(jsarray);
+        void* buf = allocFixupLen(&length);
+        if (!buf) {
+            return false;
+        }
+
+        // Get the backing memory buffer to copy out of.
+        JS::AutoCheckCannotGC nogc;
+        bool isShared = false;
+        const void* data = JS_GetArrayBufferViewData(jsarray, &isShared, nogc);
+
+        // Require opting in to shared memory - a future project.
+        if (isShared) {
+            return false;
+        }
+
+        // Directly copy data into the allocated target buffer.
+        memcpy(buf, data, length * aEltType.Stride());
+        return true;
     }
 
-    if (!s.isObject()) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
-        return false;
-    }
-
-    RootedObject jsarray(cx, &s.toObject());
-
-    // If this is a typed array, then try a fast conversion with memcpy.
-    if (JS_IsTypedArrayObject(jsarray)) {
-        return JSTypedArray2Native(d, jsarray, count, type, pErr);
-    }
-
-    bool isArray;
-    if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray) {
+    // If jsarray is not a TypedArrayObject, check for an Array object.
+    uint32_t length = 0;
+    bool isArray = false;
+    if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray ||
+        !JS_GetArrayLength(cx, jsarray, &length)) {
         if (pErr)
             *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
         return false;
     }
 
-    uint32_t len;
-    if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
-        if (pErr)
-            *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
-        return false;
-    }
-
-    if (count > (UINT32_MAX / type.Stride())) {
-        if (pErr)
-            *pErr = NS_ERROR_OUT_OF_MEMORY;
-        return false;
-    }
-
-    // Allocate the destination array, and begin converting elements.
-    *d = moz_xmalloc(count * type.Stride());
-    if (!*d) {
-        if (pErr)
-            *pErr = NS_ERROR_OUT_OF_MEMORY;
+    void* buf = allocFixupLen(&length);
+    if (!buf) {
         return false;
     }
 
+    // Translate each array element separately.
     RootedValue current(cx);
-    uint32_t initedCount;
-    for (initedCount = 0; initedCount < count; ++initedCount) {
-        if (!JS_GetElement(cx, jsarray, initedCount, &current) ||
-            !JSData2Native(type.ElementPtr(*d, initedCount),
-                           current, type, iid, 0, pErr))
-            break;
+    for (uint32_t i = 0; i < length; ++i) {
+        if (!JS_GetElement(cx, jsarray, i, &current) ||
+            !JSData2Native(aEltType.ElementPtr(buf, i), current,
+                           aEltType, aIID, 0, pErr)) {
+            // Array element conversion failed. Clean up all elements converted
+            // before the error. Caller handles freeing 'buf'.
+            for (uint32_t j = 0; j < i; ++j) {
+                CleanupValue(aEltType, aEltType.ElementPtr(buf, j));
+            }
+            return false;
+        }
     }
 
-    // Check if we handled every array element.
-    if (initedCount == count) {
-        if (pErr)
-            *pErr = NS_OK;
-        return true;
-    }
-
-    // Something failed! Clean up after ourselves.
-    for (uint32_t i = 0; i < initedCount; ++i) {
-        CleanupValue(type, type.ElementPtr(*d, i));
-    }
-    free(*d);
-    *d = nullptr;
-    return false;
+    return true;
 }
 
 /***************************************************************************/
 
 // Internal implementation details for xpc::CleanupValue.
 
 void
 xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen)
 {
     MOZ_ASSERT(!aType.IsArithmetic(),
                "Arithmetic types should not get to InnerCleanupValue!");
     MOZ_ASSERT(aArrayLen == 0 ||
                aType.Tag() == nsXPTType::T_PSTRING_SIZE_IS ||
                aType.Tag() == nsXPTType::T_PWSTRING_SIZE_IS ||
-               aType.Tag() == nsXPTType::T_ARRAY,
+               aType.Tag() == nsXPTType::T_LEGACY_ARRAY,
                "Array lengths may only appear for certain types!");
 
     switch (aType.Tag()) {
         // Pointer types
         case nsXPTType::T_DOMOBJECT:
             aType.GetDOMObjectInfo().Cleanup(*(void**)aValue);
             break;
 
@@ -1651,27 +1595,86 @@ xpc::InnerCleanupValue(const nsXPTType& 
         case nsXPTType::T_IID:
         case nsXPTType::T_CHAR_STR:
         case nsXPTType::T_WCHAR_STR:
         case nsXPTType::T_PSTRING_SIZE_IS:
         case nsXPTType::T_PWSTRING_SIZE_IS:
             free(*(void**)aValue);
             break;
 
-        // Array Types
-        case nsXPTType::T_ARRAY:
+        // Legacy Array Type
+        case nsXPTType::T_LEGACY_ARRAY:
         {
             const nsXPTType& elty = aType.ArrayElementType();
             void* elements = *(void**)aValue;
 
             for (uint32_t i = 0; i < aArrayLen; ++i) {
                 CleanupValue(elty, elty.ElementPtr(elements, i));
             }
             free(elements);
             break;
         }
+
+        // Array Type
+        case nsXPTType::T_ARRAY:
+        {
+            const nsXPTType& elty = aType.ArrayElementType();
+            auto* array = (xpt::detail::UntypedTArray*)aValue;
+
+            for (uint32_t i = 0; i < array->Length(); ++i) {
+                CleanupValue(elty, elty.ElementPtr(array->Elements(), i));
+            }
+            array->Clear();
+            break;
+        }
+
+        // Clear the JS::Value to `undefined`
+        case nsXPTType::T_JSVAL:
+            ((JS::Value*)aValue)->setUndefined();
+            break;
+
+        // Non-arithmetic types requiring no cleanup
+        case nsXPTType::T_VOID:
+            break;
+
+        default:
+            MOZ_CRASH("Unknown Type!");
     }
 
-    // Null out the pointer if we have it.
-    if (aType.HasPointerRepr()) {
-        *(void**)aValue = nullptr;
+    // Clear any non-complex values to the valid '0' state.
+    if (!aType.IsComplex()) {
+        aType.ZeroValue(aValue);
     }
 }
+
+/***************************************************************************/
+
+// Implementation of xpc::InitializeValue.
+
+void
+xpc::InitializeValue(const nsXPTType& aType, void* aValue)
+{
+    switch (aType.Tag()) {
+        // Types which require custom, specific initialization.
+        case nsXPTType::T_JSVAL:
+            new (aValue) JS::Value();
+            MOZ_ASSERT(reinterpret_cast<JS::Value*>(aValue)->isUndefined());
+            break;
+
+        case nsXPTType::T_ASTRING:
+        case nsXPTType::T_DOMSTRING:
+            new (aValue) nsString();
+            break;
+        case nsXPTType::T_CSTRING:
+        case nsXPTType::T_UTF8STRING:
+            new (aValue) nsCString();
+            break;
+
+        case nsXPTType::T_ARRAY:
+            new (aValue) xpt::detail::UntypedTArray();
+            break;
+
+        // The remaining types all have valid states where all bytes are '0'.
+        default:
+            aType.ZeroValue(aValue);
+            break;
+    }
+}
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -525,17 +525,20 @@ void ThrowBadResult(nsresult result, XPC
 inline void
 xpc::CleanupValue(const nsXPTType& aType,
                   void* aValue,
                   uint32_t aArrayLen)
 {
     // Check if we can do a cheap early return, and only perform the inner call
     // if we can't. We never have to clean up null pointer types or arithmetic
     // types.
-    if (aType.IsArithmetic() || (aType.HasPointerRepr() && !*(void**)aValue)) {
+    //
+    // NOTE: We can skip zeroing arithmetic types in CleanupValue, as they are
+    // already in a valid state.
+    if (aType.IsArithmetic() || (aType.IsPointer() && !*(void**)aValue)) {
         return;
     }
     xpc::InnerCleanupValue(aType, aValue, aArrayLen);
 }
 
 /***************************************************************************/
 
 #endif /* xpcinlines_h___ */
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -667,17 +667,17 @@ nsXPCWrappedJSClass::GetRootJSObject(JSC
 }
 
 bool
 nsXPCWrappedJSClass::GetArraySizeFromParam(const nsXPTMethodInfo* method,
                                            const nsXPTType& type,
                                            nsXPTCMiniVariant* nativeParams,
                                            uint32_t* result) const
 {
-    if (type.Tag() != nsXPTType::T_ARRAY &&
+    if (type.Tag() != nsXPTType::T_LEGACY_ARRAY &&
         type.Tag() != nsXPTType::T_PSTRING_SIZE_IS &&
         type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) {
         *result = 0;
         return true;
     }
 
     uint8_t argnum = type.ArgNum();
     const nsXPTParamInfo& param = method->Param(argnum);
@@ -744,35 +744,37 @@ nsXPCWrappedJSClass::CleanupOutparams(co
                                       bool inOutOnly, uint8_t count) const
 {
     // clean up any 'out' params handed in
     for (uint8_t i = 0; i < count; i++) {
         const nsXPTParamInfo& param = info->GetParam(i);
         if (!param.IsOut())
             continue;
 
-        // Extract the array length so we can use it in CleanupValue.
-        uint32_t arrayLen = 0;
-        if (!GetArraySizeFromParam(info, param.Type(), nativeParams, &arrayLen))
-            continue;
-
         MOZ_ASSERT(param.IsIndirect(), "Outparams are always indirect");
 
-        // The inOutOnly flag is necessary because full outparams may contain
-        // uninitialized junk before the call is made, and we don't want to try
-        // to clean up uninitialized junk.
-        if (!inOutOnly || param.IsIn()) {
+        // Call 'CleanupValue' on parameters which we know to be initialized:
+        //  1. Complex parameters (initialized by caller)
+        //  2. 'inout' parameters (initialized by caller)
+        //  3. 'out' parameters when 'inOutOnly' is 'false' (initialized by us)
+        //
+        // We skip non-complex 'out' parameters before the call, as they may
+        // contain random junk.
+        if (param.Type().IsComplex() || param.IsIn() || !inOutOnly) {
+            uint32_t arrayLen = 0;
+            if (!GetArraySizeFromParam(info, param.Type(), nativeParams, &arrayLen))
+                continue;
+
             xpc::CleanupValue(param.Type(), nativeParams[i].val.p, arrayLen);
         }
 
-        // Even if we didn't call CleanupValue, null out any pointers. This is
-        // just to protect C++ callers which may read garbage if they forget to
-        // check the error value.
-        if (param.Type().HasPointerRepr()) {
-            *(void**)nativeParams[i].val.p = nullptr;
+        // Ensure our parameters are in a clean state. Complex values are always
+        // handled by CleanupValue, and others have a valid null representation.
+        if (!param.Type().IsComplex()) {
+            param.Type().ZeroValue(nativeParams[i].val.p);
         }
     }
 }
 
 nsresult
 nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
                                        AutoEntryScript& aes,
                                        const char * aPropertyName,
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1240,34 +1240,30 @@ CallMethodHelper::Call()
     }
 
     return GatherAndConvertResults();
 }
 
 CallMethodHelper::~CallMethodHelper()
 {
     for (nsXPTCVariant& param : mDispatchParams) {
-        // Only clean up values which need cleanup.
-        if (!param.DoesValNeedCleanup())
-            continue;
-
         uint32_t arraylen = 0;
         if (!GetArraySizeFromParam(param.type, UndefinedHandleValue, &arraylen))
             continue;
 
         xpc::CleanupValue(param.type, &param.val, arraylen);
     }
 }
 
 bool
 CallMethodHelper::GetArraySizeFromParam(const nsXPTType& type,
                                         HandleValue maybeArray,
                                         uint32_t* result)
 {
-    if (type.Tag() != nsXPTType::T_ARRAY &&
+    if (type.Tag() != nsXPTType::T_LEGACY_ARRAY &&
         type.Tag() != nsXPTType::T_PSTRING_SIZE_IS &&
         type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) {
         *result = 0;
         return true;
     }
 
     uint8_t argnum = type.ArgNum();
     uint32_t* lengthp = &GetDispatchParam(argnum)->val.u32;
@@ -1472,35 +1468,49 @@ CallMethodHelper::InitializeDispatchPara
         if (mArgc < requiredArgs) {
             Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
             return false;
         }
     }
 
     mJSContextIndex = mMethodInfo->IndexOfJSContext();
 
-    // iterate through the params to clear flags (for safe cleanup later)
-    for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
-        nsXPTCVariant* dp = mDispatchParams.AppendElement();
-        dp->ClearFlags();
-        dp->val.p = nullptr;
+    // Allocate enough space in mDispatchParams up-front.
+    if (!mDispatchParams.AppendElements(paramCount + wantsJSContext + wantsOptArgc)) {
+        Throw(NS_ERROR_OUT_OF_MEMORY, mCallContext);
+        return false;
     }
 
-    // Fill in the JSContext argument
-    if (wantsJSContext) {
-        nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
-        dp->type = nsXPTType::T_VOID;
-        dp->val.p = mCallContext;
-    }
+    // Initialize each parameter to a valid state (for safe cleanup later).
+    for (uint8_t i = 0, paramIdx = 0; i < mDispatchParams.Length(); i++) {
+        nsXPTCVariant& dp = mDispatchParams[i];
 
-    // Fill in the optional_argc argument
-    if (wantsOptArgc) {
-        nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
-        dp->type = nsXPTType::T_U8;
-        dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
+        if (i == mJSContextIndex) {
+            // Fill in the JSContext argument
+            dp.type = nsXPTType::T_VOID;
+            dp.val.p = mCallContext;
+        } else if (i == mOptArgcIndex) {
+            // Fill in the optional_argc argument
+            dp.type = nsXPTType::T_U8;
+            dp.val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
+        } else {
+            // Initialize normal arguments.
+            const nsXPTParamInfo& param = mMethodInfo->Param(paramIdx);
+            dp.type = param.Type();
+            xpc::InitializeValue(dp.type, &dp.val);
+
+            // Specify the correct storage/calling semantics. This will also set
+            // the `ptr` field to be self-referential.
+            if (param.IsIndirect()) {
+                dp.SetIndirect();
+            }
+
+            // Advance to the next normal parameter.
+            paramIdx++;
+        }
     }
 
     return true;
 }
 
 bool
 CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
 {
@@ -1517,50 +1527,18 @@ CallMethodHelper::ConvertIndependentPara
 
     return true;
 }
 
 bool
 CallMethodHelper::ConvertIndependentParam(uint8_t i)
 {
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
-    const nsXPTType& type = paramInfo.GetType();
+    const nsXPTType& type = paramInfo.Type();
     nsXPTCVariant* dp = GetDispatchParam(i);
-    dp->type = type;
-    MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
-
-    // Specify the correct storage/calling semantics.
-    if (paramInfo.IsIndirect())
-        dp->SetIndirect();
-
-    // Some types are always stored within the nsXPTCVariant, and passed
-    // indirectly, regardless of in/out-ness. These types are stored in the
-    // nsXPTCVariant's extended value.
-    switch (type.Tag()) {
-        // Ensure that the jsval has a valid value.
-        case nsXPTType::T_JSVAL:
-            new (&dp->ext.jsval) JS::Value();
-            MOZ_ASSERT(dp->ext.jsval.isUndefined());
-            break;
-
-        // Initialize our temporary string class values so they can be assigned
-        // to by the XPCConvert logic.
-        case nsXPTType::T_ASTRING:
-        case nsXPTType::T_DOMSTRING:
-            new (&dp->ext.nsstr) nsString();
-            break;
-        case nsXPTType::T_CSTRING:
-        case nsXPTType::T_UTF8STRING:
-            new (&dp->ext.nscstr) nsCString();
-            break;
-    }
-
-    // Flag cleanup for anything that isn't self-contained.
-    if (!type.IsArithmetic())
-        dp->SetValNeedsCleanup();
 
     // Even if there's nothing to convert, we still need to examine the
     // JSObject container for out-params. If it's null or otherwise invalid,
     // we want to know before the call, rather than after.
     //
     // This is a no-op for 'in' params.
     RootedValue src(mCallContext);
     if (!GetOutParamSource(i, &src))
@@ -1636,28 +1614,18 @@ CallMethodHelper::ConvertDependentParams
 
     return true;
 }
 
 bool
 CallMethodHelper::ConvertDependentParam(uint8_t i)
 {
     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
-    const nsXPTType& type = paramInfo.GetType();
-
+    const nsXPTType& type = paramInfo.Type();
     nsXPTCVariant* dp = GetDispatchParam(i);
-    dp->type = type;
-
-    // Specify the correct storage/calling semantics.
-    if (paramInfo.IsIndirect())
-        dp->SetIndirect();
-
-    // Make sure we clean up all of our dependent types. All of them require
-    // allocations of some kind.
-    dp->SetValNeedsCleanup();
 
     // Even if there's nothing to convert, we still need to examine the
     // JSObject container for out-params. If it's null or otherwise invalid,
     // we want to know before the call, rather than after.
     //
     // This is a no-op for 'in' params.
     RootedValue src(mCallContext);
     if (!GetOutParamSource(i, &src))
@@ -1707,36 +1675,39 @@ CallMethodHelper::Invoke()
 
 static void
 TraceParam(JSTracer* aTrc, void* aVal, const nsXPTType& aType,
            uint32_t aArrayLen = 0)
 {
     if (aType.Tag() == nsXPTType::T_JSVAL) {
         JS::UnsafeTraceRoot(aTrc, (JS::Value*)aVal,
                             "XPCWrappedNative::CallMethod param");
-    } else if (aType.Tag() == nsXPTType::T_ARRAY && *(void**)aVal) {
+    } else if (aType.Tag() == nsXPTType::T_ARRAY) {
+        auto* array = (xpt::detail::UntypedTArray*)aVal;
         const nsXPTType& elty = aType.ArrayElementType();
-        if (elty.Tag() != nsXPTType::T_JSVAL) {
-            return;
+
+        for (uint32_t i = 0; i < array->Length(); ++i) {
+            TraceParam(aTrc, elty.ElementPtr(array->Elements(), i), elty);
         }
+    } else if (aType.Tag() == nsXPTType::T_LEGACY_ARRAY && *(void**)aVal) {
+        const nsXPTType& elty = aType.ArrayElementType();
 
         for (uint32_t i = 0; i < aArrayLen; ++i) {
-            TraceParam(aTrc, elty.ElementPtr(aVal, i), elty);
+            TraceParam(aTrc, elty.ElementPtr(*(void**)aVal, i), elty);
         }
     }
 }
 
 void
 CallMethodHelper::trace(JSTracer* aTrc)
 {
     // We need to note each of our initialized parameters which contain jsvals.
     for (nsXPTCVariant& param : mDispatchParams) {
-        if (!param.DoesValNeedCleanup()) {
-            MOZ_ASSERT(param.type.Tag() != nsXPTType::T_JSVAL,
-                       "JSVals are marked as needing cleanup (even though they don't)");
+        // We only need to trace parameters which have an innermost JSVAL.
+        if (param.type.InnermostType().Tag() != nsXPTType::T_JSVAL) {
             continue;
         }
 
         uint32_t arrayLen = 0;
         if (!GetArraySizeFromParam(param.type, UndefinedHandleValue, &arrayLen))
             continue;
 
         TraceParam(aTrc, &param.val, param.type, arrayLen);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1958,55 +1958,64 @@ public:
                                          const nsID* iid,
                                          nsISupports* aOuter,
                                          nsresult* pErr);
 
     // Note - This return the XPCWrappedNative, rather than the native itself,
     // for the WN case. You probably want UnwrapReflectorToISupports.
     static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
 
-    /**
-     * Convert a native array into a JS::Value.
-     *
-     * @param d [out] the resulting JS::Value
-     * @param s the native array we're working with
-     * @param type the type of objects in the array
-     * @param iid the interface of each object in the array that we want
-     * @param count the number of items in the array
-     * @param scope the default scope to put on the new JSObjects' parent chain
-     * @param pErr [out] relevant error code, if any.
-     */
-    static bool NativeArray2JS(JS::MutableHandleValue d, const void* const* s,
-                               const nsXPTType& type, const nsID* iid,
-                               uint32_t count, nsresult* pErr);
-
-    static bool JSArray2Native(void** d, JS::HandleValue s,
-                               uint32_t count, const nsXPTType& type,
-                               const nsID* iid, nsresult* pErr);
-
-    static bool JSTypedArray2Native(void** d,
-                                    JSObject* jsarray,
-                                    uint32_t count,
-                                    const nsXPTType& type,
-                                    nsresult* pErr);
-
     static nsresult JSValToXPCException(JS::MutableHandleValue s,
                                         const char* ifaceName,
                                         const char* methodName,
                                         mozilla::dom::Exception** exception);
 
     static nsresult ConstructException(nsresult rv, const char* message,
                                        const char* ifaceName,
                                        const char* methodName,
                                        nsISupports* data,
                                        mozilla::dom::Exception** exception,
                                        JSContext* cx,
                                        JS::Value* jsExceptionPtr);
 
 private:
+    /**
+     * Convert a native array into a JS::Value.
+     *
+     * @param d [out] the resulting JS::Value
+     * @param buf the native buffer containing input values
+     * @param type the type of objects in the array
+     * @param iid the interface of each object in the array that we want
+     * @param count the number of items in the array
+     * @param scope the default scope to put on the new JSObjects' parent chain
+     * @param pErr [out] relevant error code, if any.
+     */
+    static bool NativeArray2JS(JS::MutableHandleValue d, const void* buf,
+                               const nsXPTType& type, const nsID* iid,
+                               uint32_t count, nsresult* pErr);
+
+    typedef std::function<void* (uint32_t*)> ArrayAllocFixupLen;
+
+    /**
+     * Convert a JS::Value into a native array.
+     *
+     * @param aJSVal the JS::Value to convert
+     * @param aEltType the type of objects in the array
+     * @param aIID the interface of each object in the array
+     * @param pErr [out] relevant error code, if any
+     * @param aAllocFixupLen function called with the JS Array's length to
+     *                       allocate the backing buffer. This function may
+     *                       modify the length of array to be converted.
+     */
+    static bool JSArray2Native(JS::HandleValue aJSVal,
+                               const nsXPTType& aEltType,
+                               const nsIID* aIID,
+                               nsresult* pErr,
+                               const ArrayAllocFixupLen& aAllocFixupLen);
+
     XPCConvert() = delete;
 
 };
 
 /***************************************************************************/
 // code for throwing exceptions into JS
 
 class nsXPCException;
@@ -3034,17 +3043,17 @@ nsIPrincipal* GetObjectPrincipal(JSObjec
 //   TD_PNSIID
 //     value : nsID* (free)
 //   TD_DOMSTRING, TD_ASTRING, TD_CSTRING, TD_UTF8STRING
 //     value : ns[C]String* (truncate)
 //   TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS
 //     value : char[16_t]** (free)
 //   TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE
 //     value : nsISupports** (release)
-//   TD_ARRAY (NOTE: aArrayLen should be passed)
+//   TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed)
 //     value : void** (cleanup elements & free)
 //   TD_DOMOBJECT
 //     value : T** (cleanup)
 //   TD_PROMISE
 //     value : dom::Promise** (release)
 //
 // Other types are ignored.
 //
@@ -3055,16 +3064,27 @@ inline void CleanupValue(const nsXPTType
                          void* aValue,
                          uint32_t aArrayLen = 0);
 
 // Out-of-line internals for xpc::CleanupValue. Defined in XPCConvert.cpp.
 void InnerCleanupValue(const nsXPTType& aType,
                        void* aValue,
                        uint32_t aArrayLen);
 
+// In order to be able to safely call CleanupValue on a generated value, the
+// data behind it needs to be initialized to a safe value. This method handles
+// initializing the backing data to a safe value to use as an argument to
+// XPCConvert methods, or xpc::CleanupValue.
+//
+// The pointer `aValue` must point to a block of memory at least aType.Stride()
+// bytes large, and correctly aligned.
+//
+// This method accepts the same types as xpc::CleanupValue.
+void InitializeValue(const nsXPTType& aType, void* aValue);
+
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 extern bool
 DefineStaticJSVals(JSContext* cx);
 } // namespace dom
 } // namespace mozilla
--- a/js/xpconnect/tests/components/js/xpctest_params.js
+++ b/js/xpconnect/tests/components/js/xpctest_params.js
@@ -59,21 +59,29 @@ TestParams.prototype = {
   testString: f,
   testWchar: f,
   testWstring: f,
   testDOMString: f,
   testAString: f,
   testAUTF8String: f,
   testACString: f,
   testJsval: f,
+  testShortSequence: f,
+  testDoubleSequence: f,
+  testAStringSequence: f,
+  testACStringSequence: f,
+  testInterfaceSequence: f,
+  testJsvalSequence: f,
+  testInterfaceIsSequence: f_is,
   testShortArray: f_is,
   testDoubleArray: f_is,
   testStringArray: f_is,
   testWstringArray: f_is,
   testInterfaceArray: f_is,
+  testJsvalArray: f_is,
   testSizedString: f_is,
   testSizedWstring: f_is,
   testInterfaceIs: f_is,
   testInterfaceIsArray: f_size_and_iid,
   testOutAString: function(o) { o.value = "out"; },
   testStringArrayOptionalSize: function(arr, size) {
     if (arr.length != size) { throw "bad size passed to test method"; }
     var rv = "";
--- a/js/xpconnect/tests/components/native/xpctest_params.cpp
+++ b/js/xpconnect/tests/components/native/xpctest_params.cpp
@@ -23,16 +23,24 @@ nsXPCTestParams::~nsXPCTestParams()
 }
 
 #define STRING_METHOD_IMPL {                                                  \
     _retval.Assign(b);                                                        \
     b.Assign(a);                                                              \
     return NS_OK;                                                             \
 }
 
+#define SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP) {                                \
+    _retval.SwapElements(b);                                                  \
+    b = a;                                                                    \
+    for (uint32_t i = 0; i < b.Length(); ++i)                                 \
+        TAKE_OWNERSHIP(b[i]);                                                 \
+    return NS_OK;                                                             \
+}
+
 #define TAKE_OWNERSHIP_NOOP(val) {}
 #define TAKE_OWNERSHIP_INTERFACE(val) {static_cast<nsISupports*>(val)->AddRef();}
 #define TAKE_OWNERSHIP_STRING(val) {                                          \
     nsDependentCString vprime(val);                                           \
     val = ToNewCString(vprime);                                               \
 }
 #define TAKE_OWNERSHIP_WSTRING(val) {                                         \
     nsDependentString vprime(val);                                            \
@@ -218,16 +226,23 @@ NS_IMETHODIMP nsXPCTestParams::TestWstri
 
 NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray(uint32_t aLength, nsIXPCTestInterfaceA** a,
                                                   uint32_t* bLength, nsIXPCTestInterfaceA * **b,
                                                   uint32_t* rvLength, nsIXPCTestInterfaceA * **rv)
 {
     BUFFER_METHOD_IMPL(nsIXPCTestInterfaceA*, 0, TAKE_OWNERSHIP_INTERFACE);
 }
 
+NS_IMETHODIMP nsXPCTestParams::TestJsvalArray(uint32_t aLength, JS::Value *a,
+                                              uint32_t* bLength, JS::Value **b,
+                                              uint32_t* rvLength, JS::Value **rv)
+{
+    BUFFER_METHOD_IMPL(JS::Value, 0, TAKE_OWNERSHIP_NOOP);
+}
+
 NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char * a,
                                                uint32_t* bLength, char * *b,
                                                uint32_t* rvLength, char * *rv)
 {
     BUFFER_METHOD_IMPL(char, 1, TAKE_OWNERSHIP_NOOP);
 }
 
 NS_IMETHODIMP nsXPCTestParams::TestSizedWstring(uint32_t aLength, const char16_t * a,
@@ -301,8 +316,73 @@ NS_IMETHODIMP nsXPCTestParams::TestStrin
 {
   out.Truncate();
   for (uint32_t i = 0; i < length; ++i) {
     out.Append(a[i]);
   }
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsXPCTestParams::TestShortSequence(const nsTArray<short>& a, nsTArray<short>& b, nsTArray<short>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestDoubleSequence(const nsTArray<double>& a, nsTArray<double>& b, nsTArray<double>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestInterfaceSequence(const nsTArray<RefPtr<nsIXPCTestInterfaceA>>& a,
+                                       nsTArray<RefPtr<nsIXPCTestInterfaceA>>& b,
+                                       nsTArray<RefPtr<nsIXPCTestInterfaceA>>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestAStringSequence(const nsTArray<nsString>& a,
+                                     nsTArray<nsString>& b,
+                                     nsTArray<nsString>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestACStringSequence(const nsTArray<nsCString>& a,
+                                      nsTArray<nsCString>& b,
+                                      nsTArray<nsCString>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestJsvalSequence(const nsTArray<JS::Value>& a,
+                                   nsTArray<JS::Value>& b,
+                                   nsTArray<JS::Value>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestSequenceSequence(const nsTArray<nsTArray<short>>& a,
+                                      nsTArray<nsTArray<short>>& b,
+                                      nsTArray<nsTArray<short>>& _retval)
+{
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestInterfaceIsSequence(const nsIID* aIID, const nsTArray<void*>& a,
+                                         nsIID** bIID, nsTArray<void*>& b,
+                                         nsIID** rvIID, nsTArray<void*>& _retval)
+{
+    // Shuffle around our nsIIDs
+    *rvIID = (*bIID)->Clone();
+    *bIID = aIID->Clone();
+
+    // Perform the generic sequence shuffle.
+    SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_INTERFACE);
+}
--- a/js/xpconnect/tests/idl/xpctest_params.idl
+++ b/js/xpconnect/tests/idl/xpctest_params.idl
@@ -34,16 +34,29 @@ interface nsIXPCTestParams : nsISupports
   wchar                 testWchar(in wchar a, inout wchar b);
   wstring               testWstring(in wstring a, inout wstring b);
   DOMString             testDOMString(in DOMString a, inout DOMString b);
   AString               testAString(in AString a, inout AString b);
   AUTF8String           testAUTF8String(in AUTF8String a, inout AUTF8String b);
   ACString              testACString(in ACString a, inout ACString b);
   jsval                 testJsval(in jsval a, inout jsval b);
 
+  // Test various forms of the Array<T> type.
+  Array<short>          testShortSequence(in Array<short> a, inout Array<short> b);
+  Array<double>         testDoubleSequence(in Array<double> a, inout Array<double> b);
+  Array<nsIXPCTestInterfaceA> testInterfaceSequence(in Array<nsIXPCTestInterfaceA> a, inout Array<nsIXPCTestInterfaceA> b);
+  Array<AString>        testAStringSequence(in Array<AString> a, inout Array<AString> b);
+  Array<ACString>       testACStringSequence(in Array<ACString> a, inout Array<ACString> b);
+  Array<jsval>          testJsvalSequence(in Array<jsval> a, inout Array<jsval> b);
+  Array<Array<short> >  testSequenceSequence(in Array<Array<short> > a, inout Array<Array<short> > b);
+
+  void                  testInterfaceIsSequence(in nsIIDPtr aIID, [iid_is(aIID)] in Array<nsQIResult> a,
+                                                inout nsIIDPtr bIID, [iid_is(bIID)] inout Array<nsQIResult> b,
+                                                out nsIIDPtr rvIID, [retval, iid_is(rvIID)] out Array<nsQIResult> rv);
+
   //
   // Dependent parameters use the same types as above, but are handled much differently.
   //
 
   // Test arrays.
   void                  testShortArray(in unsigned long aLength, [array, size_is(aLength)] in short a,
                                        inout unsigned long bLength, [array, size_is(bLength)] inout short b,
                                        out unsigned long rvLength, [retval, array, size_is(rvLength)] out short rv);
@@ -77,14 +90,20 @@ interface nsIXPCTestParams : nsISupports
   // in mozilla-central, but calendar stuff depends on it.
   void                  testInterfaceIsArray(in unsigned long aLength, in nsIIDPtr aIID,
                                              [array, size_is(aLength), iid_is(aIID)] in nsQIResult a,
                                              inout unsigned long bLength, inout nsIIDPtr bIID,
                                              [array, size_is(bLength), iid_is(bIID)] inout nsQIResult b,
                                              out unsigned long rvLength, out nsIIDPtr rvIID,
                                              [retval, array, size_is(rvLength), iid_is(rvIID)] out nsQIResult rv);
 
+  // Test arrays of jsvals
+  void                  testJsvalArray(in unsigned long aLength, [array, size_is(aLength)] in jsval a,
+                                       inout unsigned long bLength, [array, size_is(bLength)] inout jsval b,
+                                       out unsigned long rvLength, [retval, array, size_is(rvLength)] out jsval rv);
+
+
   // Test for out dipper parameters
   void                 testOutAString(out AString o);
 
   // Test for optional array size_is.
   ACString             testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength);
 };
--- a/js/xpconnect/tests/unit/test_params.js
+++ b/js/xpconnect/tests/unit/test_params.js
@@ -158,16 +158,18 @@ function test_component(contractid) {
   doIsTest("testDoubleArray", [-10, -0.5], 2, [1, 3, 1e11, -8e-5 ], 4, arrayComparator(fuzzComparator));
 
   doIsTest("testStringArray", ["mary", "hat", "hey", "lid", "tell", "lam"], 6,
                               ["ids", "fleas", "woes", "wide", "has", "know", "!"], 7, arrayComparator(standardComparator));
   doIsTest("testWstringArray", ["沒有語言", "的偉大嗎?]"], 2,
                                ["we", "are", "being", "sooo", "international", "right", "now"], 7, arrayComparator(standardComparator));
   doIsTest("testInterfaceArray", [makeA(), makeA()], 2,
                                  [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator));
+  doIsTest("testJsvalArray", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/], 3,
+                             ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], 4, arrayComparator(standardComparator));
 
   // Test typed arrays and ArrayBuffer aliasing.
   var arrayBuffer = new ArrayBuffer(16);
   var int16Array = new Int16Array(arrayBuffer, 2, 3);
   int16Array.set([-32768, 0, 32767]);
   doIsTest("testShortArray", int16Array, 3, new Int16Array([1773, -32768, 32767, 7]), 4, arrayComparator(standardComparator));
   doIsTest("testDoubleArray", new Float64Array([-10, -0.5]), 2, new Float64Array([0, 3.2, 1.0e10, -8.33 ]), 4, arrayComparator(fuzzComparator));
 
@@ -190,9 +192,29 @@ function test_component(contractid) {
 
   // Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS.
   doTypedArrayMismatchTest("testShortArray", new Int16Array([-3, 7, 4]), 4,
                                              new Int16Array([1, -32, 6]), 3);
 
   // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS.
   doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4,
                                              new Uint16Array([1, 5, 6]), 3);
+
+  // Test Sequence<T> types.
+  doTest("testShortSequence", [2, 4, 6], [1, 3, 5, 7], arrayComparator(standardComparator));
+  doTest("testDoubleSequence", [-10, -0.5], [1, 3, 1e11, -8e-5 ], arrayComparator(fuzzComparator));
+  doTest("testACStringSequence", ["mary", "hat", "hey", "lid", "tell", "lam"],
+                                 ["ids", "fleas", "woes", "wide", "has", "know", "!"],
+                                 arrayComparator(standardComparator));
+  doTest("testAStringSequence", ["沒有語言", "的偉大嗎?]"],
+                                ["we", "are", "being", "sooo", "international", "right", "now"],
+                                arrayComparator(standardComparator));
+
+  doTest("testInterfaceSequence", [makeA(), makeA()],
+                                  [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], arrayComparator(interfaceComparator));
+
+  doTest("testJsvalSequence", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/],
+                              ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], arrayComparator(standardComparator));
+
+  doIsTest("testInterfaceIsSequence", [makeA(), makeA(), makeA(), makeA(), makeA()], Ci['nsIXPCTestInterfaceA'],
+                                      [makeB(), makeB(), makeB()], Ci['nsIXPCTestInterfaceB'],
+                                      arrayComparator(interfaceComparator), dotEqualsComparator);
 }
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -259,17 +259,17 @@ fuzzy-if(cocoaWidget,15,19679) fuzzy-if(
 == linked-pattern-01.svg pass.svg
 
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||skiaContent,1,800000) == markers-and-group-opacity-01.svg markers-and-group-opacity-01-ref.svg
 == marker-attribute-01.svg pass.svg
 == marker-dynamic-opacity.html marker-dynamic-opacity-ref.html
 == marker-effects-01.svg marker-effects-01-ref.svg
 fuzzy-if(skiaContent,1,100) == marker-viewBox-01.svg marker-viewBox-01-ref.svg
 fuzzy-if(skiaContent,1,100) == marker-orientation-01.svg marker-orientation-01-ref.svg
-fuzzy-if(skiaContent,1,5) pref(svg.marker-improvements.enabled,true) == marker-orientation-02.svg marker-orientation-02-ref.svg
+fuzzy-if(skiaContent,1,5) == marker-orientation-02.svg marker-orientation-02-ref.svg
 == marker-orientation-03.svg pass.svg
 == marker-orientation-04.svg pass.svg
 
 fuzzy(28,28) == mask-and-clipPath.html mask-and-clipPath-ref.html
 == mask-and-clipPath-2.svg pass.svg
 == mask-ref-loop-01.svg pass.svg
 == mask-basic-01.svg pass.svg
 fuzzy-if(skiaContent,1,10000) == mask-basic-02.svg mask-basic-02-ref.svg
@@ -546,9 +546,9 @@ fuzzy-if(skiaContent,1,100) == tspan-xy-
 == viewport-percent-graphic-user-01.svg pass.svg
 == winding-01.svg pass.svg
 
 == zero-stroke-01.svg pass.svg
 
 # currentColor override by color attribute
 == currentColor-override-flood.svg pass.svg
 == currentColor-override-lighting.svg currentColor-override-lighting-ref.svg
-== currentColor-override-stop.svg pass.svg
\ No newline at end of file
+== currentColor-override-stop.svg pass.svg
--- a/layout/style/ServoStyleConsts.h
+++ b/layout/style/ServoStyleConsts.h
@@ -73,16 +73,18 @@ enum class StyleAppearance : uint8_t {
   Menuseparator,
   Menuarrow,
   // An image in the menu gutter, like in bookmarks or history.
   Menuimage,
   // A horizontal meter bar.
   Meterbar,
   // The meter bar's meter indicator.
   Meterchunk,
+  // The dropdown button(s) that open up a dropdown list.
+  MozMenulistButton,
   // For HTML's <input type=number>
   NumberInput,
   // A horizontal progress bar.
   Progressbar,
   // The progress bar's progress indicator
   Progresschunk,
   // A vertical progress bar.
   ProgressbarVertical,
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -279,17 +279,17 @@ select[size="1"] {
 
 select > button {
   inline-size: 12px;
   white-space: nowrap;
   position: static !important;
   background-image: url("arrow.gif") !important;
   background-repeat: no-repeat !important;
   background-position: center !important;
-  -moz-appearance: menulist-button;
+  -moz-appearance: -moz-menulist-button;
 
   /* Make sure to size correctly if the combobox has a non-auto height. */
   block-size: 100% ! important;
   box-sizing: border-box ! important;
 
   /*
     Make sure to align properly with the display frame.  Note that we
     want the baseline of the combobox to match the baseline of the
--- a/layout/style/test/test_non_content_accessible_values.html
+++ b/layout/style/test/test_non_content_accessible_values.html
@@ -11,16 +11,19 @@ const NON_CONTENT_ACCESSIBLE_VALUES = {
     "-moz-grid-group",
     "-moz-grid-line",
     "-moz-stack",
     "-moz-inline-stack",
     "-moz-deck",
     "-moz-popup",
     "-moz-groupbox",
   ],
+  "-moz-appearance": [
+    "-moz-menulist-button",
+  ],
 };
 
 if (!SpecialPowers.getBoolPref("layout.css.xul-box-display-values.content.enabled")) {
   NON_CONTENT_ACCESSIBLE_VALUES.display.push("-moz-box", "-moz-inline-box");
 }
 
 const sheet = document.getElementById("sheet");
 const div = document.querySelector("div");
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -25,28 +25,27 @@
 #include <unistd.h>
 #endif
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #include "nscore.h"
-#include "mozilla/StackWalk.h"
-
-#include "js/HashTable.h"
-#include "js/Vector.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FastBernoulliTrial.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/HashTable.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/StackWalk.h"
+#include "mozilla/Vector.h"
 
 // CodeAddressService is defined entirely in the header, so this does not make
 // DMD depend on XPCOM's object file.
 #include "CodeAddressService.h"
 
 // replace_malloc.h needs to be included before replace_malloc_bridge.h,
 // which DMD.h includes, so DMD.h needs to be included after replace_malloc.h.
 #include "replace_malloc.h"
@@ -91,18 +90,18 @@ StatusMsg(const char* aFmt, ...)
 
 static malloc_table_t gMallocTable;
 
 // This provides infallible allocations (they abort on OOM).  We use it for all
 // of DMD's own allocations, which fall into the following three cases.
 //
 // - Direct allocations (the easy case).
 //
-// - Indirect allocations in js::{Vector,HashSet,HashMap} -- this class serves
-//   as their AllocPolicy.
+// - Indirect allocations in mozilla::{Vector,HashSet,HashMap} -- this class
+//   serves as their AllocPolicy.
 //
 // - Other indirect allocations (e.g. MozStackWalk) -- see the comments on
 //   Thread::mBlockIntercepts and in replace_malloc for how these work.
 //
 // It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
 // but DMD cannot use mozalloc.
 //
 class InfallibleAllocPolicy
@@ -157,25 +156,23 @@ public:
   template <typename T>
   static T* pod_calloc(size_t aNumElems)
   {
     T* p = maybe_pod_calloc<T>(aNumElems);
     ExitOnFailure(p);
     return p;
   }
 
-  // This realloc_ is the one we use for direct reallocs within DMD.
   static void* realloc_(void* aPtr, size_t aNewSize)
   {
     void* p = gMallocTable.realloc(aPtr, aNewSize);
     ExitOnFailure(p);
     return p;
   }
 
-  // This realloc_ is required for this to be a JS container AllocPolicy.
   template <typename T>
   static T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
   {
     T* p = maybe_pod_realloc(aPtr, aOldSize, aNewSize);
     ExitOnFailure(p);
     return p;
   }
 
@@ -616,28 +613,29 @@ public:
     return n;
   }
 
 private:
   struct StringHasher
   {
       typedef const char* Lookup;
 
-      static uint32_t hash(const char* const& aS)
+      static mozilla::HashNumber hash(const char* const& aS)
       {
           return HashString(aS);
       }
 
       static bool match(const char* const& aA, const char* const& aB)
       {
           return strcmp(aA, aB) == 0;
       }
   };
 
-  typedef js::HashSet<const char*, StringHasher, InfallibleAllocPolicy> StringHashSet;
+  typedef mozilla::HashSet<const char*, StringHasher, InfallibleAllocPolicy>
+          StringHashSet;
 
   StringHashSet mSet;
 };
 
 class StringAlloc
 {
 public:
   static char* copy(const char* aString)
@@ -688,17 +686,17 @@ public:
   // The stack trace returned by this function is interned in gStackTraceTable,
   // and so is immortal and unmovable.
   static const StackTrace* Get(Thread* aT);
 
   // Hash policy.
 
   typedef StackTrace* Lookup;
 
-  static uint32_t hash(const StackTrace* const& aSt)
+  static mozilla::HashNumber hash(const StackTrace* const& aSt)
   {
     return mozilla::HashBytes(aSt->mPcs, aSt->Size());
   }
 
   static bool match(const StackTrace* const& aA,
                     const StackTrace* const& aB)
   {
     return aA->mLength == aB->mLength &&
@@ -712,29 +710,31 @@ private:
     StackTrace* st = (StackTrace*) aClosure;
     MOZ_ASSERT(st->mLength < MaxFrames);
     st->mPcs[st->mLength] = aPc;
     st->mLength++;
     MOZ_ASSERT(st->mLength == aFrameNumber);
   }
 };
 
-typedef js::HashSet<StackTrace*, StackTrace, InfallibleAllocPolicy>
+typedef mozilla::HashSet<StackTrace*, StackTrace, InfallibleAllocPolicy>
         StackTraceTable;
 static StackTraceTable* gStackTraceTable = nullptr;
 
-typedef js::HashSet<const StackTrace*, js::DefaultHasher<const StackTrace*>,
-                    InfallibleAllocPolicy>
+typedef mozilla::HashSet<const StackTrace*,
+                         mozilla::DefaultHasher<const StackTrace*>,
+                         InfallibleAllocPolicy>
         StackTraceSet;
 
-typedef js::HashSet<const void*, js::DefaultHasher<const void*>,
-                    InfallibleAllocPolicy>
+typedef mozilla::HashSet<const void*, mozilla::DefaultHasher<const void*>,
+                         InfallibleAllocPolicy>
         PointerSet;
-typedef js::HashMap<const void*, uint32_t, js::DefaultHasher<const void*>,
-                    InfallibleAllocPolicy>
+typedef mozilla::HashMap<const void*, uint32_t,
+                         mozilla::DefaultHasher<const void*>,
+                         InfallibleAllocPolicy>
         PointerIdMap;
 
 // We won't GC the stack trace table until it this many elements.
 static uint32_t gGCStackTraceTableWhenSizeExceeds = 4 * 1024;
 
 /* static */ const StackTrace*
 StackTrace::Get(Thread* aT)
 {
@@ -987,37 +987,38 @@ public:
       mReportStackTrace_mReportedOnAlloc[1].Set(nullptr, 0);
     }
   }
 
   // Hash policy.
 
   typedef const void* Lookup;
 
-  static uint32_t hash(const void* const& aPtr)
+  static mozilla::HashNumber hash(const void* const& aPtr)
   {
     return mozilla::HashGeneric(aPtr);
   }
 
   static bool match(const LiveBlock& aB, const void* const& aPtr)
   {
     return aB.mPtr == aPtr;
   }
 };
 
 // A table of live blocks where the lookup key is the block address.
-typedef js::HashSet<LiveBlock, LiveBlock, InfallibleAllocPolicy> LiveBlockTable;
+typedef mozilla::HashSet<LiveBlock, LiveBlock, InfallibleAllocPolicy>
+        LiveBlockTable;
 static LiveBlockTable* gLiveBlockTable = nullptr;
 
 class AggregatedLiveBlockHashPolicy
 {
 public:
   typedef const LiveBlock* const Lookup;
 
-  static uint32_t hash(const LiveBlock* const& aB)
+  static mozilla::HashNumber hash(const LiveBlock* const& aB)
   {
     return gOptions->IsDarkMatterMode()
          ? mozilla::HashGeneric(aB->ReqSize(),
                                 aB->SlopSize(),
                                 aB->AllocStackTrace(),
                                 aB->ReportedOnAlloc1(),
                                 aB->ReportedOnAlloc2())
          : mozilla::HashGeneric(aB->ReqSize(),
@@ -1036,18 +1037,18 @@ public:
          : aA->ReqSize() == aB->ReqSize() &&
            aA->SlopSize() == aB->SlopSize() &&
            aA->AllocStackTrace() == aB->AllocStackTrace();
   }
 };
 
 // A table of live blocks where the lookup key is everything but the block
 // address. For aggregating similar live blocks at output time.
-typedef js::HashMap<const LiveBlock*, size_t, AggregatedLiveBlockHashPolicy,
-                    InfallibleAllocPolicy>
+typedef mozilla::HashMap<const LiveBlock*, size_t,
+                         AggregatedLiveBlockHashPolicy, InfallibleAllocPolicy>
         AggregatedLiveBlockTable;
 
 // A freed heap block.
 class DeadBlock
 {
   const size_t mReqSize;    // size requested
   const size_t mSlopSize;   // slop above size requested
 
@@ -1083,34 +1084,34 @@ public:
       MOZ_ALWAYS_TRUE(aStackTraces.put(AllocStackTrace()));
     }
   }
 
   // Hash policy.
 
   typedef DeadBlock Lookup;
 
-  static uint32_t hash(const DeadBlock& aB)
+  static mozilla::HashNumber hash(const DeadBlock& aB)
   {
     return mozilla::HashGeneric(aB.ReqSize(),
                                 aB.SlopSize(),
                                 aB.AllocStackTrace());
   }
 
   static bool match(const DeadBlock& aA, const DeadBlock& aB)
   {
     return aA.ReqSize() == aB.ReqSize() &&
            aA.SlopSize() == aB.SlopSize() &&
            aA.AllocStackTrace() == aB.AllocStackTrace();
   }
 };
 
 // For each unique DeadBlock value we store a count of how many actual dead
 // blocks have that value.
-typedef js::HashMap<DeadBlock, size_t, DeadBlock, InfallibleAllocPolicy>
+typedef mozilla::HashMap<DeadBlock, size_t, DeadBlock, InfallibleAllocPolicy>
         DeadBlockTable;
 static DeadBlockTable* gDeadBlockTable = nullptr;
 
 // Add the dead block to the dead block table, if that's appropriate.
 void MaybeAddToDeadBlockTable(const DeadBlock& aDb)
 {
   if (gOptions->IsCumulativeMode() && aDb.AllocStackTrace()) {
     AutoLockState lock;
--- a/mfbt/HashTable.h
+++ b/mfbt/HashTable.h
@@ -1,14 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
+// A note on the differences between mozilla::HashTable and PLDHashTable (and
+// its subclasses, such as nsTHashtable).
+//
+// - mozilla::HashTable is a lot faster, largely because it uses templates
+//   throughout *and* inlines everything. PLDHashTable inlines operations much
+//   less aggressively, and also uses "virtual ops" for operations like hashing
+//   and matching entries that require function calls.
+//
+// - Correspondingly, mozilla::HashTable use is likely to increase executable
+//   size much more than PLDHashTable.
+//
+// - mozilla::HashTable has a nicer API, with a proper HashSet vs. HashMap
+//   distinction.
+//
+// - mozilla::HashTable requires more explicit OOM checking. Use
+//   mozilla::InfallibleAllocPolicy to make allocations infallible; note that
+//   return values of possibly-allocating methods such as add() will still need
+//   checking in some fashion -- e.g. with MOZ_ALWAYS_TRUE() -- due to the use
+//   of MOZ_MUST_USE.
+//
+// - mozilla::HashTable has a default capacity on creation of 32 and a minimum
+//   capacity of 4. PLDHashTable has a default capacity on creation of 8 and a
+//   minimum capacity of 8.
+//
+// - mozilla::HashTable allocates memory eagerly. PLDHashTable delays
+//   allocating until the first element is inserted.
+
 #ifndef mozilla_HashTable_h
 #define mozilla_HashTable_h
 
 #include "mozilla/AllocPolicy.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Casting.h"
 #include "mozilla/HashFunctions.h"
@@ -19,21 +46,30 @@
 #include "mozilla/Opaque.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 
-template <class> struct DefaultHasher;
-template <class, class> class HashMapEntry;
+template<class>
+struct DefaultHasher;
+
+template<class, class>
+class HashMapEntry;
+
 namespace detail {
-    template <typename T> class HashTableEntry;
-    template <class T, class HashPolicy, class AllocPolicy> class HashTable;
+
+template<typename T>
+class HashTableEntry;
+
+template<class T, class HashPolicy, class AllocPolicy>
+class HashTable;
+
 } // namespace detail
 
 /*****************************************************************************/
 
 // The "generation" of a hash table is an opaque value indicating the state of
 // modification of the hash table through its lifetime.  If the generation of
 // a hash table compares equal at times T1 and T2, then lookups in the hash
 // table, pointers to (or into) hash table entries, etc. at time T1 are valid
@@ -55,259 +91,307 @@ using Generation = Opaque<uint64_t>;
 //  - see Hash Policy section below
 // AllocPolicy:
 //  - see AllocPolicy.h
 //
 // Note:
 // - HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
 //   called by HashMap must not call back into the same HashMap object.
 // - Due to the lack of exception handling, the user must call |init()|.
-template <class Key,
-          class Value,
-          class HashPolicy = DefaultHasher<Key>,
-          class AllocPolicy = MallocAllocPolicy>
+template<class Key,
+         class Value,
+         class HashPolicy = DefaultHasher<Key>,
+         class AllocPolicy = MallocAllocPolicy>
 class HashMap
 {
-    typedef HashMapEntry<Key, Value> TableEntry;
+  using TableEntry = HashMapEntry<Key, Value>;
 
-    struct MapHashPolicy : HashPolicy
-    {
-        using Base = HashPolicy;
-        typedef Key KeyType;
-        static const Key& getKey(TableEntry& e) { return e.key(); }
-        static void setKey(TableEntry& e, Key& k) { HashPolicy::rekey(e.mutableKey(), k); }
-    };
+  struct MapHashPolicy : HashPolicy
+  {
+    using Base = HashPolicy;
+    using KeyType = Key;
+
+    static const Key& getKey(TableEntry& aEntry) { return aEntry.key(); }
 
-    typedef detail::HashTable<TableEntry, MapHashPolicy, AllocPolicy> Impl;
-    Impl impl;
+    static void setKey(TableEntry& aEntry, Key& aKey)
+    {
+      HashPolicy::rekey(aEntry.mutableKey(), aKey);
+    }
+  };
+
+  using Impl = detail::HashTable<TableEntry, MapHashPolicy, AllocPolicy>;
+  Impl mImpl;
 
-  public:
-    typedef typename HashPolicy::Lookup Lookup;
-    typedef TableEntry Entry;
+public:
+  using Lookup = typename HashPolicy::Lookup;
+  using Entry = TableEntry;
 
-    // HashMap construction is fallible (due to OOM); thus the user must call
-    // init after constructing a HashMap and check the return value.
-    explicit HashMap(AllocPolicy a = AllocPolicy()) : impl(a)  {}
-    MOZ_MUST_USE bool init(uint32_t len = 16) { return impl.init(len); }
-    bool initialized() const                  { return impl.initialized(); }
+  // HashMap construction is fallible (due to OOM); thus the user must call
+  // init after constructing a HashMap and check the return value.
+  explicit HashMap(AllocPolicy aPolicy = AllocPolicy())
+    : mImpl(aPolicy)
+  {
+  }
 
-    // Return whether the given lookup value is present in the map. E.g.:
-    //
-    //   typedef HashMap<int,char> HM;
-    //   HM h;
-    //   if (HM::Ptr p = h.lookup(3)) {
-    //     const HM::Entry& e = *p; // p acts like a pointer to Entry
-    //     assert(p->key == 3);     // Entry contains the key
-    //     char val = p->value;     // and value
-    //   }
-    //
-    // Also see the definition of Ptr in HashTable above (with T = Entry).
-    typedef typename Impl::Ptr Ptr;
-    MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const { return impl.lookup(l); }
+  MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); }
+
+  bool initialized() const { return mImpl.initialized(); }
 
-    // Like lookup, but does not assert if two threads call lookup at the same
-    // time. Only use this method when none of the threads will modify the map.
-    MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const {
-        return impl.readonlyThreadsafeLookup(l);
-    }
+  // Return whether the given lookup value is present in the map. E.g.:
+  //
+  //   using HM = HashMap<int,char>;
+  //   HM h;
+  //   if (HM::Ptr p = h.lookup(3)) {
+  //     const HM::Entry& e = *p; // p acts like a pointer to Entry
+  //     assert(p->key == 3);     // Entry contains the key
+  //     char val = p->value;     // and value
+  //   }
+  //
+  // Also see the definition of Ptr in HashTable above (with T = Entry).
+  using Ptr = typename Impl::Ptr;
+  MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup);
+  }
 
-    // Assuming |p.found()|, remove |*p|.
-    void remove(Ptr p)                                { impl.remove(p); }
+  // Like lookup, but does not assert if two threads call lookup at the same
+  // time. Only use this method when none of the threads will modify the map.
+  MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const
+  {
+    return mImpl.readonlyThreadsafeLookup(aLookup);
+  }
+
+  // Assuming |p.found()|, remove |*p|.
+  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
 
-    // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
-    // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
-    // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
-    //
-    //   typedef HashMap<int,char> HM;
-    //   HM h;
-    //   HM::AddPtr p = h.lookupForAdd(3);
-    //   if (!p) {
-    //     if (!h.add(p, 3, 'a'))
-    //       return false;
-    //   }
-    //   const HM::Entry& e = *p;   // p acts like a pointer to Entry
-    //   assert(p->key == 3);       // Entry contains the key
-    //   char val = p->value;       // and value
-    //
-    // Also see the definition of AddPtr in HashTable above (with T = Entry).
-    //
-    // N.B. The caller must ensure that no mutating hash table operations
-    // occur between a pair of |lookupForAdd| and |add| calls. To avoid
-    // looking up the key a second time, the caller may use the more efficient
-    // relookupOrAdd method. This method reuses part of the hashing computation
-    // to more efficiently insert the key if it has not been added. For
-    // example, a mutation-handling version of the previous example:
-    //
-    //    HM::AddPtr p = h.lookupForAdd(3);
-    //    if (!p) {
-    //      call_that_may_mutate_h();
-    //      if (!h.relookupOrAdd(p, 3, 'a'))
-    //        return false;
-    //    }
-    //    const HM::Entry& e = *p;
-    //    assert(p->key == 3);
-    //    char val = p->value;
-    typedef typename Impl::AddPtr AddPtr;
-    MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
-        return impl.lookupForAdd(l);
-    }
+  // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
+  // insertion of Key |k| (where |HashPolicy::match(k,l) == true|) using
+  // |add(p,k,v)|. After |add(p,k,v)|, |p| points to the new Entry. E.g.:
+  //
+  //   using HM = HashMap<int,char>;
+  //   HM h;
+  //   HM::AddPtr p = h.lookupForAdd(3);
+  //   if (!p) {
+  //     if (!h.add(p, 3, 'a')) {
+  //       return false;
+  //     }
+  //   }
+  //   const HM::Entry& e = *p;   // p acts like a pointer to Entry
+  //   assert(p->key == 3);       // Entry contains the key
+  //   char val = p->value;       // and value
+  //
+  // Also see the definition of AddPtr in HashTable above (with T = Entry).
+  //
+  // N.B. The caller must ensure that no mutating hash table operations
+  // occur between a pair of |lookupForAdd| and |add| calls. To avoid
+  // looking up the key a second time, the caller may use the more efficient
+  // relookupOrAdd method. This method reuses part of the hashing computation
+  // to more efficiently insert the key if it has not been added. For
+  // example, a mutation-handling version of the previous example:
+  //
+  //    HM::AddPtr p = h.lookupForAdd(3);
+  //    if (!p) {
+  //      call_that_may_mutate_h();
+  //      if (!h.relookupOrAdd(p, 3, 'a')) {
+  //        return false;
+  //      }
+  //    }
+  //    const HM::Entry& e = *p;
+  //    assert(p->key == 3);
+  //    char val = p->value;
+  //
+  using AddPtr = typename Impl::AddPtr;
+  MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& aLookup) const
+  {
+    return mImpl.lookupForAdd(aLookup);
+  }
 
-    template<typename KeyInput, typename ValueInput>
-    MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
-        return impl.add(p,
-                        std::forward<KeyInput>(k),
-                        std::forward<ValueInput>(v));
-    }
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool add(AddPtr& aPtr, KeyInput&& aKey, ValueInput&& aValue)
+  {
+    return mImpl.add(
+      aPtr, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
 
-    template<typename KeyInput>
-    MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k) {
-        return impl.add(p, std::forward<KeyInput>(k), Value());
-    }
+  template<typename KeyInput>
+  MOZ_MUST_USE bool add(AddPtr& aPtr, KeyInput&& aKey)
+  {
+    return mImpl.add(aPtr, std::forward<KeyInput>(aKey), Value());
+  }
 
-    template<typename KeyInput, typename ValueInput>
-    MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
-        return impl.relookupOrAdd(p, k,
-                                  std::forward<KeyInput>(k),
-                                  std::forward<ValueInput>(v));
-    }
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr,
+                                  KeyInput&& aKey,
+                                  ValueInput&& aValue)
+  {
+    return mImpl.relookupOrAdd(aPtr,
+                               aKey,
+                               std::forward<KeyInput>(aKey),
+                               std::forward<ValueInput>(aValue));
+  }
 
-    // |all()| returns a Range containing |count()| elements. E.g.:
-    //
-    //   typedef HashMap<int,char> HM;
-    //   HM h;
-    //   for (HM::Range r = h.all(); !r.empty(); r.popFront())
-    //     char c = r.front().value();
-    //
-    // Also see the definition of Range in HashTable above (with T = Entry).
-    typedef typename Impl::Range Range;
-    Range all() const                                 { return impl.all(); }
+  // |all()| returns a Range containing |count()| elements. E.g.:
+  //
+  //   using HM = HashMap<int,char>;
+  //   HM h;
+  //   for (HM::Range r = h.all(); !r.empty(); r.popFront()) {
+  //     char c = r.front().value();
+  //   }
+  //
+  // Also see the definition of Range in HashTable above (with T = Entry).
+  using Range = typename Impl::Range;
+  Range all() const { return mImpl.all(); }
 
-    // Typedef for the enumeration class. An Enum may be used to examine and
-    // remove table entries:
-    //
-    //   typedef HashMap<int,char> HM;
-    //   HM s;
-    //   for (HM::Enum e(s); !e.empty(); e.popFront())
-    //     if (e.front().value() == 'l')
-    //       e.removeFront();
-    //
-    // Table resize may occur in Enum's destructor. Also see the definition of
-    // Enum in HashTable above (with T = Entry).
-    typedef typename Impl::Enum Enum;
+  // Typedef for the enumeration class. An Enum may be used to examine and
+  // remove table entries:
+  //
+  //   using HM = HashMap<int,char>;
+  //   HM s;
+  //   for (HM::Enum e(s); !e.empty(); e.popFront()) {
+  //     if (e.front().value() == 'l') {
+  //       e.removeFront();
+  //     }
+  //   }
+  //
+  // Table resize may occur in Enum's destructor. Also see the definition of
+  // Enum in HashTable above (with T = Entry).
+  using Enum = typename Impl::Enum;
 
-    // Remove all entries. This does not shrink the table. For that consider
-    // using the finish() method.
-    void clear()                                      { impl.clear(); }
+  // Remove all entries. This does not shrink the table. For that consider
+  // using the finish() method.
+  void clear() { mImpl.clear(); }
+
+  // Remove all entries. Unlike clear() this method tries to shrink the table.
+  // Unlike finish() it does not require the map to be initialized again.
+  void clearAndShrink() { mImpl.clearAndShrink(); }
 
-    // Remove all entries. Unlike clear() this method tries to shrink the table.
-    // Unlike finish() it does not require the map to be initialized again.
-    void clearAndShrink()                             { impl.clearAndShrink(); }
+  // Remove all the entries and release all internal buffers. The map must
+  // be initialized again before any use.
+  void finish() { mImpl.finish(); }
 
-    // Remove all the entries and release all internal buffers. The map must
-    // be initialized again before any use.
-    void finish()                                     { impl.finish(); }
+  // Does the table contain any entries?
+  bool empty() const { return mImpl.empty(); }
+
+  // Number of live elements in the map.
+  uint32_t count() const { return mImpl.count(); }
 
-    // Does the table contain any entries?
-    bool empty() const                                { return impl.empty(); }
+  // Total number of allocation in the dynamic table. Note: resize will
+  // happen well before count() == capacity().
+  size_t capacity() const { return mImpl.capacity(); }
 
-    // Number of live elements in the map.
-    uint32_t count() const                            { return impl.count(); }
+  // Don't just call |mImpl.sizeOfExcludingThis()| because there's no
+  // guarantee that |mImpl| is the first field in HashMap.
+  size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mImpl.sizeOfExcludingThis(aMallocSizeOf);
+  }
+  size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + mImpl.sizeOfExcludingThis(aMallocSizeOf);
+  }
 
-    // Total number of allocation in the dynamic table. Note: resize will
-    // happen well before count() == capacity().
-    size_t capacity() const                           { return impl.capacity(); }
+  Generation generation() const { return mImpl.generation(); }
 
-    // Don't just call |impl.sizeOfExcludingThis()| because there's no
-    // guarantee that |impl| is the first field in HashMap.
-    size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
-        return impl.sizeOfExcludingThis(mallocSizeOf);
-    }
-    size_t sizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
-        return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
-    }
+  /************************************************** Shorthand operations */
+
+  bool has(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup).found();
+  }
 
-    Generation generation() const {
-        return impl.generation();
+  // Overwrite existing value with aValue. Return false on oom.
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool put(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    AddPtr p = lookupForAdd(aKey);
+    if (p) {
+      p->value() = std::forward<ValueInput>(aValue);
+      return true;
     }
-
-    /************************************************** Shorthand operations */
-
-    bool has(const Lookup& l) const {
-        return impl.lookup(l).found();
-    }
+    return add(
+      p, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
 
-    // Overwrite existing value with v. Return false on oom.
-    template<typename KeyInput, typename ValueInput>
-    MOZ_MUST_USE bool put(KeyInput&& k, ValueInput&& v) {
-        AddPtr p = lookupForAdd(k);
-        if (p) {
-            p->value() = std::forward<ValueInput>(v);
-            return true;
-        }
-        return add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
-    }
+  // Like put, but assert that the given key is not already present.
+  template<typename KeyInput, typename ValueInput>
+  MOZ_MUST_USE bool putNew(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    return mImpl.putNew(
+      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
 
-    // Like put, but assert that the given key is not already present.
-    template<typename KeyInput, typename ValueInput>
-    MOZ_MUST_USE bool putNew(KeyInput&& k, ValueInput&& v) {
-        return impl.putNew(k, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
-    }
+  // Only call this to populate an empty map after reserving space with init().
+  template<typename KeyInput, typename ValueInput>
+  void putNewInfallible(KeyInput&& aKey, ValueInput&& aValue)
+  {
+    mImpl.putNewInfallible(
+      aKey, std::forward<KeyInput>(aKey), std::forward<ValueInput>(aValue));
+  }
 
-    // Only call this to populate an empty map after reserving space with init().
-    template<typename KeyInput, typename ValueInput>
-    void putNewInfallible(KeyInput&& k, ValueInput&& v) {
-        impl.putNewInfallible(k, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
+  // Add (aKey,aDefaultValue) if |aKey| is not found. Return a false-y Ptr on
+  // oom.
+  Ptr lookupWithDefault(const Key& aKey, const Value& aDefaultValue)
+  {
+    AddPtr p = lookupForAdd(aKey);
+    if (p) {
+      return p;
     }
+    bool ok = add(p, aKey, aDefaultValue);
+    MOZ_ASSERT_IF(!ok, !p); // p is left false-y on oom.
+    (void)ok;
+    return p;
+  }
 
-    // Add (k,defaultValue) if |k| is not found. Return a false-y Ptr on oom.
-    Ptr lookupWithDefault(const Key& k, const Value& defaultValue) {
-        AddPtr p = lookupForAdd(k);
-        if (p)
-            return p;
-        bool ok = add(p, k, defaultValue);
-        MOZ_ASSERT_IF(!ok, !p); // p is left false-y on oom.
-        (void)ok;
-        return p;
+  // Remove if present.
+  void remove(const Lookup& aLookup)
+  {
+    if (Ptr p = lookup(aLookup)) {
+      remove(p);
     }
+  }
 
-    // Remove if present.
-    void remove(const Lookup& l) {
-        if (Ptr p = lookup(l))
-            remove(p);
+  // Infallibly rekey one entry, if necessary.
+  // Requires template parameters Key and HashPolicy::Lookup to be the same
+  // type.
+  void rekeyIfMoved(const Key& aOldKey, const Key& aNewKey)
+  {
+    if (aOldKey != aNewKey) {
+      rekeyAs(aOldKey, aNewKey, aNewKey);
     }
-
-    // Infallibly rekey one entry, if necessary.
-    // Requires template parameters Key and HashPolicy::Lookup to be the same type.
-    void rekeyIfMoved(const Key& old_key, const Key& new_key) {
-        if (old_key != new_key)
-            rekeyAs(old_key, new_key, new_key);
-    }
+  }
 
-    // Infallibly rekey one entry if present, and return whether that happened.
-    bool rekeyAs(const Lookup& old_lookup, const Lookup& new_lookup, const Key& new_key) {
-        if (Ptr p = lookup(old_lookup)) {
-            impl.rekeyAndMaybeRehash(p, new_lookup, new_key);
-            return true;
-        }
-        return false;
+  // Infallibly rekey one entry if present, and return whether that happened.
+  bool rekeyAs(const Lookup& aOldLookup,
+               const Lookup& aNewLookup,
+               const Key& aNewKey)
+  {
+    if (Ptr p = lookup(aOldLookup)) {
+      mImpl.rekeyAndMaybeRehash(p, aNewLookup, aNewKey);
+      return true;
     }
+    return false;
+  }
 
-    // HashMap is movable
-    HashMap(HashMap&& rhs) : impl(std::move(rhs.impl)) {}
-    void operator=(HashMap&& rhs) {
-        MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
-        impl = std::move(rhs.impl);
-    }
+  // HashMap is movable
+  HashMap(HashMap&& aRhs)
+    : mImpl(std::move(aRhs.mImpl))
+  {
+  }
+  void operator=(HashMap&& aRhs)
+  {
+    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
+    mImpl = std::move(aRhs.mImpl);
+  }
 
-  private:
-    // HashMap is not copyable or assignable
-    HashMap(const HashMap& hm) = delete;
-    HashMap& operator=(const HashMap& hm) = delete;
+private:
+  // HashMap is not copyable or assignable
+  HashMap(const HashMap& hm) = delete;
+  HashMap& operator=(const HashMap& hm) = delete;
 
-    friend class Impl::Enum;
+  friend class Impl::Enum;
 };
 
 /*****************************************************************************/
 
 // A performant, STL-like container providing a hash-based set of values. In
 // particular, HashSet calls constructors and destructors of all objects added
 // so non-PODs may be used safely.
 //
@@ -317,245 +401,280 @@ class HashMap
 //  - see Hash Policy section below
 // AllocPolicy:
 //  - see AllocPolicy.h
 //
 // Note:
 // - HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
 //   HashSet must not call back into the same HashSet object.
 // - Due to the lack of exception handling, the user must call |init()|.
-template <class T,
-          class HashPolicy = DefaultHasher<T>,
-          class AllocPolicy = MallocAllocPolicy>
+template<class T,
+         class HashPolicy = DefaultHasher<T>,
+         class AllocPolicy = MallocAllocPolicy>
 class HashSet
 {
-    struct SetOps : HashPolicy
-    {
-        using Base = HashPolicy;
-        typedef T KeyType;
-        static const KeyType& getKey(const T& t) { return t; }
-        static void setKey(T& t, KeyType& k) { HashPolicy::rekey(t, k); }
-    };
+  struct SetOps : HashPolicy
+  {
+    using Base = HashPolicy;
+    using KeyType = T;
+
+    static const KeyType& getKey(const T& aT) { return aT; }
+    static void setKey(T& aT, KeyType& aKey) { HashPolicy::rekey(aT, aKey); }
+  };
+
+  using Impl = detail::HashTable<const T, SetOps, AllocPolicy>;
+  Impl mImpl;
 
-    typedef detail::HashTable<const T, SetOps, AllocPolicy> Impl;
-    Impl impl;
+public:
+  using Lookup = typename HashPolicy::Lookup;
+  using Entry = T;
 
-  public:
-    typedef typename HashPolicy::Lookup Lookup;
-    typedef T Entry;
+  // HashSet construction is fallible (due to OOM); thus the user must call
+  // init after constructing a HashSet and check the return value.
+  explicit HashSet(AllocPolicy a = AllocPolicy())
+    : mImpl(a)
+  {
+  }
 
-    // HashSet construction is fallible (due to OOM); thus the user must call
-    // init after constructing a HashSet and check the return value.
-    explicit HashSet(AllocPolicy a = AllocPolicy()) : impl(a)  {}
-    MOZ_MUST_USE bool init(uint32_t len = 16) { return impl.init(len); }
-    bool initialized() const                  { return impl.initialized(); }
+  MOZ_MUST_USE bool init(uint32_t aLen = 16) { return mImpl.init(aLen); }
 
-    // Return whether the given lookup value is present in the map. E.g.:
-    //
-    //   typedef HashSet<int> HS;
-    //   HS h;
-    //   if (HS::Ptr p = h.lookup(3)) {
-    //     assert(*p == 3);   // p acts like a pointer to int
-    //   }
-    //
-    // Also see the definition of Ptr in HashTable above.
-    typedef typename Impl::Ptr Ptr;
-    MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const { return impl.lookup(l); }
+  bool initialized() const { return mImpl.initialized(); }
 
-    // Like lookup, but does not assert if two threads call lookup at the same
-    // time. Only use this method when none of the threads will modify the map.
-    MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const {
-        return impl.readonlyThreadsafeLookup(l);
-    }
+  // Return whether the given lookup value is present in the map. E.g.:
+  //
+  //   using HS = HashSet<int>;
+  //   HS h;
+  //   if (HS::Ptr p = h.lookup(3)) {
+  //     assert(*p == 3);   // p acts like a pointer to int
+  //   }
+  //
+  // Also see the definition of Ptr in HashTable above.
+  using Ptr = typename Impl::Ptr;
+  MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup);
+  }
 
-    // Assuming |p.found()|, remove |*p|.
-    void remove(Ptr p)                                { impl.remove(p); }
+  // Like lookup, but does not assert if two threads call lookup at the same
+  // time. Only use this method when none of the threads will modify the map.
+  MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const
+  {
+    return mImpl.readonlyThreadsafeLookup(aLookup);
+  }
+
+  // Assuming |aPtr.found()|, remove |*aPtr|.
+  void remove(Ptr aPtr) { mImpl.remove(aPtr); }
 
-    // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
-    // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
-    // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
-    //
-    //   typedef HashSet<int> HS;
-    //   HS h;
-    //   HS::AddPtr p = h.lookupForAdd(3);
-    //   if (!p) {
-    //     if (!h.add(p, 3))
-    //       return false;
-    //   }
-    //   assert(*p == 3);   // p acts like a pointer to int
-    //
-    // Also see the definition of AddPtr in HashTable above.
-    //
-    // N.B. The caller must ensure that no mutating hash table operations
-    // occur between a pair of |lookupForAdd| and |add| calls. To avoid
-    // looking up the key a second time, the caller may use the more efficient
-    // relookupOrAdd method. This method reuses part of the hashing computation
-    // to more efficiently insert the key if it has not been added. For
-    // example, a mutation-handling version of the previous example:
-    //
-    //    HS::AddPtr p = h.lookupForAdd(3);
-    //    if (!p) {
-    //      call_that_may_mutate_h();
-    //      if (!h.relookupOrAdd(p, 3, 3))
-    //        return false;
-    //    }
-    //    assert(*p == 3);
-    //
-    // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the
-    // entry |t|, where the caller ensures match(l,t).
-    typedef typename Impl::AddPtr AddPtr;
-    MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const {
-        return impl.lookupForAdd(l);
-    }
+  // Like |lookup(l)|, but on miss, |p = lookupForAdd(l)| allows efficient
+  // insertion of T value |t| (where |HashPolicy::match(t,l) == true|) using
+  // |add(p,t)|. After |add(p,t)|, |p| points to the new element. E.g.:
+  //
+  //   using HS = HashSet<int>;
+  //   HS h;
+  //   HS::AddPtr p = h.lookupForAdd(3);
+  //   if (!p) {
+  //     if (!h.add(p, 3)) {
+  //       return false;
+  //     }
+  //   }
+  //   assert(*p == 3);   // p acts like a pointer to int
+  //
+  // Also see the definition of AddPtr in HashTable above.
+  //
+  // N.B. The caller must ensure that no mutating hash table operations
+  // occur between a pair of |lookupForAdd| and |add| calls. To avoid
+  // looking up the key a second time, the caller may use the more efficient
+  // relookupOrAdd method. This method reuses part of the hashing computation
+  // to more efficiently insert the key if it has not been added. For
+  // example, a mutation-handling version of the previous example:
+  //
+  //    HS::AddPtr p = h.lookupForAdd(3);
+  //    if (!p) {
+  //      call_that_may_mutate_h();
+  //      if (!h.relookupOrAdd(p, 3, 3)) {
+  //        return false;
+  //      }
+  //    }
+  //    assert(*p == 3);
+  //
+  // Note that relookupOrAdd(p,l,t) performs Lookup using |l| and adds the
+  // entry |t|, where the caller ensures match(l,t).
+  using AddPtr = typename Impl::AddPtr;
+  MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& aLookup) const
+  {
+    return mImpl.lookupForAdd(aLookup);
+  }
 
-    template <typename U>
-    MOZ_MUST_USE bool add(AddPtr& p, U&& u) {
-        return impl.add(p, std::forward<U>(u));
-    }
+  template<typename U>
+  MOZ_MUST_USE bool add(AddPtr& aPtr, U&& aU)
+  {
+    return mImpl.add(aPtr, std::forward<U>(aU));
+  }
 
-    template <typename U>
-    MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, U&& u) {
-        return impl.relookupOrAdd(p, l, std::forward<U>(u));
-    }
+  template<typename U>
+  MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr, const Lookup& aLookup, U&& aU)
+  {
+    return mImpl.relookupOrAdd(aPtr, aLookup, std::forward<U>(aU));
+  }
 
-    // |all()| returns a Range containing |count()| elements:
-    //
-    //   typedef HashSet<int> HS;
-    //   HS h;
-    //   for (HS::Range r = h.all(); !r.empty(); r.popFront())
-    //     int i = r.front();
-    //
-    // Also see the definition of Range in HashTable above.
-    typedef typename Impl::Range Range;
-    Range all() const                                 { return impl.all(); }
+  // |all()| returns a Range containing |count()| elements:
+  //
+  //   using HS = HashSet<int>;
+  //   HS h;
+  //   for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
+  //     int i = r.front();
+  //   }
+  //
+  // Also see the definition of Range in HashTable above.
+  using Range = typename Impl::Range;
+  Range all() const { return mImpl.all(); }
 
-    // Typedef for the enumeration class. An Enum may be used to examine and
-    // remove table entries:
-    //
-    //   typedef HashSet<int> HS;
-    //   HS s;
-    //   for (HS::Enum e(s); !e.empty(); e.popFront())
-    //     if (e.front() == 42)
-    //       e.removeFront();
-    //
-    // Table resize may occur in Enum's destructor. Also see the definition of
-    // Enum in HashTable above.
-    typedef typename Impl::Enum Enum;
+  // Typedef for the enumeration class. An Enum may be used to examine and
+  // remove table entries:
+  //
+  //   using HS = HashSet<int>;
+  //   HS s;
+  //   for (HS::Enum e(s); !e.empty(); e.popFront()) {
+  //     if (e.front() == 42) {
+  //       e.removeFront();
+  //     }
+  //   }
+  //
+  // Table resize may occur in Enum's destructor. Also see the definition of
+  // Enum in HashTable above.
+  using Enum = typename Impl::Enum;
 
-    // Remove all entries. This does not shrink the table. For that consider
-    // using the finish() method.
-    void clear()                                      { impl.clear(); }
+  // Remove all entries. This does not shrink the table. For that consider
+  // using the finish() method.
+  void clear() { mImpl.clear(); }
+
+  // Remove all entries. Unlike clear() this method tries to shrink the table.
+  // Unlike finish() it does not require the set to be initialized again.
+  void clearAndShrink() { mImpl.clearAndShrink(); }
 
-    // Remove all entries. Unlike clear() this method tries to shrink the table.
-    // Unlike finish() it does not require the set to be initialized again.
-    void clearAndShrink()                             { impl.clearAndShrink(); }
+  // Remove all the entries and release all internal buffers. The set must
+  // be initialized again before any use.
+  void finish() { mImpl.finish(); }
 
-    // Remove all the entries and release all internal buffers. The set must
-    // be initialized again before any use.
-    void finish()                                     { impl.finish(); }
+  // Does the table contain any entries?
+  bool empty() const { return mImpl.empty(); }
 
-    // Does the table contain any entries?
-    bool empty() const                                { return impl.empty(); }
+  // Number of live elements in the map.
+  uint32_t count() const { return mImpl.count(); }
 
-    // Number of live elements in the map.
-    uint32_t count() const                            { return impl.count(); }
-
-    // Total number of allocation in the dynamic table. Note: resize will
-    // happen well before count() == capacity().
-    size_t capacity() const                           { return impl.capacity(); }
+  // Total number of allocation in the dynamic table. Note: resize will
+  // happen well before count() == capacity().
+  size_t capacity() const { return mImpl.capacity(); }
 
-    // Don't just call |impl.sizeOfExcludingThis()| because there's no
-    // guarantee that |impl| is the first field in HashSet.
-    size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
-        return impl.sizeOfExcludingThis(mallocSizeOf);
-    }
-    size_t sizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
-        return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
-    }
+  // Don't just call |mImpl.sizeOfExcludingThis()| because there's no
+  // guarantee that |mImpl| is the first field in HashSet.
+  size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mImpl.sizeOfExcludingThis(aMallocSizeOf);
+  }
+  size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + mImpl.sizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  Generation generation() const { return mImpl.generation(); }
+
+  /************************************************** Shorthand operations */
 
-    Generation generation() const {
-        return impl.generation();
-    }
-
-    /************************************************** Shorthand operations */
+  bool has(const Lookup& aLookup) const
+  {
+    return mImpl.lookup(aLookup).found();
+  }
 
-    bool has(const Lookup& l) const {
-        return impl.lookup(l).found();
-    }
+  // Add |aU| if it is not present already. Return false on oom.
+  template<typename U>
+  MOZ_MUST_USE bool put(U&& aU)
+  {
+    AddPtr p = lookupForAdd(aU);
+    return p ? true : add(p, std::forward<U>(aU));
+  }
 
-    // Add |u| if it is not present already. Return false on oom.
-    template <typename U>
-    MOZ_MUST_USE bool put(U&& u) {
-        AddPtr p = lookupForAdd(u);
-        return p ? true : add(p, std::forward<U>(u));
-    }
+  // Like put, but assert that the given key is not already present.
+  template<typename U>
+  MOZ_MUST_USE bool putNew(U&& aU)
+  {
+    return mImpl.putNew(aU, std::forward<U>(aU));
+  }
 
-    // Like put, but assert that the given key is not already present.
-    template <typename U>
-    MOZ_MUST_USE bool putNew(U&& u) {
-        return impl.putNew(u, std::forward<U>(u));
-    }
+  template<typename U>
+  MOZ_MUST_USE bool putNew(const Lookup& aLookup, U&& aU)
+  {
+    return mImpl.putNew(aLookup, std::forward<U>(aU));
+  }
 
-    template <typename U>
-    MOZ_MUST_USE bool putNew(const Lookup& l, U&& u) {
-        return impl.putNew(l, std::forward<U>(u));
-    }
+  // Only call this to populate an empty set after reserving space with init().
+  template<typename U>
+  void putNewInfallible(const Lookup& aLookup, U&& aU)
+  {
+    mImpl.putNewInfallible(aLookup, std::forward<U>(aU));
+  }
 
-    // Only call this to populate an empty set after reserving space with init().
-    template <typename U>
-    void putNewInfallible(const Lookup& l, U&& u) {
-        impl.putNewInfallible(l, std::forward<U>(u));
+  void remove(const Lookup& aLookup)
+  {
+    if (Ptr p = lookup(aLookup)) {
+      remove(p);
     }
+  }
 
-    void remove(const Lookup& l) {
-        if (Ptr p = lookup(l))
-            remove(p);
+  // Infallibly rekey one entry, if present.
+  // Requires template parameters T and HashPolicy::Lookup to be the same type.
+  void rekeyIfMoved(const Lookup& aOldValue, const T& aNewValue)
+  {
+    if (aOldValue != aNewValue) {
+      rekeyAs(aOldValue, aNewValue, aNewValue);
     }
-
-    // Infallibly rekey one entry, if present.
-    // Requires template parameters T and HashPolicy::Lookup to be the same type.
-    void rekeyIfMoved(const Lookup& old_value, const T& new_value) {
-        if (old_value != new_value)
-            rekeyAs(old_value, new_value, new_value);
-    }
+  }
 
-    // Infallibly rekey one entry if present, and return whether that happened.
-    bool rekeyAs(const Lookup& old_lookup, const Lookup& new_lookup, const T& new_value) {
-        if (Ptr p = lookup(old_lookup)) {
-            impl.rekeyAndMaybeRehash(p, new_lookup, new_value);
-            return true;
-        }
-        return false;
+  // Infallibly rekey one entry if present, and return whether that happened.
+  bool rekeyAs(const Lookup& aOldLookup,
+               const Lookup& aNewLookup,
+               const T& aNewValue)
+  {
+    if (Ptr p = lookup(aOldLookup)) {
+      mImpl.rekeyAndMaybeRehash(p, aNewLookup, aNewValue);
+      return true;
     }
+    return false;
+  }
 
-    // Infallibly replace the current key at |p| with an equivalent key.
-    // Specifically, both HashPolicy::hash and HashPolicy::match must return
-    // identical results for the new and old key when applied against all
-    // possible matching values.
-    void replaceKey(Ptr p, const T& new_value) {
-        MOZ_ASSERT(p.found());
-        MOZ_ASSERT(*p != new_value);
-        MOZ_ASSERT(HashPolicy::hash(*p) == HashPolicy::hash(new_value));
-        MOZ_ASSERT(HashPolicy::match(*p, new_value));
-        const_cast<T&>(*p) = new_value;
-    }
+  // Infallibly replace the current key at |p| with an equivalent key.
+  // Specifically, both HashPolicy::hash and HashPolicy::match must return
+  // identical results for the new and old key when applied against all
+  // possible matching values.
+  void replaceKey(Ptr aPtr, const T& aNewValue)
+  {
+    MOZ_ASSERT(aPtr.found());
+    MOZ_ASSERT(*aPtr != aNewValue);
+    MOZ_ASSERT(HashPolicy::hash(*aPtr) == HashPolicy::hash(aNewValue));
+    MOZ_ASSERT(HashPolicy::match(*aPtr, aNewValue));
+    const_cast<T&>(*aPtr) = aNewValue;
+  }
 
-    // HashSet is movable
-    HashSet(HashSet&& rhs) : impl(std::move(rhs.impl)) {}
-    void operator=(HashSet&& rhs) {
-        MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
-        impl = std::move(rhs.impl);
-    }
+  // HashSet is movable
+  HashSet(HashSet&& aRhs)
+    : mImpl(std::move(aRhs.mImpl))
+  {
+  }
+  void operator=(HashSet&& aRhs)
+  {
+    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
+    mImpl = std::move(aRhs.mImpl);
+  }
 
-  private:
-    // HashSet is not copyable or assignable
-    HashSet(const HashSet& hs) = delete;
-    HashSet& operator=(const HashSet& hs) = delete;
+private:
+  // HashSet is not copyable or assignable.
+  HashSet(const HashSet& hs) = delete;
+  HashSet& operator=(const HashSet& hs) = delete;
 
-    friend class Impl::Enum;
+  friend class Impl::Enum;
 };
 
 /*****************************************************************************/
 
 // Hash Policy
 //
 // A hash policy P for a hash table with key-type Key must provide:
 //  - a type |P::Lookup| to use to lookup table entries;
@@ -578,1383 +697,1479 @@ class HashSet
 //   if (!p) {
 //     assert(P::match(k, l));  // must hold
 //     h.add(p, k);
 //   }
 
 // Pointer hashing policy that uses HashGeneric() to create good hashes for
 // pointers.  Note that we don't shift out the lowest k bits to generate a
 // good distribution for arena allocated pointers.
-template <typename Key>
+template<typename Key>
 struct PointerHasher
 {
-    typedef Key Lookup;
-    static HashNumber hash(const Lookup& l) {
-        size_t word = reinterpret_cast<size_t>(l);
-        return HashGeneric(word);
-    }
-    static bool match(const Key& k, const Lookup& l) {
-        return k == l;
-    }
-    static void rekey(Key& k, const Key& newKey) {
-        k = newKey;
-    }
+  using Lookup = Key;
+
+  static HashNumber hash(const Lookup& aLookup)
+  {
+    size_t word = reinterpret_cast<size_t>(aLookup);
+    return HashGeneric(word);
+  }
+
+  static bool match(const Key& aKey, const Lookup& aLookup)
+  {
+    return aKey == aLookup;
+  }
+
+  static void rekey(Key& aKey, const Key& aNewKey) { aKey = aNewKey; }
 };
 
 // Default hash policy: just use the 'lookup' value. This of course only
 // works if the lookup value is integral. HashTable applies ScrambleHashCode to
 // the result of the 'hash' which means that it is 'ok' if the lookup value is
 // not well distributed over the HashNumber domain.
-template <class Key>
+template<class Key>
 struct DefaultHasher
 {
-    typedef Key Lookup;
-    static HashNumber hash(const Lookup& l) {
-        // Hash if can implicitly cast to hash number type.
-        return l;
-    }
-    static bool match(const Key& k, const Lookup& l) {
-        // Use builtin or overloaded operator==.
-        return k == l;
-    }
-    static void rekey(Key& k, const Key& newKey) {
-        k = newKey;
-    }
+  using Lookup = Key;
+
+  static HashNumber hash(const Lookup& aLookup)
+  {
+    // Hash if can implicitly cast to hash number type.
+    return aLookup;
+  }
+
+  static bool match(const Key& aKey, const Lookup& aLookup)
+  {
+    // Use builtin or overloaded operator==.
+    return aKey == aLookup;
+  }
+
+  static void rekey(Key& aKey, const Key& aNewKey) { aKey = aNewKey; }
 };
 
 // Specialize hashing policy for pointer types. It assumes that the type is
 // at least word-aligned. For types with smaller size use PointerHasher.
-template <class T>
+template<class T>
 struct DefaultHasher<T*> : PointerHasher<T*>
-{};
+{
+};
 
 // Specialize hashing policy for mozilla::UniquePtr to proxy the UniquePtr's
 // raw pointer to PointerHasher.
-template <class T, class D>
+template<class T, class D>
 struct DefaultHasher<UniquePtr<T, D>>
 {
-    using Lookup = UniquePtr<T, D>;
-    using PtrHasher = PointerHasher<T*>;
+  using Lookup = UniquePtr<T, D>;
+  using PtrHasher = PointerHasher<T*>;
+
+  static HashNumber hash(const Lookup& aLookup)
+  {
+    return PtrHasher::hash(aLookup.get());
+  }
 
-    static HashNumber hash(const Lookup& l) {
-        return PtrHasher::hash(l.get());
-    }
-    static bool match(const UniquePtr<T, D>& k, const Lookup& l) {
-        return PtrHasher::match(k.get(), l.get());
-    }
-    static void rekey(UniquePtr<T, D>& k, UniquePtr<T, D>&& newKey) {
-        k = std::move(newKey);
-    }
+  static bool match(const UniquePtr<T, D>& aKey, const Lookup& aLookup)
+  {
+    return PtrHasher::match(aKey.get(), aLookup.get());
+  }
+
+  static void rekey(UniquePtr<T, D>& aKey, UniquePtr<T, D>&& aNewKey)
+  {
+    aKey = std::move(aNewKey);
+  }
 };
 
 // For doubles, we can xor the two uint32s.
-template <>
+template<>
 struct DefaultHasher<double>
 {
-    typedef double Lookup;
-    static HashNumber hash(double d) {
-        static_assert(sizeof(HashNumber) == 4,
-                      "subsequent code assumes a four-byte hash");
-        uint64_t u = BitwiseCast<uint64_t>(d);
-        return HashNumber(u ^ (u >> 32));
-    }
-    static bool match(double lhs, double rhs) {
-        return BitwiseCast<uint64_t>(lhs) == BitwiseCast<uint64_t>(rhs);
-    }
+  using Lookup = double;
+
+  static HashNumber hash(double aVal)
+  {
+    static_assert(sizeof(HashNumber) == 4,
+                  "subsequent code assumes a four-byte hash");
+    uint64_t u = BitwiseCast<uint64_t>(aVal);
+    return HashNumber(u ^ (u >> 32));
+  }
+
+  static bool match(double aLhs, double aRhs)
+  {
+    return BitwiseCast<uint64_t>(aLhs) == BitwiseCast<uint64_t>(aRhs);
+  }
 };
 
-template <>
+template<>
 struct DefaultHasher<float>
 {
-    typedef float Lookup;
-    static HashNumber hash(float f) {
-        static_assert(sizeof(HashNumber) == 4,
-                      "subsequent code assumes a four-byte hash");
-        return HashNumber(BitwiseCast<uint32_t>(f));
-    }
-    static bool match(float lhs, float rhs) {
-        return BitwiseCast<uint32_t>(lhs) == BitwiseCast<uint32_t>(rhs);
-    }
+  using Lookup = float;
+
+  static HashNumber hash(float aVal)
+  {
+    static_assert(sizeof(HashNumber) == 4,
+                  "subsequent code assumes a four-byte hash");
+    return HashNumber(BitwiseCast<uint32_t>(aVal));
+  }
+
+  static bool match(float aLhs, float aRhs)
+  {
+    return BitwiseCast<uint32_t>(aLhs) == BitwiseCast<uint32_t>(aRhs);
+  }
 };
 
 // A hash policy that compares C strings.
 struct CStringHasher
 {
-    typedef const char* Lookup;
-    static HashNumber hash(Lookup l) {
-        return HashString(l);
-    }
-    static bool match(const char* key, Lookup lookup) {
-        return strcmp(key, lookup) == 0;
-    }
+  using Lookup = const char*;
+
+  static HashNumber hash(Lookup aLookup) { return HashString(aLookup); }
+
+  static bool match(const char* key, Lookup lookup)
+  {
+    return strcmp(key, lookup) == 0;
+  }
 };
 
 // Fallible hashing interface.
 //
 // Most of the time generating a hash code is infallible so this class provides
 // default methods that always succeed.  Specialize this class for your own hash
 // policy to provide fallible hashing.
 //
 // This is used by MovableCellHasher to handle the fact that generating a unique
 // ID for cell pointer may fail due to OOM.
-template <typename HashPolicy>
+template<typename HashPolicy>
 struct FallibleHashMethods
 {
-    // Return true if a hashcode is already available for its argument.  Once
-    // this returns true for a specific argument it must continue to do so.
-    template <typename Lookup> static bool hasHash(Lookup&& l) { return true; }
+  // Return true if a hashcode is already available for its argument.  Once
+  // this returns true for a specific argument it must continue to do so.
+  template<typename Lookup>
+  static bool hasHash(Lookup&& aLookup)
+  {
+    return true;
+  }
 
-    // Fallible method to ensure a hashcode exists for its argument and create
-    // one if not.  Returns false on error, e.g. out of memory.
-    template <typename Lookup> static bool ensureHash(Lookup&& l) { return true; }
+  // Fallible method to ensure a hashcode exists for its argument and create
+  // one if not.  Returns false on error, e.g. out of memory.
+  template<typename Lookup>
+  static bool ensureHash(Lookup&& aLookup)
+  {
+    return true;
+  }
 };
 
-template <typename HashPolicy, typename Lookup>
+template<typename HashPolicy, typename Lookup>
 static bool
-HasHash(Lookup&& l) {
-    return FallibleHashMethods<typename HashPolicy::Base>::hasHash(std::forward<Lookup>(l));
+HasHash(Lookup&& aLookup)
+{
+  return FallibleHashMethods<typename HashPolicy::Base>::hasHash(
+    std::forward<Lookup>(aLookup));
 }
 
-template <typename HashPolicy, typename Lookup>
+template<typename HashPolicy, typename Lookup>
 static bool
-EnsureHash(Lookup&& l) {
-    return FallibleHashMethods<typename HashPolicy::Base>::ensureHash(std::forward<Lookup>(l));
+EnsureHash(Lookup&& aLookup)
+{
+  return FallibleHashMethods<typename HashPolicy::Base>::ensureHash(
+    std::forward<Lookup>(aLookup));
 }
 
 /*****************************************************************************/
 
 // Both HashMap and HashSet are implemented by a single HashTable that is even
 // more heavily parameterized than the other two. This leaves HashTable gnarly
 // and extremely coupled to HashMap and HashSet; thus code should not use
 // HashTable directly.
 
-template <class Key, class Value>
+template<class Key, class Value>
 class HashMapEntry
 {
-    Key key_;
-    Value value_;
-
-    template <class, class, class> friend class detail::HashTable;
-    template <class> friend class detail::HashTableEntry;
-    template <class, class, class, class> friend class HashMap;
+  Key key_;
+  Value value_;
 
-  public:
-    template<typename KeyInput, typename ValueInput>
-    HashMapEntry(KeyInput&& k, ValueInput&& v)
-      : key_(std::forward<KeyInput>(k)),
-        value_(std::forward<ValueInput>(v))
-    {}
+  template<class, class, class>
+  friend class detail::HashTable;
+  template<class>
+  friend class detail::HashTableEntry;
+  template<class, class, class, class>
+  friend class HashMap;
+
+public:
+  template<typename KeyInput, typename ValueInput>
+  HashMapEntry(KeyInput&& aKey, ValueInput&& aValue)
+    : key_(std::forward<KeyInput>(aKey))
+    , value_(std::forward<ValueInput>(aValue))
+  {
+  }
 
-    HashMapEntry(HashMapEntry&& rhs)
-      : key_(std::move(rhs.key_)),
-        value_(std::move(rhs.value_))
-    {}
+  HashMapEntry(HashMapEntry&& aRhs)
+    : key_(std::move(aRhs.key_))
+    , value_(std::move(aRhs.value_))
+  {
+  }
 
-    void operator=(HashMapEntry&& rhs) {
-        key_ = std::move(rhs.key_);
-        value_ = std::move(rhs.value_);
-    }
+  void operator=(HashMapEntry&& aRhs)
+  {
+    key_ = std::move(aRhs.key_);
+    value_ = std::move(aRhs.value_);
+  }
 
-    typedef Key KeyType;
-    typedef Value ValueType;
+  using KeyType = Key;
+  using ValueType = Value;
+
+  const Key& key() const { return key_; }
+  Key& mutableKey() { return key_; }
 
-    const Key& key() const { return key_; }
-    Key& mutableKey() { return key_; }
-    const Value& value() const { return value_; }
-    Value& value() { return value_; }
+  const Value& value() const { return value_; }
+  Value& value() { return value_; }
 
-  private:
-    HashMapEntry(const HashMapEntry&) = delete;
-    void operator=(const HashMapEntry&) = delete;
+private:
+  HashMapEntry(const HashMapEntry&) = delete;
+  void operator=(const HashMapEntry&) = delete;
 };
 
-template <typename K, typename V>
-struct IsPod<HashMapEntry<K, V> >
+template<typename K, typename V>
+struct IsPod<HashMapEntry<K, V>>
   : IntegralConstant<bool, IsPod<K>::value && IsPod<V>::value>
-{};
+{
+};
 
 namespace detail {
 
-template <class T, class HashPolicy, class AllocPolicy>
+template<class T, class HashPolicy, class AllocPolicy>
 class HashTable;
 
-template <typename T>
+template<typename T>
 class HashTableEntry
 {
-  private:
-    using NonConstT = typename RemoveConst<T>::Type;
+private:
+  using NonConstT = typename RemoveConst<T>::Type;
+
+  static const HashNumber sFreeKey = 0;
+  static const HashNumber sRemovedKey = 1;
+  static const HashNumber sCollisionBit = 1;
+
+  HashNumber mKeyHash = sFreeKey;
+  alignas(NonConstT) unsigned char mValueData[sizeof(NonConstT)];
 
-    static const HashNumber sFreeKey = 0;
-    static const HashNumber sRemovedKey = 1;
-    static const HashNumber sCollisionBit = 1;
+private:
+  template<class, class, class>
+  friend class HashTable;
 
-    HashNumber keyHash = sFreeKey;
-    alignas(NonConstT) unsigned char valueData_[sizeof(NonConstT)];
+  // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
+  // -Werror compile error) to reinterpret_cast<> |mValueData| to |T*|, even
+  // through |void*|.  Placing the latter cast in these separate functions
+  // breaks the chain such that affected GCC versions no longer warn/error.
+  void* rawValuePtr() { return mValueData; }
 
-  private:
-    template <class, class, class> friend class HashTable;
+  static bool isLiveHash(HashNumber hash) { return hash > sRemovedKey; }
+
+  HashTableEntry(const HashTableEntry&) = delete;
+  void operator=(const HashTableEntry&) = delete;
+
+  NonConstT* valuePtr() { return reinterpret_cast<NonConstT*>(rawValuePtr()); }
 
-    // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
-    // -Werror compile error) to reinterpret_cast<> |valueData_| to |T*|, even
-    // through |void*|.  Placing the latter cast in these separate functions
-    // breaks the chain such that affected GCC versions no longer warn/error.
-    void* rawValuePtr() { return valueData_; }
+  void destroyStoredT()
+  {
+    NonConstT* ptr = valuePtr();
+    ptr->~T();
+    MOZ_MAKE_MEM_UNDEFINED(ptr, sizeof(*ptr));
+  }
 
-    static bool isLiveHash(HashNumber hash)
-    {
-        return hash > sRemovedKey;
+public:
+  HashTableEntry() = default;
+
+  ~HashTableEntry()
+  {
+    if (isLive()) {
+      destroyStoredT();
     }
 
-    HashTableEntry(const HashTableEntry&) = delete;
-    void operator=(const HashTableEntry&) = delete;
+    MOZ_MAKE_MEM_UNDEFINED(this, sizeof(*this));
+  }
+
+  void destroy()
+  {
+    MOZ_ASSERT(isLive());
+    destroyStoredT();
+  }
+
+  void swap(HashTableEntry* aOther)
+  {
+    if (this == aOther) {
+      return;
+    }
+    MOZ_ASSERT(isLive());
+    if (aOther->isLive()) {
+      Swap(*valuePtr(), *aOther->valuePtr());
+    } else {
+      *aOther->valuePtr() = std::move(*valuePtr());
+      destroy();
+    }
+    Swap(mKeyHash, aOther->mKeyHash);
+  }
 
-    NonConstT* valuePtr() { return reinterpret_cast<NonConstT*>(rawValuePtr()); }
+  T& get()
+  {
+    MOZ_ASSERT(isLive());
+    return *valuePtr();
+  }
+
+  NonConstT& getMutable()
+  {
+    MOZ_ASSERT(isLive());
+    return *valuePtr();
+  }
+
+  bool isFree() const { return mKeyHash == sFreeKey; }
+
+  void clearLive()
+  {
+    MOZ_ASSERT(isLive());
+    mKeyHash = sFreeKey;
+    destroyStoredT();
+  }
+
+  void clear()
+  {
+    if (isLive()) {
+      destroyStoredT();
+    }
+    MOZ_MAKE_MEM_UNDEFINED(this, sizeof(*this));
+    mKeyHash = sFreeKey;
+  }
+
+  bool isRemoved() const { return mKeyHash == sRemovedKey; }
 
-    void destroyStoredT() {
-        NonConstT* ptr = valuePtr();
-        ptr->~T();
-        MOZ_MAKE_MEM_UNDEFINED(ptr, sizeof(*ptr));
+  void removeLive()
+  {
+    MOZ_ASSERT(isLive());
+    mKeyHash = sRemovedKey;
+    destroyStoredT();
+  }
+
+  bool isLive() const { return isLiveHash(mKeyHash); }
+
+  void setCollision()
+  {
+    MOZ_ASSERT(isLive());
+    mKeyHash |= sCollisionBit;
+  }
+
+  void unsetCollision() { mKeyHash &= ~sCollisionBit; }
+
+  bool hasCollision() const { return mKeyHash & sCollisionBit; }
+
+  bool matchHash(HashNumber hn) { return (mKeyHash & ~sCollisionBit) == hn; }
+
+  HashNumber getKeyHash() const { return mKeyHash & ~sCollisionBit; }
+
+  template<typename... Args>
+  void setLive(HashNumber aHashNumber, Args&&... aArgs)
+  {
+    MOZ_ASSERT(!isLive());
+    mKeyHash = aHashNumber;
+    new (valuePtr()) T(std::forward<Args>(aArgs)...);
+    MOZ_ASSERT(isLive());
+  }
+};
+
+template<class T, class HashPolicy, class AllocPolicy>
+class HashTable : private AllocPolicy
+{
+  friend class mozilla::ReentrancyGuard;
+
+  using NonConstT = typename RemoveConst<T>::Type;
+  using Key = typename HashPolicy::KeyType;
+  using Lookup = typename HashPolicy::Lookup;
+
+public:
+  using Entry = HashTableEntry<T>;
+
+  // A nullable pointer to a hash table element. A Ptr |p| can be tested
+  // either explicitly |if (p.found()) p->...| or using boolean conversion
+  // |if (p) p->...|. Ptr objects must not be used after any mutating hash
+  // table operations unless |generation()| is tested.
+  class Ptr
+  {
+    friend class HashTable;
+
+    Entry* mEntry;
+#ifdef DEBUG
+    const HashTable* mTable;
+    Generation mGeneration;
+#endif
+
+  protected:
+    Ptr(Entry& aEntry, const HashTable& aTable)
+      : mEntry(&aEntry)
+#ifdef DEBUG
+      , mTable(&aTable)
+      , mGeneration(aTable.generation())
+#endif
+    {
     }
 
   public:
-    HashTableEntry() = default;
-
-    ~HashTableEntry() {
-        if (isLive())
-            destroyStoredT();
-
-        MOZ_MAKE_MEM_UNDEFINED(this, sizeof(*this));
-    }
-
-    void destroy() {
-        MOZ_ASSERT(isLive());
-        destroyStoredT();
-    }
-
-    void swap(HashTableEntry* other) {
-        if (this == other)
-            return;
-        MOZ_ASSERT(isLive());
-        if (other->isLive()) {
-            Swap(*valuePtr(), *other->valuePtr());
-        } else {
-            *other->valuePtr() = std::move(*valuePtr());
-            destroy();
-        }
-        Swap(keyHash, other->keyHash);
-    }
-
-    T& get() {
-        MOZ_ASSERT(isLive());
-        return *valuePtr();
-    }
-
-    NonConstT& getMutable() {
-        MOZ_ASSERT(isLive());
-        return *valuePtr();
-    }
-
-    bool isFree() const {
-        return keyHash == sFreeKey;
+    Ptr()
+      : mEntry(nullptr)
+#ifdef DEBUG
+      , mTable(nullptr)
+      , mGeneration(0)
+#endif
+    {
     }
 
-    void clearLive() {
-        MOZ_ASSERT(isLive());
-        keyHash = sFreeKey;
-        destroyStoredT();
-    }
-
-    void clear() {
-        if (isLive())
-            destroyStoredT();
+    bool isValid() const { return !!mEntry; }
 
-        MOZ_MAKE_MEM_UNDEFINED(this, sizeof(*this));
-        keyHash = sFreeKey;
-    }
-
-    bool isRemoved() const {
-        return keyHash == sRemovedKey;
-    }
-
-    void removeLive() {
-        MOZ_ASSERT(isLive());
-        keyHash = sRemovedKey;
-        destroyStoredT();
+    bool found() const
+    {
+      if (!isValid()) {
+        return false;
+      }
+#ifdef DEBUG
+      MOZ_ASSERT(mGeneration == mTable->generation());
+#endif
+      return mEntry->isLive();
     }
 
-    bool isLive() const {
-        return isLiveHash(keyHash);
-    }
-
-    void setCollision() {
-        MOZ_ASSERT(isLive());
-        keyHash |= sCollisionBit;
-    }
+    explicit operator bool() const { return found(); }
 
-    void unsetCollision() {
-        keyHash &= ~sCollisionBit;
-    }
-
-    bool hasCollision() const {
-        return keyHash & sCollisionBit;
-    }
-
-    bool matchHash(HashNumber hn) {
-        return (keyHash & ~sCollisionBit) == hn;
-    }
-
-    HashNumber getKeyHash() const {
-        return keyHash & ~sCollisionBit;
+    bool operator==(const Ptr& aRhs) const
+    {
+      MOZ_ASSERT(found() && aRhs.found());
+      return mEntry == aRhs.mEntry;
     }
 
-    template <typename... Args>
-    void setLive(HashNumber hn, Args&&... args)
+    bool operator!=(const Ptr& aRhs) const
     {
-        MOZ_ASSERT(!isLive());
-        keyHash = hn;
-        new (valuePtr()) T(std::forward<Args>(args)...);
-        MOZ_ASSERT(isLive());
+#ifdef DEBUG
+      MOZ_ASSERT(mGeneration == mTable->generation());
+#endif
+      return !(*this == aRhs);
     }
-};
-
-template <class T, class HashPolicy, class AllocPolicy>
-class HashTable : private AllocPolicy
-{
-    friend class mozilla::ReentrancyGuard;
-
-    typedef typename RemoveConst<T>::Type NonConstT;
-    typedef typename HashPolicy::KeyType Key;
-    typedef typename HashPolicy::Lookup Lookup;
-
-  public:
-    using Entry = HashTableEntry<T>;
-
-    // A nullable pointer to a hash table element. A Ptr |p| can be tested
-    // either explicitly |if (p.found()) p->...| or using boolean conversion
-    // |if (p) p->...|. Ptr objects must not be used after any mutating hash
-    // table operations unless |generation()| is tested.
-    class Ptr
-    {
-        friend class HashTable;
-
-        Entry* entry_;
-#ifdef DEBUG
-        const HashTable* table_;
-        Generation generation;
-#endif
 
-      protected:
-        Ptr(Entry& entry, const HashTable& tableArg)
-          : entry_(&entry)
+    T& operator*() const
+    {
 #ifdef DEBUG
-          , table_(&tableArg)
-          , generation(tableArg.generation())
-#endif
-        {}
-
-      public:
-        Ptr()
-          : entry_(nullptr)
-#ifdef DEBUG
-          , table_(nullptr)
-          , generation(0)
+      MOZ_ASSERT(found());
+      MOZ_ASSERT(mGeneration == mTable->generation());
 #endif
-        {}
-
-        bool isValid() const {
-            return !!entry_;
-        }
-
-        bool found() const {
-            if (!isValid())
-                return false;
-#ifdef DEBUG
-            MOZ_ASSERT(generation == table_->generation());
-#endif
-            return entry_->isLive();
-        }
-
-        explicit operator bool() const {
-            return found();
-        }
-
-        bool operator==(const Ptr& rhs) const {
-            MOZ_ASSERT(found() && rhs.found());
-            return entry_ == rhs.entry_;
-        }
+      return mEntry->get();
+    }
 
-        bool operator!=(const Ptr& rhs) const {
-#ifdef DEBUG
-            MOZ_ASSERT(generation == table_->generation());
-#endif
-            return !(*this == rhs);
-        }
-
-        T& operator*() const {
-#ifdef DEBUG
-            MOZ_ASSERT(found());
-            MOZ_ASSERT(generation == table_->generation());
-#endif
-            return entry_->get();
-        }
-
-        T* operator->() const {
-#ifdef DEBUG
-            MOZ_ASSERT(found());
-            MOZ_ASSERT(generation == table_->generation());
-#endif
-            return &entry_->get();
-        }
-    };
-
-    // A Ptr that can be used to add a key after a failed lookup.
-    class AddPtr : public Ptr
+    T* operator->() const
     {
-        friend class HashTable;
-        HashNumber keyHash;
-#ifdef DEBUG
-        uint64_t mutationCount;
-#endif
-
-        AddPtr(Entry& entry, const HashTable& tableArg, HashNumber hn)
-          : Ptr(entry, tableArg)
-          , keyHash(hn)
 #ifdef DEBUG
-          , mutationCount(tableArg.mutationCount)
+      MOZ_ASSERT(found());
+      MOZ_ASSERT(mGeneration == mTable->generation());
 #endif
-        {}
-
-      public:
-        AddPtr() : keyHash(0) {}
-    };
-
-    // A collection of hash table entries. The collection is enumerated by
-    // calling |front()| followed by |popFront()| as long as |!empty()|. As
-    // with Ptr/AddPtr, Range objects must not be used after any mutating hash
-    // table operation unless the |generation()| is tested.
-    class Range
-    {
-      protected:
-        friend class HashTable;
+      return &mEntry->get();
+    }
+  };
 
-        Range(const HashTable& tableArg, Entry* c, Entry* e)
-          : cur(c)
-          , end(e)
+  // A Ptr that can be used to add a key after a failed lookup.
+  class AddPtr : public Ptr
+  {
+    friend class HashTable;
+
+    HashNumber mKeyHash;
 #ifdef DEBUG
-          , table_(&tableArg)
-          , mutationCount(tableArg.mutationCount)
-          , generation(tableArg.generation())
-          , validEntry(true)
-#endif
-        {
-            while (cur < end && !cur->isLive())
-                ++cur;
-        }
-
-        Entry* cur;
-        Entry* end;
-#ifdef DEBUG
-        const HashTable* table_;
-        uint64_t mutationCount;
-        Generation generation;
-        bool validEntry;
+    uint64_t mMutationCount;
 #endif
 
-      public:
-        Range()
-          : cur(nullptr)
-          , end(nullptr)
-#ifdef DEBUG
-          , table_(nullptr)
-          , mutationCount(0)
-          , generation(0)
-          , validEntry(false)
-#endif
-        {}
-
-        bool empty() const {
-#ifdef DEBUG
-            MOZ_ASSERT(generation == table_->generation());
-            MOZ_ASSERT(mutationCount == table_->mutationCount);
-#endif
-            return cur == end;
-        }
-
-        T& front() const {
-            MOZ_ASSERT(!empty());
-#ifdef DEBUG
-            MOZ_ASSERT(validEntry);
-            MOZ_ASSERT(generation == table_->generation());
-            MOZ_ASSERT(mutationCount == table_->mutationCount);
-#endif
-            return cur->get();
-        }
-
-        void popFront() {
-            MOZ_ASSERT(!empty());
-#ifdef DEBUG
-            MOZ_ASSERT(generation == table_->generation());
-            MOZ_ASSERT(mutationCount == table_->mutationCount);
-#endif
-            while (++cur < end && !cur->isLive())
-                continue;
+    AddPtr(Entry& aEntry, const HashTable& aTable, HashNumber aHashNumber)
+      : Ptr(aEntry, aTable)
+      , mKeyHash(aHashNumber)
 #ifdef DEBUG
-            validEntry = true;
-#endif
-        }
-    };
-
-    // A Range whose lifetime delimits a mutating enumeration of a hash table.
-    // Since rehashing when elements were removed during enumeration would be
-    // bad, it is postponed until the Enum is destructed.  Since the Enum's
-    // destructor touches the hash table, the user must ensure that the hash
-    // table is still alive when the destructor runs.
-    class Enum : public Range
-    {
-        friend class HashTable;
-
-        HashTable& table_;
-        bool rekeyed;
-        bool removed;
-
-        // Enum is movable but not copyable.
-        Enum(const Enum&) = delete;
-        void operator=(const Enum&) = delete;
-
-      public:
-        template<class Map>
-        explicit Enum(Map& map)
-          : Range(map.all()), table_(map.impl), rekeyed(false), removed(false) {}
-
-        MOZ_IMPLICIT Enum(Enum&& other)
-          : Range(other), table_(other.table_), rekeyed(other.rekeyed), removed(other.removed)
-        {
-            other.rekeyed = false;
-            other.removed = false;
-        }
-
-        // Removes the |front()| element from the table, leaving |front()|
-        // invalid until the next call to |popFront()|. For example:
-        //
-        //   HashSet<int> s;
-        //   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
-        //     if (e.front() == 42)
-        //       e.removeFront();
-        void removeFront() {
-            table_.remove(*this->cur);
-            removed = true;
-#ifdef DEBUG
-            this->validEntry = false;
-            this->mutationCount = table_.mutationCount;
-#endif
-        }
-
-        NonConstT& mutableFront() {
-            MOZ_ASSERT(!this->empty());
-#ifdef DEBUG
-            MOZ_ASSERT(this->validEntry);
-            MOZ_ASSERT(this->generation == this->Range::table_->generation());
-            MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount);
-#endif
-            return this->cur->getMutable();
-        }
-
-        // Removes the |front()| element and re-inserts it into the table with
-        // a new key at the new Lookup position.  |front()| is invalid after
-        // this operation until the next call to |popFront()|.
-        void rekeyFront(const Lookup& l, const Key& k) {
-            MOZ_ASSERT(&k != &HashPolicy::getKey(this->cur->get()));
-            Ptr p(*this->cur, table_);
-            table_.rekeyWithoutRehash(p, l, k);
-            rekeyed = true;
-#ifdef DEBUG
-            this->validEntry = false;
-            this->mutationCount = table_.mutationCount;
+      , mMutationCount(aTable.mMutationCount)
 #endif
-        }
-
-        void rekeyFront(const Key& k) {
-            rekeyFront(k, k);
-        }
-
-        // Potentially rehashes the table.
-        ~Enum() {
-            if (rekeyed) {
-                table_.gen++;
-                table_.checkOverRemoved();
-            }
-
-            if (removed)
-                table_.compactIfUnderloaded();
-        }
-    };
-
-    // HashTable is movable
-    HashTable(HashTable&& rhs)
-      : AllocPolicy(rhs)
     {
-        PodAssign(this, &rhs);
-        rhs.table = nullptr;
-    }
-    void operator=(HashTable&& rhs) {
-        MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
-        if (table)
-            destroyTable(*this, table, capacity());
-        PodAssign(this, &rhs);
-        rhs.table = nullptr;
-    }
-
-  private:
-    // HashTable is not copyable or assignable
-    HashTable(const HashTable&) = delete;
-    void operator=(const HashTable&) = delete;
-
-  private:
-    static const size_t CAP_BITS = 30;
-
-  public:
-    uint64_t    gen:56;                 // entry storage generation number
-    uint64_t    hashShift:8;            // multiplicative hash shift
-    Entry*      table;                  // entry storage
-    uint32_t    entryCount;             // number of entries in table
-    uint32_t    removedCount;           // removed entry sentinels in table
-
-#ifdef DEBUG
-    uint64_t     mutationCount;
-    mutable bool mEntered;
-    // Note that some updates to these stats are not thread-safe. See the
-    // comment on the three-argument overloading of HashTable::lookup().
-    mutable struct Stats
-    {
-        uint32_t        searches;       // total number of table searches
-        uint32_t        steps;          // hash chain links traversed
-        uint32_t        hits;           // searches that found key
-        uint32_t        misses;         // searches that didn't find key
-        uint32_t        addOverRemoved; // adds that recycled a removed entry
-        uint32_t        removes;        // calls to remove
-        uint32_t        removeFrees;    // calls to remove that freed the entry
-        uint32_t        grows;          // table expansions
-        uint32_t        shrinks;        // table contractions
-        uint32_t        compresses;     // table compressions
-        uint32_t        rehashes;       // tombstone decontaminations
-    } stats;
-#   define METER(x) x
-#else
-#   define METER(x)
-#endif
-
-    // The default initial capacity is 32 (enough to hold 16 elements), but it
-    // can be as low as 4.
-    static const uint32_t sMinCapacity  = 4;
-    static const uint32_t sMaxInit      = 1u << (CAP_BITS - 1);
-    static const uint32_t sMaxCapacity  = 1u << CAP_BITS;
-
-    // Hash-table alpha is conceptually a fraction, but to avoid floating-point
-    // math we implement it as a ratio of integers.
-    static const uint8_t sAlphaDenominator = 4;
-    static const uint8_t sMinAlphaNumerator = 1; // min alpha: 1/4
-    static const uint8_t sMaxAlphaNumerator = 3; // max alpha: 3/4
-
-    static const HashNumber sFreeKey = Entry::sFreeKey;
-    static const HashNumber sRemovedKey = Entry::sRemovedKey;
-    static const HashNumber sCollisionBit = Entry::sCollisionBit;
-
-    void setTableSizeLog2(uint32_t sizeLog2)
-    {
-        hashShift = kHashNumberBits - sizeLog2;
-    }
-
-    static bool isLiveHash(HashNumber hash)
-    {
-        return Entry::isLiveHash(hash);
-    }
-
-    static HashNumber prepareHash(const Lookup& l)
-    {
-        HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l));
-
-        // Avoid reserved hash codes.
-        if (!isLiveHash(keyHash))
-            keyHash -= (sRemovedKey + 1);
-        return keyHash & ~sCollisionBit;
-    }
-
-    enum FailureBehavior { DontReportFailure = false, ReportFailure = true };
-
-    static Entry* createTable(AllocPolicy& alloc, uint32_t capacity,
-                              FailureBehavior reportFailure = ReportFailure)
-    {
-        Entry* table = reportFailure
-                       ? alloc.template pod_malloc<Entry>(capacity)
-                       : alloc.template maybe_pod_malloc<Entry>(capacity);
-        if (table) {
-            for (uint32_t i = 0; i < capacity; i++)
-                new (&table[i]) Entry();
-        }
-        return table;
-    }
-
-    static Entry* maybeCreateTable(AllocPolicy& alloc, uint32_t capacity)
-    {
-        Entry* table = alloc.template maybe_pod_malloc<Entry>(capacity);
-        if (table) {
-            for (uint32_t i = 0; i < capacity; i++)
-                new (&table[i]) Entry();
-        }
-        return table;
-    }
-
-    static void destroyTable(AllocPolicy& alloc, Entry* oldTable, uint32_t capacity)
-    {
-        Entry* end = oldTable + capacity;
-        for (Entry* e = oldTable; e < end; ++e)
-            e->~Entry();
-        alloc.free_(oldTable, capacity);
     }
 
   public:
-    explicit HashTable(AllocPolicy ap)
-      : AllocPolicy(ap)
-      , gen(0)
-      , hashShift(kHashNumberBits)
-      , table(nullptr)
-      , entryCount(0)
-      , removedCount(0)
-#ifdef DEBUG
-      , mutationCount(0)
-      , mEntered(false)
-#endif
-    {}
-
-    MOZ_MUST_USE bool init(uint32_t length)
+    AddPtr()
+      : mKeyHash(0)
     {
-        MOZ_ASSERT(!initialized());
-
-        // Reject all lengths whose initial computed capacity would exceed
-        // sMaxCapacity.  Round that maximum length down to the nearest power
-        // of two for speedier code.
-        if (MOZ_UNLIKELY(length > sMaxInit)) {
-            this->reportAllocOverflow();
-            return false;
-        }
-
-        static_assert((sMaxInit * sAlphaDenominator) / sAlphaDenominator == sMaxInit,
-                      "multiplication in numerator below could overflow");
-        static_assert(sMaxInit * sAlphaDenominator <= UINT32_MAX - sMaxAlphaNumerator,
-                      "numerator calculation below could potentially overflow");
-
-        // Compute the smallest capacity allowing |length| elements to be
-        // inserted without rehashing: ceil(length / max-alpha).  (Ceiling
-        // integral division: <http://stackoverflow.com/a/2745086>.)
-        uint32_t newCapacity =
-            (length * sAlphaDenominator + sMaxAlphaNumerator - 1) / sMaxAlphaNumerator;
-        if (newCapacity < sMinCapacity)
-            newCapacity = sMinCapacity;
-
-        // Round up capacity to next power-of-two.
-        uint32_t log2 = mozilla::CeilingLog2(newCapacity);
-        newCapacity = 1u << log2;
-
-        MOZ_ASSERT(newCapacity >= length);
-        MOZ_ASSERT(newCapacity <= sMaxCapacity);
-
-        table = createTable(*this, newCapacity);
-        if (!table)
-            return false;
-
-        setTableSizeLog2(log2);
-        METER(memset(&stats, 0, sizeof(stats)));
-        return true;
     }
-
-    bool initialized() const
-    {
-        return !!table;
-    }
-
-    ~HashTable()
-    {
-        if (table)
-            destroyTable(*this, table, capacity());
-    }
-
-  private:
-    HashNumber hash1(HashNumber hash0) const
-    {
-        return hash0 >> hashShift;
-    }
-
-    struct DoubleHash
-    {
-        HashNumber h2;
-        HashNumber sizeMask;
-    };
-
-    DoubleHash hash2(HashNumber curKeyHash) const
-    {
-        uint32_t sizeLog2 = kHashNumberBits - hashShift;
-        DoubleHash dh = {
-            ((curKeyHash << sizeLog2) >> hashShift) | 1,
-            (HashNumber(1) << sizeLog2) - 1
-        };
-        return dh;
-    }
-
-    static HashNumber applyDoubleHash(HashNumber h1, const DoubleHash& dh)
-    {
-        return (h1 - dh.h2) & dh.sizeMask;
-    }
-
-    bool overloaded()
-    {
-        static_assert(sMaxCapacity <= UINT32_MAX / sMaxAlphaNumerator,
-                      "multiplication below could overflow");
-        return entryCount + removedCount >=
-               capacity() * sMaxAlphaNumerator / sAlphaDenominator;
-    }
-
-    // Would the table be underloaded if it had the given capacity and entryCount?
-    static bool wouldBeUnderloaded(uint32_t capacity, uint32_t entryCount)
-    {
-        static_assert(sMaxCapacity <= UINT32_MAX / sMinAlphaNumerator,
-                      "multiplication below could overflow");
-        return capacity > sMinCapacity &&
-               entryCount <= capacity * sMinAlphaNumerator / sAlphaDenominator;
-    }
+  };
 
-    bool underloaded()
-    {
-        return wouldBeUnderloaded(capacity(), entryCount);
-    }
-
-    static MOZ_ALWAYS_INLINE bool match(Entry& e, const Lookup& l)
-    {
-        return HashPolicy::match(HashPolicy::getKey(e.get()), l);
-    }
-
-    // Warning: in order for readonlyThreadsafeLookup() to be safe this
-    // function must not modify the table in any way when |collisionBit| is 0.
-    // (The use of the METER() macro to increment stats violates this
-    // restriction but we will live with that for now because it's enabled so
-    // rarely.)
-    MOZ_ALWAYS_INLINE Entry&
-    lookup(const Lookup& l, HashNumber keyHash, uint32_t collisionBit) const
-    {
-        MOZ_ASSERT(isLiveHash(keyHash));
-        MOZ_ASSERT(!(keyHash & sCollisionBit));
-        MOZ_ASSERT(collisionBit == 0 || collisionBit == sCollisionBit);
-        MOZ_ASSERT(table);
-        METER(stats.searches++);
-
-        // Compute the primary hash address.
-        HashNumber h1 = hash1(keyHash);
-        Entry* entry = &table[h1];
-
-        // Miss: return space for a new entry.
-        if (entry->isFree()) {
-            METER(stats.misses++);
-            return *entry;
-        }
-
-        // Hit: return entry.
-        if (entry->matchHash(keyHash) && match(*entry, l)) {
-            METER(stats.hits++);
-            return *entry;
-        }
-
-        // Collision: double hash.
-        DoubleHash dh = hash2(keyHash);
-
-        // Save the first removed entry pointer so we can recycle later.
-        Entry* firstRemoved = nullptr;
-
-        while (true) {
-            if (MOZ_UNLIKELY(entry->isRemoved())) {
-                if (!firstRemoved)
-                    firstRemoved = entry;
-            } else {
-                if (collisionBit == sCollisionBit)
-                    entry->setCollision();
-            }
+  // A collection of hash table entries. The collection is enumerated by
+  // calling |front()| followed by |popFront()| as long as |!empty()|. As
+  // with Ptr/AddPtr, Range objects must not be used after any mutating hash
+  // table operation unless the |generation()| is tested.
+  class Range
+  {
+  protected:
+    friend class HashTable;
 
-            METER(stats.steps++);
-            h1 = applyDoubleHash(h1, dh);
-
-            entry = &table[h1];
-            if (entry->isFree()) {
-                METER(stats.misses++);
-                return firstRemoved ? *firstRemoved : *entry;
-            }
-
-            if (entry->matchHash(keyHash) && match(*entry, l)) {
-                METER(stats.hits++);
-                return *entry;
-            }
-        }
-    }
-
-    // This is a copy of lookup hardcoded to the assumptions:
-    //   1. the lookup is a lookupForAdd
-    //   2. the key, whose |keyHash| has been passed is not in the table,
-    //   3. no entries have been removed from the table.
-    // This specialized search avoids the need for recovering lookup values
-    // from entries, which allows more flexible Lookup/Key types.
-    Entry& findFreeEntry(HashNumber keyHash)
+    Range(const HashTable& aTable, Entry* aCur, Entry* aEnd)
+      : mCur(aCur)
+      , mEnd(aEnd)
+#ifdef DEBUG
+      , mTable(&aTable)
+      , mMutationCount(aTable.mMutationCount)
+      , mGeneration(aTable.generation())
+      , mValidEntry(true)
+#endif
     {
-        MOZ_ASSERT(!(keyHash & sCollisionBit));
-        MOZ_ASSERT(table);
-        METER(stats.searches++);
-
-        // We assume 'keyHash' has already been distributed.
-
-        // Compute the primary hash address.
-        HashNumber h1 = hash1(keyHash);
-        Entry* entry = &table[h1];
-
-        // Miss: return space for a new entry.
-        if (!entry->isLive()) {
-            METER(stats.misses++);
-            return *entry;
-        }
-
-        // Collision: double hash.
-        DoubleHash dh = hash2(keyHash);
-
-        while (true) {
-            MOZ_ASSERT(!entry->isRemoved());
-            entry->setCollision();
-
-            METER(stats.steps++);
-            h1 = applyDoubleHash(h1, dh);
-
-            entry = &table[h1];
-            if (!entry->isLive()) {
-                METER(stats.misses++);
-                return *entry;
-            }
-        }
+      while (mCur < mEnd && !mCur->isLive()) {
+        ++mCur;
+      }
     }
 
-    enum RebuildStatus { NotOverloaded, Rehashed, RehashFailed };
-
-    RebuildStatus changeTableSize(int deltaLog2, FailureBehavior reportFailure = ReportFailure)
-    {
-        // Look, but don't touch, until we succeed in getting new entry store.
-        Entry* oldTable = table;
-        uint32_t oldCap = capacity();
-        uint32_t newLog2 = kHashNumberBits - hashShift + deltaLog2;
-        uint32_t newCapacity = 1u << newLog2;
-        if (MOZ_UNLIKELY(newCapacity > sMaxCapacity)) {
-            if (reportFailure)
-                this->reportAllocOverflow();
-            return RehashFailed;
-        }
-
-        Entry* newTable = createTable(*this, newCapacity, reportFailure);
-        if (!newTable)
-            return RehashFailed;
-
-        // We can't fail from here on, so update table parameters.
-        setTableSizeLog2(newLog2);
-        removedCount = 0;
-        gen++;
-        table = newTable;
-
-        // Copy only live entries, leaving removed ones behind.
-        Entry* end = oldTable + oldCap;
-        for (Entry* src = oldTable; src < end; ++src) {
-            if (src->isLive()) {
-                HashNumber hn = src->getKeyHash();
-                findFreeEntry(hn).setLive(
-                    hn, std::move(const_cast<typename Entry::NonConstT&>(src->get())));
-            }
-
-            src->~Entry();
-        }
-
-        // All entries have been destroyed, no need to destroyTable.
-        this->free_(oldTable, oldCap);
-        return Rehashed;
-    }
-
-    bool shouldCompressTable()
-    {
-        // Compress if a quarter or more of all entries are removed.
-        return removedCount >= (capacity() >> 2);
-    }
-
-    RebuildStatus checkOverloaded(FailureBehavior reportFailure = ReportFailure)
-    {
-        if (!overloaded())
-            return NotOverloaded;
-
-        int deltaLog2;
-        if (shouldCompressTable()) {
-            METER(stats.compresses++);
-            deltaLog2 = 0;
-        } else {
-            METER(stats.grows++);
-            deltaLog2 = 1;
-        }
-
-        return changeTableSize(deltaLog2, reportFailure);
-    }
-
-    // Infallibly rehash the table if we are overloaded with removals.
-    void checkOverRemoved()
-    {
-        if (overloaded()) {
-            if (checkOverloaded(DontReportFailure) == RehashFailed)
-                rehashTableInPlace();
-        }
-    }
-
-    void remove(Entry& e)
-    {
-        MOZ_ASSERT(table);
-        METER(stats.removes++);
-
-        if (e.hasCollision()) {
-            e.removeLive();
-            removedCount++;
-        } else {
-            METER(stats.removeFrees++);
-            e.clearLive();
-        }
-        entryCount--;
+    Entry* mCur;
+    Entry* mEnd;
 #ifdef DEBUG
-        mutationCount++;
+    const HashTable* mTable;
+    uint64_t mMutationCount;
+    Generation mGeneration;
+    bool mValidEntry;
 #endif
-    }
-
-    void checkUnderloaded()
-    {
-        if (underloaded()) {
-            METER(stats.shrinks++);
-            (void) changeTableSize(-1, DontReportFailure);
-        }
-    }
-
-    // Resize the table down to the largest capacity which doesn't underload the
-    // table.  Since we call checkUnderloaded() on every remove, you only need
-    // to call this after a bulk removal of items done without calling remove().
-    void compactIfUnderloaded()
-    {
-        int32_t resizeLog2 = 0;
-        uint32_t newCapacity = capacity();
-        while (wouldBeUnderloaded(newCapacity, entryCount)) {
-            newCapacity = newCapacity >> 1;
-            resizeLog2--;
-        }
-
-        if (resizeLog2 != 0)
-            (void) changeTableSize(resizeLog2, DontReportFailure);
-    }
-
-    // This is identical to changeTableSize(currentSize), but without requiring
-    // a second table.  We do this by recycling the collision bits to tell us if
-    // the element is already inserted or still waiting to be inserted.  Since
-    // already-inserted elements win any conflicts, we get the same table as we
-    // would have gotten through random insertion order.
-    void rehashTableInPlace()
-    {
-        METER(stats.rehashes++);
-        removedCount = 0;
-        gen++;
-        for (size_t i = 0; i < capacity(); ++i)
-            table[i].unsetCollision();
-
-        for (size_t i = 0; i < capacity();) {
-            Entry* src = &table[i];
-
-            if (!src->isLive() || src->hasCollision()) {
-                ++i;
-                continue;
-            }
-
-            HashNumber keyHash = src->getKeyHash();
-            HashNumber h1 = hash1(keyHash);
-            DoubleHash dh = hash2(keyHash);
-            Entry* tgt = &table[h1];
-            while (true) {
-                if (!tgt->hasCollision()) {
-                    src->swap(tgt);
-                    tgt->setCollision();
-                    break;
-                }
-
-                h1 = applyDoubleHash(h1, dh);
-                tgt = &table[h1];
-            }
-        }
-
-        // TODO: this algorithm leaves collision bits on *all* elements, even if
-        // they are on no collision path. We have the option of setting the
-        // collision bits correctly on a subsequent pass or skipping the rehash
-        // unless we are totally filled with tombstones: benchmark to find out
-        // which approach is best.
-    }
-
-    // Note: |l| may be a reference to a piece of |u|, so this function
-    // must take care not to use |l| after moving |u|.
-    //
-    // Prefer to use putNewInfallible; this function does not check
-    // invariants.
-    template <typename... Args>
-    void putNewInfallibleInternal(const Lookup& l, Args&&... args)
-    {
-        MOZ_ASSERT(table);
-
-        HashNumber keyHash = prepareHash(l);
-        Entry* entry = &findFreeEntry(keyHash);
-        MOZ_ASSERT(entry);
-
-        if (entry->isRemoved()) {
-            METER(stats.addOverRemoved++);
-            removedCount--;
-            keyHash |= sCollisionBit;
-        }
-
-        entry->setLive(keyHash, std::forward<Args>(args)...);
-        entryCount++;
-#ifdef DEBUG
-        mutationCount++;
-#endif
-    }
 
   public:
-    void clear()
-    {
-        Entry* end = table + capacity();
-        for (Entry* e = table; e < end; ++e)
-            e->clear();
-
-        removedCount = 0;
-        entryCount = 0;
+    Range()
+      : mCur(nullptr)
+      , mEnd(nullptr)
 #ifdef DEBUG
-        mutationCount++;
+      , mTable(nullptr)
+      , mMutationCount(0)
+      , mGeneration(0)
+      , mValidEntry(false)
 #endif
-    }
-
-    void clearAndShrink()
-    {
-        clear();
-        compactIfUnderloaded();
-    }
-
-    void finish()
     {
-#ifdef DEBUG
-        MOZ_ASSERT(!mEntered);
-#endif
-        if (!table)
-            return;
-
-        destroyTable(*this, table, capacity());
-        table = nullptr;
-        gen++;
-        entryCount = 0;
-        removedCount = 0;
-#ifdef DEBUG
-        mutationCount++;
-#endif
-    }
-
-    Range all() const
-    {
-        MOZ_ASSERT(table);
-        return Range(*this, table, table + capacity());
     }
 
     bool empty() const
     {
-        MOZ_ASSERT(table);
-        return !entryCount;
+#ifdef DEBUG
+      MOZ_ASSERT(mGeneration == mTable->generation());
+      MOZ_ASSERT(mMutationCount == mTable->mMutationCount);
+#endif
+      return mCur == mEnd;
     }
 
-    uint32_t count() const
+    T& front() const
     {
-        MOZ_ASSERT(table);
-        return entryCount;
+      MOZ_ASSERT(!empty());
+#ifdef DEBUG
+      MOZ_ASSERT(mValidEntry);
+      MOZ_ASSERT(mGeneration == mTable->generation());
+      MOZ_ASSERT(mMutationCount == mTable->mMutationCount);
+#endif
+      return mCur->get();
     }
 
-    uint32_t capacity() const
+    void popFront()
     {
-        MOZ_ASSERT(table);
-        return 1u << (kHashNumberBits - hashShift);
+      MOZ_ASSERT(!empty());
+#ifdef DEBUG
+      MOZ_ASSERT(mGeneration == mTable->generation());
+      MOZ_ASSERT(mMutationCount == mTable->mMutationCount);
+#endif
+      while (++mCur < mEnd && !mCur->isLive()) {
+        continue;
+      }
+#ifdef DEBUG
+      mValidEntry = true;
+#endif
+    }
+  };
+
+  // A Range whose lifetime delimits a mutating enumeration of a hash table.
+  // Since rehashing when elements were removed during enumeration would be
+  // bad, it is postponed until the Enum is destructed.  Since the Enum's
+  // destructor touches the hash table, the user must ensure that the hash
+  // table is still alive when the destructor runs.
+  class Enum : public Range
+  {
+    friend class HashTable;
+
+    HashTable& mTable;
+    bool mRekeyed;
+    bool mRemoved;
+
+    // Enum is movable but not copyable.
+    Enum(const Enum&) = delete;
+    void operator=(const Enum&) = delete;
+
+  public:
+    template<class Map>
+    explicit Enum(Map& map)
+      : Range(map.all())
+      , mTable(map.mImpl)
+      , mRekeyed(false)
+      , mRemoved(false)
+    {
     }
 
-    Generation generation() const
+    MOZ_IMPLICIT Enum(Enum&& aOther)
+      : Range(aOther)
+      , mTable(aOther.mTable)
+      , mRekeyed(aOther.mRekeyed)
+      , mRemoved(aOther.mRemoved)
     {
-        MOZ_ASSERT(table);
-        return Generation(gen);
+      aOther.mRekeyed = false;
+      aOther.mRemoved = false;
     }
 
-    size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+    // Removes the |front()| element from the table, leaving |front()|
+    // invalid until the next call to |popFront()|. For example:
+    //
+    //   HashSet<int> s;
+    //   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront()) {
+    //     if (e.front() == 42) {
+    //       e.removeFront();
+    //     }
+    //   }
+    void removeFront()
     {
-        return mallocSizeOf(table);
-    }
-
-    size_t sizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
-    {
-        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+      mTable.remove(*this->mCur);
+      mRemoved = true;
+#ifdef DEBUG
+      this->mValidEntry = false;
+      this->mMutationCount = mTable.mMutationCount;
+#endif
     }
 
-    MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& l) const
+    NonConstT& mutableFront()
     {
-        ReentrancyGuard g(*this);
-        if (!HasHash<HashPolicy>(l))
-            return Ptr();
-        HashNumber keyHash = prepareHash(l);
-        return Ptr(lookup(l, keyHash, 0), *this);
+      MOZ_ASSERT(!this->empty());
+#ifdef DEBUG
+      MOZ_ASSERT(this->mValidEntry);
+      MOZ_ASSERT(this->mGeneration == this->Range::mTable->generation());
+      MOZ_ASSERT(this->mMutationCount == this->Range::mTable->mMutationCount);
+#endif
+      return this->mCur->getMutable();
     }
 
-    MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& l) const
+    // Removes the |front()| element and re-inserts it into the table with
+    // a new key at the new Lookup position.  |front()| is invalid after
+    // this operation until the next call to |popFront()|.
+    void rekeyFront(const Lookup& aLookup, const Key& aKey)
     {
-        if (!HasHash<HashPolicy>(l))
-            return Ptr();
-        HashNumber keyHash = prepareHash(l);
-        return Ptr(lookup(l, keyHash, 0), *this);
+      MOZ_ASSERT(&aKey != &HashPolicy::getKey(this->mCur->get()));
+      Ptr p(*this->mCur, mTable);
+      mTable.rekeyWithoutRehash(p, aLookup, aKey);
+      mRekeyed = true;
+#ifdef DEBUG
+      this->mValidEntry = false;
+      this->mMutationCount = mTable.mMutationCount;
+#endif
     }
 
-    MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& l) const
+    void rekeyFront(const Key& aKey) { rekeyFront(aKey, aKey); }
+
+    // Potentially rehashes the table.
+    ~Enum()
     {
-        ReentrancyGuard g(*this);
-        if (!EnsureHash<HashPolicy>(l))
-            return AddPtr();
-        HashNumber keyHash = prepareHash(l);
-        // Directly call the constructor in the return statement to avoid
-        // excess copying when building with Visual Studio 2017.
-        // See bug 1385181.
-        return AddPtr(lookup(l, keyHash, sCollisionBit), *this, keyHash);
+      if (mRekeyed) {
+        mTable.mGen++;
+        mTable.checkOverRemoved();
+      }
+
+      if (mRemoved) {
+        mTable.compactIfUnderloaded();
+      }
     }
+  };
+
+  // HashTable is movable
+  HashTable(HashTable&& aRhs)
+    : AllocPolicy(aRhs)
+  {
+    PodAssign(this, &aRhs);
+    aRhs.mTable = nullptr;
+  }
+  void operator=(HashTable&& aRhs)
+  {
+    MOZ_ASSERT(this != &aRhs, "self-move assignment is prohibited");
+    if (mTable) {
+      destroyTable(*this, mTable, capacity());
+    }
+    PodAssign(this, &aRhs);
+    aRhs.mTable = nullptr;
+  }
 
-    template <typename... Args>
-    MOZ_MUST_USE bool add(AddPtr& p, Args&&... args)
-    {
-        ReentrancyGuard g(*this);
-        MOZ_ASSERT(table);
-        MOZ_ASSERT_IF(p.isValid(), p.table_ == this);
-        MOZ_ASSERT(!p.found());
-        MOZ_ASSERT(!(p.keyHash & sCollisionBit));
+private:
+  // HashTable is not copyable or assignable
+  HashTable(const HashTable&) = delete;
+  void operator=(const HashTable&) = delete;
+
+  static const size_t CAP_BITS = 30;
+
+public:
+  uint64_t mGen : 56;      // entry storage generation number
+  uint64_t mHashShift : 8; // multiplicative hash shift
+  Entry* mTable;           // entry storage
+  uint32_t mEntryCount;    // number of entries in mTable
+  uint32_t mRemovedCount;  // removed entry sentinels in mTable
+
+#ifdef DEBUG
+  uint64_t mMutationCount;
+  mutable bool mEntered;
 
-        // Check for error from ensureHash() here.
-        if (!p.isValid())
-            return false;
-
-        MOZ_ASSERT(p.generation == generation());
-#ifdef DEBUG
-        MOZ_ASSERT(p.mutationCount == mutationCount);
+  // Note that some updates to these stats are not thread-safe. See the
+  // comment on the three-argument overloading of HashTable::lookup().
+  mutable struct Stats
+  {
+    uint32_t mSearches;       // total number of table searches
+    uint32_t mSteps;          // hash chain links traversed
+    uint32_t mHits;           // searches that found key
+    uint32_t mMisses;         // searches that didn't find key
+    uint32_t mAddOverRemoved; // adds that recycled a removed entry
+    uint32_t mRemoves;        // calls to remove
+    uint32_t mRemoveFrees;    // calls to remove that freed the entry
+    uint32_t mGrows;          // table expansions
+    uint32_t mShrinks;        // table contractions
+    uint32_t mCompresses;     // table compressions
+    uint32_t mRehashes;       // tombstone decontaminations
+  } mStats;
+#define METER(x) x
+#else
+#define METER(x)
 #endif
 
-        // Changing an entry from removed to live does not affect whether we
-        // are overloaded and can be handled separately.
-        if (p.entry_->isRemoved()) {
-            if (!this->checkSimulatedOOM())
-                return false;
-            METER(stats.addOverRemoved++);
-            removedCount--;
-            p.keyHash |= sCollisionBit;
-        } else {
-            // Preserve the validity of |p.entry_|.
-            RebuildStatus status = checkOverloaded();
-            if (status == RehashFailed)
-                return false;
-            if (status == NotOverloaded && !this->checkSimulatedOOM())
-                return false;
-            if (status == Rehashed)
-                p.entry_ = &findFreeEntry(p.keyHash);
-        }
+  // The default initial capacity is 32 (enough to hold 16 elements), but it
+  // can be as low as 4.
+  static const uint32_t sMinCapacity = 4;
+  static const uint32_t sMaxInit = 1u << (CAP_BITS - 1);
+  static const uint32_t sMaxCapacity = 1u << CAP_BITS;
+
+  // Hash-table alpha is conceptually a fraction, but to avoid floating-point
+  // math we implement it as a ratio of integers.
+  static const uint8_t sAlphaDenominator = 4;
+  static const uint8_t sMinAlphaNumerator = 1; // min alpha: 1/4
+  static const uint8_t sMaxAlphaNumerator = 3; // max alpha: 3/4
+
+  static const HashNumber sFreeKey = Entry::sFreeKey;
+  static const HashNumber sRemovedKey = Entry::sRemovedKey;
+  static const HashNumber sCollisionBit = Entry::sCollisionBit;
+
+  void setTableSizeLog2(uint32_t aSizeLog2)
+  {
+    mHashShift = kHashNumberBits - aSizeLog2;
+  }
+
+  static bool isLiveHash(HashNumber aHash) { return Entry::isLiveHash(aHash); }
+
+  static HashNumber prepareHash(const Lookup& aLookup)
+  {
+    HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(aLookup));
+
+    // Avoid reserved hash codes.
+    if (!isLiveHash(keyHash)) {
+      keyHash -= (sRemovedKey + 1);
+    }
+    return keyHash & ~sCollisionBit;
+  }
+
+  enum FailureBehavior
+  {
+    DontReportFailure = false,
+    ReportFailure = true
+  };
+
+  static Entry* createTable(AllocPolicy& aAllocPolicy,
+                            uint32_t aCapacity,
+                            FailureBehavior aReportFailure = ReportFailure)
+  {
+    Entry* table = aReportFailure
+                     ? aAllocPolicy.template pod_malloc<Entry>(aCapacity)
+                     : aAllocPolicy.template maybe_pod_malloc<Entry>(aCapacity);
+    if (table) {
+      for (uint32_t i = 0; i < aCapacity; i++) {
+        new (&table[i]) Entry();
+      }
+    }
+    return table;
+  }
 
-        p.entry_->setLive(p.keyHash, std::forward<Args>(args)...);
-        entryCount++;
+  static Entry* maybeCreateTable(AllocPolicy& aAllocPolicy, uint32_t aCapacity)
+  {
+    Entry* table = aAllocPolicy.template maybe_pod_malloc<Entry>(aCapacity);
+    if (table) {
+      for (uint32_t i = 0; i < aCapacity; i++) {
+        new (&table[i]) Entry();
+      }
+    }
+    return table;
+  }
+
+  static void destroyTable(AllocPolicy& aAllocPolicy,
+                           Entry* aOldTable,
+                           uint32_t aCapacity)
+  {
+    Entry* end = aOldTable + aCapacity;
+    for (Entry* e = aOldTable; e < end; ++e) {
+      e->~Entry();
+    }
+    aAllocPolicy.free_(aOldTable, aCapacity);
+  }
+
+public:
+  explicit HashTable(AllocPolicy aAllocPolicy)
+    : AllocPolicy(aAllocPolicy)
+    , mGen(0)
+    , mHashShift(kHashNumberBits)
+    , mTable(nullptr)
+    , mEntryCount(0)
+    , mRemovedCount(0)
 #ifdef DEBUG
-        mutationCount++;
-        p.generation = generation();
-        p.mutationCount = mutationCount;
+    , mMutationCount(0)
+    , mEntered(false)
 #endif
-        return true;
+  {
+  }
+
+  MOZ_MUST_USE bool init(uint32_t aLen)
+  {
+    MOZ_ASSERT(!initialized());
+
+    // Reject all lengths whose initial computed capacity would exceed
+    // sMaxCapacity. Round that maximum aLen down to the nearest power of two
+    // for speedier code.
+    if (MOZ_UNLIKELY(aLen > sMaxInit)) {
+      this->reportAllocOverflow();
+      return false;
+    }
+
+    static_assert((sMaxInit * sAlphaDenominator) / sAlphaDenominator ==
+                    sMaxInit,
+                  "multiplication in numerator below could overflow");
+    static_assert(sMaxInit * sAlphaDenominator <=
+                    UINT32_MAX - sMaxAlphaNumerator,
+                  "numerator calculation below could potentially overflow");
+
+    // Compute the smallest capacity allowing |aLen| elements to be
+    // inserted without rehashing: ceil(aLen / max-alpha).  (Ceiling
+    // integral division: <http://stackoverflow.com/a/2745086>.)
+    uint32_t newCapacity =
+      (aLen * sAlphaDenominator + sMaxAlphaNumerator - 1) / sMaxAlphaNumerator;
+    if (newCapacity < sMinCapacity) {
+      newCapacity = sMinCapacity;
     }
 
-    // Note: |l| may be a reference to a piece of |u|, so this function
-    // must take care not to use |l| after moving |u|.
-    template <typename... Args>
-    void putNewInfallible(const Lookup& l, Args&&... args)
-    {
-        MOZ_ASSERT(!lookup(l).found());
-        ReentrancyGuard g(*this);
-        putNewInfallibleInternal(l, std::forward<Args>(args)...);
+    // Round up capacity to next power-of-two.
+    uint32_t log2 = mozilla::CeilingLog2(newCapacity);
+    newCapacity = 1u << log2;
+
+    MOZ_ASSERT(newCapacity >= aLen);
+    MOZ_ASSERT(newCapacity <= sMaxCapacity);
+
+    mTable = createTable(*this, newCapacity);
+    if (!mTable) {
+      return false;
+    }
+    setTableSizeLog2(log2);
+    METER(memset(&mStats, 0, sizeof(mStats)));
+    return true;
+  }
+
+  bool initialized() const { return !!mTable; }
+
+  ~HashTable()
+  {
+    if (mTable) {
+      destroyTable(*this, mTable, capacity());
+    }
+  }
+
+private:
+  HashNumber hash1(HashNumber aHash0) const { return aHash0 >> mHashShift; }
+
+  struct DoubleHash
+  {
+    HashNumber mHash2;
+    HashNumber mSizeMask;
+  };
+
+  DoubleHash hash2(HashNumber aCurKeyHash) const
+  {
+    uint32_t sizeLog2 = kHashNumberBits - mHashShift;
+    DoubleHash dh = { ((aCurKeyHash << sizeLog2) >> mHashShift) | 1,
+                      (HashNumber(1) << sizeLog2) - 1 };
+    return dh;
+  }
+
+  static HashNumber applyDoubleHash(HashNumber aHash1,
+                                    const DoubleHash& aDoubleHash)
+  {
+    return (aHash1 - aDoubleHash.mHash2) & aDoubleHash.mSizeMask;
+  }
+
+  bool overloaded()
+  {
+    static_assert(sMaxCapacity <= UINT32_MAX / sMaxAlphaNumerator,
+                  "multiplication below could overflow");
+    return mEntryCount + mRemovedCount >=
+           capacity() * sMaxAlphaNumerator / sAlphaDenominator;
+  }
+
+  // Would the table be underloaded if it had the given capacity and entryCount?
+  static bool wouldBeUnderloaded(uint32_t aCapacity, uint32_t aEntryCount)
+  {
+    static_assert(sMaxCapacity <= UINT32_MAX / sMinAlphaNumerator,
+                  "multiplication below could overflow");
+    return aCapacity > sMinCapacity &&
+           aEntryCount <= aCapacity * sMinAlphaNumerator / sAlphaDenominator;
+  }
+
+  bool underloaded() { return wouldBeUnderloaded(capacity(), mEntryCount); }
+
+  static MOZ_ALWAYS_INLINE bool match(Entry& aEntry, const Lookup& aLookup)
+  {
+    return HashPolicy::match(HashPolicy::getKey(aEntry.get()), aLookup);
+  }
+
+  // Warning: in order for readonlyThreadsafeLookup() to be safe this
+  // function must not modify the table in any way when |collisionBit| is 0.
+  // (The use of the METER() macro to increment stats violates this
+  // restriction but we will live with that for now because it's enabled so
+  // rarely.)
+  MOZ_ALWAYS_INLINE Entry& lookup(const Lookup& aLookup,
+                                  HashNumber aKeyHash,
+                                  uint32_t aCollisionBit) const
+  {
+    MOZ_ASSERT(isLiveHash(aKeyHash));
+    MOZ_ASSERT(!(aKeyHash & sCollisionBit));
+    MOZ_ASSERT(aCollisionBit == 0 || aCollisionBit == sCollisionBit);
+    MOZ_ASSERT(mTable);
+    METER(mStats.mSearches++);
+
+    // Compute the primary hash address.
+    HashNumber h1 = hash1(aKeyHash);
+    Entry* entry = &mTable[h1];
+
+    // Miss: return space for a new entry.
+    if (entry->isFree()) {
+      METER(mStats.mMisses++);
+      return *entry;
+    }
+
+    // Hit: return entry.
+    if (entry->matchHash(aKeyHash) && match(*entry, aLookup)) {
+      METER(mStats.mHits++);
+      return *entry;
+    }
+
+    // Collision: double hash.
+    DoubleHash dh = hash2(aKeyHash);
+
+    // Save the first removed entry pointer so we can recycle later.
+    Entry* firstRemoved = nullptr;
+
+    while (true) {
+      if (MOZ_UNLIKELY(entry->isRemoved())) {
+        if (!firstRemoved) {
+          firstRemoved = entry;
+        }
+      } else {
+        if (aCollisionBit == sCollisionBit) {
+          entry->setCollision();
+        }
+      }
+
+      METER(mStats.mSteps++);
+      h1 = applyDoubleHash(h1, dh);
+
+      entry = &mTable[h1];
+      if (entry->isFree()) {
+        METER(mStats.mMisses++);
+        return firstRemoved ? *firstRemoved : *entry;
+      }
+
+      if (entry->matchHash(aKeyHash) && match(*entry, aLookup)) {
+        METER(mStats.mHits++);
+        return *entry;
+      }
+    }
+  }
+
+  // This is a copy of lookup hardcoded to the assumptions:
+  //   1. the lookup is a lookupForAdd
+  //   2. the key, whose |keyHash| has been passed is not in the table,
+  //   3. no entries have been removed from the table.
+  // This specialized search avoids the need for recovering lookup values
+  // from entries, which allows more flexible Lookup/Key types.
+  Entry& findFreeEntry(HashNumber aKeyHash)
+  {
+    MOZ_ASSERT(!(aKeyHash & sCollisionBit));
+    MOZ_ASSERT(mTable);
+    METER(mStats.mSearches++);
+
+    // We assume 'aKeyHash' has already been distributed.
+
+    // Compute the primary hash address.
+    HashNumber h1 = hash1(aKeyHash);
+    Entry* entry = &mTable[h1];
+
+    // Miss: return space for a new entry.
+    if (!entry->isLive()) {
+      METER(mStats.mMisses++);
+      return *entry;
+    }
+
+    // Collision: double hash.
+    DoubleHash dh = hash2(aKeyHash);
+
+    while (true) {
+      MOZ_ASSERT(!entry->isRemoved());
+      entry->setCollision();
+
+      METER(mStats.mSteps++);
+      h1 = applyDoubleHash(h1, dh);
+
+      entry = &mTable[h1];
+      if (!entry->isLive()) {
+        METER(mStats.mMisses++);
+        return *entry;
+      }
+    }
+  }
+
+  enum RebuildStatus
+  {
+    NotOverloaded,
+    Rehashed,
+    RehashFailed
+  };
+
+  RebuildStatus changeTableSize(int aDeltaLog2,
+                                FailureBehavior aReportFailure = ReportFailure)
+  {
+    // Look, but don't touch, until we succeed in getting new entry store.
+    Entry* oldTable = mTable;
+    uint32_t oldCap = capacity();
+    uint32_t newLog2 = kHashNumberBits - mHashShift + aDeltaLog2;
+    uint32_t newCapacity = 1u << newLog2;
+    if (MOZ_UNLIKELY(newCapacity > sMaxCapacity)) {
+      if (aReportFailure) {
+        this->reportAllocOverflow();
+      }
+      return RehashFailed;
+    }
+
+    Entry* newTable = createTable(*this, newCapacity, aReportFailure);
+    if (!newTable) {
+      return RehashFailed;
+    }
+
+    // We can't fail from here on, so update table parameters.
+    setTableSizeLog2(newLog2);
+    mRemovedCount = 0;
+    mGen++;
+    mTable = newTable;
+
+    // Copy only live entries, leaving removed ones behind.
+    Entry* end = oldTable + oldCap;
+    for (Entry* src = oldTable; src < end; ++src) {
+      if (src->isLive()) {
+        HashNumber hn = src->getKeyHash();
+        findFreeEntry(hn).setLive(
+          hn, std::move(const_cast<typename Entry::NonConstT&>(src->get())));
+      }
+
+      src->~Entry();
+    }
+
+    // All entries have been destroyed, no need to destroyTable.
+    this->free_(oldTable, oldCap);
+    return Rehashed;
+  }
+
+  bool shouldCompressTable()
+  {
+    // Compress if a quarter or more of all entries are removed.
+    return mRemovedCount >= (capacity() >> 2);
+  }
+
+  RebuildStatus checkOverloaded(FailureBehavior aReportFailure = ReportFailure)
+  {
+    if (!overloaded()) {
+      return NotOverloaded;
+    }
+
+    int deltaLog2;
+    if (shouldCompressTable()) {
+      METER(mStats.mCompresses++);
+      deltaLog2 = 0;
+    } else {
+      METER(mStats.mGrows++);
+      deltaLog2 = 1;
     }
 
-    // Note: |l| may be alias arguments in |args|, so this function must take
-    // care not to use |l| after moving |args|.
-    template <typename... Args>
-    MOZ_MUST_USE bool putNew(const Lookup& l, Args&&... args)
-    {
-        if (!this->checkSimulatedOOM())
-            return false;
+    return changeTableSize(deltaLog2, aReportFailure);
+  }
+
+  // Infallibly rehash the table if we are overloaded with removals.
+  void checkOverRemoved()
+  {
+    if (overloaded()) {
+      if (checkOverloaded(DontReportFailure) == RehashFailed) {
+        rehashTableInPlace();
+      }
+    }
+  }
+
+  void remove(Entry& aEntry)
+  {
+    MOZ_ASSERT(mTable);
+    METER(mStats.mRemoves++);
+
+    if (aEntry.hasCollision()) {
+      aEntry.removeLive();
+      mRemovedCount++;
+    } else {
+      METER(mStats.mRemoveFrees++);
+      aEntry.clearLive();
+    }
+    mEntryCount--;
+#ifdef DEBUG
+    mMutationCount++;
+#endif
+  }
+
+  void checkUnderloaded()
+  {
+    if (underloaded()) {
+      METER(mStats.mShrinks++);
+      (void)changeTableSize(-1, DontReportFailure);
+    }
+  }
+
+  // Resize the table down to the largest capacity which doesn't underload the
+  // table.  Since we call checkUnderloaded() on every remove, you only need
+  // to call this after a bulk removal of items done without calling remove().
+  void compactIfUnderloaded()
+  {
+    int32_t resizeLog2 = 0;
+    uint32_t newCapacity = capacity();
+    while (wouldBeUnderloaded(newCapacity, mEntryCount)) {
+      newCapacity = newCapacity >> 1;
+      resizeLog2--;
+    }
+
+    if (resizeLog2 != 0) {
+      (void)changeTableSize(resizeLog2, DontReportFailure);
+    }
+  }
+
+  // This is identical to changeTableSize(currentSize), but without requiring
+  // a second table.  We do this by recycling the collision bits to tell us if
+  // the element is already inserted or still waiting to be inserted.  Since
+  // already-inserted elements win any conflicts, we get the same table as we
+  // would have gotten through random insertion order.
+  void rehashTableInPlace()
+  {
+    METER(mStats.mRehashes++);
+    mRemovedCount = 0;
+    mGen++;
+    for (size_t i = 0; i < capacity(); ++i) {
+      mTable[i].unsetCollision();
+    }
+    for (size_t i = 0; i < capacity();) {
+      Entry* src = &mTable[i];
+
+      if (!src->isLive() || src->hasCollision()) {
+        ++i;
+        continue;
+      }
 
-        if (!EnsureHash<HashPolicy>(l))
-            return false;
+      HashNumber keyHash = src->getKeyHash();
+      HashNumber h1 = hash1(keyHash);
+      DoubleHash dh = hash2(keyHash);
+      Entry* tgt = &mTable[h1];
+      while (true) {
+        if (!tgt->hasCollision()) {
+          src->swap(tgt);
+          tgt->setCollision();
+          break;
+        }
+
+        h1 = applyDoubleHash(h1, dh);
+        tgt = &mTable[h1];
+      }
+    }
+
+    // TODO: this algorithm leaves collision bits on *all* elements, even if
+    // they are on no collision path. We have the option of setting the
+    // collision bits correctly on a subsequent pass or skipping the rehash
+    // unless we are totally filled with tombstones: benchmark to find out
+    // which approach is best.
+  }
+
+  // Note: |aLookup| may be a reference to a piece of |u|, so this function
+  // must take care not to use |aLookup| after moving |u|.
+  //
+  // Prefer to use putNewInfallible; this function does not check
+  // invariants.
+  template<typename... Args>
+  void putNewInfallibleInternal(const Lookup& aLookup, Args&&... aArgs)
+  {
+    MOZ_ASSERT(mTable);
+
+    HashNumber keyHash = prepareHash(aLookup);
+    Entry* entry = &findFreeEntry(keyHash);
+    MOZ_ASSERT(entry);
 
-        if (checkOverloaded() == RehashFailed)
-            return false;
+    if (entry->isRemoved()) {
+      METER(mStats.mAddOverRemoved++);
+      mRemovedCount--;
+      keyHash |= sCollisionBit;
+    }
+
+    entry->setLive(keyHash, std::forward<Args>(aArgs)...);
+    mEntryCount++;
+#ifdef DEBUG
+    mMutationCount++;
+#endif
+  }
 
-        putNewInfallible(l, std::forward<Args>(args)...);
-        return true;
+public:
+  void clear()
+  {
+    Entry* end = mTable + capacity();
+    for (Entry* e = mTable; e < end; ++e) {
+      e->clear();
+    }
+    mRemovedCount = 0;
+    mEntryCount = 0;
+#ifdef DEBUG
+    mMutationCount++;
+#endif
+  }
+
+  void clearAndShrink()
+  {
+    clear();
+    compactIfUnderloaded();
+  }
+
+  void finish()
+  {
+#ifdef DEBUG
+    MOZ_ASSERT(!mEntered);
+#endif
+    if (!mTable) {
+      return;
     }
 
-    // Note: |l| may be a reference to a piece of |u|, so this function
-    // must take care not to use |l| after moving |u|.
-    template <typename... Args>
-    MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, const Lookup& l, Args&&... args)
-    {
-        // Check for error from ensureHash() here.
-        if (!p.isValid())
-            return false;
-
+    destroyTable(*this, mTable, capacity());
+    mTable = nullptr;
+    mGen++;
+    mEntryCount = 0;
+    mRemovedCount = 0;
 #ifdef DEBUG
-        p.generation = generation();
-        p.mutationCount = mutationCount;
+    mMutationCount++;
 #endif
-        {
-            ReentrancyGuard g(*this);
-            MOZ_ASSERT(prepareHash(l) == p.keyHash); // l has not been destroyed
-            p.entry_ = &lookup(l, p.keyHash, sCollisionBit);
-        }
-        return p.found() || add(p, std::forward<Args>(args)...);
+  }
+
+  Range all() const
+  {
+    MOZ_ASSERT(mTable);
+    return Range(*this, mTable, mTable + capacity());
+  }
+
+  bool empty() const
+  {
+    MOZ_ASSERT(mTable);
+    return !mEntryCount;
+  }
+
+  uint32_t count() const
+  {
+    MOZ_ASSERT(mTable);
+    return mEntryCount;
+  }
+
+  uint32_t capacity() const
+  {
+    MOZ_ASSERT(mTable);
+    return 1u << (kHashNumberBits - mHashShift);
+  }
+
+  Generation generation() const
+  {
+    MOZ_ASSERT(mTable);
+    return Generation(mGen);
+  }
+
+  size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(mTable);
+  }
+
+  size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  MOZ_ALWAYS_INLINE Ptr lookup(const Lookup& aLookup) const
+  {
+    ReentrancyGuard g(*this);
+    if (!HasHash<HashPolicy>(aLookup)) {
+      return Ptr();
+    }
+    HashNumber keyHash = prepareHash(aLookup);
+    return Ptr(lookup(aLookup, keyHash, 0), *this);
+  }
+
+  MOZ_ALWAYS_INLINE Ptr readonlyThreadsafeLookup(const Lookup& aLookup) const
+  {
+    if (!HasHash<HashPolicy>(aLookup)) {
+      return Ptr();
+    }
+    HashNumber keyHash = prepareHash(aLookup);
+    return Ptr(lookup(aLookup, keyHash, 0), *this);
+  }
+
+  MOZ_ALWAYS_INLINE AddPtr lookupForAdd(const Lookup& aLookup) const
+  {
+    ReentrancyGuard g(*this);
+    if (!EnsureHash<HashPolicy>(aLookup)) {
+      return AddPtr();
+    }
+    HashNumber keyHash = prepareHash(aLookup);
+    // Directly call the constructor in the return statement to avoid
+    // excess copying when building with Visual Studio 2017.
+    // See bug 1385181.
+    return AddPtr(lookup(aLookup, keyHash, sCollisionBit), *this, keyHash);
+  }
+
+  template<typename... Args>
+  MOZ_MUST_USE bool add(AddPtr& aPtr, Args&&... aArgs)
+  {
+    ReentrancyGuard g(*this);
+    MOZ_ASSERT(mTable);
+    MOZ_ASSERT_IF(aPtr.isValid(), aPtr.mTable == this);
+    MOZ_ASSERT(!aPtr.found());
+    MOZ_ASSERT(!(aPtr.mKeyHash & sCollisionBit));
+
+    // Check for error from ensureHash() here.
+    if (!aPtr.isValid()) {
+      return false;
     }
 
-    void remove(Ptr p)
-    {
-        MOZ_ASSERT(table);
-        ReentrancyGuard g(*this);
-        MOZ_ASSERT(p.found());
-        MOZ_ASSERT(p.generation == generation());
-        remove(*p.entry_);
-        checkUnderloaded();
+    MOZ_ASSERT(aPtr.mGeneration == generation());
+#ifdef DEBUG
+    MOZ_ASSERT(aPtr.mMutationCount == mMutationCount);
+#endif
+
+    // Changing an entry from removed to live does not affect whether we
+    // are overloaded and can be handled separately.
+    if (aPtr.mEntry->isRemoved()) {
+      if (!this->checkSimulatedOOM()) {
+        return false;
+      }
+      METER(mStats.mAddOverRemoved++);
+      mRemovedCount--;
+      aPtr.mKeyHash |= sCollisionBit;
+    } else {
+      // Preserve the validity of |aPtr.mEntry|.
+      RebuildStatus status = checkOverloaded();
+      if (status == RehashFailed) {
+        return false;
+      }
+      if (status == NotOverloaded && !this->checkSimulatedOOM()) {
+        return false;
+      }
+      if (status == Rehashed) {
+        aPtr.mEntry = &findFreeEntry(aPtr.mKeyHash);
+      }
     }
 
-    void rekeyWithoutRehash(Ptr p, const Lookup& l, const Key& k)
+    aPtr.mEntry->setLive(aPtr.mKeyHash, std::forward<Args>(aArgs)...);
+    mEntryCount++;
+#ifdef DEBUG
+    mMutationCount++;
+    aPtr.mGeneration = generation();
+    aPtr.mMutationCount = mMutationCount;
+#endif
+    return true;
+  }
+
+  // Note: |aLookup| may be a reference to a piece of |u|, so this function
+  // must take care not to use |aLookup| after moving |u|.
+  template<typename... Args>
+  void putNewInfallible(const Lookup& aLookup, Args&&... aArgs)
+  {
+    MOZ_ASSERT(!lookup(aLookup).found());
+    ReentrancyGuard g(*this);
+    putNewInfallibleInternal(aLookup, std::forward<Args>(aArgs)...);
+  }
+
+  // Note: |aLookup| may be alias arguments in |aArgs|, so this function must
+  // take care not to use |aLookup| after moving |aArgs|.
+  template<typename... Args>
+  MOZ_MUST_USE bool putNew(const Lookup& aLookup, Args&&... aArgs)
+  {
+    if (!this->checkSimulatedOOM()) {
+      return false;
+    }
+    if (!EnsureHash<HashPolicy>(aLookup)) {
+      return false;
+    }
+    if (checkOverloaded() == RehashFailed) {
+      return false;
+    }
+    putNewInfallible(aLookup, std::forward<Args>(aArgs)...);
+    return true;
+  }
+
+  // Note: |aLookup| may be a reference to a piece of |u|, so this function
+  // must take care not to use |aLookup| after moving |u|.
+  template<typename... Args>
+  MOZ_MUST_USE bool relookupOrAdd(AddPtr& aPtr,
+                                  const Lookup& aLookup,
+                                  Args&&... aArgs)
+  {
+    // Check for error from ensureHash() here.
+    if (!aPtr.isValid()) {
+      return false;
+    }
+#ifdef DEBUG
+    aPtr.mGeneration = generation();
+    aPtr.mMutationCount = mMutationCount;
+#endif
     {
-        MOZ_ASSERT(table);
-        ReentrancyGuard g(*this);
-        MOZ_ASSERT(p.found());
-        MOZ_ASSERT(p.generation == generation());
-        typename HashTableEntry<T>::NonConstT t(std::move(*p));
-        HashPolicy::setKey(t, const_cast<Key&>(k));
-        remove(*p.entry_);
-        putNewInfallibleInternal(l, std::move(t));
+      ReentrancyGuard g(*this);
+      // Check that aLookup has not been destroyed.
+      MOZ_ASSERT(prepareHash(aLookup) == aPtr.mKeyHash);
+      aPtr.mEntry = &lookup(aLookup, aPtr.mKeyHash, sCollisionBit);
     }
+    return aPtr.found() || add(aPtr, std::forward<Args>(aArgs)...);
+  }
 
-    void rekeyAndMaybeRehash(Ptr p, const Lookup& l, const Key& k)
-    {
-        rekeyWithoutRehash(p, l, k);
-        checkOverRemoved();
-    }
+  void remove(Ptr aPtr)
+  {
+    MOZ_ASSERT(mTable);
+    ReentrancyGuard g(*this);
+    MOZ_ASSERT(aPtr.found());
+    MOZ_ASSERT(aPtr.mGeneration == generation());
+    remove(*aPtr.mEntry);
+    checkUnderloaded();
+  }
+
+  void rekeyWithoutRehash(Ptr aPtr, const Lookup& aLookup, const Key& aKey)
+  {
+    MOZ_ASSERT(mTable);
+    ReentrancyGuard g(*this);
+    MOZ_ASSERT(aPtr.found());
+    MOZ_ASSERT(aPtr.mGeneration == generation());
+    typename HashTableEntry<T>::NonConstT t(std::move(*aPtr));
+    HashPolicy::setKey(t, const_cast<Key&>(aKey));
+    remove(*aPtr.mEntry);
+    putNewInfallibleInternal(aLookup, std::move(t));
+  }
+
+  void rekeyAndMaybeRehash(Ptr aPtr, const Lookup& aLookup, const Key& aKey)
+  {
+    rekeyWithoutRehash(aPtr, aLookup, aKey);
+    checkOverRemoved();
+  }
 
 #undef METER
 };
 
 } // namespace detail
 } // namespace mozilla
 
-#endif  /* mozilla_HashTable_h */
+#endif /* mozilla_HashTable_h */
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3274,19 +3274,16 @@ pref("browser.tabs.remote.allowLinkedWeb
 
 // Pref to control whether we use separate privileged content processes.
 pref("browser.tabs.remote.separatePrivilegedContentProcess", false);
 
 // Enable the use of display-lists for SVG hit-testing and painting.
 pref("svg.display-lists.hit-testing.enabled", true);
 pref("svg.display-lists.painting.enabled", true);
 
-// Is support for the <marker orient="auto-start-reverse"> feature enabled?
-pref("svg.marker-improvements.enabled", true);
-
 // Is support for the new getBBox method from SVG 2 enabled?
 // See https://svgwg.org/svg2-draft/single-page.html#types-SVGBoundingBoxOptions
 pref("svg.new-getBBox.enabled", false);
 
 pref("svg.transform-box.enabled", true);
 
 # This pref controls whether the 'context-fill' and 'context-stroke' keywords
 # can be used in SVG-as-an-image in the content processes to use the fill/
--- a/mozglue/misc/interceptor/MMPolicies.h
+++ b/mozglue/misc/interceptor/MMPolicies.h
@@ -117,16 +117,24 @@ public:
   }
 
   bool Write(void* aToPtr, const void* aFromPtr, size_t aLen) const
   {
     ::memcpy(aToPtr, aFromPtr, aLen);
     return true;
   }
 
+#if defined(_M_IX86)
+  bool WriteAtomic(void* aDestPtr, const uint16_t aValue) const
+  {
+    *static_cast<uint16_t*>(aDestPtr) = aValue;
+    return true;
+  }
+#endif // defined(_M_IX86)
+
   bool Protect(void* aVAddress, size_t aSize, uint32_t aProtFlags,
                uint32_t* aPrevProtFlags) const
   {
     MOZ_ASSERT(aPrevProtFlags);
     BOOL ok = ::VirtualProtect(aVAddress, aSize, aProtFlags,
                                reinterpret_cast<PDWORD>(aPrevProtFlags));
     if (!ok && aPrevProtFlags) {
       // VirtualProtect can fail but still set valid protection flags.
--- a/mozglue/misc/interceptor/PatcherNopSpace.h
+++ b/mozglue/misc/interceptor/PatcherNopSpace.h
@@ -46,18 +46,17 @@ public:
       WritableTargetFunction<MMPolicyT> fn(mVMPolicy,
                                            reinterpret_cast<uintptr_t>(ptr),
                                            sizeof(uint16_t));
       if (!fn) {
         continue;
       }
 
       // mov edi, edi
-      fn.WriteShort(0xff8b);
-      fn.Commit();
+      fn.CommitAndWriteShort(0xff8b);
     }
 
     mPatchedFns.clear();
   }
 
   /**
    * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
    * in our address space. There is a bug in Detours 2.x that causes it to
@@ -201,18 +200,17 @@ public:
     }
 
     // Set aOrigFunc here, because after this point, aHookDest might be called,
     // and aHookDest might use the aOrigFunc pointer.
     *aOrigFunc = reinterpret_cast<void*>(writableFn.GetCurrentAddress() +
                                          sizeof(uint16_t));
 
     // Short jump up into our long jump.
-    writableFn.WriteShort(0xF9EB); // jmp $-5
-    return writableFn.Commit();
+    return writableFn.CommitAndWriteShort(0xF9EB); // jmp $-5
   }
 };
 
 } // namespace interceptor
 } // namespace mozilla
 
 #endif // defined(_M_IX86)
 
--- a/mozglue/misc/interceptor/TargetFunction.h
+++ b/mozglue/misc/interceptor/TargetFunction.h
@@ -172,16 +172,19 @@ public:
 
     bool ok = mMMPolicy.Write(reinterpret_cast<void*>(mFunc + mStartWriteOffset),
                               mLocalBytes.begin(), mLocalBytes.length());
     if (!ok) {
       return false;
     }
 
     mMMPolicy.FlushInstructionCache();
+
+    mStartWriteOffset += mLocalBytes.length();
+
     mLocalBytes.clear();
     return true;
   }
 
   explicit operator bool() const
   {
     return mProtect && mAccumulatedStatus;
   }
@@ -223,16 +226,63 @@ public:
                             sizeof(uint16_t))) {
       mAccumulatedStatus = false;
       return;
     }
 
     mOffset += sizeof(uint16_t);
   }
 
+#if defined(_M_IX86)
+private:
+  template <typename T>
+  bool CommitAndWriteShortInternal(const T& aMMPolicy, void* aDest, uint16_t aValue);
+
+  template <>
+  bool CommitAndWriteShortInternal<MMPolicyInProcess>(const MMPolicyInProcess& aMMPolicy,
+                                                      void* aDest, uint16_t aValue)
+  {
+    return aMMPolicy.WriteAtomic(aDest, aValue);
+  }
+
+  template <>
+  bool CommitAndWriteShortInternal<MMPolicyOutOfProcess>(const MMPolicyOutOfProcess& aMMPolicy,
+                                                         void* aDest, uint16_t aValue)
+  {
+    return aMMPolicy.Write(aDest, &aValue, sizeof(uint16_t));
+  }
+
+public:
+  /**
+   * Commits any dirty writes, and then writes a short, atomically if possible.
+   * This call may succeed in both inproc and outproc cases, but atomicity
+   * is only guaranteed in the inproc case.
+   */
+  bool CommitAndWriteShort(const uint16_t aValue)
+  {
+    // First, commit everything that has been written until now
+    if (!Commit()) {
+      return false;
+    }
+
+    // Now immediately write the short, atomically if inproc
+    bool ok = CommitAndWriteShortInternal(mMMPolicy,
+                                          reinterpret_cast<void*>(mFunc +
+                                                                  mStartWriteOffset),
+                                          aValue);
+    if (!ok) {
+      return false;
+    }
+
+    mMMPolicy.FlushInstructionCache();
+    mStartWriteOffset += sizeof(uint16_t);
+    return true;
+  }
+#endif // defined(_M_IX86)
+
   void WriteDisp32(const uintptr_t aAbsTarget)
   {
     intptr_t diff = static_cast<intptr_t>(aAbsTarget) -
                     static_cast<intptr_t>(mFunc + mOffset + sizeof(int32_t));
 
     CheckedInt<int32_t> checkedDisp(diff);
     MOZ_ASSERT(checkedDisp.isValid());
     if (!checkedDisp.isValid()) {
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -77,16 +77,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mTopOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false)
   , mIsDocshellReload(false)
+  , mSendCSPViolationEvents(true)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
@@ -310,16 +311,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mTopOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
   , mIsDocshellReload(false)
+  , mSendCSPViolationEvents(true)
   , mForcePreflight(false)
   , mIsPreflight(false)
   , mLoadTriggeredFromExternal(false)
   , mServiceWorkerTaintingSynthesized(false)
 {
   // Top-level loads are never third-party
   // Grab the information we can out of the window.
   MOZ_ASSERT(aOuterWindow);
@@ -396,16 +398,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mOuterWindowID(rhs.mOuterWindowID)
   , mParentOuterWindowID(rhs.mParentOuterWindowID)
   , mTopOuterWindowID(rhs.mTopOuterWindowID)
   , mFrameOuterWindowID(rhs.mFrameOuterWindowID)
   , mEnforceSecurity(rhs.mEnforceSecurity)
   , mInitialSecurityCheckDone(rhs.mInitialSecurityCheckDone)
   , mIsThirdPartyContext(rhs.mIsThirdPartyContext)
   , mIsDocshellReload(rhs.mIsDocshellReload)
+  , mSendCSPViolationEvents(rhs.mSendCSPViolationEvents)
   , mOriginAttributes(rhs.mOriginAttributes)
   , mRedirectChainIncludingInternalRedirects(
       rhs.mRedirectChainIncludingInternalRedirects)
   , mRedirectChain(rhs.mRedirectChain)
   , mAncestorPrincipals(rhs.mAncestorPrincipals)
   , mAncestorOuterWindowIDs(rhs.mAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
   , mForcePreflight(rhs.mForcePreflight)
@@ -442,16 +445,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    uint64_t aOuterWindowID,
                    uint64_t aParentOuterWindowID,
                    uint64_t aTopOuterWindowID,
                    uint64_t aFrameOuterWindowID,
                    bool aEnforceSecurity,
                    bool aInitialSecurityCheckDone,
                    bool aIsThirdPartyContext,
                    bool aIsDocshellReload,
+                   bool aSendCSPViolationEvents,
                    const OriginAttributes& aOriginAttributes,
                    RedirectHistoryArray& aRedirectChainIncludingInternalRedirects,
                    RedirectHistoryArray& aRedirectChain,
                    nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
                    const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
                    const nsTArray<nsCString>& aCorsUnsafeHeaders,
                    bool aForcePreflight,
                    bool aIsPreflight,
@@ -483,16 +487,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mOuterWindowID(aOuterWindowID)
   , mParentOuterWindowID(aParentOuterWindowID)
   , mTopOuterWindowID(aTopOuterWindowID)
   , mFrameOuterWindowID(aFrameOuterWindowID)
   , mEnforceSecurity(aEnforceSecurity)
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
   , mIsDocshellReload(aIsDocshellReload)
+  , mSendCSPViolationEvents(aSendCSPViolationEvents)
   , mOriginAttributes(aOriginAttributes)
   , mAncestorPrincipals(std::move(aAncestorPrincipals))
   , mAncestorOuterWindowIDs(aAncestorOuterWindowIDs)
   , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
   , mForcePreflight(aForcePreflight)
   , mIsPreflight(aIsPreflight)
   , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal)
   , mServiceWorkerTaintingSynthesized(aServiceWorkerTaintingSynthesized)
@@ -835,16 +840,30 @@ LoadInfo::GetIsDocshellReload(bool* aRes
 NS_IMETHODIMP
 LoadInfo::SetIsDocshellReload(bool aValue)
 {
   mIsDocshellReload = aValue;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetSendCSPViolationEvents(bool* aResult)
+{
+  *aResult = mSendCSPViolationEvents;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetSendCSPViolationEvents(bool aValue)
+{
+  mSendCSPViolationEvents = aValue;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetExternalContentPolicyType(nsContentPolicyType* aResult)
 {
   *aResult = nsContentUtils::InternalContentPolicyTypeToExternal(mInternalContentPolicyType);
   return NS_OK;
 }
 
 nsContentPolicyType
 LoadInfo::InternalContentPolicyType()
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -119,16 +119,17 @@ private:
            uint64_t aOuterWindowID,
            uint64_t aParentOuterWindowID,
            uint64_t aTopOuterWindowID,
            uint64_t aFrameOuterWindowID,
            bool aEnforceSecurity,
            bool aInitialSecurityCheckDone,
            bool aIsThirdPartyRequest,
            bool aIsDocshellReload,
+           bool aSendCSPViolationEvents,
            const OriginAttributes& aOriginAttributes,
            RedirectHistoryArray& aRedirectChainIncludingInternalRedirects,
            RedirectHistoryArray& aRedirectChain,
            nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals,
            const nsTArray<uint64_t>& aAncestorOuterWindowIDs,
            const nsTArray<nsCString>& aUnsafeHeaders,
            bool aForcePreflight,
            bool aIsPreflight,
@@ -189,16 +190,17 @@ private:
   uint64_t                         mOuterWindowID;
   uint64_t                         mParentOuterWindowID;
   uint64_t                         mTopOuterWindowID;
   uint64_t                         mFrameOuterWindowID;
   bool                             mEnforceSecurity;
   bool                             mInitialSecurityCheckDone;
   bool                             mIsThirdPartyContext;
   bool                             mIsDocshellReload;
+  bool                             mSendCSPViolationEvents;
   OriginAttributes                 mOriginAttributes;
   RedirectHistoryArray             mRedirectChainIncludingInternalRedirects;
   RedirectHistoryArray             mRedirectChain;
   nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
   nsTArray<uint64_t>               mAncestorOuterWindowIDs;
   nsTArray<nsCString>              mCorsUnsafeHeaders;
   bool                             mForcePreflight;
   bool                             mIsPreflight;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -497,16 +497,22 @@ interface nsILoadInfo : nsISupports
    * The external contentPolicyType of the channel, used for security checks
    * like Mixed Content Blocking and Content Security Policy.
    *
    * Specifically, content policy types with _INTERNAL_ in their name will
    * never get returned from this attribute.
    */
   readonly attribute nsContentPolicyType externalContentPolicyType;
 
+  /**
+   * CSP uses this parameter to send or not CSP violation events.
+   * Default value: true.
+   */
+  [infallible] attribute boolean sendCSPViolationEvents;
+
 %{ C++
   inline nsContentPolicyType GetExternalContentPolicyType()
   {
     nsContentPolicyType result;
     mozilla::DebugOnly<nsresult> rv = GetExternalContentPolicyType(&result);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     return result;
   }
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -60,16 +60,17 @@ struct LoadInfoArgs
   uint64_t                    outerWindowID;
   uint64_t                    parentOuterWindowID;
   uint64_t                    topOuterWindowID;
   uint64_t                    frameOuterWindowID;
   bool                        enforceSecurity;
   bool                        initialSecurityCheckDone;
   bool                        isInThirdPartyContext;
   bool                        isDocshellReload;
+  bool                        sendCSPViolationEvents;
   OriginAttributes            originAttributes;
   RedirectHistoryEntryInfo[]  redirectChainIncludingInternalRedirects;
   RedirectHistoryEntryInfo[]  redirectChain;
 
   /**
    * Ancestor data for use with the WebRequest API.
    * See nsILoadInfo.idl for details.
    */
--- a/servo/components/style/properties/longhands/box.mako.rs
+++ b/servo/components/style/properties/longhands/box.mako.rs
@@ -510,17 +510,16 @@
 // Non-standard
 ${helpers.predefined_type(
     "-moz-appearance",
     "Appearance",
     "computed::Appearance::None",
     products="gecko",
     alias="-webkit-appearance:layout.css.webkit-appearance.enabled",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
-    needs_context=False,
     animation_value_type="discrete",
 )}
 
 ${helpers.predefined_type("-moz-binding", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="none",
                           gecko_ffi_name="mBinding",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)")}
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -15,32 +15,35 @@ use style_traits::{CssWriter, KeywordsCo
 use values::{CustomIdent, KeyframesName};
 use values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
 use values::generics::box_::Perspective as GenericPerspective;
 use values::generics::box_::VerticalAlign as GenericVerticalAlign;
 use values::specified::{AllowQuirks, Number};
 use values::specified::length::{LengthOrPercentage, NonNegativeLength};
 
 #[cfg(feature = "gecko")]
+fn in_ua_or_chrome_sheet(context: &ParserContext) -> bool {
+    use stylesheets::Origin;
+    context.stylesheet_origin == Origin::UserAgent ||
+    context.chrome_rules_enabled()
+}
+
+#[cfg(feature = "gecko")]
 fn moz_display_values_enabled(context: &ParserContext) -> bool {
     use gecko_bindings::structs;
-    use stylesheets::Origin;
-    context.stylesheet_origin == Origin::UserAgent ||
-    context.chrome_rules_enabled() ||
+    in_ua_or_chrome_sheet(context) ||
     unsafe {
         structs::StaticPrefs_sVarCache_layout_css_xul_display_values_content_enabled
     }
 }
 
 #[cfg(feature = "gecko")]
 fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
     use gecko_bindings::structs;
-    use stylesheets::Origin;
-    context.stylesheet_origin == Origin::UserAgent ||
-    context.chrome_rules_enabled() ||
+    in_ua_or_chrome_sheet(context) ||
     unsafe {
         structs::StaticPrefs_sVarCache_layout_css_xul_box_display_values_content_enabled
     }
 }
 
 /// Defines an element’s display type, which consists of
 /// the two basic qualities of how an element generates boxes
 /// <https://drafts.csswg.org/css-display/#propdef-display>
@@ -943,16 +946,19 @@ pub enum Appearance {
     Menuseparator,
     Menuarrow,
     /// An image in the menu gutter, like in bookmarks or history.
     Menuimage,
     /// A horizontal meter bar.
     Meterbar,
     /// The meter bar's meter indicator.
     Meterchunk,
+    /// The "arrowed" part of the dropdown button that open up a dropdown list.
+    #[parse(condition = "in_ua_or_chrome_sheet")]
+    MozMenulistButton,
     /// For HTML's <input type=number>
     NumberInput,
     /// A horizontal progress bar.
     Progressbar,
     /// The progress bar's progress indicator
     Progresschunk,
     /// A vertical progress bar.
     ProgressbarVertical,
--- a/taskcluster/ci/bouncer-check/kind.yml
+++ b/taskcluster/ci/bouncer-check/kind.yml
@@ -37,16 +37,20 @@ jobs:
             job-name: firefox-bouncer-check
         run:
             config:
                 by-project:
                     mozilla-beta:
                         - releases/bouncer_firefox_beta.py
                     mozilla-release:
                         - releases/bouncer_firefox_release.py
+                    mozilla-esr60:
+                        - releases/bouncer_firefox_esr.py
+                    jamun:
+                        - releases/dev_bouncer_firefox_esr.py
                     default:
                         - releases/dev_bouncer_firefox_beta.py
             product-field:
                 by-project:
                     mozilla-beta: LATEST_FIREFOX_RELEASED_DEVEL_VERSION
                     mozilla-release: LATEST_FIREFOX_VERSION
                     default: LATEST_FIREFOX_DEVEL_VERSION
             products-url: https://product-details.mozilla.org/1.0/firefox_versions.json
--- a/taskcluster/ci/release-balrog-scheduling/kind.yml
+++ b/taskcluster/ci/release-balrog-scheduling/kind.yml
@@ -25,18 +25,20 @@ jobs:
       name: release-firefox_schedule_publishing_in_balrog
       shipping-product: firefox
       worker:
          product: firefox
          publish-rules:
             by-project:
                maple: [32]
                birch: [145]
+               jamun: [724]
                mozilla-beta: [32]
                mozilla-release: [145]
+               mozilla-esr60: [806]
                default: []
       treeherder:
          platform: firefox-release/opt
          symbol: Rel(BSFx)
          tier: 1
          kind: build
    devedition:
       description: Schedule Devedition publishing in balrog
--- a/taskcluster/ci/release-balrog-submit-toplevel/kind.yml
+++ b/taskcluster/ci/release-balrog-submit-toplevel/kind.yml
@@ -23,42 +23,50 @@ jobs:
       description: Submit toplevel Firefox release to balrog
       shipping-product: firefox
       worker:
          product: firefox
          archive-domain:
             by-project:
                mozilla-beta: archive.mozilla.org
                mozilla-release: archive.mozilla.org
+               mozilla-esr60: archive.mozilla.org
                default: ftp.stage.mozaws.net
          download-domain:
             by-project:
                mozilla-beta: download.mozilla.org
                mozilla-release: download.mozilla.org
+               mozilla-esr60: download.mozilla.org
                default: download.mozilla.org
          channel-names:
             by-project:
                maple: ["beta", "beta-localtest", "beta-cdntest"]
                birch: ["release", "release-localtest", "release-cdntest"]
+               jamun: ["esr", "esr-localtest", "esr-cdntest"]
                mozilla-beta: ["beta", "beta-localtest", "beta-cdntest"]
                mozilla-release: ["release", "release-localtest", "release-cdntest"]
+               mozilla-esr60: ["esr", "esr-localtest", "esr-cdntest"]
                default: []
          publish-rules:
             by-project:
                maple: [32]
                birch: [145]
+               jamun: [724]
                mozilla-beta: [32]
                mozilla-release: [145]
+               mozilla-esr60: [806]
                default: []
          rules-to-update:
             by-project:
                maple: ["firefox-beta-cdntest", "firefox-beta-localtest"]
                birch: ["firefox-release-cdntest", "firefox-release-localtest"]
+               jamun: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                mozilla-beta: ["firefox-beta-cdntest", "firefox-beta-localtest"]
                mozilla-release: ["firefox-release-cdntest", "firefox-release-localtest"]
+               mozilla-esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                default: []
          platforms: ["linux", "linux64", "macosx64", "win32", "win64"]
       treeherder:
          platform: firefox-release/opt
          symbol: Rel(BPFx)
          tier: 1
          kind: build
 
--- a/taskcluster/ci/release-bouncer-sub/kind.yml
+++ b/taskcluster/ci/release-bouncer-sub/kind.yml
@@ -9,16 +9,17 @@ transforms:
    - taskgraph.transforms.task:transforms
 
 job-defaults:
    description: release bouncer submission job
    worker-type:
       by-project:
          mozilla-beta: scriptworker-prov-v1/bouncer-v1
          mozilla-release: scriptworker-prov-v1/bouncer-v1
+         mozilla-esr60: scriptworker-prov-v1/bouncer-v1
          default: scriptworker-prov-v1/bouncer-dev
    worker:
       implementation: bouncer-submission
    scopes:
       by-project:
          mozilla-beta:
             - project:releng:bouncer:action:submission
             - project:releng:bouncer:server:production
--- a/taskcluster/ci/release-generate-checksums/kind.yml
+++ b/taskcluster/ci/release-generate-checksums/kind.yml
@@ -54,17 +54,17 @@ jobs:
    firefox:
       shipping-product: firefox
       attributes:
          build_platform: firefox-release
          build_type: opt
       run:
          extra-config:
             by-project:
-               mozilla-(release|beta):
+               mozilla-(release|beta|esr.*):
                   stage_product: "firefox"
                   bucket_name: "net-mozaws-prod-delivery-firefox"
                default:
                   stage_product: "firefox"
                   bucket_name: "net-mozaws-stage-delivery-firefox"
       treeherder:
          platform: firefox-release/opt
 
--- a/taskcluster/ci/release-secondary-balrog-scheduling/kind.yml
+++ b/taskcluster/ci/release-secondary-balrog-scheduling/kind.yml
@@ -27,25 +27,29 @@ jobs:
       name: release-firefox_schedule_publishing_in_balrog
       shipping-product: firefox
       worker:
          product: firefox
          release-eta: ''
          channel-names:
             by-project:
                birch: ["beta", "beta-localtest", "beta-cdntest"]
+               jamun: ["esr", "esr-localtest", "esr-cdntest"]
                mozilla-release: ["beta", "beta-localtest", "beta-cdntest"]
+               mozilla-esr60: ["esr", "esr-localtest", "esr-cdntest"]
                default: []
          publish-rules:
             by-project:
                birch: [32]
                mozilla-release: [32]
                default: []
          rules-to-update:
             by-project:
                birch: ["firefox-beta-cdntest", "firefox-beta-localtest"]
+               jamun: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                mozilla-release: ["firefox-beta-cdntest", "firefox-beta-localtest"]
+               mozilla-esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                default: []
       treeherder:
          platform: linux64/opt
          symbol: Rel(BSFxRC)
          tier: 1
          kind: build
--- a/taskcluster/ci/release-secondary-balrog-submit-toplevel/kind.yml
+++ b/taskcluster/ci/release-secondary-balrog-submit-toplevel/kind.yml
@@ -23,33 +23,41 @@ jobs:
       name: submit-toplevel-rc-firefox-release-to-balrog
       description: submit toplevel RC Firefox release to balrog
       shipping-product: firefox
       worker:
          product: firefox
          archive-domain:
             by-project:
                mozilla-release: archive.mozilla.org
+               mozilla-esr60: archive.mozilla.org
                default: ftp.stage.mozaws.net
          download-domain:
             by-project:
                mozilla-release: download.mozilla.org
+               mozilla-esr60: download.mozilla.org
                default: download.mozilla.org
          channel-names:
             by-project:
                birch: ["beta", "beta-localtest", "beta-cdntest"]
+               jamun: ["esr", "esr-localtest", "esr-cdntest"]
                mozilla-release: ["beta", "beta-localtest", "beta-cdntest"]
+               mozilla-esr60: ["esr", "esr-localtest", "esr-cdntest"]
                default: []
          publish-rules:
             by-project:
                birch: [32]
+               jamun: [724]
                mozilla-release: [32]
+               mozilla-esr60: []
                default: []
          rules-to-update:
             by-project:
                birch: ["firefox-beta-cdntest", "firefox-beta-localtest"]
+               jamun: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                mozilla-release: ["firefox-beta-cdntest", "firefox-beta-localtest"]
+               mozilla-esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
                default: []
       treeherder:
          platform: linux64/opt
          symbol: Rel(BPFxRC)
          tier: 1
          kind: build
--- a/taskcluster/ci/release-update-verify-config/kind.yml
+++ b/taskcluster/ci/release-update-verify-config/kind.yml
@@ -49,17 +49,17 @@ job-defaults:
       # This is overridden for devedition to exclude 58.0b1
       # because of the special case added by
       # https://bugzilla.mozilla.org/show_bug.cgi?id=1419189
       # The devedition override can be removed after 58.0b1
       # is behind a watershed
       include-version:
          by-project:
             birch: nonbeta
-            jamun: beta
+            jamun: esr
             maple: beta
             mozilla-beta: beta
             mozilla-release: nonbeta
             mozilla-esr60: esr
             default: null
       last-watershed:
          by-project:
             birch:
--- a/taskcluster/ci/release-version-bump/kind.yml
+++ b/taskcluster/ci/release-version-bump/kind.yml
@@ -37,24 +37,29 @@ job-defaults:
                 mozilla-esr52:
                     - "browser/config/version.txt"
                     - "browser/config/version_display.txt"
                     - "config/milestone.txt"
                 mozilla-esr60:
                     - "browser/config/version.txt"
                     - "browser/config/version_display.txt"
                     - "config/milestone.txt"
+                jamun:
+                    - "browser/config/version.txt"
+                    - "browser/config/version_display.txt"
+                    - "config/milestone.txt"
         push:
             by-project:
                 mozilla-beta: true
                 mozilla-release: true
                 mozilla-esr52: true
                 mozilla-esr60: true
                 maple: true
                 birch: true
+                jamun: true
                 default: false
 
 jobs:
     fennec:
         name: fennec-version-bump
         shipping-product: fennec
 
     firefox:
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -82,17 +82,17 @@ PER_PROJECT_PARAMETERS = {
 
     'comm-beta': {
         'target_tasks_method': 'mozilla_beta_tasks',
         'optimize_target_tasks': True,
         'include_nightly': True,
     },
 
     'comm-esr60': {
-        'target_tasks_method': 'mozilla_beta_tasks',
+        'target_tasks_method': 'mozilla_esr60_tasks',
         'optimize_target_tasks': True,
         'include_nightly': True,
     },
 
     'pine': {
         'target_tasks_method': 'pine_tasks',
         'optimize_target_tasks': True,
         'include_nightly': False,
--- a/taskcluster/taskgraph/util/attributes.py
+++ b/taskcluster/taskgraph/util/attributes.py
@@ -16,16 +16,17 @@ TRUNK_PROJECTS = INTEGRATION_PROJECTS | 
 
 RELEASE_PROJECTS = {
     'mozilla-central',
     'mozilla-beta',
     'mozilla-release',
     'mozilla-esr60',
     'comm-central',
     'comm-beta',
+    'comm-esr60',
 }
 
 RELEASE_PROMOTION_PROJECTS = {
     'jamun',
     'maple',
     'try',
     'try-comm-central',
 } | RELEASE_PROJECTS
--- a/taskcluster/taskgraph/util/scriptworker.py
+++ b/taskcluster/taskgraph/util/scriptworker.py
@@ -41,17 +41,19 @@ SIGNING_SCOPE_ALIAS_TO_PROJECT = [[
     'all-nightly-branches', set([
         'mozilla-central',
         'comm-central',
     ])
 ], [
     'all-release-branches', set([
         'mozilla-beta',
         'mozilla-release',
+        'mozilla-esr60',
         'comm-beta',
+        'comm-esr60',
     ])
 ]]
 
 """Map the signing scope aliases to the actual scopes.
 """
 SIGNING_CERT_SCOPES = {
     'all-release-branches': 'signing:cert:release-signing',
     'all-nightly-branches': 'signing:cert:nightly-signing',
@@ -71,22 +73,26 @@ DEVEDITION_SIGNING_CERT_SCOPES = {
 
 """Map beetmover scope aliases to sets of projects.
 """
 BEETMOVER_SCOPE_ALIAS_TO_PROJECT = [[
     'all-nightly-branches', set([
         'mozilla-central',
         'mozilla-beta',
         'mozilla-release',
+        'mozilla-esr60',
         'comm-central',
     ])
 ], [
     'all-release-branches', set([
         'mozilla-beta',
         'mozilla-release',
+        'mozilla-esr60',
+        'comm-beta',
+        'comm-esr60',
     ])
 ]]
 
 """The set of all beetmover release target tasks.
 
 Used for both `BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK` and `get_release_build_number`
 """
 BEETMOVER_CANDIDATES_TARGET_TASKS = set([
@@ -165,35 +171,42 @@ This is a list of list-pairs, for orderi
 BALROG_SCOPE_ALIAS_TO_PROJECT = [[
     'nightly', set([
         'mozilla-central',
         'comm-central'
     ])
 ], [
     'beta', set([
         'mozilla-beta',
+        'comm-beta',
     ])
 ], [
     'release', set([
         'mozilla-release',
     ])
 ], [
+    'esr60', set([
+        'mozilla-esr60',
+        'comm-esr60',
+    ])
+], [
     'esr', set([
         'mozilla-esr52',
     ])
 ]]
 
 """Map the balrog scope aliases to the actual scopes.
 """
 BALROG_SERVER_SCOPES = {
     'nightly': 'balrog:server:nightly',
     'aurora': 'balrog:server:aurora',
     'beta': 'balrog:server:beta',
     'release': 'balrog:server:release',
     'esr': 'balrog:server:esr',
+    'esr60': 'balrog:server:esr',
     'default': 'balrog:server:dep',
 }
 
 
 PUSH_APK_SCOPE_ALIAS_TO_PROJECT = [[
     'central', set([
         'mozilla-central',
     ])
@@ -437,17 +450,17 @@ def get_release_config(config):
     release_config['next_version'] = str(config.params['next_version'])
     release_config['build_number'] = config.params['build_number']
     return release_config
 
 
 def get_signing_cert_scope_per_platform(build_platform, is_nightly, config):
     if 'devedition' in build_platform:
         return get_devedition_signing_cert_scope(config)
-    elif is_nightly or build_platform in ('firefox-source', 'fennec-source'):
+    elif is_nightly or build_platform in ('firefox-source', 'fennec-source', 'thunderbird-source'):
         return get_signing_cert_scope(config)
     else:
         return add_scope_prefix(config, 'signing:cert:dep-signing')
 
 
 def get_worker_type_for_scope(config, scope):
     """Get the scriptworker type that will accept the given scope.
 
--- a/testing/mozharness/configs/releases/bouncer_firefox_esr.py
+++ b/testing/mozharness/configs/releases/bouncer_firefox_esr.py
@@ -1,17 +1,18 @@
 # lint_ignore=E501
 config = {
     "shipped-locales-url": "https://hg.mozilla.org/%(repo)s/raw-file/%(revision)s/browser/locales/shipped-locales",
     "bouncer_prefix": "https://download.mozilla.org/",
     "products": {
         "installer": {
             "product-name": "Firefox-%(version)s",
             "check_uptake": True,
-            "alias": "firefox-esr-latest",
+            # convert to firefox-esr-latest when ESR52 stops
+            "alias": "firefox-esr-next-latest",
             "ssl-only": True,
             "add-locales": True,
             "paths": {
                 "linux": {
                     "path": "/firefox/releases/%(version)s/linux-i686/:lang/firefox-%(version)s.tar.bz2",
                     "bouncer-platform": "linux",
                 },
                 "linux64": {
@@ -30,17 +31,18 @@ config = {
                     "path": "/firefox/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win64",
                 },
             },
         },
         "installer-ssl": {
             "product-name": "Firefox-%(version)s-SSL",
             "check_uptake": True,
-            "alias": "firefox-esr-latest-ssl",
+            # convert to firefox-esr-latest-ssl when ESR52 stops
+            "alias": "firefox-esr-next-latest-ssl",
             "ssl-only": True,
             "add-locales": True,
             "paths": {
                 "linux": {
                     "path": "/firefox/releases/%(version)s/linux-i686/:lang/firefox-%(version)s.tar.bz2",
                     "bouncer-platform": "linux",
                 },
                 "linux64": {
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/single_locale/mozilla-esr60.py
@@ -0,0 +1,31 @@
+import os
+
+config = {
+    "nightly_build": True,
+    "branch": "mozilla-esr60",
+    "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-esr60/",
+    "update_channel": "esr",
+
+    # l10n
+    "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
+
+    # mar
+    "mar_tools_url": os.environ["MAR_TOOLS_URL"],
+
+    # repositories
+    "repos": [{
+        "vcs": "hg",
+        "repo": "https://hg.mozilla.org/build/tools",
+        "branch": "default",
+        "dest": "tools",
+    }, {
+        "vcs": "hg",
+        "repo": "https://hg.mozilla.org/releases/mozilla-esr60",
+        "revision": "%(revision)s",
+        "dest": "mozilla-esr60",
+        "clone_upstream_url": "https://hg.mozilla.org/mozilla-unified",
+    }],
+    # purge options
+    'purge_minsize': 12,
+    'is_automation': True,
+}
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -194419,16 +194419,21 @@
      {}
     ]
    ],
    "clear-site-data/support/echo-clear-site-data.py": [
     [
      {}
     ]
    ],
+   "clear-site-data/support/iframe_executionContexts.html": [
+    [
+     {}
+    ]
+   ],
    "clear-site-data/support/page_with_resource.sub.html": [
     [
      {}
     ]
    ],
    "clear-site-data/support/send_report.html": [
     [
      {}
@@ -321461,16 +321466,22 @@
      "/budget-api/interfaces.any.html",
      {}
     ],
     [
      "/budget-api/interfaces.any.worker.html",
      {}
     ]
    ],
+   "clear-site-data/executionContexts.sub.html": [
+    [
+     "/clear-site-data/executionContexts.sub.html",
+     {}
+    ]
+   ],
    "clear-site-data/navigation-insecure.html": [
     [
      "/clear-site-data/navigation-insecure.html",
      {}
     ]
    ],
    "clear-site-data/navigation.https.html": [
     [
@@ -429472,16 +429483,20 @@
   "check_stability.ini": [
    "b939328be1cb69c3dcc47495930ba0df28b2bad2",
    "support"
   ],
   "clear-site-data/META.yml": [
    "65ca96dbb9d774e97cc3a60f6b6dde952104893b",
    "support"
   ],
+  "clear-site-data/executionContexts.sub.html": [
+   "f09a3f73b59994ae82532a4412116815012e4371",
+   "testharness"
+  ],
   "clear-site-data/navigation-insecure.html": [
    "9ccd712a222085ae7ef75a52e7f542e996a8eb29",
    "testharness"
   ],
   "clear-site-data/navigation.https.html": [
    "dbddce3c13681ca31f96e4ccebfe100a5b64fa9e",
    "testharness"
   ],
@@ -429492,16 +429507,20 @@
   "clear-site-data/storage.https.html": [
    "35c9fd1a03bc2c6d01900454f1209a26d82ca2d1",
    "testharness"
   ],
   "clear-site-data/support/echo-clear-site-data.py": [
    "f7c48bb39579c991af9945d2ac6e98b604b7113b",
    "support"
   ],
+  "clear-site-data/support/iframe_executionContexts.html": [
+   "9c20c9e0db5861970e1f72245b47f128be4a4b3d",
+   "support"
+  ],
   "clear-site-data/support/page_with_resource.sub.html": [
    "703519a2f6f0e5d11a4fb4fb0e40e5fc3d8624fe",
    "support"
   ],
   "clear-site-data/support/send_report.html": [
    "6e90c626ea64541b0a147cc2c2d579216c3834cb",
    "support"
   ],
deleted file mode 100644
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_1_2.sub.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[media-src-7_1_2.sub.html]
-  expected: TIMEOUT
-  [Test that securitypolicyviolation events are fired]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_2_2.sub.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[media-src-7_2_2.sub.html]
-  expected: TIMEOUT
-  [Test that securitypolicyviolation events are fired]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-blocked.sub.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[media-src-blocked.sub.html]
-  expected: TIMEOUT
-  [Test that securitypolicyviolation events are fired]
-    expected: TIMEOUT
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/service-workers/service-worker/fetch-csp.https.html.ini
@@ -0,0 +1,3 @@
+[fetch-csp.https.html]
+  [Verify CSP control of fetch() in a Service Worker]
+    expected: FAIL
--- a/testing/web-platform/meta/subresource-integrity/subresource-ed25519-with-csp.tentative.html.ini
+++ b/testing/web-platform/meta/subresource-integrity/subresource-ed25519-with-csp.tentative.html.ini
@@ -1,7 +1,4 @@
 [subresource-ed25519-with-csp.tentative.html]
   [Script: Ed25519-with-CSP, passes, valid key, valid signature.]
     expected: FAIL
 
-  [Script: Ed25519-with-CSP, fails, valid key, valid signature, key not in CSP.]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/clear-site-data/executionContexts.sub.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+
+  <body>
+    <script>
+      function createAndLoadIframe() {
+        return new Promise(resolve => {
+          addEventListener("message", function() {
+            assert_true(true, "Iframe loaded");
+            resolve();
+          }, { once: true });
+
+          let ifr = document.createElement('iframe');
+          document.body.appendChild(ifr);
+          ifr.src = "https://{{domains[www2]}}:{{ports[https][0]}}/clear-site-data/support/iframe_executionContexts.html";
+        });
+      }
+
+      function loadClearSiteDataResource(what) {
+        return new Promise(resolve => {
+          addEventListener("message", function() {
+            assert_true(true, "Iframe re-loaded");
+            resolve();
+          }, { once: true });
+
+          let image = new Image();
+          image.src = "https://{{domains[www2]}}:{{ports[https][0]}}/clear-site-data/support/echo-clear-site-data.py?" + what;
+        });
+      }
+
+      promise_test(function(test) {
+        return createAndLoadIframe()
+          .then(() => loadClearSiteDataResource("executionContexts"))
+      }, "executionContexts triggers the reload of contexts");
+
+      promise_test(function(test) {
+        return createAndLoadIframe()
+          .then(() => loadClearSiteDataResource("*"));
+      }, "* triggers the reload of contexts");
+    </script>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/clear-site-data/support/iframe_executionContexts.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <script>
+      parent.postMessage("Hello world!", "*");
+    </script>
+  </body>
+</html>
--- a/toolkit/themes/linux/global/dropmarker.css
+++ b/toolkit/themes/linux/global/dropmarker.css
@@ -1,7 +1,7 @@
 /* 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/. */
 
 dropmarker {
-  -moz-appearance: menulist-button;
+  -moz-appearance: -moz-menulist-button;
 }
--- a/toolkit/themes/linux/global/menulist.css
+++ b/toolkit/themes/linux/global/menulist.css
@@ -61,17 +61,17 @@ menulist[editable="true"] {
   padding-top: 3px;
   padding-bottom: 3px;
   padding-inline-start: 2px;
   padding-inline-end: 0px;
 }
 
 menulist[editable="true"] > .menulist-dropmarker {
   display: -moz-box;
-  -moz-appearance: menulist-button;
+  -moz-appearance: -moz-menulist-button;
 }
 
 html|*.menulist-editable-input {
   margin: 0px !important;
   border: none !important;
   padding: 0px !important;
   font: inherit;
 }
--- a/toolkit/themes/osx/global/dropmarker.css
+++ b/toolkit/themes/osx/global/dropmarker.css
@@ -1,14 +1,14 @@
 /* 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/. */
 
 dropmarker {
-  -moz-appearance: menulist-button;
+  -moz-appearance: -moz-menulist-button;
   width: 16px;
   -moz-box-align: center;
   -moz-box-pack: center;
   padding: 1px;
   list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
   -moz-image-region: auto;
 }
 
--- a/toolkit/themes/windows/global/dropmarker.css
+++ b/toolkit/themes/windows/global/dropmarker.css
@@ -1,14 +1,14 @@
 /* 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/. */
 
 dropmarker {
-  -moz-appearance: menulist-button;
+  -moz-appearance: -moz-menulist-button;
   width: 16px;
   height: 16px;
   -moz-box-align: center;
   -moz-box-pack: center;
   padding: 1px;
   list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
   -moz-image-region: auto;
 }
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -3322,16 +3322,17 @@ nsNativeThemeCocoa::ComputeWidgetInfo(ns
       DropdownParams params;
       params.controlParams = controlParams;
       params.pullsDown = false;
       params.editable = aWidgetType == StyleAppearance::MenulistTextfield;
       return Some(WidgetInfo::Dropdown(params));
     }
 
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       return Some(WidgetInfo::Button(
         ButtonParams{ComputeControlParams(aFrame, eventState),
                      ButtonType::eArrowButton}));
 
     case StyleAppearance::Groupbox:
       return Some(WidgetInfo::GroupBox());
 
     case StyleAppearance::Textfield:
@@ -3863,16 +3864,17 @@ nsNativeThemeCocoa::CreateWebRenderComma
     case StyleAppearance::Toolbarbutton:
     case StyleAppearance::Separator:
     case StyleAppearance::Toolbar:
     case StyleAppearance::MozWindowTitlebar:
     case StyleAppearance::Statusbar:
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistTextfield:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::Groupbox:
     case StyleAppearance::Textfield:
     case StyleAppearance::NumberInput:
     case StyleAppearance::Searchfield:
     case StyleAppearance::Progressbar:
     case StyleAppearance::ProgressbarVertical:
     case StyleAppearance::Meterbar:
     case StyleAppearance::Treetwisty:
@@ -4028,16 +4030,17 @@ nsNativeThemeCocoa::GetWidgetBorder(nsDe
       // nsCheckboxRadioFrame::GetIntrinsicWidth and nsCheckboxRadioFrame::GetIntrinsicHeight
       // assume a border width of 2px.
       result.SizeTo(2, 2, 2, 2);
       break;
     }
 
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       result = DirectionAwareMargin(kAquaDropdownBorder, aFrame);
       break;
 
     case StyleAppearance::MenulistTextfield:
       result = DirectionAwareMargin(kAquaComboboxBorder, aFrame);
       break;
 
     case StyleAppearance::NumberInput:
@@ -4155,16 +4158,17 @@ nsNativeThemeCocoa::GetWidgetOverflow(ns
     case StyleAppearance::Toolbarbutton:
     case StyleAppearance::NumberInput:
     case StyleAppearance::Textfield:
     case StyleAppearance::TextfieldMultiline:
     case StyleAppearance::Searchfield:
     case StyleAppearance::Listbox:
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::MenulistTextfield:
     case StyleAppearance::Checkbox:
     case StyleAppearance::Radio:
     case StyleAppearance::Tab:
     case StyleAppearance::FocusOutline:
     {
       overflow.SizeTo(kMaxFocusRingWidth,
                       kMaxFocusRingWidth,
@@ -4285,16 +4289,17 @@ nsNativeThemeCocoa::GetMinimumWidgetSize
       }
       aResult->SizeTo(buttonWidth, buttonHeight);
       *aIsOverridable = true;
       break;
     }
 
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     {
       SInt32 popupHeight = 0;
       ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight);
       aResult->SizeTo(0, popupHeight);
       break;
     }
 
     case StyleAppearance::NumberInput:
@@ -4611,26 +4616,28 @@ nsNativeThemeCocoa::ThemeChanged()
   return NS_OK;
 }
 
 bool
 nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame,
                                       WidgetType aWidgetType)
 {
   // if this is a dropdown button in a combobox the answer is always no
-  if (aWidgetType == StyleAppearance::MenulistButton) {
+  if (aWidgetType == StyleAppearance::MenulistButton ||
+      aWidgetType == StyleAppearance::MozMenulistButton) {
     nsIFrame* parentFrame = aFrame->GetParent();
     if (parentFrame && parentFrame->IsComboboxControlFrame())
       return false;
   }
 
   switch (aWidgetType) {
     // Combobox dropdowns don't support native theming in vertical mode.
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::MenulistText:
     case StyleAppearance::MenulistTextfield:
       if (aFrame && aFrame->GetWritingMode().IsVertical()) {
         return false;
       }
       MOZ_FALLTHROUGH;
 
     case StyleAppearance::Listbox:
@@ -4750,16 +4757,17 @@ nsNativeThemeCocoa::ThemeSupportsWidget(
 }
 
 bool
 nsNativeThemeCocoa::WidgetIsContainer(WidgetType aWidgetType)
 {
   // flesh this out at some point
   switch (aWidgetType) {
    case StyleAppearance::MenulistButton:
+   case StyleAppearance::MozMenulistButton:
    case StyleAppearance::Radio:
    case StyleAppearance::Checkbox:
    case StyleAppearance::Progressbar:
    case StyleAppearance::Meterbar:
    case StyleAppearance::Range:
    case StyleAppearance::MozMacHelpButton:
    case StyleAppearance::MozMacDisclosureButtonOpen:
    case StyleAppearance::MozMacDisclosureButtonClosed:
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -299,17 +299,18 @@ nsNativeThemeGTK::GetGtkWidgetAndState(S
 
       aState->focused = TRUE;
       aState->depressed = TRUE; // see moz_gtk_entry_paint()
     } else if (aWidgetType == StyleAppearance::Button ||
                aWidgetType == StyleAppearance::Toolbarbutton ||
                aWidgetType == StyleAppearance::Dualbutton ||
                aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
                aWidgetType == StyleAppearance::Menulist ||
-               aWidgetType == StyleAppearance::MenulistButton) {
+               aWidgetType == StyleAppearance::MenulistButton ||
+               aWidgetType == StyleAppearance::MozMenulistButton) {
       aState->active &= aState->inHover;
     } else if (aWidgetType == StyleAppearance::Treetwisty ||
                aWidgetType == StyleAppearance::Treetwistyopen) {
       nsTreeBodyFrame *treeBodyFrame = do_QueryFrame(aFrame);
       if (treeBodyFrame) {
         const mozilla::AtomArray& atoms =
           treeBodyFrame->GetPropertyArrayForCurrentDrawingItem();
         aState->selected = atoms.Contains(nsGkAtoms::selected);
@@ -416,26 +417,29 @@ nsNativeThemeGTK::GetGtkWidgetAndState(S
 
       // A button with drop down menu open or an activated toggle button
       // should always appear depressed.
       if (aWidgetType == StyleAppearance::Button ||
           aWidgetType == StyleAppearance::Toolbarbutton ||
           aWidgetType == StyleAppearance::Dualbutton ||
           aWidgetType == StyleAppearance::ToolbarbuttonDropdown ||
           aWidgetType == StyleAppearance::Menulist ||
-          aWidgetType == StyleAppearance::MenulistButton) {
+          aWidgetType == StyleAppearance::MenulistButton ||
+          aWidgetType == StyleAppearance::MozMenulistButton) {
         bool menuOpen = IsOpenButton(aFrame);
         aState->depressed = IsCheckedButton(aFrame) || menuOpen;
         // we must not highlight buttons with open drop down menus on hover.
         aState->inHover = aState->inHover && !menuOpen;
       }
 
       // When the input field of the drop down button has focus, some themes
       // should draw focus for the drop down button as well.
-      if (aWidgetType == StyleAppearance::MenulistButton && aWidgetFlags) {
+      if ((aWidgetType == StyleAppearance::MenulistButton ||
+           aWidgetType == StyleAppearance::MozMenulistButton) &&
+          aWidgetFlags) {
         *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
       }
     }
   }
 
   switch (aWidgetType) {
   case StyleAppearance::Button:
     if (aWidgetFlags)
@@ -618,16 +622,17 @@ nsNativeThemeGTK::GetGtkWidgetAndState(S
         *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
     break;
   case StyleAppearance::MenulistText:
     return false; // nothing to do, but prevents the bg from being drawn
   case StyleAppearance::MenulistTextfield:
     aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY;
     break;
   case StyleAppearance::MenulistButton:
+  case StyleAppearance::MozMenulistButton:
     aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW;
     break;
   case StyleAppearance::ToolbarbuttonDropdown:
   case StyleAppearance::ButtonArrowDown:
   case StyleAppearance::ButtonArrowUp:
   case StyleAppearance::ButtonArrowNext:
   case StyleAppearance::ButtonArrowPrevious:
     aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
@@ -1393,16 +1398,17 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev
     case StyleAppearance::MozWindowButtonClose:
     case StyleAppearance::MozWindowButtonMinimize:
     case StyleAppearance::MozWindowButtonMaximize:
     case StyleAppearance::MozWindowButtonRestore:
     case StyleAppearance::Dualbutton:
     case StyleAppearance::TabScrollArrowBack:
     case StyleAppearance::TabScrollArrowForward:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::ToolbarbuttonDropdown:
     case StyleAppearance::ButtonArrowUp:
     case StyleAppearance::ButtonArrowDown:
     case StyleAppearance::ButtonArrowNext:
     case StyleAppearance::ButtonArrowPrevious:
     case StyleAppearance::RangeThumb:
     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
     // and have a meaningful baseline, so they can't have
@@ -1595,16 +1601,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
     case StyleAppearance::TabScrollArrowBack:
     case StyleAppearance::TabScrollArrowForward:
       {
         moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height);
         *aIsOverridable = false;
       }
       break;
   case StyleAppearance::MenulistButton:
+  case StyleAppearance::MozMenulistButton:
     {
       moz_gtk_get_combo_box_entry_button_size(&aResult->width,
                                               &aResult->height);
       *aIsOverridable = false;
     }
     break;
   case StyleAppearance::Menuseparator:
     {
@@ -1947,16 +1954,17 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns
   case StyleAppearance::MozWindowTitlebar:
   case StyleAppearance::MozWindowTitlebarMaximized:
     // GtkHeaderBar is available on GTK 3.10+, which is used for styling
     // title bars and title buttons.
     return gtk_check_version(3, 10, 0) == nullptr &&
            !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 
   case StyleAppearance::MenulistButton:
+  case StyleAppearance::MozMenulistButton:
     if (aFrame && aFrame->GetWritingMode().IsVertical()) {
       return false;
     }
     // "Native" dropdown buttons cause padding and margin problems, but only
     // in HTML so allow them in XUL.
     return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
            !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
 
@@ -1969,16 +1977,17 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns
   return false;
 }
 
 NS_IMETHODIMP_(bool)
 nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aWidgetType)
 {
   // XXXdwh At some point flesh all of this out.
   if (aWidgetType == StyleAppearance::MenulistButton ||
+      aWidgetType == StyleAppearance::MozMenulistButton ||
       aWidgetType == StyleAppearance::Radio ||
       aWidgetType == StyleAppearance::RangeThumb ||
       aWidgetType == StyleAppearance::Checkbox ||
       aWidgetType == StyleAppearance::TabScrollArrowBack ||
       aWidgetType == StyleAppearance::TabScrollArrowForward ||
       aWidgetType == StyleAppearance::ButtonArrowUp ||
       aWidgetType == StyleAppearance::ButtonArrowDown ||
       aWidgetType == StyleAppearance::ButtonArrowNext ||
--- a/widget/headless/HeadlessThemeGTK.cpp
+++ b/widget/headless/HeadlessThemeGTK.cpp
@@ -83,16 +83,17 @@ HeadlessThemeGTK::GetWidgetBorder(nsDevi
       break;
     case StyleAppearance::Menulist:
       result.top = 6;
       result.right = 22;
       result.bottom = 6;
       result.left = 7;
       break;
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       result.top = 1;
       result.right = 1;
       result.bottom = 1;
       result.left = 0;
       break;
     case StyleAppearance::MenulistTextfield:
       result.top = 1;
       result.right = 0;
@@ -130,16 +131,17 @@ HeadlessThemeGTK::GetWidgetPadding(nsDev
     case StyleAppearance::ToolbarbuttonDropdown:
     case StyleAppearance::ButtonArrowUp:
     case StyleAppearance::ButtonArrowDown:
     case StyleAppearance::ButtonArrowNext:
     case StyleAppearance::ButtonArrowPrevious:
     case StyleAppearance::TabScrollArrowBack:
     case StyleAppearance::TabScrollArrowForward:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::RangeThumb:
     case StyleAppearance::ButtonFocus:
       aResult->top = 0;
       aResult->right = 0;
       aResult->bottom = 0;
       aResult->left = 0;
       return true;
     case StyleAppearance::Menuitem:
@@ -267,16 +269,17 @@ HeadlessThemeGTK::GetMinimumWidgetSize(n
       aResult->height = 31;
       *aIsOverridable = false;
       break;
     case StyleAppearance::Menulist:
       aResult->width = 44;
       aResult->height = 27;
       break;
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       aResult->width = 29;
       aResult->height = 28;
       *aIsOverridable = false;
       break;
     case StyleAppearance::ScalethumbHorizontal:
     case StyleAppearance::RangeThumb:
       aResult->width = 14;
       aResult->height = 18;
@@ -404,28 +407,30 @@ HeadlessThemeGTK::ThemeSupportsWidget(ns
     case StyleAppearance::Menuitem:
     case StyleAppearance::Checkmenuitem:
     case StyleAppearance::Radiomenuitem:
     case StyleAppearance::Menuseparator:
     case StyleAppearance::Menuarrow:
     case StyleAppearance::MozGtkInfoBar:
       return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) &&
               !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
     default:
       break;
   }
   return false;
 }
 
 NS_IMETHODIMP_(bool)
 HeadlessThemeGTK::WidgetIsContainer(WidgetType aWidgetType)
 {
     if (aWidgetType == StyleAppearance::MenulistButton ||
+        aWidgetType == StyleAppearance::MozMenulistButton ||
         aWidgetType == StyleAppearance::Radio ||
         aWidgetType == StyleAppearance::RangeThumb ||
         aWidgetType == StyleAppearance::Checkbox ||
         aWidgetType == StyleAppearance::TabScrollArrowBack ||
         aWidgetType == StyleAppearance::TabScrollArrowForward ||
         aWidgetType == StyleAppearance::ButtonArrowUp ||
         aWidgetType == StyleAppearance::ButtonArrowDown ||
         aWidgetType == StyleAppearance::ButtonArrowNext ||
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -780,16 +780,17 @@ mozilla::Maybe<nsUXThemeClass> nsNativeT
       return Some(eUXSpin);
     case StyleAppearance::Statusbar:
     case StyleAppearance::Statusbarpanel:
     case StyleAppearance::Resizerpanel:
     case StyleAppearance::Resizer:
       return Some(eUXStatus);
     case StyleAppearance::Menulist:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       return Some(eUXCombobox);
     case StyleAppearance::Treeheadercell:
     case StyleAppearance::Treeheadersortarrow:
       return Some(eUXHeader);
     case StyleAppearance::Listbox:
     case StyleAppearance::Listitem:
     case StyleAppearance::Treeview:
     case StyleAppearance::Treetwistyopen:
@@ -1292,17 +1293,18 @@ nsNativeThemeWin::GetThemePartAndState(n
         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
           aState = TS_HOVER;
         else
           aState = TS_NORMAL;
       }
 
       return NS_OK;
     }
-    case StyleAppearance::MenulistButton: {
+    case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton: {
       bool isHTML = IsHTMLContent(aFrame);
       nsIFrame* parentFrame = aFrame->GetParent();
       bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
       bool isOpen = false;
 
       // HTML select and XUL menulist dropdown buttons get state from the parent.
       if (isHTML || isMenulist)
         aFrame = parentFrame;
@@ -1823,17 +1825,18 @@ RENDER_AGAIN:
       renderRect.left = glyphSize.cx;
       renderRect.right = renderRect.left + glyphSize.cx;
     }
     DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
                         IsFrameRTL(aFrame));
   }
   // The following widgets need to be RTL-aware
   else if (aWidgetType == StyleAppearance::Resizer ||
-           aWidgetType == StyleAppearance::MenulistButton)
+           aWidgetType == StyleAppearance::MenulistButton ||
+           aWidgetType == StyleAppearance::MozMenulistButton)
   {
     DrawThemeBGRTLAware(theme, hdc, part, state,
                         &widgetRect, &clipRect, IsFrameRTL(aFrame));
   }
   else if (aWidgetType == StyleAppearance::NumberInput ||
            aWidgetType == StyleAppearance::Textfield ||
            aWidgetType == StyleAppearance::TextfieldMultiline) {
     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
@@ -2253,16 +2256,18 @@ nsNativeThemeWin::GetWidgetOverflow(nsDe
                                     nsRect* aOverflowRect)
 {
   /* This is disabled for now, because it causes invalidation problems --
    * see bug 420381.  The effect of not updating the overflow area is that
    * for dropdown buttons in content areas, there is a 1px border on 3 sides
    * where, if invalidated, the dropdown control probably won't be repainted.
    * This is fairly minor, as by default there is nothing in that area, and
    * a border only shows up if the widget is being hovered.
+   *
+   * TODO(jwatt): Figure out what do to about StyleAppearance::MozMenulistButton too.
    */
 #if 0
   /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
    * and bottom so that they overlap the dropdown's border like they're
    * supposed to.
    */
   if (aWidgetType == StyleAppearance::MenulistButton &&
       IsHTMLContent(aFrame) &&
@@ -2346,17 +2351,18 @@ nsNativeThemeWin::GetMinimumWidgetSize(n
     case StyleAppearance::ScrollbarthumbVertical:
     case StyleAppearance::ScrollbarthumbHorizontal:
     case StyleAppearance::ScrollbarbuttonUp:
     case StyleAppearance::ScrollbarbuttonDown:
     case StyleAppearance::ScrollbarbuttonLeft:
     case StyleAppearance::ScrollbarbuttonRight:
     case StyleAppearance::ScrollbarHorizontal:
     case StyleAppearance::ScrollbarVertical:
-    case StyleAppearance::MenulistButton: {
+    case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton: {
       rv = ClassicGetMinimumWidgetSize(aFrame, aWidgetType, aResult, aIsOverridable);
       ScaleForFrameDPI(aResult, aFrame);
       return rv;
     }
     case StyleAppearance::Menuitem:
     case StyleAppearance::Checkmenuitem:
     case StyleAppearance::Radiomenuitem:
       if(!IsTopLevelMenu(aFrame))
@@ -2561,17 +2567,19 @@ nsNativeThemeWin::WidgetStateChanged(nsI
       aWidgetType == StyleAppearance::MozWindowButtonMaximize ||
       aWidgetType == StyleAppearance::MozWindowButtonRestore) {
     *aShouldRepaint = true;
     return NS_OK;
   }
 
   // We need to repaint the dropdown arrow in vista HTML combobox controls when
   // the control is closed to get rid of the hover effect.
-  if ((aWidgetType == StyleAppearance::Menulist || aWidgetType == StyleAppearance::MenulistButton) &&
+  if ((aWidgetType == StyleAppearance::Menulist ||
+       aWidgetType == StyleAppearance::MenulistButton ||
+       aWidgetType == StyleAppearance::MozMenulistButton) &&
       IsHTMLContent(aFrame))
   {
     *aShouldRepaint = true;
     return NS_OK;
   }
 
   // XXXdwh Not sure what can really be done here.  Can at least guess for
   // specific widgets that they're highly unlikely to have certain states.
@@ -2638,16 +2646,17 @@ nsNativeThemeWin::ThemeSupportsWidget(ns
   return false;
 }
 
 bool 
 nsNativeThemeWin::WidgetIsContainer(WidgetType aWidgetType)
 {
   // XXXdwh At some point flesh all of this out.
   if (aWidgetType == StyleAppearance::MenulistButton || 
+      aWidgetType == StyleAppearance::MozMenulistButton || 
       aWidgetType == StyleAppearance::Radio ||
       aWidgetType == StyleAppearance::Checkbox)
     return false;
   return true;
 }
 
 bool
 nsNativeThemeWin::ThemeDrawsFocusForWidget(WidgetType aWidgetType)
@@ -2783,16 +2792,17 @@ nsNativeThemeWin::ClassicThemeSupportsWi
     case StyleAppearance::ScrollbarHorizontal:
     case StyleAppearance::ScrollbarNonDisappearing:
     case StyleAppearance::Scrollcorner:
     case StyleAppearance::ScaleHorizontal:
     case StyleAppearance::ScaleVertical:
     case StyleAppearance::ScalethumbHorizontal:
     case StyleAppearance::ScalethumbVertical:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::InnerSpinButton:
     case StyleAppearance::SpinnerUpbutton:
     case StyleAppearance::SpinnerDownbutton:
     case StyleAppearance::Listbox:
     case StyleAppearance::Treeview:
     case StyleAppearance::MenulistTextfield:
     case StyleAppearance::Menulist:
     case StyleAppearance::Tooltip:
@@ -2993,16 +3003,17 @@ nsNativeThemeWin::ClassicGetMinimumWidge
       *aIsOverridable = false;
       break;
     case StyleAppearance::ScalethumbVertical:
       (*aResult).width = 20;
       (*aResult).height = 12;
       *aIsOverridable = false;
       break;
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
       (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
       break;
     case StyleAppearance::Menulist:
     case StyleAppearance::Button:
     case StyleAppearance::Groupbox:
     case StyleAppearance::Listbox:
     case StyleAppearance::Treeview:
     case StyleAppearance::NumberInput:
@@ -3272,17 +3283,18 @@ nsresult nsNativeThemeWin::ClassicGetThe
     case StyleAppearance::Tab:
     case StyleAppearance::Tabpanel:
     case StyleAppearance::Tabpanels:
     case StyleAppearance::Menubar:
     case StyleAppearance::Menupopup:
     case StyleAppearance::Groupbox:
       // these don't use DrawFrameControl
       return NS_OK;
-    case StyleAppearance::MenulistButton: {
+    case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton: {
 
       aPart = DFC_SCROLL;
       aState = DFCS_SCROLLCOMBOBOX;
 
       nsIFrame* parentFrame = aFrame->GetParent();
       bool isHTML = IsHTMLContent(aFrame);
       bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
       bool isOpen = false;
@@ -3657,16 +3669,17 @@ RENDER_AGAIN:
     case StyleAppearance::ScrollbarbuttonUp:
     case StyleAppearance::ScrollbarbuttonDown:
     case StyleAppearance::ScrollbarbuttonLeft:
     case StyleAppearance::ScrollbarbuttonRight:
     case StyleAppearance::InnerSpinButton:
     case StyleAppearance::SpinnerUpbutton:
     case StyleAppearance::SpinnerDownbutton:
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     case StyleAppearance::Resizer: {
       int32_t oldTA;
       // setup DC to make DrawFrameControl draw correctly
       oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
       ::DrawFrameControl(hdc, &widgetRect, part, state);
       ::SetTextAlign(hdc, oldTA);
       break;
     }
@@ -4088,16 +4101,17 @@ nsNativeThemeWin::GetWidgetNativeDrawing
     case StyleAppearance::Menupopup:
     case StyleAppearance::Menuitem:
       break;
 
     // the dropdown button /almost/ renders correctly with scaling,
     // except that the graphic in the dropdown button (the downward arrow)
     // doesn't get scaled up.
     case StyleAppearance::MenulistButton:
+    case StyleAppearance::MozMenulistButton:
     // these are definitely no; they're all graphics that don't get scaled up
     case StyleAppearance::Checkbox:
     case StyleAppearance::Radio:
     case StyleAppearance::Groupbox:
     case StyleAppearance::Checkmenuitem:
     case StyleAppearance::Radiomenuitem:
     case StyleAppearance::Menucheckbox:
     case StyleAppearance::Menuradio:
--- a/xpcom/ds/PLDHashTable.h
+++ b/xpcom/ds/PLDHashTable.h
@@ -1,14 +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/. */
 
+// See the comment at the top of mfbt/HashTable.h for a comparison between
+// PLDHashTable and mozilla::HashTable.
+
 #ifndef PLDHashTable_h
 #define PLDHashTable_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
 #include "mozilla/fallible.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
--- a/xpcom/ds/nsIVariant.idl
+++ b/xpcom/ds/nsIVariant.idl
@@ -34,17 +34,17 @@ struct nsIDataType
         VTYPE_WCHAR             = TD_WCHAR            ,
         VTYPE_VOID              = TD_VOID             ,
         VTYPE_ID                = TD_PNSIID           ,
         VTYPE_DOMSTRING         = TD_DOMSTRING        ,
         VTYPE_CHAR_STR          = TD_PSTRING          ,
         VTYPE_WCHAR_STR         = TD_PWSTRING         ,
         VTYPE_INTERFACE         = TD_INTERFACE_TYPE   ,
         VTYPE_INTERFACE_IS      = TD_INTERFACE_IS_TYPE,
-        VTYPE_ARRAY             = TD_ARRAY            ,
+        VTYPE_ARRAY             = TD_LEGACY_ARRAY     ,
         VTYPE_STRING_SIZE_IS    = TD_PSTRING_SIZE_IS  ,
         VTYPE_WSTRING_SIZE_IS   = TD_PWSTRING_SIZE_IS ,
         VTYPE_UTF8STRING        = TD_UTF8STRING       ,
         VTYPE_CSTRING           = TD_CSTRING          ,
         VTYPE_ASTRING           = TD_ASTRING          ,
 
         // Non-xpt variant types
         VTYPE_EMPTY_ARRAY       = 254                 ,
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -2545,16 +2545,23 @@ public:
 
   AutoTArray(const self_type& aOther)
     : nsTArray<E>()
   {
     Init();
     this->AppendElements(aOther);
   }
 
+  AutoTArray(self_type&& aOther)
+    : nsTArray<E>()
+  {
+    Init();
+    this->SwapElements(aOther);
+  }
+
   explicit AutoTArray(const base_type& aOther)
     : mAlign()
   {
     Init();
     this->AppendElements(aOther);
   }
 
   explicit AutoTArray(base_type&& aOther)
@@ -2579,16 +2586,22 @@ public:
   }
 
   self_type& operator=(const self_type& aOther)
   {
     base_type::operator=(aOther);
     return *this;
   }
 
+  self_type& operator=(self_type&& aOther)
+  {
+    base_type::operator=(std::move(aOther));
+    return *this;
+  }
+
   template<typename Allocator>
   self_type& operator=(const nsTArray_Impl<elem_type, Allocator>& aOther)
   {
     base_type::operator=(aOther);
     return *this;
   }
 
 private:
--- a/xpcom/ds/nsTHashtable.h
+++ b/xpcom/ds/nsTHashtable.h
@@ -1,14 +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/. */
 
+// See the comment at the top of mfbt/HashTable.h for a comparison between
+// PLDHashTable and mozilla::HashTable.
+
 #ifndef nsTHashtable_h__
 #define nsTHashtable_h__
 
 #include "PLDHashTable.h"
 #include "nsPointerHashKeys.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/fallible.h"
--- a/xpcom/idl-parser/xpidl/jsonxpt.py
+++ b/xpcom/idl-parser/xpidl/jsonxpt.py
@@ -58,20 +58,28 @@ def get_type(type, calltype, iid_is=None
     if isinstance(type, xpidl.Builtin):
         ret = {'tag': TypeMap[type.name]}
         if type.name in ['string', 'wstring'] and size_is is not None:
             ret['tag'] += '_SIZE_IS'
             ret['size_is'] = size_is
         return ret
 
     if isinstance(type, xpidl.Array):
-        # NB: For an Array<T> we pass down the iid_is to get the type of T.
+        # NB: For a Array<T> we pass down the iid_is to get the type of T.
         #     This allows Arrays of InterfaceIs types to work.
         return {
             'tag': 'TD_ARRAY',
+            'element': get_type(type.type, calltype, iid_is),
+        }
+
+    if isinstance(type, xpidl.LegacyArray):
+        # NB: For a Legacy [array] T we pass down iid_is to get the type of T.
+        #     This allows [array] of InterfaceIs types to work.
+        return {
+            'tag': 'TD_LEGACY_ARRAY',
             'size_is': size_is,
             'element': get_type(type.type, calltype, iid_is),
         }
 
     if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
         return {
             'tag': 'TD_INTERFACE_TYPE',
             'name': type.name,
--- a/xpcom/idl-parser/xpidl/runtests.py
+++ b/xpcom/idl-parser/xpidl/runtests.py
@@ -55,51 +55,51 @@ class TestParser(unittest.TestCase):
 void bar();
 };""", filename='f')
         self.assertTrue(isinstance(i, xpidl.IDL))
         self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
         iface = i.productions[0]
         m = iface.members[0]
         self.assertTrue(isinstance(m, xpidl.Method))
         self.assertEqual("bar", m.name)
-        self.assertEqual("void", m.type)
+        self.assertEqual(xpidl.TypeId("void"), m.type)
 
     def testMethodParams(self):
         i = self.p.parse("""[uuid(abc)] interface foo {
 long bar(in long a, in float b, [array] in long c);
 };""", filename='f')
         i.resolve([], self.p, {})
         self.assertTrue(isinstance(i, xpidl.IDL))
         self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
         iface = i.productions[0]
         m = iface.members[0]
         self.assertTrue(isinstance(m, xpidl.Method))
         self.assertEqual("bar", m.name)
-        self.assertEqual("long", m.type)
+        self.assertEqual(xpidl.TypeId("long"), m.type)
         self.assertEqual(3, len(m.params))
-        self.assertEqual("long", m.params[0].type)
+        self.assertEqual(xpidl.TypeId("long"), m.params[0].type)
         self.assertEqual("in", m.params[0].paramtype)
-        self.assertEqual("float", m.params[1].type)
+        self.assertEqual(xpidl.TypeId("float"), m.params[1].type)
         self.assertEqual("in", m.params[1].paramtype)
-        self.assertEqual("long", m.params[2].type)
+        self.assertEqual(xpidl.TypeId("long"), m.params[2].type)
         self.assertEqual("in", m.params[2].paramtype)
-        self.assertTrue(isinstance(m.params[2].realtype, xpidl.Array))
+        self.assertTrue(isinstance(m.params[2].realtype, xpidl.LegacyArray))
         self.assertEqual("long", m.params[2].realtype.type.name)
 
     def testAttribute(self):
         i = self.p.parse("""[uuid(abc)] interface foo {
 attribute long bar;
 };""", filename='f')
         self.assertTrue(isinstance(i, xpidl.IDL))
         self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
         iface = i.productions[0]
         a = iface.members[0]
         self.assertTrue(isinstance(a, xpidl.Attribute))
         self.assertEqual("bar", a.name)
-        self.assertEqual("long", a.type)
+        self.assertEqual(xpidl.TypeId("long"), a.type)
 
     def testOverloadedVirtual(self):
         i = self.p.parse("""[uuid(abc)] interface foo {
 attribute long bar;
 void getBar();
 };""", filename='f')
         self.assertTrue(isinstance(i, xpidl.IDL))
         i.resolve([], self.p, {})
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -7,25 +7,26 @@
 
 """A parser for cross-platform IDL (XPIDL) files."""
 
 import sys
 import os.path
 import re
 from ply import lex
 from ply import yacc
+from collections import namedtuple
 
 """A type conforms to the following pattern:
 
     def isScriptable(self):
         'returns True or False'
 
     def nativeType(self, calltype):
         'returns a string representation of the native type
-        calltype must be 'in', 'out', or 'inout'
+        calltype must be 'in', 'out', 'inout', or 'element'
 
 Interface members const/method/attribute conform to the following pattern:
 
     name = 'string'
 
     def toIDL(self):
         'returns the member signature as IDL'
 """
@@ -126,30 +127,33 @@ class Builtin(object):
     def isScriptable(self):
         return True
 
     def isPointer(self):
         """Check if this type is a pointer type - this will control how pointers act"""
         return self.nativename.endswith('*')
 
     def nativeType(self, calltype, shared=False, const=False):
+        if self.name in ["string", "wstring"] and calltype == 'element':
+            raise IDLError("Use string class types for string Array elements", self.location)
+
         if const:
             print >>sys.stderr, IDLError(
                 "[const] doesn't make sense on builtin types.", self.location, warning=True)
             const = 'const '
         elif calltype == 'in' and self.isPointer():
             const = 'const '
         elif shared:
             if not self.isPointer():
                 raise IDLError("[shared] not applicable to non-pointer types.", self.location)
             const = 'const '
         else:
             const = ''
         return "%s%s %s" % (const, self.nativename,
-                            calltype != 'in' and '*' or '')
+                            '*' if 'out' in calltype else '')
 
     def rustType(self, calltype, shared=False, const=False):
         # We want to rewrite any *mut pointers to *const pointers if constness
         # was requested.
         const = const or (calltype == 'in' and self.isPointer()) or shared
         rustname = self.rustname
         if const and self.isPointer():
             rustname = self.rustname.replace("*mut", "*const")
@@ -316,27 +320,37 @@ class Include(object):
             parent.deps.extend(self.IDL.deps)
             return
 
         raise IDLError("File '%s' not found" % self.filename, self.location)
 
 
 class IDL(object):
     def __init__(self, productions):
+        self.hasSequence = False
         self.productions = productions
         self.deps = []
 
     def setName(self, object):
         self.namemap.set(object)
 
     def getName(self, id, location):
+        if id.name == 'Array':
+            if id.params is None or len(id.params) != 1:
+                raise IDLError("Array takes exactly 1 parameter", location)
+            self.hasSequence = True
+            return Array(self.getName(id.params[0], location), location)
+
+        if id.params is not None:
+            raise IDLError("Generic type '%s' unrecognized" % id.name, location)
+
         try:
-            return self.namemap[id]
+            return self.namemap[id.name]
         except KeyError:
-            raise IDLError("type '%s' not found" % id, location)
+            raise IDLError("type '%s' not found" % id.name, location)
 
     def hasName(self, id):
         return id in self.namemap
 
     def getNames(self):
         return iter(self.namemap)
 
     def __str__(self):
@@ -349,16 +363,18 @@ class IDL(object):
         self.webidlconfig = webidlconfig
         for p in self.productions:
             p.resolve(self)
 
     def includes(self):
         for p in self.productions:
             if p.kind == 'include':
                 yield p
+        if self.hasSequence:
+            yield Include("nsTArray.h", BuiltinLocation)
 
     def needsJSTypes(self):
         for p in self.productions:
             if p.kind == 'interface' and p.needsJSTypes():
                 return True
         return False
 
 
@@ -391,22 +407,24 @@ class Typedef(object):
 
     def __eq__(self, other):
         return self.name == other.name and self.type == other.type
 
     def resolve(self, parent):
         parent.setName(self)
         self.realtype = parent.getName(self.type, self.location)
 
+        if not isinstance(self.realtype, (Builtin, Native, Typedef)):
+            raise IDLError("Unsupported typedef target type", self.location)
+
     def isScriptable(self):
         return self.realtype.isScriptable()
 
     def nativeType(self, calltype):
-        return "%s %s" % (self.name,
-                          calltype != 'in' and '*' or '')
+        return "%s %s" % (self.name, '*' if 'out' in calltype else '')
 
     def rustType(self, calltype):
         return "%s%s" % (calltype != 'in' and '*mut ' or '',
                          self.name)
 
     def __str__(self):
         return "typedef %s %s\n" % (self.type, self.name)
 
@@ -435,18 +453,19 @@ class Forward(object):
                     break
 
         parent.setName(self)
 
     def isScriptable(self):
         return True
 
     def nativeType(self, calltype):
-        return "%s %s" % (self.name,
-                          calltype != 'in' and '* *' or '*')
+        if calltype == 'element':
+            return 'RefPtr<%s>' % self.name
+        return "%s *%s" % (self.name, '*' if 'out' in calltype else '')
 
     def rustType(self, calltype):
         if rustBlacklistedForward(self.name):
             raise RustNoncompat("forward declaration %s is unsupported" % self.name)
         return "%s*const %s" % (calltype != 'in' and '*mut ' or '',
                                 self.name)
 
     def __str__(self):
@@ -454,39 +473,29 @@ class Forward(object):
 
 
 class Native(object):
     kind = 'native'
 
     modifier = None
     specialtype = None
 
+    # A tuple type here means that a custom value is used for each calltype:
+    #   (in, out/inout, array element) respectively.
+    # A `None` here means that the written type should be used as-is.
     specialtypes = {
         'nsid': None,
-        'domstring': 'nsAString',
-        'utf8string': 'nsACString',
-        'cstring': 'nsACString',
-        'astring': 'nsAString',
-        'jsval': 'JS::Value',
+        'domstring': ('const nsAString&', 'nsAString&', 'nsString'),
+        'utf8string': ('const nsACString&', 'nsACString&', 'nsCString'),
+        'cstring': ('const nsACString&', 'nsACString&', 'nsCString'),
+        'astring': ('const nsAString&', 'nsAString&', 'nsString'),
+        'jsval': ('JS::HandleValue', 'JS::MutableHandleValue', 'JS::Value'),
         'promise': '::mozilla::dom::Promise',
     }
 
-    # Mappings from C++ native name types to rust native names. Types which
-    # aren't listed here are incompatible with rust code.
-    rust_nativenames = {
-        'void': "libc::c_void",
-        'char': "u8",
-        'char16_t': "u16",
-        'nsID': "nsID",
-        'nsIID': "nsIID",
-        'nsCID': "nsCID",
-        'nsAString': "::nsstring::nsAString",
-        'nsACString': "::nsstring::nsACString",
-    }
-
     def __init__(self, name, nativename, attlist, location):
         self.name = name
         self.nativename = nativename
         self.location = location
 
         for name, value, aloc in attlist:
             if value is not None:
                 raise IDLError("Unexpected attribute value", aloc)
@@ -528,57 +537,85 @@ class Native(object):
         return self.modifier == 'ptr'
 
     def isRef(self, calltype):
         return self.modifier == 'ref'
 
     def nativeType(self, calltype, const=False, shared=False):
         if shared:
             if calltype != 'out':
-                raise IDLError("[shared] only applies to out parameters.")
+                raise IDLError("[shared] only applies to out parameters.", self.location)
             const = True
 
-        if self.specialtype not in [None, 'promise'] and calltype == 'in':
+        if isinstance(self.nativename, tuple):
+            if calltype == 'in':
+                return self.nativename[0] + ' '
+            elif 'out' in calltype:
+                return self.nativename[1] + ' '
+            else:
+                return self.nativename[2] + ' '
+
+        # 'in' nsid parameters should be made 'const'
+        if self.specialtype == 'nsid' and calltype == 'in':
             const = True
 
-        if self.specialtype == 'jsval':
-            if calltype == 'out' or calltype == 'inout':
-                return "JS::MutableHandleValue "
-            return "JS::HandleValue "
+        if calltype == 'element':
+            if self.isRef(calltype):
+                raise IDLError("[ref] qualified type unsupported in Array<T>", self.location)
+
+            # Promises should be held in RefPtr<T> in Array<T>s
+            if self.specialtype == 'promise':
+                return 'RefPtr<mozilla::dom::Promise>'
+
+            # We don't support nsIDPtr, in Array<T> currently, although
+            # this or support for Array<nsID> will be needed to replace
+            # [array] completely.
+            if self.specialtype == 'nsid':
+                raise IDLError("Array<nsIDPtr> not yet supported. "
+                               "File an XPConnect bug if you need it.", self.location)
 
         if self.isRef(calltype):
-            m = '& '
-        elif self.isPtr(calltype):
-            m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
+            m = '& '  # [ref] is always passed with a single indirection
         else:
-            m = calltype != 'in' and '*' or ''
+            m = '* ' if 'out' in calltype else ''
+            if self.isPtr(calltype):
+                m += '* '
         return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
 
     def rustType(self, calltype, const=False, shared=False):
-        if shared:
-            if calltype != 'out':
-                raise IDLError("[shared] only applies to out parameters.")
-            const = True
+        # For the most part, 'native' types don't make sense in rust, as they
+        # are native C++ types. However, we can support a few types here, as
+        # they're important.
+        #
+        # NOTE: This code doesn't try to perfectly match C++ constness, as
+        # constness doesn't affect ABI, and raw pointers are already unsafe.
 
-        if self.specialtype is not None and calltype == 'in':
-            const = True
+        if self.modifier not in ['ptr', 'ref']:
+            raise RustNoncompat('Rust only supports [ref] / [ptr] native types')
+
+        prefix = '*mut ' if 'out' in calltype else '*const '
+        if 'out' in calltype and self.modifier == 'ptr':
+            prefix += '*mut '
 
-        if self.nativename not in self.rust_nativenames:
-            raise RustNoncompat("native type %s is unsupported" % self.nativename)
-        name = self.rust_nativenames[self.nativename]
+        if self.specialtype == 'nsid':
+            return prefix + self.nativename
+        if self.specialtype in ['cstring', 'utf8string']:
+            if 'element' in calltype:
+                return '::nsstring::nsCString'
+            return prefix + '::nsstring::nsACString'
+        if self.specialtype in ['astring', 'domstring']:
+            if 'element' in calltype:
+                return '::nsstring::nsString'
+            return prefix + '::nsstring::nsAString'
+        if self.nativename == 'void':
+            return prefix + 'libc::c_void'
 
-        if self.isRef(calltype):
-            m = const and '&' or '&mut '
-        elif self.isPtr(calltype):
-            m = (const and '*const ' or '*mut ')
-            if self.modifier == 'ptr' and calltype != 'in':
-                m += '*mut '
-        else:
-            m = calltype != 'in' and '*mut ' or ''
-        return "%s%s" % (m, name)
+        if self.specialtype:
+            raise RustNoncompat("specialtype %s unsupported" % self.specialtype)
+        raise RustNoncompat("native type %s unsupported" % self.nativename)
 
     def __str__(self):
         return "native %s(%s)\n" % (self.name, self.nativename)
 
 
 class WebIDL(object):
     kind = 'webidl'
 
@@ -608,21 +645,23 @@ class WebIDL(object):
             self.headerFile = self.native.replace('::', '/') + '.h'
 
         parent.setName(self)
 
     def isScriptable(self):
         return True  # All DOM objects are script exposed.
 
     def nativeType(self, calltype):
-        return "%s %s" % (self.native, calltype != 'in' and '* *' or '*')
+        if calltype == 'element':
+            return 'RefPtr<%s>' % self.native
+        return "%s *%s" % (self.native, '*' if 'out' in calltype else '')
 
     def rustType(self, calltype):
         # Just expose the type as a void* - we can't do any better.
-        return "%s*const libc::c_void" % (calltype != 'in' and '*mut ' or '')
+        return "%s*const libc::c_void" % ('*mut ' if 'out' in calltype else '')
 
     def __str__(self):
         return "webidl %s\n" % self.name
 
 
 class Interface(object):
     kind = 'interface'
 
@@ -657,32 +696,32 @@ class Interface(object):
 
         # Hack alert: if an identifier is already present, libIDL assigns
         # doc comments incorrectly. This is quirks-mode extraordinaire!
         if parent.hasName(self.name):
             for member in self.members:
                 if hasattr(member, 'doccomments'):
                     member.doccomments[0:0] = self.doccomments
                     break
-            self.doccomments = parent.getName(self.name, None).doccomments
+            self.doccomments = parent.getName(TypeId(self.name), None).doccomments
 
         if self.attributes.function:
             has_method = False
             for member in self.members:
                 if member.kind is 'method':
                     if has_method:
                         raise IDLError(
                             "interface '%s' has multiple methods, but marked 'function'" %
                             self.name, self.location)
                     else:
                         has_method = True
 
         parent.setName(self)
         if self.base is not None:
-            realbase = parent.getName(self.base, self.location)
+            realbase = parent.getName(TypeId(self.base), self.location)
             if realbase.kind != 'interface':
                 raise IDLError("interface '%s' inherits from non-interface type '%s'" %
                                (self.name, self.base), self.location)
 
             if self.attributes.scriptable and not realbase.attributes.scriptable:
                 raise IDLError("interface '%s' is scriptable but derives from "
                                "non-scriptable '%s'" %
                                (self.name, self.base), self.location, warning=True)
@@ -708,22 +747,23 @@ class Interface(object):
 
     def isScriptable(self):
         # NOTE: this is not whether *this* interface is scriptable... it's
         # whether, when used as a type, it's scriptable, which is true of all
         # interfaces.
         return True
 
     def nativeType(self, calltype, const=False):
-        return "%s%s %s" % (const and 'const ' or '',
-                            self.name,
-                            calltype != 'in' and '* *' or '*')
+        if calltype == 'element':
+            return 'RefPtr<%s>' % self.name
+        return "%s%s *%s" % ('const ' if const else '', self.name,
+                             '*' if 'out' in calltype else '')
 
-    def rustType(self, calltype):
-        return "%s*const %s" % (calltype != 'in' and '*mut ' or '',
+    def rustType(self, calltype, const=False):
+        return "%s*const %s" % ('*mut ' if 'out' in calltype else '',
                                 self.name)
 
     def __str__(self):
         l = ["interface %s\n" % self.name]
         if self.base is not None:
             l.append("\tbase %s\n" % self.base)
         l.append(str(self.attributes))
         if self.members is None:
@@ -732,38 +772,38 @@ class Interface(object):
             for m in self.members:
                 l.append(str(m))
         return "".join(l)
 
     def getConst(self, name, location):
         # The constant may be in a base class
         iface = self
         while name not in iface.namemap and iface is not None:
-            iface = self.idl.getName(self.base, self.location)
+            iface = self.idl.getName(TypeId(self.base), self.location)
         if iface is None:
-            raise IDLError("cannot find symbol '%s'" % name)
+            raise IDLError("cannot find symbol '%s'" % name, self.location)
         c = iface.namemap.get(name, location)
         if c.kind != 'const':
             raise IDLError("symbol '%s' is not a constant", c.location)
 
         return c.getValue()
 
     def needsJSTypes(self):
         for m in self.members:
-            if m.kind == "attribute" and m.type == "jsval":
+            if m.kind == "attribute" and m.type == TypeId("jsval"):
                 return True
             if m.kind == "method" and m.needsJSTypes():
                 return True
         return False
 
     def countEntries(self):
         ''' Returns the number of entries in the vtable for this interface. '''
         total = sum(member.count() for member in self.members)
         if self.base is not None:
-            realbase = self.idl.getName(self.base, self.location)
+            realbase = self.idl.getName(TypeId(self.base), self.location)
             total += realbase.countEntries()
         return total
 
 
 class InterfaceAttributes(object):
     uuid = None
     scriptable = False
     builtinclass = False
@@ -1079,17 +1119,17 @@ class Method(object):
                                     self.name,
                                     ", ".join([p.toIDL()
                                                for p in self.params]),
                                     raises)
 
     def needsJSTypes(self):
         if self.implicit_jscontext:
             return True
-        if self.type == "jsval":
+        if self.type == TypeId("jsval"):
             return True
         for p in self.params:
             t = p.realtype
             if isinstance(t, Native) and t.specialtype == "jsval":
                 return True
         return False
 
     def count(self):
@@ -1161,17 +1201,17 @@ class Param(object):
                 elif name == 'optional':
                     self.optional = True
                 else:
                     raise IDLError("Unexpected attribute '%s'" % name, aloc)
 
     def resolve(self, method):
         self.realtype = method.iface.idl.getName(self.type, self.location)
         if self.array:
-            self.realtype = Array(self.realtype)
+            self.realtype = LegacyArray(self.realtype)
         if (self.null is not None and
                 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
             raise IDLError("'Null' attribute can only be used on DOMString",
                            self.location)
         if (self.undefined is not None and
                 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
             raise IDLError("'Undefined' attribute can only be used on DOMString",
                            self.location)
@@ -1206,30 +1246,90 @@ class Param(object):
 
     def toIDL(self):
         return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
                                self.paramtype,
                                self.type,
                                self.name)
 
 
-class Array(object):
+class LegacyArray(object):
     def __init__(self, basetype):
         self.type = basetype
+        self.location = self.type.location
 
     def isScriptable(self):
         return self.type.isScriptable()
 
     def nativeType(self, calltype, const=False):
-        return "%s%s*" % (const and 'const ' or '',
-                          self.type.nativeType(calltype))
+        if 'element' in calltype:
+            raise IDLError("nested [array] unsupported", self.location)
+
+        # For legacy reasons, we have to add a 'const ' to builtin pointer array
+        # types. (`[array] in string` and `[array] in wstring` parameters)
+        if calltype == 'in' and isinstance(self.type, Builtin) and self.type.isPointer():
+            const = True
+
+        return "%s%s*%s" % ('const ' if const else '',
+                            self.type.nativeType('legacyelement'),
+                            '*' if 'out' in calltype else '')
 
     def rustType(self, calltype, const=False):
-        return "%s %s" % (const and '*const' or '*mut',
-                          self.type.rustType(calltype))
+        return "%s%s%s" % ('*mut ' if 'out' in calltype else '',
+                           '*const ' if const else '*mut ',
+                           self.type.rustType('legacyelement'))
+
+
+class Array(object):
+    kind = 'array'
+
+    def __init__(self, type, location):
+        self.type = type
+        self.location = location
+
+    @property
+    def name(self):
+        return "Array<%s>" % self.type.name
+
+    def resolve(self, idl):
+        idl.getName(self.type, self.location)
+
+    def isScriptable(self):
+        return self.type.isScriptable()
+
+    def nativeType(self, calltype):
+        if calltype == 'legacyelement':
+            raise IDLError("[array] Array<T> is unsupported", self.location)
+
+        base = 'nsTArray<%s>' % self.type.nativeType('element')
+        if 'out' in calltype:
+            return '%s& ' % base
+        elif 'in' == calltype:
+            return 'const %s& ' % base
+        else:
+            return base
+
+    def rustType(self, calltype):
+        # NOTE: To add Rust support, ensure 'element' is handled correctly in
+        # all rustType callees.
+        raise RustNoncompat("Array<...> types")
+
+
+TypeId = namedtuple('TypeId', 'name params')
+
+
+# Make str(TypeId) produce a nicer value
+TypeId.__str__ = lambda self: \
+    "%s<%s>" % (self.name, ', '.join(str(p) for p in self.params)) \
+    if self.params is not None \
+    else self.name
+
+
+# Allow skipping 'params' in TypeId(..)
+TypeId.__new__.__defaults__ = (None,)
 
 
 class IDLParser(object):
     keywords = {
         'const': 'CONST',
         'interface': 'INTERFACE',
         'in': 'IN',
         'inout': 'INOUT',
@@ -1262,17 +1362,17 @@ class IDLParser(object):
 
     hexchar = r'[a-fA-F0-9]'
 
     t_NUMBER = r'-?\d+'
     t_HEXNUM = r'0x%s+' % hexchar
     t_LSHIFT = r'<<'
     t_RSHIFT = r'>>'
 
-    literals = '"(){}[],;:=|+-*'
+    literals = '"(){}[]<>,;:=|+-*'
 
     t_ignore = ' \t'
 
     def t_multilinecomment(self, t):
         r'/\*(?s).*?\*/'
         t.lexer.lineno += t.value.count('\n')
         if t.value.startswith("/**"):
             self._doccomments.append(t.value)
@@ -1355,17 +1455,17 @@ class IDLParser(object):
         """productions : interface productions
                        | typedef productions
                        | native productions
                        | webidl productions"""
         p[0] = list(p[2])
         p[0].insert(0, p[1])
 
     def p_typedef(self, p):
-        """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
+        """typedef : TYPEDEF type IDENTIFIER ';'"""
         p[0] = Typedef(type=p[2],
                        name=p[3],
                        location=self.getLocation(p, 1),
                        doccomments=p.slice[1].doccomments)
 
     def p_native(self, p):
         """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
         p[0] = Native(name=p[3],
@@ -1468,17 +1568,17 @@ class IDLParser(object):
         p[0] = list(p[2])
         p[0].insert(0, p[1])
 
     def p_member_cdata(self, p):
         """member : CDATA"""
         p[0] = CDATA(p[1], self.getLoca