Merge autoland to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Mon, 07 Oct 2019 12:29:02 +0300
changeset 496474 3955e0a93047dec7ae5aebba4143aa61feed56ee
parent 496449 0d0bf1a4fa878505bc6fa36f5748107b42cebe12 (current diff)
parent 496473 011193f69f2fad43eeb92f597954b52d3e7755e8 (diff)
child 496475 9158256c73219920798e3c15c28d9ed9e068f9e0
child 496661 8208dff5da97fbd111c7497ea15ed9c5553d0b39
push id114143
push userrgurzau@mozilla.com
push dateMon, 07 Oct 2019 09:35:08 +0000
treeherdermozilla-inbound@3955e0a93047 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
3955e0a93047 / 71.0a1 / 20191007092954 / files
nightly linux64
3955e0a93047 / 71.0a1 / 20191007092954 / files
nightly mac
3955e0a93047 / 71.0a1 / 20191007092954 / files
nightly win32
3955e0a93047 / 71.0a1 / 20191007092954 / files
nightly win64
3955e0a93047 / 71.0a1 / 20191007092954 / 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 autoland to mozilla-central. a=merge
--- a/.cron.yml
+++ b/.cron.yml
@@ -114,16 +114,17 @@ jobs:
           type: decision-task
           treeherder-symbol: Searchfox
           target-tasks-method: searchfox_index
       run-on-projects:
           - mozilla-central
           - mozilla-beta
           - mozilla-release
           - mozilla-esr68
+          - ash
       when:
           - {hour: 10, minute: 0}
 
     - name: customv8-update
       job:
           type: decision-task
           treeherder-symbol: customv8
           target-tasks-method: customv8_update
--- a/accessible/base/Pivot.h
+++ b/accessible/base/Pivot.h
@@ -61,17 +61,16 @@ class Pivot final {
   Accessible* PrevText(Accessible* aAnchor, int32_t* aStartOffset,
                        int32_t* aEndOffset, int32_t aBoundaryType);
 
   // Return the accessible at the given screen coordinate if it matches the
   // pivot rule.
   Accessible* AtPoint(int32_t aX, int32_t aY, PivotRule& aRule);
 
  private:
-
   Accessible* AdjustStartPosition(Accessible* aAnchor, PivotRule& aRule,
                                   uint16_t* aFilterResult);
 
   // Search in preorder for the first accessible to match the rule.
   Accessible* SearchForward(Accessible* aAnchor, PivotRule& aRule,
                             bool aSearchCurrent);
 
   // Reverse search in preorder for the first accessible to match the rule.
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -1036,17 +1036,17 @@ class Accessible : public nsISupports {
     eIsNotInDocument = 1 << 1,  // accessible is not in document
     eSharedNode = 1 << 2,  // accessible shares DOM node from another accessible
     eNotNodeMapEntry = 1 << 3,   // accessible shouldn't be in document node map
     eHasNumericValue = 1 << 4,   // accessible has a numeric value
     eGroupInfoDirty = 1 << 5,    // accessible needs to update group info
     eKidsMutating = 1 << 6,      // subtree is being mutated
     eIgnoreDOMUIEvent = 1 << 7,  // don't process DOM UI events for a11y events
     eRelocated = 1 << 8,         // accessible was moved in tree
-    eNoKidsFromDOM = 1 << 9,    // accessible doesn't allow children from DOM
+    eNoKidsFromDOM = 1 << 9,     // accessible doesn't allow children from DOM
     eHasTextKids = 1 << 10,      // accessible have a text leaf in children
 
     eLastStateFlag = eHasTextKids
   };
 
   /**
    * Flags used for contextual information about the accessible.
    */
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -154,12 +154,12 @@ void HTMLListBulletAccessible::AppendTex
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: public
 
 bool HTMLListBulletAccessible::IsInside() const {
   if (nsIFrame* frame = mContent->GetPrimaryFrame()) {
     return frame->StyleList()->mListStylePosition ==
-        NS_STYLE_LIST_STYLE_POSITION_INSIDE;
+           NS_STYLE_LIST_STYLE_POSITION_INSIDE;
   }
   return false;
 }
--- a/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp
+++ b/accessible/ipc/extension/android/DocAccessiblePlatformExtChild.cpp
@@ -17,18 +17,18 @@ mozilla::ipc::IPCResult DocAccessiblePla
   if (auto acc = IdToAccessibleWrap(aID)) {
     acc->Pivot(aGranularity, aForward, aInclusive);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvNavigateText(
-    uint64_t aID, int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset,
-    bool aForward, bool aSelect) {
+    uint64_t aID, int32_t aGranularity, int32_t aStartOffset,
+    int32_t aEndOffset, bool aForward, bool aSelect) {
   if (auto acc = IdToAccessibleWrap(aID)) {
     acc->NavigateText(aGranularity, aStartOffset, aEndOffset, aForward,
                       aSelect);
   }
 
   return IPC_OK();
 }
 
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -31,18 +31,17 @@ using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULMenuitemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULMenuitemAccessible::XULMenuitemAccessible(nsIContent* aContent,
                                              DocAccessible* aDoc)
-    : AccessibleWrap(aContent, aDoc) {
-}
+    : AccessibleWrap(aContent, aDoc) {}
 
 uint64_t XULMenuitemAccessible::NativeState() const {
   uint64_t state = Accessible::NativeState();
 
   // Has Popup?
   if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
     state |= states::HASPOPUP;
     if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::open))
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -135,17 +135,16 @@
 
 #urlbar.megabar,
 #urlbar.megabar > #urlbar-input-container,
 #urlbar.megabar > .urlbarView {
   position: relative;
 }
 
 #urlbar.megabar > #urlbar-background {
-  content: "";
   display: block;
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
 }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2129,17 +2129,18 @@ nsDocShell::HistoryPurged(int32_t aNumEn
 
   return NS_OK;
 }
 
 void nsDocShell::TriggerParentCheckDocShellIsEmpty() {
   if (RefPtr<nsDocShell> parent = GetInProcessParentDocshell()) {
     parent->DocLoaderIsEmpty(true);
   }
-  if (GetBrowsingContext()->IsContentSubframe() && !GetBrowsingContext()->GetParent()->IsInProcess()) {
+  if (GetBrowsingContext()->IsContentSubframe() &&
+      !GetBrowsingContext()->GetParent()->IsInProcess()) {
     if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
       mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
           /*aIsTrusted*/ true, /*aFireLoadAtEmbeddingElement*/ false);
     }
   }
 }
 
 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
@@ -3961,17 +3962,18 @@ NS_IMETHODIMP
 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
                              const char16_t* aURL, nsIChannel* aFailedChannel,
                              bool* aDisplayedErrorPage) {
   // If we have a cross-process parent document, we must notify it that we no
   // longer block its load event.  This is necessary for OOP sub-documents
   // because error documents do not result in a call to
   // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
   // (Obviously, we must do this before any of the returns below.)
-  if (GetBrowsingContext()->IsContentSubframe() && !GetBrowsingContext()->GetParent()->IsInProcess()) {
+  if (GetBrowsingContext()->IsContentSubframe() &&
+      !GetBrowsingContext()->GetParent()->IsInProcess()) {
     if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
       mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
           /*aIsTrusted*/ true, /*aFireLoadAtEmbeddingElement*/ false);
     }
   }
 
   *aDisplayedErrorPage = false;
   // Get prompt and string bundle services
--- a/dom/base/UseCounter.h
+++ b/dom/base/UseCounter.h
@@ -46,17 +46,18 @@ enum UseCounter : int16_t {
 #undef CSS_PROP_LONGHAND
 #undef CSS_PROP_PUBLIC_OR_PRIVATE
 #undef CSS_PROP_USE_COUNTER
 
   eUseCounter_EndCSSProperties,
   eUseCounter_FirstCountedUnknownProperty = eUseCounter_EndCSSProperties,
   __reset_hack_2 = eUseCounter_FirstCountedUnknownProperty - 1,
 
-#define COUNTED_UNKNOWN_PROPERTY(name_, method_) eUseCounter_unknown_property_##method_,
+#define COUNTED_UNKNOWN_PROPERTY(name_, method_) \
+  eUseCounter_unknown_property_##method_,
 #include "mozilla/CountedUnknownProperties.h"
 #undef COUNTED_UNKNOWN_PROPERTY
 
   eUseCounter_Count
 };
 
 }  // namespace mozilla
 
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -616,17 +616,18 @@ void nsDOMMutationObserver::Observe(
   if (!aOptions.mCharacterData.WasPassed() &&
       aOptions.mCharacterDataOldValue.WasPassed()) {
     characterData = true;
   }
 
   if (!(childList || attributes || characterData || animations ||
         nativeAnonymousChildList)) {
     aRv.ThrowTypeError(
-        u"One of 'childList', 'attributes', 'characterData' must not be false.");
+        u"One of 'childList', 'attributes', 'characterData' must not be "
+        u"false.");
     return;
   }
 
   if (aOptions.mAttributeOldValue.WasPassed() &&
       aOptions.mAttributeOldValue.Value() && !attributes) {
     aRv.ThrowTypeError(
         u"If 'attributeOldValue' is true, 'attributes' must not be false.");
     return;
--- a/dom/base/nsFrameLoaderOwner.cpp
+++ b/dom/base/nsFrameLoaderOwner.cpp
@@ -78,19 +78,17 @@ void nsFrameLoaderOwner::ChangeRemotenes
 
   // When we destroy the original frameloader, it will stop blocking the parent
   // document's load event, and immediately trigger the load event if there are
   // no other blockers. Since we're going to be adding a new blocker as soon as
   // we recreate the frame loader, this is not what we want, so add our own
   // blocker until the process is complete.
   Document* doc = owner->OwnerDoc();
   doc->BlockOnload();
-  auto cleanup = MakeScopeExit([&]() {
-    doc->UnblockOnload(false);
-  });
+  auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });
 
   // If we already have a Frameloader, destroy it, possibly preserving its
   // browsing context.
   if (mFrameLoader) {
     if (ShouldPreserveBrowsingContext(aOptions)) {
       bc = mFrameLoader->GetBrowsingContext();
       mFrameLoader->SkipBrowsingContextDetach();
     }
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -771,17 +771,18 @@ nsresult nsPlainTextSerializer::DoOpenCo
       if (mOLStackIndex > 0) {
         nsAutoString valueAttr;
         if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) {
           nsresult rv = NS_OK;
           int32_t valueAttrVal = valueAttr.ToInteger(&rv);
           if (NS_SUCCEEDED(rv)) mOLStack[mOLStackIndex - 1] = valueAttrVal;
         }
         // This is what nsBulletFrame does for OLs:
-        mCurrentLine.mIndentation.mHeader.AppendInt(mOLStack[mOLStackIndex - 1]++, 10);
+        mCurrentLine.mIndentation.mHeader.AppendInt(
+            mOLStack[mOLStackIndex - 1]++, 10);
       } else {
         mCurrentLine.mIndentation.mHeader.Append(char16_t('#'));
       }
 
       mCurrentLine.mIndentation.mHeader.Append(char16_t('.'));
 
     } else {
       static const char bulletCharArray[] = "*o+#";
@@ -1001,17 +1002,18 @@ nsresult nsPlainTextSerializer::DoCloseC
     mLineBreakDue = true;
   } else if (aTag == nsGkAtoms::blockquote) {
     mOutputManager->Flush(mCurrentLine);  // Is this needed?
 
     // Pop
     bool isInCiteBlockquote = PopBool(mIsInCiteBlockquote);
 
     if (isInCiteBlockquote) {
-      NS_ASSERTION(mCurrentLine.mCiteQuoteLevel, "CiteQuote level will be negative!");
+      NS_ASSERTION(mCurrentLine.mCiteQuoteLevel,
+                   "CiteQuote level will be negative!");
       mCurrentLine.mCiteQuoteLevel--;
       mFloatingLines = 0;
       mHasWrittenCiteBlockquote = true;
     } else {
       mCurrentLine.mIndentation.mLength -= kTabSize;
       mFloatingLines = 1;
     }
     mLineBreakDue = true;
@@ -1590,27 +1592,26 @@ void nsPlainTextSerializer::Write(const 
   // We have two major codepaths here. One that does preformatted text and one
   // that does normal formatted text. The one for preformatted text calls
   // Output directly while the other code path goes through AddToLine.
   if ((mPreFormattedMail && !mSettings.GetWrapColumn()) ||
       (IsElementPreformatted() && !mPreFormattedMail) ||
       (mSpanLevel > 0 && mEmptyLines >= 0 && IsQuotedLine(str))) {
     // No intelligent wrapping.
 
-        // This mustn't be mixed with intelligent wrapping without clearing
-        // the mCurrentLine.mContent buffer before!!!
-        NS_ASSERTION(
-            mCurrentLine.mContent.IsEmpty() ||
-                (IsElementPreformatted() && !mPreFormattedMail),
-            "Mixed wrapping data and nonwrapping data on the same line");
-        MOZ_ASSERT(mOutputManager);
+    // This mustn't be mixed with intelligent wrapping without clearing
+    // the mCurrentLine.mContent buffer before!!!
+    NS_ASSERTION(mCurrentLine.mContent.IsEmpty() ||
+                     (IsElementPreformatted() && !mPreFormattedMail),
+                 "Mixed wrapping data and nonwrapping data on the same line");
+    MOZ_ASSERT(mOutputManager);
 
-        if (!mCurrentLine.mContent.IsEmpty()) {
-          mOutputManager->Flush(mCurrentLine);
-        }
+    if (!mCurrentLine.mContent.IsEmpty()) {
+      mOutputManager->Flush(mCurrentLine);
+    }
 
     ConvertToLinesAndOutput(str);
     return;
   }
 
   // Intelligent handling of text
   // If needed, strip out all "end of lines"
   // and multiple whitespace between words
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -131,19 +131,17 @@ class nsWrapperCache {
    * This getter does not check whether the wrapper is dead and in the process
    * of being finalized.
    *
    * This should only be called if you really need to see the raw contents of
    * this cache, for example as part of finalization. Don't store the result
    * anywhere or pass it into JSAPI functions that may cause the value to
    * escape.
    */
-  JSObject* GetWrapperMaybeDead() const {
-    return mWrapper;
-  }
+  JSObject* GetWrapperMaybeDead() const { return mWrapper; }
 
 #ifdef DEBUG
  private:
   static bool HasJSObjectMovedOp(JSObject* aWrapper);
 
  public:
 #endif
 
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -104,18 +104,18 @@ class TestInterface : public nsISupports
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&);
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      const nsAString&);
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      uint32_t,
                                                      const Nullable<bool>&);
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      TestInterface*);
-  static already_AddRefed<TestInterface> Constructor(
-      const GlobalObject&, uint32_t, TestInterface&);
+  static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
+                                                     uint32_t, TestInterface&);
 
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      Date&);
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      const ArrayBuffer&);
   static already_AddRefed<TestInterface> Constructor(const GlobalObject&,
                                                      const Uint8Array&);
   /*  static
@@ -1517,18 +1517,17 @@ class TestThrowingConstructorInterface :
       const GlobalObject&, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, const nsAString&, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, uint32_t, const Nullable<bool>&, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, TestInterface*, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
-      const GlobalObject&, uint32_t, TestInterface&,
-      ErrorResult&);
+      const GlobalObject&, uint32_t, TestInterface&, ErrorResult&);
 
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, Date&, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, const ArrayBuffer&, ErrorResult&);
   static already_AddRefed<TestThrowingConstructorInterface> Constructor(
       const GlobalObject&, const Uint8Array&, ErrorResult&);
   /*  static
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -264,17 +264,18 @@ void WebGLContext::BufferData(GLenum tar
 
   const auto checkedSize = CheckedInt<size_t>(size);
   if (!checkedSize.isValid())
     return ErrorOutOfMemory("size too large for platform.");
 
 #if defined(XP_MACOSX)
   // bug 1573048
   if (gl->WorkAroundDriverBugs() && size > 1200000000) {
-    return ErrorOutOfMemory("Allocations larger than 1200000000 fail on macOS.");
+    return ErrorOutOfMemory(
+        "Allocations larger than 1200000000 fail on macOS.");
   }
 #endif
 
   const UniqueBuffer zeroBuffer(calloc(checkedSize.value(), 1u));
   if (!zeroBuffer) return ErrorOutOfMemory("Failed to allocate zeros.");
 
   BufferDataImpl(target, uint64_t{checkedSize.value()},
                  (const uint8_t*)zeroBuffer.get(), usage);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2109,18 +2109,17 @@ void HTMLMediaElement::Load() {
        "handlingInput=%d hasAutoplayAttr=%d IsAllowedToPlay=%d "
        "ownerDoc=%p (%s) ownerDocUserActivated=%d "
        "muted=%d volume=%f",
        this, !!mSrcAttrStream, HasAttr(kNameSpaceID_None, nsGkAtoms::src),
        HasSourceChildren(this), UserActivation::IsHandlingUserInput(),
        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay),
        AutoplayPolicy::IsAllowedToPlay(*this), OwnerDoc(),
        DocumentOrigin(OwnerDoc()).get(),
-       OwnerDoc()->HasBeenUserGestureActivated(), mMuted,
-       mVolume));
+       OwnerDoc()->HasBeenUserGestureActivated(), mMuted, mVolume));
 
   if (mIsRunningLoadMethod) {
     return;
   }
 
   mIsDoingExplicitLoad = true;
   DoLoad();
 }
@@ -4399,23 +4398,23 @@ void HTMLMediaElement::UnbindFromTree(bo
   // paused if we're no longer in a document. Note that we need to dispatch this
   // even if there are other tasks in flight for this because these can be
   // cancelled if there's a new load.
   //
   // FIXME(emilio): Per that spec section, we should only do this if we used to
   // be connected, though other browsers match our current behavior...
   //
   // Also, https://github.com/whatwg/html/issues/4928
-  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
-      "dom::HTMLMediaElement::UnbindFromTree",
-      [self = RefPtr<HTMLMediaElement>(this)]() {
-        if (!self->IsInComposedDoc()) {
-          self->Pause();
-        }
-      });
+  nsCOMPtr<nsIRunnable> task =
+      NS_NewRunnableFunction("dom::HTMLMediaElement::UnbindFromTree",
+                             [self = RefPtr<HTMLMediaElement>(this)]() {
+                               if (!self->IsInComposedDoc()) {
+                                 self->Pause();
+                               }
+                             });
   RunInStableState(task);
 }
 
 /* static */
 CanPlayStatus HTMLMediaElement::GetCanPlay(
     const nsAString& aType, DecoderDoctorDiagnostics* aDiagnostics) {
   Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
   if (!containerType) {
@@ -7359,42 +7358,41 @@ already_AddRefed<Promise> HTMLMediaEleme
             }
             // No media attached to the element save it for later.
             return SinkInfoPromise::CreateAndResolve(aInfo, __func__);
           },
           [](nsresult res) {
             // Promise is rejected, sink not found.
             return SinkInfoPromise::CreateAndReject(res, __func__);
           })
-      ->Then(
-          mAbstractMainThread, __func__,
-          [promise, self = RefPtr<HTMLMediaElement>(this),
-           sinkId](const SinkInfoPromise::ResolveOrRejectValue& aValue) {
-            if (aValue.IsResolve()) {
-              self->mSink = MakePair(sinkId, aValue.ResolveValue());
-              promise->MaybeResolveWithUndefined();
-            } else {
-              switch (aValue.RejectValue()) {
-                case NS_ERROR_ABORT:
-                  promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
-                  break;
-                case NS_ERROR_NOT_AVAILABLE: {
-                  promise->MaybeRejectWithDOMException(
-                      NS_ERROR_DOM_NOT_FOUND_ERR,
-                      "The object can not be found here.");
-                  break;
-                }
-                case NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR:
-                  promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-                  break;
-                default:
-                  MOZ_ASSERT_UNREACHABLE("Invalid error.");
-              }
-            }
-          });
+      ->Then(mAbstractMainThread, __func__,
+             [promise, self = RefPtr<HTMLMediaElement>(this),
+              sinkId](const SinkInfoPromise::ResolveOrRejectValue& aValue) {
+               if (aValue.IsResolve()) {
+                 self->mSink = MakePair(sinkId, aValue.ResolveValue());
+                 promise->MaybeResolveWithUndefined();
+               } else {
+                 switch (aValue.RejectValue()) {
+                   case NS_ERROR_ABORT:
+                     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+                     break;
+                   case NS_ERROR_NOT_AVAILABLE: {
+                     promise->MaybeRejectWithDOMException(
+                         NS_ERROR_DOM_NOT_FOUND_ERR,
+                         "The object can not be found here.");
+                     break;
+                   }
+                   case NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR:
+                     promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+                     break;
+                   default:
+                     MOZ_ASSERT_UNREACHABLE("Invalid error.");
+                 }
+               }
+             });
 
   aRv = NS_OK;
   return promise.forget();
 }
 
 void HTMLMediaElement::NotifyTextTrackModeChanged() {
   if (mPendingTextTrackChanged) {
     return;
--- a/dom/ipc/JSWindowActor.h
+++ b/dom/ipc/JSWindowActor.h
@@ -74,17 +74,17 @@ class JSWindowActor : public nsISupports
 
   void StartDestroy();
 
   void AfterDestroy();
 
   void InvokeCallback(CallbackFunction willDestroy);
 
  private:
-  friend class ::nsQueryActor; // for QueryInterfaceActor
+  friend class ::nsQueryActor;  // for QueryInterfaceActor
 
   nsresult QueryInterfaceActor(const nsIID& aIID, void** aPtr);
 
   void ReceiveMessageOrQuery(JSContext* aCx,
                              const JSWindowActorMessageMeta& aMetadata,
                              JS::Handle<JS::Value> aData, ErrorResult& aRv);
 
   void ReceiveQueryReply(JSContext* aCx,
--- a/dom/localstorage/ActorsChild.cpp
+++ b/dom/localstorage/ActorsChild.cpp
@@ -148,46 +148,65 @@ mozilla::ipc::IPCResult LSObserverChild:
 
   return IPC_OK();
 }
 
 /*******************************************************************************
  * LocalStorageRequestChild
  ******************************************************************************/
 
-LSRequestChild::LSRequestChild(LSRequestChildCallback* aCallback)
-    : mCallback(aCallback), mFinishing(false) {
+LSRequestChild::LSRequestChild() : mFinishing(false) {
   AssertIsOnOwningThread();
 
   MOZ_COUNT_CTOR(LSRequestChild);
 }
 
 LSRequestChild::~LSRequestChild() {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(!mCallback);
 
   MOZ_COUNT_DTOR(LSRequestChild);
 }
 
+void LSRequestChild::SetCallback(LSRequestChildCallback* aCallback) {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!mCallback);
+  MOZ_ASSERT(Manager());
+
+  mCallback = aCallback;
+}
+
 bool LSRequestChild::Finishing() const {
   AssertIsOnOwningThread();
 
   return mFinishing;
 }
 
 void LSRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
   AssertIsOnOwningThread();
+
+  if (mCallback) {
+    MOZ_ASSERT(aWhy != Deletion);
+
+    mCallback->OnResponse(NS_ERROR_FAILURE);
+
+    mCallback = nullptr;
+  }
 }
 
 mozilla::ipc::IPCResult LSRequestChild::Recv__delete__(
     const LSRequestResponse& aResponse) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mCallback);
 
   mCallback->OnResponse(aResponse);
 
+  mCallback = nullptr;
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LSRequestChild::RecvReady() {
   AssertIsOnOwningThread();
 
   mFinishing = true;
 
@@ -197,41 +216,59 @@ mozilla::ipc::IPCResult LSRequestChild::
 
   return IPC_OK();
 }
 
 /*******************************************************************************
  * LSSimpleRequestChild
  ******************************************************************************/
 
-LSSimpleRequestChild::LSSimpleRequestChild(
-    LSSimpleRequestChildCallback* aCallback)
-    : mCallback(aCallback) {
+LSSimpleRequestChild::LSSimpleRequestChild() {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(aCallback);
 
   MOZ_COUNT_CTOR(LSSimpleRequestChild);
 }
 
 LSSimpleRequestChild::~LSSimpleRequestChild() {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(!mCallback);
 
   MOZ_COUNT_DTOR(LSSimpleRequestChild);
 }
 
+void LSSimpleRequestChild::SetCallback(LSSimpleRequestChildCallback* aCallback) {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!mCallback);
+  MOZ_ASSERT(Manager());
+
+  mCallback = aCallback;
+}
+
 void LSSimpleRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
   AssertIsOnOwningThread();
+
+  if (mCallback) {
+    MOZ_ASSERT(aWhy != Deletion);
+
+    mCallback->OnResponse(NS_ERROR_FAILURE);
+
+    mCallback = nullptr;
+  }
 }
 
 mozilla::ipc::IPCResult LSSimpleRequestChild::Recv__delete__(
     const LSSimpleRequestResponse& aResponse) {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(mCallback);
 
   mCallback->OnResponse(aResponse);
 
+  mCallback = nullptr;
+
   return IPC_OK();
 }
 
 /*******************************************************************************
  * LSSnapshotChild
  ******************************************************************************/
 
 LSSnapshotChild::LSSnapshotChild(LSSnapshot* aSnapshot) : mSnapshot(aSnapshot) {
--- a/dom/localstorage/ActorsChild.h
+++ b/dom/localstorage/ActorsChild.h
@@ -153,21 +153,23 @@ class LSRequestChild final : public PBac
   void AssertIsOnOwningThread() const {
     NS_ASSERT_OWNINGTHREAD(LSReqeustChild);
   }
 
   bool Finishing() const;
 
  private:
   // Only created by LSObject.
-  explicit LSRequestChild(LSRequestChildCallback* aCallback);
+  LSRequestChild();
 
   // Only destroyed by mozilla::ipc::BackgroundChildImpl.
   ~LSRequestChild();
 
+  void SetCallback(LSRequestChildCallback* aCallback);
+
   // IPDL methods are only called by IPDL.
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   mozilla::ipc::IPCResult Recv__delete__(
       const LSRequestResponse& aResponse) override;
 
   mozilla::ipc::IPCResult RecvReady() override;
 };
@@ -200,17 +202,19 @@ class LSSimpleRequestChild final : publi
 
  public:
   void AssertIsOnOwningThread() const {
     NS_ASSERT_OWNINGTHREAD(LSSimpleReqeustChild);
   }
 
  private:
   // Only created by LocalStorageManager2.
-  explicit LSSimpleRequestChild(LSSimpleRequestChildCallback* aCallback);
+  LSSimpleRequestChild();
+
+  void SetCallback(LSSimpleRequestChildCallback* aCallback);
 
   // Only destroyed by mozilla::ipc::BackgroundChildImpl.
   ~LSSimpleRequestChild();
 
   // IPDL methods are only called by IPDL.
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   mozilla::ipc::IPCResult Recv__delete__(
--- a/dom/localstorage/LSObject.cpp
+++ b/dom/localstorage/LSObject.cpp
@@ -471,19 +471,26 @@ LSRequestChild* LSObject::StartRequest(n
   PBackgroundChild* backgroundActor =
       XRE_IsParentProcess()
           ? BackgroundChild::GetOrCreateForCurrentThread(aMainEventTarget)
           : BackgroundChild::GetForCurrentThread();
   if (NS_WARN_IF(!backgroundActor)) {
     return nullptr;
   }
 
-  LSRequestChild* actor = new LSRequestChild(aCallback);
+  LSRequestChild* actor = new LSRequestChild();
 
-  backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams);
+  if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
+    return nullptr;
+  }
+
+  // Must set callback after calling SendPBackgroundLSRequestConstructor since
+  // it can be called synchronously when SendPBackgroundLSRequestConstructor
+  // fails.
+  actor->SetCallback(aCallback);
 
   return actor;
 }
 
 Storage::StorageType LSObject::Type() const {
   AssertIsOnOwningThread();
 
   return eLocalStorage;
--- a/dom/localstorage/LocalStorageManager2.cpp
+++ b/dom/localstorage/LocalStorageManager2.cpp
@@ -353,45 +353,55 @@ LSRequestChild* LocalStorageManager2::St
   AssertIsOnDOMFileThread();
 
   PBackgroundChild* backgroundActor =
       BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!backgroundActor)) {
     return nullptr;
   }
 
-  auto actor = new LSRequestChild(aCallback);
+  auto actor = new LSRequestChild();
 
   if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
     return nullptr;
   }
 
+  // Must set callback after calling SendPBackgroundLSRequestConstructor since
+  // it can be called synchronously when SendPBackgroundLSRequestConstructor
+  // fails.
+  actor->SetCallback(aCallback);
+
   return actor;
 }
 
 nsresult LocalStorageManager2::StartSimpleRequest(
     Promise* aPromise, const LSSimpleRequestParams& aParams) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPromise);
 
   PBackgroundChild* backgroundActor =
       BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!backgroundActor)) {
     return NS_ERROR_FAILURE;
   }
 
-  RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
-
-  auto actor = new LSSimpleRequestChild(resolver);
+  auto actor = new LSSimpleRequestChild();
 
   if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
                                                                   aParams)) {
     return NS_ERROR_FAILURE;
   }
 
+  RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
+
+  // Must set callback after calling SendPBackgroundLSRequestConstructor since
+  // it can be called synchronously when SendPBackgroundLSRequestConstructor
+  // fails.
+  actor->SetCallback(resolver);
+
   return NS_OK;
 }
 
 nsresult AsyncRequestHelper::Dispatch() {
   AssertIsOnOwningThread();
 
   nsCOMPtr<nsIEventTarget> domFileThread =
       IPCBlobInputStreamThread::GetOrCreate();
--- a/dom/media/MediaMIMETypes.cpp
+++ b/dom/media/MediaMIMETypes.cpp
@@ -82,17 +82,18 @@ bool MediaCodecs::ContainsAll(const Medi
   }
   return true;
 }
 
 bool MediaCodecs::ContainsPrefix(const nsAString& aCodecPrefix) const {
   const size_t prefixLength = aCodecPrefix.Length();
   for (const auto& myCodec : Range()) {
     if (myCodec.Length() >= prefixLength &&
-        memcmp(myCodec.Data(), aCodecPrefix.Data(), prefixLength * sizeof(char16_t)) == 0) {
+        memcmp(myCodec.Data(), aCodecPrefix.Data(),
+               prefixLength * sizeof(char16_t)) == 0) {
       return true;
     }
   }
   return false;
 }
 
 size_t MediaCodecs::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
   return mCodecs.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
--- a/dom/media/systemservices/DeviceChangeCallback.h
+++ b/dom/media/systemservices/DeviceChangeCallback.h
@@ -16,18 +16,17 @@ class DeviceChangeCallback {
  public:
   virtual ~DeviceChangeCallback() = default;
   virtual void OnDeviceChange() = 0;
 };
 
 class DeviceChangeNotifier {
  public:
   DeviceChangeNotifier()
-      : mCallbackMutex("mozilla::DeviceChangeCallback::mCallbackMutex") {
-  }
+      : mCallbackMutex("mozilla::DeviceChangeCallback::mCallbackMutex") {}
 
   void NotifyDeviceChange() {
     MutexAutoLock lock(mCallbackMutex);
     for (DeviceChangeCallback* observer : mDeviceChangeCallbackList) {
       observer->OnDeviceChange();
     }
   }
 
@@ -39,18 +38,17 @@ class DeviceChangeNotifier {
     return 0;
   }
 
   int RemoveDeviceChangeCallback(DeviceChangeCallback* aCallback) {
     MutexAutoLock lock(mCallbackMutex);
     return RemoveDeviceChangeCallbackLocked(aCallback);
   }
 
-  int RemoveDeviceChangeCallbackLocked(
-      DeviceChangeCallback* aCallback) {
+  int RemoveDeviceChangeCallbackLocked(DeviceChangeCallback* aCallback) {
     mCallbackMutex.AssertCurrentThreadOwns();
     if (mDeviceChangeCallbackList.IndexOf(aCallback) !=
         mDeviceChangeCallbackList.NoIndex)
       mDeviceChangeCallbackList.RemoveElement(aCallback);
     return 0;
   }
 
   virtual ~DeviceChangeNotifier() = default;
--- a/dom/media/webrtc/CubebDeviceEnumerator.cpp
+++ b/dom/media/webrtc/CubebDeviceEnumerator.cpp
@@ -309,16 +309,16 @@ void CubebDeviceEnumerator::AudioDeviceL
     if (aSide == Side::INPUT) {
       mInputDevices.Clear();
     } else {
       MOZ_ASSERT(aSide == Side::OUTPUT);
       mOutputDevices.Clear();
     }
   }
 
-  NS_DispatchToMainThread(NS_NewRunnableFunction(
-      "CubebDeviceEnumerator::AudioDeviceListChanged",
-      [self = RefPtr<CubebDeviceEnumerator>(this)]() {
-        self->NotifyDeviceChange();
-      }));
+  NS_DispatchToMainThread(
+      NS_NewRunnableFunction("CubebDeviceEnumerator::AudioDeviceListChanged",
+                             [self = RefPtr<CubebDeviceEnumerator>(this)]() {
+                               self->NotifyDeviceChange();
+                             }));
 }
 
 }  // namespace mozilla
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -25,18 +25,17 @@ class MediaEngineSource;
 
 enum MediaSinkEnum {
   Speaker,
   Other,
 };
 
 enum { kVideoTrack = 1, kAudioTrack = 2, kTrackCount };
 
-class MediaEngine : public DeviceChangeNotifier,
-                    public DeviceChangeCallback {
+class MediaEngine : public DeviceChangeNotifier, public DeviceChangeCallback {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
   NS_DECL_OWNINGTHREAD
 
   void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(MediaEngine); }
 
   /**
    * Populate an array of sources of the requested type in the nsTArray.
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -115,18 +115,17 @@ nsresult MediaEngineWebRTCMicrophoneSour
   // A pref can force the channel count to use. If the pref has a value of zero
   // or lower, it has no effect.
   if (aInPrefs.mChannels <= 0) {
     prefs.mChannels = maxChannels;
   }
 
   // Get the number of channels asked for by content, and clamp it between the
   // pref and the maximum number of channels that the device supports.
-  prefs.mChannels =
-      c.mChannelCount.Get(std::min(prefs.mChannels, maxChannels));
+  prefs.mChannels = c.mChannelCount.Get(std::min(prefs.mChannels, maxChannels));
   prefs.mChannels = std::max(1, std::min(prefs.mChannels, maxChannels));
 
   LOG("Audio config: aec: %d, agc: %d, noise: %d, channels: %d",
       prefs.mAecOn ? prefs.mAec : -1, prefs.mAgcOn ? prefs.mAgc : -1,
       prefs.mNoiseOn ? prefs.mNoise : -1, prefs.mChannels);
 
   *aOutPrefs = prefs;
 
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -18,17 +18,16 @@ class SharedBuffer;
 namespace dom {
 
 class SpeechSynthesisUtterance;
 class SpeechSynthesis;
 
 class nsSpeechTask : public nsISpeechTask,
                      public nsIAudioChannelAgentCallback,
                      public nsSupportsWeakReference {
-
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsSpeechTask, nsISpeechTask)
 
   NS_DECL_NSISPEECHTASK
   NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
 
   explicit nsSpeechTask(SpeechSynthesisUtterance* aUtterance, bool aIsChrome);
--- a/dom/payments/BasicCardPayment.cpp
+++ b/dom/payments/BasicCardPayment.cpp
@@ -106,17 +106,18 @@ bool BasicCardService::IsValidExpiryYear
       if (aExpiryYear.CharAt(index) < '0' || aExpiryYear.CharAt(index) > '9') {
         return false;
       }
     }
   }
   return true;
 }
 
-void BasicCardService::CheckForValidBasicCardErrors(JSContext* aCx, JSObject* aData,
+void BasicCardService::CheckForValidBasicCardErrors(JSContext* aCx,
+                                                    JSObject* aData,
                                                     ErrorResult& aRv) {
   MOZ_ASSERT(aData, "Don't pass null data");
   JS::RootedValue data(aCx, JS::ObjectValue(*aData));
 
   // XXXbz Just because aData converts to BasicCardErrors right now doesn't mean
   // it will if someone tries again!  Should we be replacing aData with a
   // conversion of the BasicCardErrors dictionary to a JS object in a clean
   // compartment or something?
--- a/dom/script/ScriptLoadHandler.cpp
+++ b/dom/script/ScriptLoadHandler.cpp
@@ -72,17 +72,18 @@ nsresult ScriptLoadHandler::DecodeRawDat
   MOZ_ASSERT(written <= needed.value());
 
   // Telemetry: Measure the throughput at which bytes are streamed and the ratio
   // of streamed bytes, such that we can determine the effectiveness of a
   // streaming parser for JavaScript.
   using namespace mozilla::Telemetry;
   if (aEndOfStream && haveRead) {
     // Compute the percent of data transfered incrementally.
-    Accumulate(DOM_SCRIPT_LOAD_INCREMENTAL_RATIO, 100 * haveRead / (haveRead + written));
+    Accumulate(DOM_SCRIPT_LOAD_INCREMENTAL_RATIO,
+               100 * haveRead / (haveRead + written));
     // Compute the rate of transfer of the incremental data calls averaged
     // across the time needed to complete the request.
     auto streamingTime = TimeStamp::Now() - mFirstOnIncrementalData;
     double ms = streamingTime.ToMilliseconds();
     Accumulate(DOM_SCRIPT_LOAD_INCREMENTAL_AVG_TRANSFER_RATE, haveRead / ms);
     mRequest->mStreamingTime = streamingTime;
   }
   if (!aEndOfStream && !haveRead) {
--- a/dom/script/ScriptLoadHandler.h
+++ b/dom/script/ScriptLoadHandler.h
@@ -94,16 +94,17 @@ class ScriptLoadHandler final : public n
   nsAutoPtr<SRICheckDataVerifier> mSRIDataVerifier;
 
   // Status of SRI data operations.
   nsresult mSRIStatus;
 
   // Unicode decoder for charset.
   mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 
-  // Used to compute the DOM_SCRIPT_LOAD_INCREMENTAL_AVG_TRANSFER_RATE telemetry.
+  // Used to compute the DOM_SCRIPT_LOAD_INCREMENTAL_AVG_TRANSFER_RATE
+  // telemetry.
   TimeStamp mFirstOnIncrementalData;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ScriptLoadHandler_h
--- a/dom/security/nsContentSecurityUtils.cpp
+++ b/dom/security/nsContentSecurityUtils.cpp
@@ -504,30 +504,32 @@ void nsContentSecurityUtils::AssertAbout
     // pages.
     return;
   }
 
   MOZ_ASSERT(!foundUnsafeEval,
              "about: page must not contain a CSP including 'unsafe-eval'");
 
   static nsLiteralCString sLegacyUnsafeInlineAllowList[] = {
-    // Bug 1579160: Remove 'unsafe-inline' from style-src within about:preferences
-    NS_LITERAL_CSTRING("about:preferences"),
-    // Bug 1571346: Remove 'unsafe-inline' from style-src within about:addons
-    NS_LITERAL_CSTRING("about:addons"),
-    // Bug 1584485: Remove 'unsafe-inline' from style-src within:
-    // * about:newtab
-    // * about:welcome
-    // * about:home
-    NS_LITERAL_CSTRING("about:newtab"),
-    NS_LITERAL_CSTRING("about:welcome"),
-    NS_LITERAL_CSTRING("about:home"),
+      // Bug 1579160: Remove 'unsafe-inline' from style-src within
+      // about:preferences
+      NS_LITERAL_CSTRING("about:preferences"),
+      // Bug 1571346: Remove 'unsafe-inline' from style-src within about:addons
+      NS_LITERAL_CSTRING("about:addons"),
+      // Bug 1584485: Remove 'unsafe-inline' from style-src within:
+      // * about:newtab
+      // * about:welcome
+      // * about:home
+      NS_LITERAL_CSTRING("about:newtab"),
+      NS_LITERAL_CSTRING("about:welcome"),
+      NS_LITERAL_CSTRING("about:home"),
   };
 
-  for (const nsLiteralCString& aUnsafeInlineEntry : sLegacyUnsafeInlineAllowList) {
+  for (const nsLiteralCString& aUnsafeInlineEntry :
+       sLegacyUnsafeInlineAllowList) {
     // please note that we perform a substring match here on purpose,
     // so we don't have to deal and parse out all the query arguments
     // the various about pages rely on.
     if (StringBeginsWith(aboutSpec, aUnsafeInlineEntry)) {
       return;
     }
   }
 
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -2,43 +2,103 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EditorUtils_h
 #define mozilla_EditorUtils_h
 
 #include "mozilla/ContentIterator.h"
-#include "mozilla/dom/Selection.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorBase.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/RangeBoundary.h"
+#include "mozilla/dom/Selection.h"
+#include "mozilla/dom/StaticRange.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsIEditor.h"
+#include "nsRange.h"
 #include "nscore.h"
 
 class nsAtom;
 class nsISimpleEnumerator;
 class nsITransferable;
-class nsRange;
 
 namespace mozilla {
 class MoveNodeResult;
 template <class T>
 class OwningNonNull;
 
 namespace dom {
 class Element;
 class Text;
 }  // namespace dom
 
 /***************************************************************************
+ * EditResult returns nsresult and preferred point where selection should be
+ * collapsed or the range where selection should select.
+ *
+ * NOTE: If we stop modifying selection at every DOM tree change, perhaps,
+ *       the following classes need to inherit this class.
+ */
+class MOZ_STACK_CLASS EditResult final {
+ public:
+  bool Succeeded() const { return NS_SUCCEEDED(mRv); }
+  bool Failed() const { return NS_FAILED(mRv); }
+  nsresult Rv() const { return mRv; }
+  bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; }
+  const EditorDOMPoint& PointRefToCollapseSelection() const {
+    MOZ_DIAGNOSTIC_ASSERT(mStartPoint.IsSet());
+    MOZ_DIAGNOSTIC_ASSERT(mStartPoint == mEndPoint);
+    return mStartPoint;
+  }
+  const EditorDOMPoint& StartPointRef() const { return mStartPoint; }
+  const EditorDOMPoint& EndPointRef() const { return mEndPoint; }
+  already_AddRefed<dom::StaticRange> CreateStaticRange() const {
+    return dom::StaticRange::Create(mStartPoint.ToRawRangeBoundary(),
+                                    mEndPoint.ToRawRangeBoundary(),
+                                    IgnoreErrors());
+  }
+  already_AddRefed<nsRange> CreateRange() const {
+    return nsRange::Create(mStartPoint.ToRawRangeBoundary(),
+                           mEndPoint.ToRawRangeBoundary(), IgnoreErrors());
+  }
+
+  EditResult() = delete;
+  explicit EditResult(nsresult aRv) : mRv(aRv) {
+    MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
+  }
+  template <typename PT, typename CT>
+  explicit EditResult(const EditorDOMPointBase<PT, CT>& aPointToPutCaret)
+      : mRv(aPointToPutCaret.IsSet() ? NS_OK : NS_ERROR_FAILURE),
+        mStartPoint(aPointToPutCaret),
+        mEndPoint(aPointToPutCaret) {}
+
+  template <typename SPT, typename SCT, typename EPT, typename ECT>
+  EditResult(const EditorDOMPointBase<SPT, SCT>& aStartPoint,
+             const EditorDOMPointBase<EPT, ECT>& aEndPoint)
+      : mRv(aStartPoint.IsSet() && aEndPoint.IsSet() ? NS_OK
+                                                     : NS_ERROR_FAILURE),
+        mStartPoint(aStartPoint),
+        mEndPoint(aEndPoint) {}
+
+  EditResult(const EditResult& aOther) = delete;
+  EditResult& operator=(const EditResult& aOther) = delete;
+  EditResult(EditResult&& aOther) = default;
+  EditResult& operator=(EditResult&& aOther) = default;
+
+ private:
+  nsresult mRv;
+  EditorDOMPoint mStartPoint;
+  EditorDOMPoint mEndPoint;
+};
+
+/***************************************************************************
  * EditActionResult is useful to return multiple results of an editor
  * action handler without out params.
  * Note that when you return an anonymous instance from a method, you should
  * use EditActionIgnored(), EditActionHandled() or EditActionCanceled() for
  * easier to read.  In other words, EditActionResult should be used when
  * declaring return type of a method, being an argument or defined as a local
  * variable.
  */
@@ -324,16 +384,18 @@ inline MoveNodeResult MoveNodeHandled(
  * EditorBase::SplitNodeDeepWithTransaction().
  * This makes the callers' code easier to read.
  */
 class MOZ_STACK_CLASS SplitNodeResult final {
  public:
   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
   bool Failed() const { return NS_FAILED(mRv); }
   nsresult Rv() const { return mRv; }
+  bool Handled() const { return mPreviousNode || mNextNode; }
+  bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; }
 
   /**
    * DidSplit() returns true if a node was actually split.
    */
   bool DidSplit() const { return mPreviousNode && mNextNode; }
 
   /**
    * GetLeftNode() simply returns the left node which was created at splitting.
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -3768,39 +3768,33 @@ EditActionResult HTMLEditor::TryToJoinBl
 
       // Because we don't want the moving content to receive the style of the
       // previous content, we split the previous content's style.
 
       RefPtr<Element> editingHost = GetActiveEditingHost();
       // XXX It's odd to continue handling this edit action if there is no
       //     editing host.
       if (!editingHost || &aLeftContentInBlock != editingHost) {
-        nsCOMPtr<nsIContent> splittedPreviousContent;
-        nsCOMPtr<nsINode> previousContentParent =
-            atPreviousContent.GetContainer();
-        int32_t previousContentOffset = atPreviousContent.Offset();
-        rv = SplitStyleAbovePoint(
-            address_of(previousContentParent), &previousContentOffset, nullptr,
-            nullptr, nullptr, getter_AddRefs(splittedPreviousContent));
-        if (NS_WARN_IF(Destroyed())) {
-          return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
-        }
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return EditActionIgnored(rv);
-        }
-
-        if (splittedPreviousContent) {
-          atPreviousContent.Set(splittedPreviousContent);
-          if (NS_WARN_IF(!atPreviousContent.IsSet())) {
-            return EditActionIgnored(NS_ERROR_NULL_POINTER);
-          }
-        } else {
-          atPreviousContent.Set(previousContentParent, previousContentOffset);
-          if (NS_WARN_IF(!atPreviousContent.IsSet())) {
-            return EditActionIgnored(NS_ERROR_NULL_POINTER);
+        SplitNodeResult splitResult = SplitAncestorStyledInlineElementsAt(
+            atPreviousContent, nullptr, nullptr);
+        if (NS_WARN_IF(splitResult.Failed())) {
+          return EditActionIgnored(splitResult.Rv());
+        }
+
+        if (splitResult.Handled()) {
+          if (splitResult.GetNextNode()) {
+            atPreviousContent.Set(splitResult.GetNextNode());
+            if (NS_WARN_IF(!atPreviousContent.IsSet())) {
+              return EditActionIgnored(NS_ERROR_NULL_POINTER);
+            }
+          } else {
+            atPreviousContent = splitResult.SplitPoint();
+            if (NS_WARN_IF(!atPreviousContent.IsSet())) {
+              return EditActionIgnored(NS_ERROR_NULL_POINTER);
+            }
           }
         }
       }
 
       ret |= MoveOneHardLineContents(EditorDOMPoint(rightBlock, 0),
                                      atPreviousContent);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
@@ -6192,124 +6186,113 @@ CreateElementResult HTMLEditor::ChangeLi
   return CreateElementResult(listElement.forget());
 }
 
 nsresult HTMLEditor::CreateStyleForInsertText(AbstractRange& aAbstractRange) {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(aAbstractRange.IsPositioned());
   MOZ_ASSERT(mTypeInState);
 
-  nsCOMPtr<nsINode> node = aAbstractRange.GetStartContainer();
-  int32_t offset = aAbstractRange.StartOffset();
-
   RefPtr<Element> documentRootElement = GetDocument()->GetRootElement();
   if (NS_WARN_IF(!documentRootElement)) {
     return NS_ERROR_FAILURE;
   }
 
   // process clearing any styles first
   UniquePtr<PropItem> item = mTypeInState->TakeClearProperty();
 
-  bool weDidSomething = false;
+  EditorDOMPoint pointToPutCaret(aAbstractRange.StartRef());
+  bool putCaret = false;
   {
     // Transactions may set selection, but we will set selection if necessary.
     AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
-    while (item && node != documentRootElement) {
-      // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
-      //     method.
-      nsresult rv =
-          ClearStyle(address_of(node), &offset, MOZ_KnownLive(item->tag),
-                     MOZ_KnownLive(item->attr));
-      if (NS_WARN_IF(Destroyed())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+    while (item && pointToPutCaret.GetContainer() != documentRootElement) {
+      EditResult result = ClearStyleAt(
+          pointToPutCaret, MOZ_KnownLive(item->tag), MOZ_KnownLive(item->attr));
+      if (NS_WARN_IF(result.Failed())) {
+        return result.Rv();
+      }
+      pointToPutCaret = result.PointRefToCollapseSelection();
       item = mTypeInState->TakeClearProperty();
-      weDidSomething = true;
+      putCaret = true;
     }
   }
 
   // then process setting any styles
   int32_t relFontSize = mTypeInState->TakeRelativeFontSize();
   item = mTypeInState->TakeSetProperty();
 
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
-    if (RefPtr<Text> text = node->GetAsText()) {
+    if (pointToPutCaret.IsInTextNode()) {
       // if we are in a text node, split it
       SplitNodeResult splitTextNodeResult = SplitNodeDeepWithTransaction(
-          *text, EditorDOMPoint(text, offset),
+          MOZ_KnownLive(*pointToPutCaret.GetContainerAsText()), pointToPutCaret,
           SplitAtEdges::eAllowToCreateEmptyContainer);
       if (NS_WARN_IF(Destroyed())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(splitTextNodeResult.Failed())) {
         return splitTextNodeResult.Rv();
       }
-      EditorRawDOMPoint splitPoint(splitTextNodeResult.SplitPoint());
-      node = splitPoint.GetContainer();
-      offset = splitPoint.Offset();
-    }
-    if (!IsContainer(node)) {
+      pointToPutCaret = splitTextNodeResult.SplitPoint();
+    }
+    if (!IsContainer(pointToPutCaret.GetContainer())) {
       return NS_OK;
     }
-    RefPtr<Text> newNode = CreateTextNode(EmptyString());
-    if (NS_WARN_IF(!newNode)) {
+    RefPtr<Text> newEmptyTextNode = CreateTextNode(EmptyString());
+    if (NS_WARN_IF(!newEmptyTextNode)) {
       return NS_ERROR_FAILURE;
     }
-    nsresult rv =
-        InsertNodeWithTransaction(*newNode, EditorDOMPoint(node, offset));
+    nsresult rv = InsertNodeWithTransaction(*newEmptyTextNode, pointToPutCaret);
     if (NS_WARN_IF(Destroyed())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-    node = newNode;
-    offset = 0;
-    weDidSomething = true;
+    pointToPutCaret.Set(newEmptyTextNode, 0);
+    putCaret = true;
 
     if (relFontSize) {
       // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
       HTMLEditor::FontSize dir = relFontSize > 0 ? HTMLEditor::FontSize::incr
                                                  : HTMLEditor::FontSize::decr;
       for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
-        rv = RelativeFontChangeOnTextNode(dir, *newNode, 0, -1);
+        rv = RelativeFontChangeOnTextNode(dir, *newEmptyTextNode, 0, -1);
         if (NS_WARN_IF(Destroyed())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
 
     while (item) {
-      rv = SetInlinePropertyOnNode(MOZ_KnownLive(*node->AsContent()),
-                                   MOZ_KnownLive(*item->tag),
-                                   MOZ_KnownLive(item->attr), item->value);
+      rv = SetInlinePropertyOnNode(
+          MOZ_KnownLive(*pointToPutCaret.GetContainerAsContent()),
+          MOZ_KnownLive(*item->tag), MOZ_KnownLive(item->attr), item->value);
       if (NS_WARN_IF(Destroyed())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       item = mTypeInState->TakeSetProperty();
     }
   }
 
-  if (!weDidSomething) {
+  if (!putCaret) {
     return NS_OK;
   }
 
-  nsresult rv = SelectionRefPtr()->Collapse(node, offset);
+  nsresult rv = SelectionRefPtr()->Collapse(pointToPutCaret);
   if (NS_WARN_IF(Destroyed())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Selection::Collapse() failed");
   return rv;
 }
 
 bool HTMLEditor::IsEmptyBlockElement(Element& aElement,
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -308,19 +308,18 @@ void HTMLEditor::PreDestroy(bool aDestro
   // stay around (which they would, since the frames have an owning reference).
   PresShell* presShell = GetPresShell();
   if (presShell && presShell->IsDestroying()) {
     // Just destroying PresShell now.
     // We have to keep UI elements of anonymous content until PresShell
     // is destroyed.
     RefPtr<HTMLEditor> self = this;
     nsContentUtils::AddScriptRunner(
-        NS_NewRunnableFunction("HTMLEditor::PreDestroy", [self]() {
-          self->HideAnonymousEditingUIs();
-        }));
+        NS_NewRunnableFunction("HTMLEditor::PreDestroy",
+                               [self]() { self->HideAnonymousEditingUIs(); }));
   } else {
     // PresShell is alive or already gone.
     HideAnonymousEditingUIs();
   }
 
   EditorBase::PreDestroy(aDestroyingFrames);
 }
 
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -39,16 +39,17 @@ class nsIClipboard;
 class nsTableWrapperFrame;
 class nsRange;
 
 namespace mozilla {
 class AlignStateAtSelection;
 class AutoSelectionSetterAfterTableEdit;
 class AutoSetTemporaryAncestorLimiter;
 class EditActionResult;
+class EditResult;
 class EmptyEditableFunctor;
 class ListElementSelectionState;
 class ListItemElementSelectionState;
 class MoveNodeResult;
 class ParagraphStateAtSelection;
 class ResizerSelectionListener;
 class SplitRangeOffFromNodeResult;
 class WSRunObject;
@@ -972,21 +973,34 @@ class HTMLEditor final : public TextEdit
   nsresult RelativeFontChangeOnTextNode(FontSize aDir, Text& aTextNode,
                                         int32_t aStartOffset,
                                         int32_t aEndOffset);
 
   MOZ_CAN_RUN_SCRIPT
   nsresult SetInlinePropertyOnNode(nsIContent& aNode, nsAtom& aProperty,
                                    nsAtom* aAttribute, const nsAString& aValue);
 
-  MOZ_CAN_RUN_SCRIPT
-  nsresult SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-                                nsAtom* aProperty, nsAtom* aAttribute,
-                                nsIContent** aOutLeftNode = nullptr,
-                                nsIContent** aOutRightNode = nullptr);
+  /**
+   * SplitAncestorStyledInlineElementsAt() splits ancestor inline elements at
+   * aPointToSplit if specified style matches with them.
+   *
+   * @param aPointToSplit       The point to split style at.
+   * @param aProperty           The style tag name which you want to split.
+   *                            Set nullptr if you want to split any styled
+   *                            elements.
+   * @param aAttribute          Attribute name if aProperty has some styles
+   *                            like nsGkAtoms::font.
+   * @return                    The result of SplitNodeDeepWithTransaction()
+   *                            with topmost split element.  If this didn't
+   *                            find inline elements to be split, Handled()
+   *                            returns false.
+   */
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult
+  SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
+                                      nsAtom* aProperty, nsAtom* aAttribute);
 
   nsIContent* GetPriorHTMLSibling(nsINode* aNode);
 
   nsIContent* GetNextHTMLSibling(nsINode* aNode);
 
   /**
    * GetPreviousHTMLElementOrText*() methods are similar to
    * EditorBase::GetPreviousElementOrText*() but this won't return nodes
@@ -1133,19 +1147,30 @@ class HTMLEditor final : public TextEdit
   nsIContent* GetFirstEditableLeaf(nsINode& aNode);
   nsIContent* GetLastEditableLeaf(nsINode& aNode);
 
   nsresult GetInlinePropertyBase(nsAtom& aProperty, nsAtom* aAttribute,
                                  const nsAString* aValue, bool* aFirst,
                                  bool* aAny, bool* aAll,
                                  nsAString* outValue) const;
 
-  MOZ_CAN_RUN_SCRIPT
-  nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-                      nsAtom* aProperty, nsAtom* aAttribute);
+  /**
+   * ClearStyleAt() splits parent elements to remove the specified style.
+   * If this splits some parent elements at near their start or end, such
+   * empty elements will be removed.  Then, remove the specified style
+   * from the point and returns DOM point to put caret.
+   *
+   * @param aPoint      The point to clear style at.
+   * @param aProperty   An HTML tag name which represents a style.
+   *                    Set nullptr if you want to clear all styles.
+   * @param aAttribute  Attribute name if aProperty has some styles like
+   *                    nsGkAtoms::font.
+   */
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditResult ClearStyleAt(
+      const EditorDOMPoint& aPoint, nsAtom* aProperty, nsAtom* aAttribute);
 
   MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsolute(Element& aElement);
   MOZ_CAN_RUN_SCRIPT nsresult SetPositionToStatic(Element& aElement);
 
   /**
    * OnModifyDocument() is called when the editor is changed.  This should
    * be called only by runnable in HTMLEditor::OnDocumentModified() to call
    * HTMLEditor::OnModifyDocument() with AutoEditActionDataSetter instance.
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -283,22 +283,20 @@ nsresult HTMLEditor::DoInsertHTMLWithCon
   if (!cellSelectionMode) {
     rv = DeleteSelectionAndPrepareToCreateNode();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (aClearStyle) {
       // pasting does not inherit local inline styles
-      nsCOMPtr<nsINode> tmpNode = SelectionRefPtr()->GetAnchorNode();
-      int32_t tmpOffset =
-          static_cast<int32_t>(SelectionRefPtr()->AnchorOffset());
-      rv = ClearStyle(address_of(tmpNode), &tmpOffset, nullptr, nullptr);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+      EditResult result = ClearStyleAt(
+          EditorDOMPoint(SelectionRefPtr()->AnchorRef()), nullptr, nullptr);
+      if (NS_WARN_IF(result.Failed())) {
+        return result.Rv();
       }
     }
   } else {
     // Delete whole cells: we will replace with new table content.
 
     // Braces for artificial block to scope AutoSelectionRestorer.
     // Save current selection since DeleteTableCellWithTransaction() perturbs
     // it.
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -7,19 +7,20 @@
 
 #include "HTMLEditUtils.h"
 #include "TypeInState.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ContentIterator.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/SelectionState.h"
-#include "mozilla/dom/Selection.h"
+#include "mozilla/mozalloc.h"
 #include "mozilla/dom/Element.h"
-#include "mozilla/mozalloc.h"
+#include "mozilla/dom/HTMLBRElement.h"
+#include "mozilla/dom/Selection.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsCOMPtr.h"
 #include "nsCaseTreatment.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
@@ -568,212 +569,292 @@ nsresult HTMLEditor::SetInlinePropertyOn
 
   return NS_OK;
 }
 
 nsresult HTMLEditor::SplitStyleAboveRange(nsRange* aRange, nsAtom* aProperty,
                                           nsAtom* aAttribute) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
-  if (NS_WARN_IF(!aRange)) {
+  if (NS_WARN_IF(!aRange) || NS_WARN_IF(!aRange->IsPositioned())) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsCOMPtr<nsINode> startNode = aRange->GetStartContainer();
-  int32_t startOffset = aRange->StartOffset();
-  nsCOMPtr<nsINode> endNode = aRange->GetEndContainer();
-  int32_t endOffset = aRange->EndOffset();
-
-  nsCOMPtr<nsINode> origStartNode = startNode;
+  EditorDOMPoint startOfRange(aRange->StartRef());
+  EditorDOMPoint endOfRange(aRange->EndRef());
 
   // split any matching style nodes above the start of range
   {
-    AutoTrackDOMPoint tracker(RangeUpdaterRef(), address_of(endNode),
-                              &endOffset);
-    nsresult rv = SplitStyleAbovePoint(address_of(startNode), &startOffset,
-                                       aProperty, aAttribute);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+    AutoTrackDOMPoint tracker(RangeUpdaterRef(), &endOfRange);
+    SplitNodeResult result = SplitAncestorStyledInlineElementsAt(
+        startOfRange, aProperty, aAttribute);
+    if (NS_WARN_IF(result.Failed())) {
+      return result.Rv();
+    }
+    if (result.Handled()) {
+      startOfRange = result.SplitPoint();
+      if (NS_WARN_IF(!startOfRange.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
     }
   }
 
   // second verse, same as the first...
-  nsresult rv = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty,
-                                     aAttribute);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  {
+    AutoTrackDOMPoint tracker(RangeUpdaterRef(), &startOfRange);
+    SplitNodeResult result =
+        SplitAncestorStyledInlineElementsAt(endOfRange, aProperty, aAttribute);
+    if (NS_WARN_IF(result.Failed())) {
+      return result.Rv();
+    }
+    if (result.Handled()) {
+      endOfRange = result.SplitPoint();
+      if (NS_WARN_IF(!endOfRange.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
+    }
   }
 
   // reset the range
-  rv = aRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
+  nsresult rv = aRange->SetStartAndEnd(startOfRange.ToRawRangeBoundary(),
+                                       endOfRange.ToRawRangeBoundary());
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetStartAndEnd() failed");
+  return rv;
 }
 
-nsresult HTMLEditor::SplitStyleAbovePoint(
-    nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-    // null here means we split all properties
-    nsAtom* aProperty, nsAtom* aAttribute, nsIContent** aOutLeftNode,
-    nsIContent** aOutRightNode) {
-  NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
-
-  if (aOutLeftNode) {
-    *aOutLeftNode = nullptr;
+SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
+    const EditorDOMPoint& aPointToSplit, nsAtom* aProperty,
+    nsAtom* aAttribute) {
+  if (NS_WARN_IF(!aPointToSplit.IsSet()) ||
+      NS_WARN_IF(!aPointToSplit.GetContainerAsContent())) {
+    return SplitNodeResult(NS_ERROR_INVALID_ARG);
   }
-  if (aOutRightNode) {
-    *aOutRightNode = nullptr;
-  }
-
-  // Split any matching style nodes above the node/offset
-  nsCOMPtr<nsIContent> node = (*aNode)->AsContent();
 
   bool useCSS = IsCSSEnabled();
 
-  bool isSet;
-  while (!IsBlockNode(node) && node->GetParent() &&
-         IsEditable(node->GetParent())) {
-    isSet = false;
+  // Split any matching style nodes above the point.
+  SplitNodeResult result(aPointToSplit);
+  MOZ_ASSERT(!result.Handled());
+  for (nsCOMPtr<nsIContent> content = aPointToSplit.GetContainerAsContent();
+       !IsBlockNode(content) && content->GetParent() &&
+       IsEditable(content->GetParent());
+       content = content->GetParent()) {
+    bool isSetByCSS = false;
     if (useCSS &&
-        CSSEditUtils::IsCSSEditableProperty(node, aProperty, aAttribute)) {
+        CSSEditUtils::IsCSSEditableProperty(content, aProperty, aAttribute)) {
       // The HTML style defined by aProperty/aAttribute has a CSS equivalence
       // in this implementation for the node; let's check if it carries those
       // CSS styles
       nsAutoString firstValue;
-      isSet = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
-          node, aProperty, aAttribute, firstValue, CSSEditUtils::eSpecified);
+      isSetByCSS = CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
+          content, aProperty, aAttribute, firstValue, CSSEditUtils::eSpecified);
     }
-    if (  // node is the correct inline prop
-        (aProperty && node->IsHTMLElement(aProperty)) ||
-        // node is href - test if really <a href=...
-        (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
-        // or node is any prop, and we asked to split them all
-        (!aProperty && node->IsElement() && IsEditable(node) &&
-         HTMLEditUtils::IsRemovableInlineStyleElement(*node->AsElement())) ||
-        // or the style is specified in the style attribute
-        isSet) {
-      // Found a style node we need to split
-      SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
-          *node, EditorDOMPoint(*aNode, *aOffset),
-          SplitAtEdges::eAllowToCreateEmptyContainer);
-      NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
-                           "Failed to split the node");
-
-      EditorRawDOMPoint atRightNode(splitNodeResult.SplitPoint());
-      *aNode = atRightNode.GetContainer();
-      *aOffset = atRightNode.Offset();
-      if (aOutLeftNode) {
-        NS_IF_ADDREF(*aOutLeftNode = splitNodeResult.GetPreviousNode());
+    if (!isSetByCSS) {
+      if (!content->IsElement()) {
+        continue;
       }
-      if (aOutRightNode) {
-        NS_IF_ADDREF(*aOutRightNode = splitNodeResult.GetNextNode());
+      // If aProperty is set, we need to split only elements which applies the
+      // given style.
+      if (aProperty) {
+        // If the content is an inline element represents aProperty or
+        // the content is a link element and aProperty is `href`, we should
+        // split the content.
+        if (!content->IsHTMLElement(aProperty) &&
+            !(aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(content))) {
+          continue;
+        }
+      }
+      // If aProperty is nullptr, we need to split any style.
+      else if (!IsEditable(content) ||
+               !HTMLEditUtils::IsRemovableInlineStyleElement(
+                   *content->AsElement())) {
+        continue;
       }
     }
-    node = node->GetParent();
-    if (NS_WARN_IF(!node)) {
-      return NS_ERROR_FAILURE;
+
+    // Found a style node we need to split.
+    // XXX If first content is a text node and CSS is enabled, we call this
+    //     with text node but in such case, this does nothing, but returns
+    //     as handled with setting only previous or next node.  If its parent
+    //     is a block, we do nothing but return as handled.
+    SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
+        *content, result.SplitPoint(),
+        SplitAtEdges::eAllowToCreateEmptyContainer);
+    if (NS_WARN_IF(splitNodeResult.Failed())) {
+      return splitNodeResult;
     }
+    MOZ_ASSERT(splitNodeResult.Handled());
+    // Mark the final result as handled forcibly.
+    result = SplitNodeResult(splitNodeResult.GetPreviousNode(),
+                             splitNodeResult.GetNextNode());
+    MOZ_ASSERT(result.Handled());
   }
 
-  return NS_OK;
+  return result;
 }
 
-nsresult HTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-                                nsAtom* aProperty, nsAtom* aAttribute) {
+EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint,
+                                    nsAtom* aProperty, nsAtom* aAttribute) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
-  nsCOMPtr<nsIContent> leftNode, rightNode;
-  nsresult rv =
-      SplitStyleAbovePoint(aNode, aOffset, aProperty, aAttribute,
-                           getter_AddRefs(leftNode), getter_AddRefs(rightNode));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(!aPoint.IsSet())) {
+    return EditResult(NS_ERROR_INVALID_ARG);
+  }
+
+  // First, split inline elements at the point.
+  // E.g., if aProperty is nsGkAtoms::b and `<p><b><i>a[]bc</i></b></p>`,
+  //       we want to make it as `<p><b><i>a</i></b><b><i>bc</i></b></p>`.
+  SplitNodeResult splitResult =
+      SplitAncestorStyledInlineElementsAt(aPoint, aProperty, aAttribute);
+  if (NS_WARN_IF(splitResult.Failed())) {
+    return EditResult(splitResult.Rv());
+  }
 
-  if (leftNode) {
-    bool bIsEmptyNode;
-    IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
-    if (bIsEmptyNode) {
-      // delete leftNode if it became empty
-      rv = DeleteNodeWithTransaction(*leftNode);
+  // If there is no styled inline elements of aProperty/aAttribute, we just
+  // return the given point.
+  // E.g., `<p><i>a[]bc</i></p>` for nsGkAtoms::b.
+  if (!splitResult.Handled()) {
+    return EditResult(aPoint);
+  }
+
+  // If it did split nodes, but topmost ancestor inline element is split
+  // at start of it, we don't need the empty inline element.  Let's remove
+  // it now.
+  if (splitResult.GetPreviousNode()) {
+    bool isEmpty = false;
+    IsEmptyNode(splitResult.GetPreviousNode(), &isEmpty, false, true);
+    if (isEmpty) {
+      // Delete previous node if it's empty.
+      nsresult rv = DeleteNodeWithTransaction(
+          MOZ_KnownLive(*splitResult.GetPreviousNode()));
+      if (NS_WARN_IF(Destroyed())) {
+        return EditResult(NS_ERROR_EDITOR_DESTROYED);
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return EditResult(rv);
       }
     }
   }
-  if (rightNode) {
-    nsCOMPtr<nsINode> secondSplitParent = GetLeftmostChild(rightNode);
-    // don't try to split non-containers (br's, images, hr's, etc.)
-    if (!secondSplitParent) {
-      secondSplitParent = rightNode;
-    }
-    RefPtr<Element> savedBR;
-    if (!IsContainer(secondSplitParent)) {
-      if (secondSplitParent->IsHTMLElement(nsGkAtoms::br)) {
-        savedBR = Element::FromNode(secondSplitParent);
-        MOZ_ASSERT(savedBR);
-      }
+
+  // If we reached block from end of a text node, we can do nothing here.
+  // E.g., `<p style="font-weight: bold;">a[]bc</p>` for nsGkAtoms::b and
+  // we're in CSS mode.
+  // XXX Chrome resets block style and creates `<span>` elements for each
+  //     line in this case.
+  if (!splitResult.GetNextNode()) {
+    MOZ_ASSERT(IsCSSEnabled());
+    return EditResult(aPoint);
+  }
 
-      secondSplitParent = secondSplitParent->GetParentNode();
+  // Otherwise, the next node is topmost ancestor inline element which has
+  // the style.  We want to put caret between the split nodes, but we need
+  // to keep other styles.  Therefore, next, we need to split at start of
+  // the next node.  The first example should become
+  // `<p><b><i>a</i></b><b><i></i></b><b><i>bc</i></b></p>`.
+  //                    ^^^^^^^^^^^^^^
+  nsIContent* leftmostChildOfNextNode =
+      GetLeftmostChild(splitResult.GetNextNode());
+  EditorDOMPoint atStartOfNextNode(leftmostChildOfNextNode
+                                       ? leftmostChildOfNextNode
+                                       : splitResult.GetNextNode(),
+                                   0);
+  RefPtr<HTMLBRElement> brElement;
+  // But don't try to split non-containers like `<br>`, `<hr>` and `<img>`
+  // element.
+  if (!IsContainer(atStartOfNextNode.GetContainer())) {
+    // If it's a `<br>` element, let's move it into new node later.
+    brElement = HTMLBRElement::FromNode(atStartOfNextNode.GetContainer());
+    if (NS_WARN_IF(!atStartOfNextNode.GetContainerParentAsContent())) {
+      return EditResult(NS_ERROR_FAILURE);
     }
-    *aOffset = 0;
-    rv = SplitStyleAbovePoint(address_of(secondSplitParent), aOffset, aProperty,
-                              aAttribute, getter_AddRefs(leftNode),
-                              getter_AddRefs(rightNode));
-    NS_ENSURE_SUCCESS(rv, rv);
+    atStartOfNextNode.Set(atStartOfNextNode.GetContainerParent(), 0);
+  }
+  SplitNodeResult splitResultAtStartOfNextNode =
+      SplitAncestorStyledInlineElementsAt(atStartOfNextNode, aProperty,
+                                          aAttribute);
+  if (NS_WARN_IF(splitResultAtStartOfNextNode.Failed())) {
+    return EditResult(splitResultAtStartOfNextNode.Rv());
+  }
 
-    if (rightNode) {
-      bool bIsEmptyNode;
-      IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
-      if (bIsEmptyNode) {
-        // delete rightNode if it became empty
-        rv = DeleteNodeWithTransaction(*rightNode);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
+  // Let's remove the next node if it becomes empty by splitting it.
+  // XXX Is this possible case without mutation event listener?
+  if (splitResultAtStartOfNextNode.Handled() &&
+      splitResultAtStartOfNextNode.GetNextNode()) {
+    bool isEmpty = false;
+    IsEmptyNode(splitResultAtStartOfNextNode.GetNextNode(), &isEmpty, false,
+                true);
+    if (isEmpty) {
+      // Delete next node if it's empty.
+      nsresult rv = DeleteNodeWithTransaction(
+          MOZ_KnownLive(*splitResultAtStartOfNextNode.GetNextNode()));
+      if (NS_WARN_IF(Destroyed())) {
+        return EditResult(NS_ERROR_EDITOR_DESTROYED);
+      }
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return EditResult(rv);
       }
     }
-
-    if (!leftNode) {
-      return NS_OK;
-    }
+  }
 
-    // should be impossible to not get a new leftnode here
-    nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
-    if (!newSelParent) {
-      newSelParent = leftNode;
-    }
-    // If rightNode starts with a br, suck it out of right node and into
-    // leftNode.  This is so we you don't revert back to the previous style
-    // if you happen to click at the end of a line.
-    if (savedBR) {
-      rv = MoveNodeWithTransaction(*savedBR, EditorDOMPoint(newSelParent, 0));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-    // remove the style on this new hierarchy
-    int32_t newSelOffset = 0;
-    {
-      // Track the point at the new hierarchy.  This is so we can know where
-      // to put the selection after we call RemoveStyleInside().
-      // RemoveStyleInside() could remove any and all of those nodes, so I
-      // have to use the range tracking system to find the right spot to put
-      // selection.
-      AutoTrackDOMPoint tracker(RangeUpdaterRef(), address_of(newSelParent),
-                                &newSelOffset);
-      rv = RemoveStyleInside(*leftNode, aProperty, aAttribute);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    // reset our node offset values to the resulting new sel point
-    *aNode = newSelParent;
-    *aOffset = newSelOffset;
+  // If there is no content, we should return here.
+  // XXX Is this possible case without mutation event listener?
+  if (NS_WARN_IF(!splitResultAtStartOfNextNode.Handled()) ||
+      !splitResultAtStartOfNextNode.GetPreviousNode()) {
+    // XXX This is really odd, but we retrun this value...
+    return EditResult(
+        EditorDOMPoint(splitResult.SplitPoint().GetContainer(),
+                       splitResultAtStartOfNextNode.SplitPoint().Offset()));
   }
 
-  return NS_OK;
+  // Now, we want to put `<br>` element into the empty split node if
+  // it was in next node of the first split.
+  // E.g., `<p><b><i>a</i></b><b><i><br></i></b><b><i>bc</i></b></p>`
+  nsIContent* leftmostChild =
+      GetLeftmostChild(splitResultAtStartOfNextNode.GetPreviousNode());
+  EditorDOMPoint pointToPutCaret(
+      leftmostChild ? leftmostChild
+                    : splitResultAtStartOfNextNode.GetPreviousNode(),
+      0);
+  // If the right node starts with a `<br>`, suck it out of right node and into
+  // the left node left node.  This is so we you don't revert back to the
+  // previous style if you happen to click at the end of a line.
+  if (brElement) {
+    nsresult rv = MoveNodeWithTransaction(*brElement, pointToPutCaret);
+    if (NS_WARN_IF(Destroyed())) {
+      return EditResult(NS_ERROR_EDITOR_DESTROYED);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return EditResult(rv);
+    }
+    // Update the child.
+    pointToPutCaret.Set(pointToPutCaret.GetContainer(), 0);
+  }
+  // Finally, remove the specified style in the previous node at the
+  // second split and tells good insertion point to the caller.  I.e., we
+  // want to make the first example as:
+  // `<p><b><i>a</i></b><i>[]</i><b><i>bc</i></b></p>`
+  //                    ^^^^^^^^^
+  {
+    // Track the point at the new hierarchy.  This is so we can know where
+    // to put the selection after we call RemoveStyleInside().
+    // RemoveStyleInside() could remove any and all of those nodes, so I
+    // have to use the range tracking system to find the right spot to put
+    // selection.
+    AutoTrackDOMPoint tracker(RangeUpdaterRef(), &pointToPutCaret);
+    nsresult rv = RemoveStyleInside(
+        MOZ_KnownLive(*splitResultAtStartOfNextNode.GetPreviousNode()),
+        aProperty, aAttribute);
+    if (NS_WARN_IF(Destroyed())) {
+      return EditResult(NS_ERROR_EDITOR_DESTROYED);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return EditResult(rv);
+    }
+  }
+  return EditResult(pointToPutCaret);
 }
 
 nsresult HTMLEditor::RemoveStyleInside(nsIContent& aNode, nsAtom* aProperty,
                                        nsAtom* aAttribute,
                                        const bool aChildrenOnly /* = false */) {
   if (!aNode.IsElement()) {
     return NS_OK;
   }
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -2291,42 +2291,41 @@ static inline void DebugOnlyCheckColorSa
     const uint8_t* aBoundsEnd) {
   MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
   MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
 }
 #else
 #  define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
 #endif
 
-static inline uint8_t ColorComponentAtPoint(
-    const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
-    const uint8_t* aBoundsEnd, int32_t x, int32_t y, ptrdiff_t bpp,
-    ptrdiff_t c) {
+static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
+                                            ptrdiff_t aStride,
+                                            const uint8_t* aBoundsBegin,
+                                            const uint8_t* aBoundsEnd,
+                                            int32_t x, int32_t y, ptrdiff_t bpp,
+                                            ptrdiff_t c) {
   DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c],
                                     aBoundsBegin, aBoundsEnd);
   return aData[y * aStride + bpp * x + c];
 }
 
 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride,
                                    const uint8_t* aBoundsBegin,
                                    const uint8_t* aBoundsEnd, int32_t x,
                                    int32_t y) {
   DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin,
                                     aBoundsEnd);
   return *(uint32_t*)(aData + y * aStride + 4 * x);
 }
 
 // Accepts fractional x & y and does bilinear interpolation.
 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
-static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
-                                            ptrdiff_t aStride,
-                                            const uint8_t* aBoundsBegin,
-                                            const uint8_t* aBoundsEnd, Float x,
-                                            Float y, ptrdiff_t bpp,
-                                            ptrdiff_t c) {
+static inline uint8_t ColorComponentAtPoint(
+    const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
+    const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) {
   const uint32_t f = 256;
   const int32_t lx = floor(x);
   const int32_t ly = floor(y);
   const int32_t tux = uint32_t((x - lx) * f);
   const int32_t tlx = f - tux;
   const int32_t tuy = uint32_t((y - ly) * f);
   const int32_t tly = f - tuy;
   const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
--- a/gfx/2d/InlineTranslator.cpp
+++ b/gfx/2d/InlineTranslator.cpp
@@ -13,18 +13,17 @@
 #include "mozilla/gfx/RecordingTypes.h"
 #include "mozilla/UniquePtr.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gfx {
 
-InlineTranslator::InlineTranslator()
-    : mFontContext(nullptr) {}
+InlineTranslator::InlineTranslator() : mFontContext(nullptr) {}
 
 InlineTranslator::InlineTranslator(DrawTarget* aDT, void* aFontContext)
     : mBaseDT(aDT), mFontContext(aFontContext) {}
 
 bool InlineTranslator::TranslateRecording(char* aData, size_t aLen) {
   // an istream like class for reading from memory
   struct MemReader {
     MemReader(char* aData, size_t aLen) : mData(aData), mEnd(aData + aLen) {}
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -15,17 +15,17 @@
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/SyncObject.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/SyncRunnable.h"
 #ifdef XP_MACOSX
-#include "nsCocoaFeatures.h"
+#  include "nsCocoaFeatures.h"
 #endif
 #include "nsIPropertyBag2.h"
 #include "nsIThreadManager.h"
 #include "nsServiceManagerUtils.h"
 #include "prsystem.h"
 
 // Uncomment the following line to dispatch sync runnables when
 // painting so that rasterization happens synchronously from
@@ -75,19 +75,20 @@ void PaintThread::Start() {
 
 static uint32_t GetPaintThreadStackSize() {
 #ifndef XP_MACOSX
   return nsIThreadManager::DEFAULT_STACK_SIZE;
 #else
   // Workaround bug 1578075 by increasing the stack size of paint threads
   if (nsCocoaFeatures::OnCatalinaOrLater()) {
     static const uint32_t kCatalinaPaintThreadStackSize = 512 * 1024;
-    static_assert(kCatalinaPaintThreadStackSize >= nsIThreadManager::DEFAULT_STACK_SIZE,
-                  "update default stack size of paint "
-                  "workers");
+    static_assert(
+        kCatalinaPaintThreadStackSize >= nsIThreadManager::DEFAULT_STACK_SIZE,
+        "update default stack size of paint "
+        "workers");
     return kCatalinaPaintThreadStackSize;
   }
   return nsIThreadManager::DEFAULT_STACK_SIZE;
 #endif
 }
 
 bool PaintThread::Init() {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2004,21 +2004,27 @@ nsEventStatus AsyncPanZoomController::On
   CSSPoint destination = GetKeyboardDestination(aEvent.mAction);
   bool scrollSnapped =
       MaybeAdjustDestinationForScrollSnapping(aEvent, destination);
 
   // If smooth scrolling is disabled, then scroll immediately to the destination
   if (!StaticPrefs::general_smoothScroll()) {
     CancelAnimation();
 
-    // CallDispatchScroll interprets the start and end points as the start and
-    // end of a touch scroll so they need to be reversed.
-    ParentLayerPoint startPoint = destination * Metrics().GetZoom();
-    ParentLayerPoint endPoint =
-        Metrics().GetScrollOffset() * Metrics().GetZoom();
+    ParentLayerPoint startPoint, endPoint;
+
+    {
+      RecursiveMutexAutoLock lock(mRecursiveMutex);
+
+      // CallDispatchScroll interprets the start and end points as the start and
+      // end of a touch scroll so they need to be reversed.
+      startPoint = destination * Metrics().GetZoom();
+      endPoint = Metrics().GetScrollOffset() * Metrics().GetZoom();
+    }
+
     ParentLayerPoint delta = endPoint - startPoint;
 
     ScreenPoint distance = ToScreenCoordinates(
         ParentLayerPoint(fabs(delta.x), fabs(delta.y)), startPoint);
 
     OverscrollHandoffState handoffState(
         *mInputQueue->GetCurrentKeyboardBlock()->GetOverscrollHandoffChain(),
         distance, ScrollSource::Keyboard);
--- a/gfx/layers/apz/util/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -16,18 +16,17 @@
 
 #define AEM_LOG(...)
 // #define AEM_LOG(...) printf_stderr("AEM: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 ActiveElementManager::ActiveElementManager()
-    : mCanBePan(false), mCanBePanSet(false), mSetActiveTask(nullptr) {
-}
+    : mCanBePan(false), mCanBePanSet(false), mSetActiveTask(nullptr) {}
 
 ActiveElementManager::~ActiveElementManager() {}
 
 void ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget) {
   if (mTarget) {
     // Multiple fingers on screen (since HandleTouchEnd clears mTarget).
     AEM_LOG("Multiple fingers on-screen, clearing target element\n");
     CancelTask();
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -630,18 +630,17 @@ mozilla::ipc::IPCResult ContentComposito
                      "Canvas Parent must be released before recreating.");
 
   mCanvasParent = CanvasParent::Create(std::move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentCompositorBridgeParent::RecvReleasePCanvasParent() {
-  MOZ_RELEASE_ASSERT(mCanvasParent,
-                     "Canvas Parent hasn't been created.");
+  MOZ_RELEASE_ASSERT(mCanvasParent, "Canvas Parent hasn't been created.");
 
   mCanvasParent = nullptr;
   return IPC_OK();
 }
 
 UniquePtr<SurfaceDescriptor>
 ContentCompositorBridgeParent::LookupSurfaceDescriptorForClientDrawTarget(
     const uintptr_t aDrawTarget) {
--- a/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp
+++ b/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp
@@ -95,22 +95,22 @@ static IntRect GetTransformedBounds(Laye
 Maybe<IntRect> ContainerLayerMLGPU::FindVisibleBounds(
     Layer* aLayer, const Maybe<RenderTargetIntRect>& aClip) {
   AL_LOG("  visiting child %p\n", aLayer);
   AL_LOG_IF(aClip, "  parent clip: %s\n", Stringify(aClip.value()).c_str());
 
   ContainerLayer* container = aLayer->AsContainerLayer();
   if (container) {
     if (container->UseIntermediateSurface()) {
-      ContainerLayerMLGPU* c = container->AsHostLayer()
-          ->AsLayerMLGPU()
-          ->AsContainerLayerMLGPU();
+      ContainerLayerMLGPU* c =
+          container->AsHostLayer()->AsLayerMLGPU()->AsContainerLayerMLGPU();
       if (!c) {
-        gfxCriticalError() << "not container: " << container->AsHostLayer()
-          ->AsLayerMLGPU()->GetType();
+        gfxCriticalError()
+            << "not container: "
+            << container->AsHostLayer()->AsLayerMLGPU()->GetType();
       }
       MOZ_RELEASE_ASSERT(c);
       c->ComputeIntermediateSurfaceBounds();
     } else {
       Maybe<IntRect> accumulated = Some(IntRect());
 
       // Traverse children.
       for (Layer* child = container->GetFirstChild(); child;
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -302,20 +302,21 @@ struct DIGroup {
 
   // This is the intersection of mVisibleRect and mLastVisibleRect
   // we ensure that mInvalidRect is contained in mPreservedRect
   IntRect mPreservedRect;
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   ScrollableLayerGuid::ViewID mScrollId;
   LayerPoint mResidualOffset;
-  LayerIntRect mLayerBounds; // mGroupBounds converted to Layer space
+  LayerIntRect mLayerBounds;  // mGroupBounds converted to Layer space
   // mLayerBounds clipped to the container/parent of the
   // current item being processed.
-  IntRect mClippedImageBounds; // mLayerBounds with the clipping of any containers applied
+  IntRect mClippedImageBounds;  // mLayerBounds with the clipping of any
+                                // containers applied
   Maybe<mozilla::Pair<wr::RenderRoot, wr::BlobImageKey>> mKey;
   std::vector<RefPtr<ScaledFont>> mFonts;
 
   DIGroup()
       : mAppUnitsPerDevPixel(0),
         mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID) {}
 
   void InvalidateRect(const IntRect& aRect) {
@@ -436,18 +437,18 @@ struct DIGroup {
       GP("else invalidate: %s\n", aItem->Name());
       nsRegion combined;
       // this includes situations like reflow changing the position
       aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(),
                                        &combined);
       if (!combined.IsEmpty()) {
         // There might be no point in doing this elaborate tracking here to get
         // smaller areas
-        InvalidateRect(aData->mRect);  // invalidate the old area -- in theory combined
-                             // should take care of this
+        InvalidateRect(aData->mRect);  // invalidate the old area -- in theory
+                                       // combined should take care of this
         UniquePtr<nsDisplayItemGeometry> geometry(
             aItem->AllocateGeometry(aBuilder));
         // invalidate the invalidated area.
 
         aData->mGeometry = std::move(geometry);
 
         nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
             aData->mGeometry->ComputeInvalidationRegion());
@@ -535,18 +536,18 @@ struct DIGroup {
             auto rect = transformedRect.Intersect(mClippedImageBounds);
             if (!rect.IsEqualEdges(aData->mRect)) {
               GP("ContainerLayer image rect bounds change\n");
               InvalidateRect(aData->mRect);
               aData->mRect = rect;
               InvalidateRect(aData->mRect);
             } else {
               GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
-               aData->mRect.x, aData->mRect.y, aData->mRect.XMost(),
-               aData->mRect.YMost());
+                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(),
+                 aData->mRect.YMost());
             }
           }
         } else {
           UniquePtr<nsDisplayItemGeometry> geometry(
               aItem->AllocateGeometry(aBuilder));
           nsRect clippedBounds = clip.ApplyNonRoundedIntersection(
               geometry->ComputeInvalidationRegion());
           IntRect transformedRect =
@@ -554,19 +555,18 @@ struct DIGroup {
           auto rect = transformedRect.Intersect(mClippedImageBounds);
           // Make sure we update mRect for mClippedImageBounds changes
           if (!rect.IsEqualEdges(aData->mRect)) {
             GP("ContainerLayer image rect bounds change\n");
             InvalidateRect(aData->mRect);
             aData->mRect = rect;
             InvalidateRect(aData->mRect);
           } else {
-            GP("NoChange: %s %d %d %d %d\n", aItem->Name(),
-               aData->mRect.x, aData->mRect.y, aData->mRect.XMost(),
-               aData->mRect.YMost());
+            GP("NoChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x,
+               aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
           }
         }
       }
     }
     aData->mClip = clip;
     aData->mMatrix = aMatrix;
     aData->mImageRect = mClippedImageBounds;
     GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
@@ -1244,17 +1244,19 @@ void Grouper::ConstructGroups(nsDisplayL
       groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
       groupData->mFollowingGroup.mClippedImageBounds =
           currentGroup->mClippedImageBounds;
       groupData->mFollowingGroup.mScale = currentGroup->mScale;
       groupData->mFollowingGroup.mResidualOffset =
           currentGroup->mResidualOffset;
       groupData->mFollowingGroup.mVisibleRect = currentGroup->mVisibleRect;
       groupData->mFollowingGroup.mPreservedRect =
-        groupData->mFollowingGroup.mVisibleRect.Intersect(groupData->mFollowingGroup.mLastVisibleRect).ToUnknownRect();
+          groupData->mFollowingGroup.mVisibleRect
+              .Intersect(groupData->mFollowingGroup.mLastVisibleRect)
+              .ToUnknownRect();
 
       currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder,
                              aBuilder, aResources, this, startOfCurrentGroup,
                              item);
 
       {
         MOZ_ASSERT(item->GetType() != DisplayItemType::TYPE_RENDER_ROOT);
         auto spaceAndClipChain = mClipManager.SwitchItem(item);
@@ -1450,22 +1452,23 @@ void WebRenderCommandBuilder::DoGrouping
   // overall size even though they may each be much smaller. This can lead to
   // allocating much larger textures than necessary in webrender.
   //
   // Don't bother fixing this unless we run into this in the real world, though.
   auto layerBounds = LayerIntRect::FromUnknownRect(
       ScaleToOutsidePixelsOffset(groupBounds, scale.width, scale.height,
                                  appUnitsPerDevPixel, residualOffset));
 
-  const nsRect& untransformedPaintRect = aWrappingItem->GetUntransformedPaintRect();
+  const nsRect& untransformedPaintRect =
+      aWrappingItem->GetUntransformedPaintRect();
 
   auto visibleRect = LayerIntRect::FromUnknownRect(
                          ScaleToOutsidePixelsOffset(
-                             untransformedPaintRect, scale.width,
-                             scale.height, appUnitsPerDevPixel, residualOffset))
+                             untransformedPaintRect, scale.width, scale.height,
+                             appUnitsPerDevPixel, residualOffset))
                          .Intersect(layerBounds);
 
   GP("LayerBounds: %d %d %d %d\n", layerBounds.x, layerBounds.y,
      layerBounds.width, layerBounds.height);
   GP("VisibleRect: %d %d %d %d\n", visibleRect.x, visibleRect.y,
      visibleRect.width, visibleRect.height);
 
   GP("Inherrited scale %f %f\n", scale.width, scale.height);
@@ -1508,17 +1511,18 @@ void WebRenderCommandBuilder::DoGrouping
     scrollId = asr->GetViewId();
   }
 
   g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   group.mResidualOffset = residualOffset;
   group.mGroupBounds = groupBounds;
   group.mLayerBounds = layerBounds;
   group.mVisibleRect = visibleRect;
-  group.mPreservedRect = group.mVisibleRect.Intersect(group.mLastVisibleRect).ToUnknownRect();
+  group.mPreservedRect =
+      group.mVisibleRect.Intersect(group.mLastVisibleRect).ToUnknownRect();
   group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   group.mClippedImageBounds = layerBounds.ToUnknownRect();
 
   g.mTransform = Matrix::Scaling(scale.width, scale.height)
                      .PostTranslate(residualOffset.x, residualOffset.y);
   group.mScale = scale;
   group.mScrollId = scrollId;
   g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group,
@@ -2249,17 +2253,17 @@ WebRenderCommandBuilder::GenerateFallbac
           gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
       RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(
           recorder, dummyDt, visibleRect.ToUnknownRect());
       if (!fallbackData->mBasicLayerManager) {
         fallbackData->mBasicLayerManager =
             new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
       }
       bool isInvalidated = PaintItemByDrawTarget(
-          aItem, dt, (dtRect/layerScale).TopLeft(),
+          aItem, dt, (dtRect / layerScale).TopLeft(),
           /*aVisibleRect: */ dt->GetRect(), aDisplayListBuilder,
           fallbackData->mBasicLayerManager, scale, highlight);
       if (!isInvalidated) {
         if (!aItem->GetBuildingRect().IsEqualInterior(
                 fallbackData->mBuildingRect)) {
           // The building rect has changed but we didn't see any invalidations.
           // We should still consider this an invalidation.
           isInvalidated = true;
@@ -2354,20 +2358,19 @@ WebRenderCommandBuilder::GenerateFallbac
       }
     }
 
     fallbackData->mScale = scale;
     fallbackData->SetInvalid(false);
   }
 
   if (useBlobImage) {
-      aResources.SetBlobImageVisibleArea(
-            fallbackData->GetBlobImageKey().value(),
-            ViewAs<ImagePixel>(visibleRect,
-                               PixelCastJustification::LayerIsImage));
+    aResources.SetBlobImageVisibleArea(
+        fallbackData->GetBlobImageKey().value(),
+        ViewAs<ImagePixel>(visibleRect, PixelCastJustification::LayerIsImage));
   }
 
   // Update current bounds to fallback data
   fallbackData->mBounds = paintBounds;
   fallbackData->mBuildingRect = aItem->GetBuildingRect();
 
   MOZ_ASSERT(fallbackData->GetImageKey());
 
--- a/gfx/thebes/gfxFontFeatures.cpp
+++ b/gfx/thebes/gfxFontFeatures.cpp
@@ -31,18 +31,17 @@ nsTArray<uint32_t>* gfxFontFeatureValueS
   FeatureValueHashKey key(aFamily, aAlternate, aName);
   FeatureValueHashEntry* entry = mFontFeatureValues.PutEntry(key);
   entry->mKey = key;
   return &entry->mValues;
 }
 
 bool gfxFontFeatureValueSet::FeatureValueHashEntry::KeyEquals(
     const KeyTypePointer aKey) const {
-  return aKey->mPropVal == mKey.mPropVal &&
-         aKey->mName == mKey.mName &&
+  return aKey->mPropVal == mKey.mPropVal && aKey->mName == mKey.mName &&
          aKey->mFamily.Equals(mKey.mFamily);
 }
 
 PLDHashNumber gfxFontFeatureValueSet::FeatureValueHashEntry::HashKey(
     const KeyTypePointer aKey) {
   return HashString(aKey->mFamily) + aKey->mName->hash() +
          aKey->mPropVal * uint32_t(0xdeadbeef);
 }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -594,18 +594,17 @@ static void WebRenderDebugPrefChangeCall
   GFX_WEBRENDER_DEBUG(".small-screen", wr::DebugFlags_SMALL_SCREEN)
   GFX_WEBRENDER_DEBUG(".disable-opaque-pass",
                       wr::DebugFlags_DISABLE_OPAQUE_PASS)
   GFX_WEBRENDER_DEBUG(".disable-alpha-pass", wr::DebugFlags_DISABLE_ALPHA_PASS)
   GFX_WEBRENDER_DEBUG(".disable-clip-masks", wr::DebugFlags_DISABLE_CLIP_MASKS)
   GFX_WEBRENDER_DEBUG(".disable-text-prims", wr::DebugFlags_DISABLE_TEXT_PRIMS)
   GFX_WEBRENDER_DEBUG(".disable-gradient-prims",
                       wr::DebugFlags_DISABLE_GRADIENT_PRIMS)
-  GFX_WEBRENDER_DEBUG(".obscure-images",
-                      wr::DebugFlags_OBSCURE_IMAGES)
+  GFX_WEBRENDER_DEBUG(".obscure-images", wr::DebugFlags_OBSCURE_IMAGES)
   GFX_WEBRENDER_DEBUG(".log-transactions", wr::DebugFlags_LOG_TRANSACTIONS)
 #undef GFX_WEBRENDER_DEBUG
 
   gfx::gfxVars::SetWebRenderDebugFlags(flags.bits);
 }
 
 #if defined(USE_SKIA)
 static uint32_t GetSkiaGlyphCacheSize() {
@@ -2647,29 +2646,29 @@ static void UpdateWRQualificationForNvid
     HardwareTooOldForWR(aFeature);
     return;
   }
 
   // Any additional Nvidia checks go here. Make sure to leave
   // aOutGuardedByQualifiedPref as true unless the hardware is qualified
   // for users on the release channel.
 
-#if defined(XP_WIN)
+#  if defined(XP_WIN)
   // Nvidia devices with device id >= 0x6c0 got WR in release Firefox 67.
   *aOutGuardedByQualifiedPref = false;
-#elif defined(NIGHTLY_BUILD)
+#  elif defined(NIGHTLY_BUILD)
   // Qualify on Linux Nightly, but leave *aOutGuardedByQualifiedPref as true
   // to indicate users on release don't have it yet, and it's still guarded
   // by the qualified pref.
-#else
+#  else
   // Disqualify everywhere else
   aFeature.Disable(
       FeatureStatus::BlockedReleaseChannelNvidia, "Release channel and Nvidia",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_NVIDIA"));
-#endif
+#  endif
 }
 
 static void UpdateWRQualificationForAMD(FeatureState& aFeature,
                                         int32_t aDeviceId,
                                         bool* aOutGuardedByQualifiedPref) {
   // AMD deviceIDs are not very well ordered. This
   // condition is based off the information in gpu-db
   bool supported = (aDeviceId >= 0x6600 && aDeviceId < 0x66b0) ||
@@ -2684,29 +2683,29 @@ static void UpdateWRQualificationForAMD(
 
   if (!supported) {
     HardwareTooOldForWR(aFeature);
     return;
   }
 
   // we have a desktop CAYMAN, SI, CIK, VI, or GFX9 device.
 
-#if defined(XP_WIN)
+#  if defined(XP_WIN)
   // These devices got WR in release Firefox 68.
   *aOutGuardedByQualifiedPref = false;
-#elif defined(NIGHTLY_BUILD)
+#  elif defined(NIGHTLY_BUILD)
   // Qualify on Linux Nightly, but leave *aOutGuardedByQualifiedPref as true
   // to indicate users on release don't have it yet, and it's still guarded
   // by the qualified pref.
-#else
+#  else
   // Disqualify everywhere else
   aFeature.Disable(FeatureStatus::BlockedReleaseChannelAMD,
                    "Release channel and AMD",
                    NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_AMD"));
-#endif
+#  endif
 }
 
 static void UpdateWRQualificationForIntel(FeatureState& aFeature,
                                           int32_t aDeviceId,
                                           int64_t aScreenPixels,
                                           bool* aOutGuardedByQualifiedPref) {
   const uint16_t supportedDevices[] = {
       // skylake gt2+
@@ -2801,56 +2800,56 @@ static void UpdateWRQualificationForInte
   if (!supported) {
     HardwareTooOldForWR(aFeature);
     return;
   }
 
   // Performance is not great on 4k screens with WebRender.
   // Disable it for now on all release platforms, and also on Linux
   // nightly. We only allow it on Windows nightly.
-#if defined(XP_WIN) && defined(NIGHTLY_BUILD)
+#  if defined(XP_WIN) && defined(NIGHTLY_BUILD)
   // Windows nightly, so don't do screen size checks
-#else
+#  else
   // Windows release, Linux nightly, Linux release. Do screen size
   // checks. (macOS is still completely blocked by the blocklist).
   // On Windows release, we only allow really small screens (sub-WUXGA). On
   // Linux we allow medium size screens as well (anything sub-4k).
-#  if defined(XP_WIN)
+#    if defined(XP_WIN)
   // Allow up to WUXGA on Windows release
   const int64_t kMaxPixels = 1920 * 1200;  // WUXGA
-#  else
+#    else
   // Allow up to 4k on Linux
   const int64_t kMaxPixels = 3440 * 1440;  // UWQHD
-#  endif
+#    endif
   if (aScreenPixels > kMaxPixels) {
     aFeature.Disable(
         FeatureStatus::BlockedScreenTooLarge, "Screen size too large",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_TOO_LARGE"));
     return;
   }
   if (aScreenPixels <= 0) {
     aFeature.Disable(FeatureStatus::BlockedScreenUnknown, "Screen size unknown",
                      NS_LITERAL_CSTRING("FEATURE_FAILURE_SCREEN_SIZE_UNKNOWN"));
     return;
   }
-#endif
-
-#if (defined(XP_WIN) || (defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)))
+#  endif
+
+#  if (defined(XP_WIN) || (defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)))
   // Qualify Intel graphics cards on Windows to release and on Linux nightly
   // (subject to device whitelist and screen size checks above).
   // Leave *aOutGuardedByQualifiedPref as true to indicate no existing
   // release users have this yet, and it's still guarded by the qualified pref.
-#else
+#  else
   // Disqualify everywhere else
   aFeature.Disable(FeatureStatus::BlockedReleaseChannelIntel,
                    "Release channel and Intel",
                    NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_INTEL"));
-#endif
+#  endif
 }
-#endif // !MOZ_WIDGET_ANDROID
+#endif  // !MOZ_WIDGET_ANDROID
 
 static FeatureState& WebRenderHardwareQualificationStatus(
     int64_t aScreenPixels, bool aHasBattery, bool* aOutGuardedByQualifiedPref) {
   FeatureState& featureWebRenderQualified =
       gfxConfig::GetFeature(Feature::WEBRENDER_QUALIFIED);
   featureWebRenderQualified.EnableByDefault();
   MOZ_ASSERT(aOutGuardedByQualifiedPref && *aOutGuardedByQualifiedPref);
 
@@ -2915,48 +2914,48 @@ static FeatureState& WebRenderHardwareQu
     // this population must still be guarded by the qualified pref.
     MOZ_ASSERT(*aOutGuardedByQualifiedPref);
     return featureWebRenderQualified;
   }
 
   // We leave checking the battery for last because we would like to know
   // which users were denied WebRender only because they have a battery.
   if (aHasBattery) {
-#ifndef XP_WIN
+#  ifndef XP_WIN
     // aHasBattery is only ever true on Windows, we don't check it on other
     // platforms.
     MOZ_ASSERT(false);
-#endif
+#  endif
     // We never released WR to the battery populations, so let's keep the pref
     // guard for these populations. That way we can do a gradual rollout to
     // the battery population using the pref.
     *aOutGuardedByQualifiedPref = true;
 
     // if we have a battery, ignore it if the screen is small enough.
     const int64_t kMaxPixelsBattery = 1920 * 1200;  // WUXGA
     if (aScreenPixels > 0 && aScreenPixels <= kMaxPixelsBattery) {
-#ifndef NIGHTLY_BUILD
+#  ifndef NIGHTLY_BUILD
       featureWebRenderQualified.Disable(
           FeatureStatus::BlockedReleaseChannelBattery,
           "Release channel and battery",
           NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_BATTERY"));
-#endif  // !NIGHTLY_BUILD
+#  endif  // !NIGHTLY_BUILD
     } else {
       featureWebRenderQualified.Disable(
           FeatureStatus::BlockedHasBattery, "Has battery",
           NS_LITERAL_CSTRING("FEATURE_FAILURE_WR_HAS_BATTERY"));
     }
   }
-#else // !MOZ_WIDGET_ANDROID
-#ifndef NIGHTLY_BUILD
+#else  // !MOZ_WIDGET_ANDROID
+#  ifndef NIGHTLY_BUILD
   featureWebRenderQualified.Disable(
-    FeatureStatus::BlockedReleaseChannelAndroid,
-    "Release channel and Android",
-    NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_ANDROID"));
-#endif
+      FeatureStatus::BlockedReleaseChannelAndroid,
+      "Release channel and Android",
+      NS_LITERAL_CSTRING("FEATURE_FAILURE_RELEASE_CHANNEL_ANDROID"));
+#  endif
 #endif
   return featureWebRenderQualified;
 }
 
 void gfxPlatform::InitWebRenderConfig() {
   bool prefEnabled = WebRenderPrefEnabled();
   bool envvarEnabled = WebRenderEnvvarEnabled();
 
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AlphaType, ClipMode, ExternalImageType, ImageRendering};
 use api::{YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF, RasterSpace};
 use api::units::*;
 use crate::clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore};
 use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId};
-use crate::composite::{CompositeConfig, CompositeTile, CompositeTileSurface};
+use crate::composite::{CompositeState, CompositeTile, CompositeTileSurface};
 use crate::glyph_rasterizer::GlyphFormat;
 use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheHandle, GpuCacheAddress};
 use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator};
 use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance};
 use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
 use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
 use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSource, Filter};
 use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, TileSurface};
@@ -640,34 +640,34 @@ impl BatchBuilder {
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskGraph,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         root_spatial_node_index: SpatialNodeIndex,
         surface_spatial_node_index: SpatialNodeIndex,
         z_generator: &mut ZBufferIdGenerator,
-        composite_config: &mut CompositeConfig,
+        composite_state: &mut CompositeState,
     ) {
         for cluster in &pic.prim_list.clusters {
             // Add each run in this picture to the batch.
             for prim_instance in &cluster.prim_instances {
                 self.add_prim_to_batch(
                     prim_instance,
                     cluster.spatial_node_index,
                     ctx,
                     gpu_cache,
                     render_tasks,
                     deferred_resolves,
                     prim_headers,
                     transforms,
                     root_spatial_node_index,
                     surface_spatial_node_index,
                     z_generator,
-                    composite_config,
+                    composite_state,
                 );
             }
         }
     }
 
     // Adds a primitive to a batch.
     // It can recursively call itself in some situations, for
     // example if it encounters a picture where the items
@@ -680,17 +680,17 @@ impl BatchBuilder {
         gpu_cache: &mut GpuCache,
         render_tasks: &RenderTaskGraph,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         root_spatial_node_index: SpatialNodeIndex,
         surface_spatial_node_index: SpatialNodeIndex,
         z_generator: &mut ZBufferIdGenerator,
-        composite_config: &mut CompositeConfig,
+        composite_state: &mut CompositeState,
     ) {
         if prim_instance.visibility_info == PrimitiveVisibilityIndex::INVALID {
             return;
         }
 
         #[cfg(debug_assertions)] //TODO: why is this needed?
         debug_assert_eq!(prim_instance.prepared_frame_id, render_tasks.frame_id());
 
@@ -1194,55 +1194,59 @@ impl BatchBuilder {
                                     None => {
                                         return;
                                     }
                                 };
                                 let world_clip_rect = map_local_to_world
                                     .map(&local_tile_clip_rect)
                                     .expect("bug: unable to map clip rect");
                                 let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();
-                                let z_id = composite_config.z_generator.next();
+                                let z_id = composite_state.z_generator.next();
                                 for key in &tile_cache.tiles_to_draw {
                                     let tile = &tile_cache.tiles[key];
                                     let device_rect = (tile.world_rect * ctx.global_device_pixel_scale).round();
+                                    let dirty_rect = (tile.world_dirty_rect * ctx.global_device_pixel_scale).round();
                                     let surface = tile.surface.as_ref().expect("no tile surface set!");
                                     match surface {
                                         TileSurface::Color { color } => {
-                                            composite_config.opaque_tiles.push(CompositeTile {
+                                            composite_state.opaque_tiles.push(CompositeTile {
                                                 surface: CompositeTileSurface::Color { color: *color },
                                                 rect: device_rect,
+                                                dirty_rect,
                                                 clip_rect: device_clip_rect,
                                                 z_id,
                                             });
                                         }
                                         TileSurface::Clear => {
-                                            composite_config.clear_tiles.push(CompositeTile {
+                                            composite_state.clear_tiles.push(CompositeTile {
                                                 surface: CompositeTileSurface::Clear,
                                                 rect: device_rect,
+                                                dirty_rect,
                                                 clip_rect: device_clip_rect,
                                                 z_id,
                                             });
                                         }
                                         TileSurface::Texture { handle, .. } => {
                                             let cache_item = ctx.resource_cache.texture_cache.get(handle);
 
                                             let composite_tile = CompositeTile {
                                                 surface: CompositeTileSurface::Texture {
                                                     texture_id: cache_item.texture_id,
                                                     texture_layer: cache_item.texture_layer,
                                                 },
                                                 rect: device_rect,
+                                                dirty_rect,
                                                 clip_rect: device_clip_rect,
                                                 z_id,
                                             };
 
                                             if tile.is_opaque || tile_cache.is_opaque() {
-                                                composite_config.opaque_tiles.push(composite_tile);
+                                                composite_state.opaque_tiles.push(composite_tile);
                                             } else {
-                                                composite_config.alpha_tiles.push(composite_tile);
+                                                composite_state.alpha_tiles.push(composite_tile);
                                             }
                                         }
                                     }
                                 }
                             }
                             PictureCompositeMode::Filter(ref filter) => {
                                 assert!(filter.is_visible());
                                 match filter {
@@ -1705,17 +1709,17 @@ impl BatchBuilder {
                             gpu_cache,
                             render_tasks,
                             deferred_resolves,
                             prim_headers,
                             transforms,
                             root_spatial_node_index,
                             surface_spatial_node_index,
                             z_generator,
-                            composite_config,
+                            composite_state,
                         );
                     }
                 }
             }
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
                 let prim_data = &ctx.data_stores.image_border[data_handle];
                 let common_data = &prim_data.common;
                 let border_data = &prim_data.kind;
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -28,31 +28,40 @@ pub enum CompositeTileSurface {
 
 /// Describes the geometry and surface of a tile to be composited
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CompositeTile {
     pub surface: CompositeTileSurface,
     pub rect: DeviceRect,
     pub clip_rect: DeviceRect,
+    pub dirty_rect: DeviceRect,
     pub z_id: ZBufferId,
 }
 
 /// The list of tiles to be drawn this frame
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct CompositeConfig {
+pub struct CompositeState {
     pub opaque_tiles: Vec<CompositeTile>,
     pub alpha_tiles: Vec<CompositeTile>,
     pub clear_tiles: Vec<CompositeTile>,
     pub z_generator: ZBufferIdGenerator,
+    // If false, we can't rely on the dirty rects in the CompositeTile
+    // instances. This currently occurs during a scroll event, as a
+    // signal to refresh the whole screen. This is only a temporary
+    // measure until we integrate with OS compositors. In the meantime
+    // it gives us the ability to partial present for any non-scroll
+    // case as a simple win (e.g. video, animation etc).
+    pub dirty_rects_are_valid: bool,
 }
 
-impl CompositeConfig {
+impl CompositeState {
     pub fn new() -> Self {
-        CompositeConfig {
+        CompositeState {
             opaque_tiles: Vec::new(),
             alpha_tiles: Vec::new(),
             clear_tiles: Vec::new(),
             z_generator: ZBufferIdGenerator::new(0),
+            dirty_rects_are_valid: true,
         }
     }
 }
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF};
 use api::{PipelineId};
 use api::units::*;
 use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
 use crate::clip::{ClipStore, ClipChainStack};
 use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
-use crate::composite::CompositeConfig;
+use crate::composite::CompositeState;
 use crate::debug_render::DebugItem;
 use crate::gpu_cache::{GpuCache, GpuCacheHandle};
 use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use crate::gpu_types::TransformData;
 use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
 use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
 use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
 use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
@@ -124,16 +124,17 @@ pub struct FrameVisibilityState<'a> {
     pub resource_cache: &'a mut ResourceCache,
     pub gpu_cache: &'a mut GpuCache,
     pub scratch: &'a mut PrimitiveScratchBuffer,
     pub tile_cache: Option<Box<TileCacheInstance>>,
     pub retained_tiles: &'a mut RetainedTiles,
     pub data_stores: &'a mut DataStores,
     pub clip_chain_stack: ClipChainStack,
     pub render_tasks: &'a mut RenderTaskGraph,
+    pub composite_state: &'a mut CompositeState,
 }
 
 pub struct FrameBuildingContext<'a> {
     pub global_device_pixel_scale: DevicePixelScale,
     pub scene_properties: &'a SceneProperties,
     pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
     pub global_screen_world_rect: WorldRect,
     pub clip_scroll_tree: &'a ClipScrollTree,
@@ -233,16 +234,17 @@ impl FrameBuilder {
         global_device_pixel_scale: DevicePixelScale,
         scene_properties: &SceneProperties,
         transform_palette: &mut TransformPalette,
         data_stores: &mut DataStores,
         surfaces: &mut Vec<SurfaceInfo>,
         scratch: &mut PrimitiveScratchBuffer,
         debug_flags: DebugFlags,
         texture_cache_profile: &mut TextureCacheProfileCounters,
+        composite_state: &mut CompositeState,
     ) -> Option<RenderTaskId> {
         profile_scope!("cull");
 
         if scene.prim_store.pictures.is_empty() {
             return None
         }
 
         scratch.begin_frame();
@@ -331,16 +333,17 @@ impl FrameBuilder {
                 gpu_cache,
                 clip_store: &mut scene.clip_store,
                 scratch,
                 tile_cache: None,
                 retained_tiles: &mut retained_tiles,
                 data_stores,
                 clip_chain_stack: ClipChainStack::new(),
                 render_tasks,
+                composite_state,
             };
 
             scene.prim_store.update_visibility(
                 scene.root_pic_index,
                 ROOT_SURFACE_INDEX,
                 &global_screen_world_rect,
                 &visibility_context,
                 &mut visibility_state,
@@ -470,39 +473,40 @@ impl FrameBuilder {
         let mut render_tasks = RenderTaskGraph::new(
             stamp.frame_id(),
             render_task_counters,
         );
         let mut surfaces = Vec::new();
 
         let output_size = scene.output_rect.size.to_i32();
         let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
+        let mut composite_state = CompositeState::new();
 
         let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
             scene,
             screen_world_rect,
             resource_cache,
             gpu_cache,
             &mut render_tasks,
             &mut profile_counters,
             global_device_pixel_scale,
             scene_properties,
             &mut transform_palette,
             data_stores,
             &mut surfaces,
             scratch,
             debug_flags,
             texture_cache_profile,
+            &mut composite_state,
         );
 
         let mut passes;
         let mut deferred_resolves = vec![];
         let mut has_texture_cache_tasks = false;
         let mut prim_headers = PrimitiveHeaders::new();
-        let mut composite_config = CompositeConfig::new();
 
         {
             profile_marker!("Batching");
 
             passes = render_tasks.generate_passes(
                 main_render_task_id,
                 output_size,
                 scene.config.gpu_supports_fast_clears,
@@ -535,17 +539,17 @@ impl FrameBuilder {
                     &mut ctx,
                     gpu_cache,
                     &mut render_tasks,
                     &mut deferred_resolves,
                     &scene.clip_store,
                     &mut transform_palette,
                     &mut prim_headers,
                     &mut z_generator,
-                    &mut composite_config,
+                    &mut composite_state,
                 );
 
                 match pass.kind {
                     RenderPassKind::MainFramebuffer { .. } => {}
                     RenderPassKind::OffScreen {
                         ref texture_cache,
                         ref picture_cache,
                         ..
@@ -576,19 +580,18 @@ impl FrameBuilder {
             transform_palette: transform_palette.finish(),
             render_tasks,
             deferred_resolves,
             gpu_cache_frame_id,
             has_been_rendered: false,
             has_texture_cache_tasks,
             prim_headers,
             recorded_dirty_regions: mem::replace(&mut scratch.recorded_dirty_regions, Vec::new()),
-            dirty_rects: mem::replace(&mut scratch.dirty_rects, Vec::new()),
             debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
-            composite_config,
+            composite_state,
         }
     }
 }
 
 /// Processes this pass to prepare it for rendering.
 ///
 /// Among other things, this allocates output regions for each of our tasks
 /// (added via `add_render_task`) in a RenderTarget and assigns it into that
@@ -598,17 +601,17 @@ pub fn build_render_pass(
     ctx: &mut RenderTargetContext,
     gpu_cache: &mut GpuCache,
     render_tasks: &mut RenderTaskGraph,
     deferred_resolves: &mut Vec<DeferredResolve>,
     clip_store: &ClipStore,
     transforms: &mut TransformPalette,
     prim_headers: &mut PrimitiveHeaders,
     z_generator: &mut ZBufferIdGenerator,
-    composite_config: &mut CompositeConfig,
+    composite_state: &mut CompositeState,
 ) {
     profile_scope!("RenderPass::build");
 
     match pass.kind {
         RenderPassKind::MainFramebuffer { ref mut main_target, .. } => {
             for &task_id in &pass.tasks {
                 assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
                 main_target.add_task(
@@ -624,17 +627,17 @@ pub fn build_render_pass(
             main_target.build(
                 ctx,
                 gpu_cache,
                 render_tasks,
                 deferred_resolves,
                 prim_headers,
                 transforms,
                 z_generator,
-                composite_config,
+                composite_state,
             );
         }
         RenderPassKind::OffScreen {
             ref mut color,
             ref mut alpha,
             ref mut texture_cache,
             ref mut picture_cache,
         } => {
@@ -822,17 +825,17 @@ pub fn build_render_pass(
                     gpu_cache,
                     render_tasks,
                     deferred_resolves,
                     prim_headers,
                     transforms,
                     root_spatial_node_index,
                     surface_spatial_node_index,
                     z_generator,
-                    composite_config,
+                    composite_state,
                 );
 
                 // Create picture cache targets, one per render task, and assign
                 // the correct batcher to them.
                 let batchers = batch_builder.finalize();
                 for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) {
                     let task = &render_tasks[task_id];
                     let (target_rect, _) = task.get_target_rect();
@@ -877,28 +880,28 @@ pub fn build_render_pass(
                 ctx,
                 gpu_cache,
                 render_tasks,
                 deferred_resolves,
                 saved_color,
                 prim_headers,
                 transforms,
                 z_generator,
-                composite_config,
+                composite_state,
             );
             alpha.build(
                 ctx,
                 gpu_cache,
                 render_tasks,
                 deferred_resolves,
                 saved_alpha,
                 prim_headers,
                 transforms,
                 z_generator,
-                composite_config,
+                composite_state,
             );
         }
     }
 }
 
 /// A rendering-oriented representation of the frame built by the render backend
 /// and presented to the renderer.
 #[cfg_attr(feature = "capture", derive(Serialize))]
@@ -935,27 +938,23 @@ pub struct Frame {
     /// renderer.
     pub has_been_rendered: bool,
 
     /// Dirty regions recorded when generating this frame. Empty when not in
     /// testing.
     #[cfg_attr(feature = "serde", serde(skip))]
     pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
 
-    /// Dirty rects calculated when generating this frame.
-    #[cfg_attr(feature = "serde", serde(skip))]
-    pub dirty_rects: Vec<DeviceIntRect>,
-
     /// Debugging information to overlay for this frame.
     pub debug_items: Vec<DebugItem>,
 
     /// Contains picture cache tiles, and associated information.
     /// Used by the renderer to composite tiles into the framebuffer,
     /// or hand them off to an OS compositor.
-    pub composite_config: CompositeConfig,
+    pub composite_state: CompositeState,
 }
 
 impl Frame {
     // This frame must be flushed if it writes to the
     // texture cache, and hasn't been drawn yet.
     pub fn must_be_drawn(&self) -> bool {
         self.has_texture_cache_tasks && !self.has_been_rendered
     }
--- a/gfx/wr/webrender/src/lib.rs
+++ b/gfx/wr/webrender/src/lib.rs
@@ -206,14 +206,14 @@ pub use crate::device::{ProgramBinary, P
 pub use crate::device::Device;
 pub use crate::frame_builder::ChasePrimitive;
 pub use crate::prim_store::PrimitiveDebugId;
 pub use crate::profiler::{ProfilerHooks, set_profiler_hooks};
 pub use crate::renderer::{
     AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind, ExternalImage,
     ExternalImageHandler, ExternalImageSource, GpuProfile, GraphicsApi, GraphicsApiInfo,
     PipelineInfo, Renderer, RendererOptions, RenderResults, RendererStats, SceneBuilderHooks,
-    ThreadListener, ShaderPrecacheFlags, MAX_VERTEX_TEXTURE_WIDTH,
+    ThreadListener, ShaderPrecacheFlags, MAX_VERTEX_TEXTURE_WIDTH, PresentConfig,
 };
 pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};
 pub use crate::shade::{Shaders, WrShaders};
 pub use api as webrender_api;
 pub use webrender_build::shader::ProgramSourceDigest;
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -32,17 +32,17 @@ use crate::render_task_graph::RenderTask
 use crate::render_target::RenderTargetKind;
 use crate::render_task::{RenderTask, RenderTaskLocation, BlurTaskCache, ClearMode};
 use crate::resource_cache::ResourceCache;
 use crate::scene::SceneProperties;
 use smallvec::SmallVec;
 use std::{mem, u8, marker};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use crate::texture_cache::TextureCacheHandle;
-use crate::util::{TransformedRectKind, MatrixHelpers, MaxRect, scale_factors, VecHelper, subtract_rect};
+use crate::util::{TransformedRectKind, MatrixHelpers, MaxRect, scale_factors, VecHelper};
 use crate::filterdata::{FilterDataHandle};
 
 /*
  A picture represents a dynamically rendered image. It consists of:
 
  * A number of primitives that are drawn onto the picture.
  * A composite operation describing how to composite this
    picture into its parent.
@@ -421,17 +421,17 @@ pub struct Tile {
     pub is_opaque: bool,
     /// Root node of the quadtree dirty rect tracker.
     root: TileNode,
     /// The picture space dirty rect for this tile.
     dirty_rect: PictureRect,
     /// The world space dirty rect for this tile.
     /// TODO(gw): We have multiple dirty rects available due to the quadtree above. In future,
     ///           expose these as multiple dirty rects, which will help in some cases.
-    world_dirty_rect: WorldRect,
+    pub world_dirty_rect: WorldRect,
     /// The last rendered background color on this tile.
     background_color: Option<ColorF>,
 }
 
 impl Tile {
     /// Construct a new, invalid tile.
     fn new(
         id: TileId,
@@ -1748,20 +1748,18 @@ impl TileCacheInstance {
         true
     }
 
     /// Apply any updates after prim dependency updates. This applies
     /// any late tile invalidations, and sets up the dirty rect and
     /// set of tile blits.
     pub fn post_update(
         &mut self,
-        resource_cache: &mut ResourceCache,
-        gpu_cache: &mut GpuCache,
         frame_context: &FrameVisibilityContext,
-        scratch: &mut PrimitiveScratchBuffer,
+        frame_state: &mut FrameVisibilityState,
     ) {
         self.tiles_to_draw.clear();
         self.dirty_region.clear();
 
         // Detect if the picture cache was scrolled or scaled. In this case,
         // the device space dirty rects aren't applicable (until we properly
         // integrate with OS compositors that can handle scrolling slices).
         let root_transform = frame_context
@@ -1769,16 +1767,17 @@ impl TileCacheInstance {
             .get_relative_transform(
                 self.spatial_node_index,
                 ROOT_SPATIAL_NODE_INDEX,
             )
             .into();
         let root_transform_changed = root_transform != self.root_transform;
         if root_transform_changed {
             self.root_transform = root_transform;
+            frame_state.composite_state.dirty_rects_are_valid = false;
         }
 
         // Diff the state of the spatial nodes between last frame build and now.
         let mut old_spatial_nodes = mem::replace(&mut self.spatial_nodes, FastHashMap::default());
 
         // TODO(gw): Maybe remove the used_spatial_nodes set and just mutate / create these
         //           diffs inside add_prim_dependency?
         for spatial_node_index in self.used_spatial_nodes.drain() {
@@ -1823,79 +1822,36 @@ impl TileCacheInstance {
             backdrop: self.backdrop,
             spatial_nodes: &self.spatial_nodes,
             opacity_bindings: &self.opacity_bindings,
             pic_to_world_mapper,
             current_tile_size: self.current_tile_size,
         };
 
         let mut state = TilePostUpdateState {
-            resource_cache,
-            gpu_cache,
-            scratch,
+            resource_cache: frame_state.resource_cache,
+            gpu_cache: frame_state.gpu_cache,
+            scratch: frame_state.scratch,
             dirty_region: &mut self.dirty_region,
         };
 
         // Step through each tile and invalidate if the dependencies have changed.
         for (key, tile) in self.tiles.iter_mut() {
             if tile.post_update(
                 &ctx,
                 &mut state,
             ) {
-                // If we have dirty tiles and a scroll didn't occur (e.g. video
-                // playback or animation) we can produce a valid dirty rect for
-                // Gecko to use as partial present.
-                if !root_transform_changed && !tile.dirty_rect.is_empty() {
-                    state.scratch.dirty_rects.push(
-                        (tile.world_dirty_rect * frame_context.global_device_pixel_scale).to_i32()
-                    );
-                }
-
                 self.tiles_to_draw.push(*key);
             }
         }
 
-        // If the cache was scrolled / scaled, just push a full-screen dirty rect
-        // for now, to ensure correctness. This won't be required once we fully
-        // support OS compositors, where we can pass the transform of the cache slice.
-        if root_transform_changed {
-            scratch.dirty_rects.push(
-                (frame_context.global_screen_world_rect * frame_context.global_device_pixel_scale).to_i32()
-            );
-        } else {
-            // TODO(gw): This is a total hack! While experimenting with dirty rects and
-            //           partial present, we need to include a dirty rect for the area
-            //           of the screen that is _not_ picture cached. Since we know there
-            //           is only a single picture cache right now, we can derive this
-            //           area by subtracting the picture cache rect from the screen rect.
-            //           This only works reliably while we can assume that (a) there is
-            //           only a single picture cache slice and (b) it's opaque. In future,
-            //           once we enable multiple picture cache slices, we won't need to
-            //           do this at all, since all content will be in a cache slice that
-            //           can provide valid dirty rects.
-            let world_clip_rect = ctx.pic_to_world_mapper
-                .map(&self.local_clip_rect)
-                .expect("bug - unable to map picture clip rect to world");
-            let mut non_cached_rects = Vec::new();
-            subtract_rect(
-                &ctx.global_screen_world_rect,
-                &world_clip_rect,
-                &mut non_cached_rects,
-            );
-            for rect in non_cached_rects {
-                scratch.dirty_rects.push(
-                    (rect * frame_context.global_device_pixel_scale).to_i32()
-                );
-            }
-        }
-
         // When under test, record a copy of the dirty region to support
         // invalidation testing in wrench.
         if frame_context.config.testing {
-            scratch.recorded_dirty_regions.push(self.dirty_region.record());
+            frame_state.scratch.recorded_dirty_regions.push(self.dirty_region.record());
         }
     }
 }
 
 /// Maintains a stack of picture and surface information, that
 /// is used during the initial picture traversal.
 pub struct PictureUpdateState<'a> {
     surfaces: &'a mut Vec<SurfaceInfo>,
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1687,34 +1687,30 @@ pub struct PrimitiveScratchBuffer {
 
     /// List of the visibility information for currently visible primitives.
     pub prim_info: Vec<PrimitiveVisibility>,
 
     /// List of dirty regions for the cached pictures in this document, used to
     /// verify invalidation in wrench reftests. Only collected in testing.
     pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
 
-    /// List of dirty rects for the cached pictures in this document.
-    pub dirty_rects: Vec<DeviceIntRect>,
-
     /// List of debug display items for rendering.
     pub debug_items: Vec<DebugItem>,
 }
 
 impl PrimitiveScratchBuffer {
     pub fn new() -> Self {
         PrimitiveScratchBuffer {
             clip_mask_instances: Vec::new(),
             glyph_keys: GlyphKeyStorage::new(0),
             border_cache_handles: BorderHandleStorage::new(0),
             segments: SegmentStorage::new(0),
             segment_instances: SegmentInstanceStorage::new(0),
             gradient_tiles: GradientTileStorage::new(0),
             recorded_dirty_regions: Vec::new(),
-            dirty_rects: Vec::new(),
             debug_items: Vec::new(),
             prim_info: Vec::new(),
         }
     }
 
     pub fn recycle(&mut self, recycler: &mut Recycler) {
         recycler.recycle_vec(&mut self.clip_mask_instances);
         recycler.recycle_vec(&mut self.prim_info);
@@ -1741,17 +1737,16 @@ impl PrimitiveScratchBuffer {
         //           should fix this in the future to retain handles.
         self.gradient_tiles.clear();
 
         self.prim_info.clear();
 
         self.debug_items.clear();
 
         assert!(self.recorded_dirty_regions.is_empty(), "Should have sent to Renderer");
-        assert!(self.dirty_rects.is_empty(), "Should have sent to Renderer");
     }
 
     #[allow(dead_code)]
     pub fn push_debug_rect(
         &mut self,
         rect: DeviceRect,
         outer_color: ColorF,
         inner_color: ColorF,
@@ -2310,20 +2305,18 @@ impl PrimitiveStore {
                 pic.precise_local_rect = pic_local_rect;
             }
 
             if let PictureCompositeMode::TileCache { .. } = rc.composite_mode {
                 let mut tile_cache = frame_state.tile_cache.take().unwrap();
 
                 // Build the dirty region(s) for this tile cache.
                 tile_cache.post_update(
-                    frame_state.resource_cache,
-                    frame_state.gpu_cache,
                     frame_context,
-                    frame_state.scratch,
+                    frame_state,
                 );
 
                 pic.tile_cache = Some(tile_cache);
             }
 
             None
         } else {
             let parent_surface = &frame_context.surfaces[parent_surface_index.0 as usize];
--- a/gfx/wr/webrender/src/render_target.rs
+++ b/gfx/wr/webrender/src/render_target.rs
@@ -4,17 +4,17 @@
 
 
 use api::units::*;
 use api::{ColorF, PremultipliedColorF, ImageFormat, LineOrientation, BorderStyle, PipelineId};
 use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, resolve_image};
 use crate::batch::{ClipBatcher, BatchBuilder};
 use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX};
 use crate::clip::ClipStore;
-use crate::composite::CompositeConfig;
+use crate::composite::CompositeState;
 use crate::device::Texture;
 use crate::frame_builder::{FrameGlobalResources};
 use crate::gpu_cache::{GpuCache, GpuCacheAddress};
 use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
 use crate::gpu_types::{TransformPalette, ZBufferIdGenerator};
 use crate::internal_types::{FastHashMap, TextureSource, LayerIndex, Swizzle, SavedTargetIndex};
 use crate::picture::SurfaceInfo;
 use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask};
@@ -97,17 +97,17 @@ pub trait RenderTarget {
         &mut self,
         _ctx: &mut RenderTargetContext,
         _gpu_cache: &mut GpuCache,
         _render_tasks: &mut RenderTaskGraph,
         _deferred_resolves: &mut Vec<DeferredResolve>,
         _prim_headers: &mut PrimitiveHeaders,
         _transforms: &mut TransformPalette,
         _z_generator: &mut ZBufferIdGenerator,
-        _composite_config: &mut CompositeConfig,
+        _composite_state: &mut CompositeState,
     ) {
     }
 
     /// Associates a `RenderTask` with this target. That task must be assigned
     /// to a region returned by invoking `allocate()` on this target.
     ///
     /// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
     /// GPU cache here. They are typically used by the build step above. They
@@ -198,31 +198,31 @@ impl<T: RenderTarget> RenderTargetList<T
         ctx: &mut RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskGraph,
         deferred_resolves: &mut Vec<DeferredResolve>,
         saved_index: Option<SavedTargetIndex>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         z_generator: &mut ZBufferIdGenerator,
-        composite_config: &mut CompositeConfig,
+        composite_state: &mut CompositeState,
     ) {
         debug_assert_eq!(None, self.saved_index);
         self.saved_index = saved_index;
 
         for target in &mut self.targets {
             target.build(
                 ctx,
                 gpu_cache,
                 render_tasks,
                 deferred_resolves,
                 prim_headers,
                 transforms,
                 z_generator,
-                composite_config,
+                composite_state,
             );
         }
     }
 
     pub fn allocate(
         &mut self,
         alloc_size: DeviceIntSize,
     ) -> (RenderTargetIndex, DeviceIntPoint) {
@@ -331,17 +331,17 @@ impl RenderTarget for ColorRenderTarget 
         &mut self,
         ctx: &mut RenderTargetContext,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskGraph,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_headers: &mut PrimitiveHeaders,
         transforms: &mut TransformPalette,
         z_generator: &mut ZBufferIdGenerator,
-        composite_config: &mut CompositeConfig,
+        composite_state: &mut CompositeState,
     ) {
         let mut merged_batches = AlphaBatchContainer::new(None);
 
         for task_id in &self.alpha_tasks {
             let task = &render_tasks[*task_id];
 
             match task.clear_mode {
                 ClearMode::One |
@@ -399,17 +399,17 @@ impl RenderTarget for ColorRenderTarget 
                         gpu_cache,
                         render_tasks,
                         deferred_resolves,
                         prim_headers,
                         transforms,
                         raster_spatial_node_index,
                         pic_task.surface_spatial_node_index,
                         z_generator,
-                        composite_config,
+                        composite_state,
                     );
 
                     let alpha_batch_builders = batch_builder.finalize();
 
                     for batcher in alpha_batch_builders {
                         batcher.build(
                             &mut self.alpha_batch_containers,
                             &mut merged_batches,
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -42,17 +42,17 @@ use api::{DebugCommand, MemoryReport, Vo
 use api::{RenderApiSender, RenderNotifier, TextureTarget};
 use api::channel;
 use api::units::*;
 pub use api::DebugFlags;
 use api::channel::{MsgSender, PayloadReceiverHelperMethods};
 use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList};
 #[cfg(any(feature = "capture", feature = "replay"))]
 use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
-use crate::composite::{CompositeConfig, CompositeTileSurface, CompositeTile};
+use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile};
 use crate::debug_colors;
 use crate::debug_render::{DebugItem, DebugRenderer};
 use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO};
 use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot};
 use crate::device::{ShaderError, TextureFilter, TextureFlags,
              VertexUsageHint, VAO, VBO, CustomVAO};
 use crate::device::ProgramCache;
 use crate::device::query::GpuTimer;
@@ -935,16 +935,44 @@ impl CpuProfile {
             frame_id,
             backend_time_ns,
             composite_time_ns,
             draw_calls,
         }
     }
 }
 
+/// Defines the configuration that the client is using to present results
+/// to the underlying device.
+#[derive(Debug, Copy, Clone)]
+pub enum PresentConfig {
+    /// In this mode, the device supports updating some number of dirty
+    /// regions since the last frame was drawn, which can provide significant
+    /// power savings.
+    PartialPresent {
+        /// The maximum number of dirty rects that are supported by the device.
+        max_dirty_rects: usize,
+    },
+}
+
+/// The selected partial present mode for a given frame.
+#[derive(Debug, Copy, Clone)]
+enum PartialPresentMode {
+    /// The device supports fewer dirty rects than the number of dirty rects
+    /// that WR produced. In this case, the WR dirty rects are union'ed into
+    /// a single dirty rect, that is provided to the caller.
+    Single {
+        dirty_rect: DeviceRect,
+    },
+    /// The device supports at least the same number of dirty rects as the
+    /// number of dirty rects produced by WR this frame. In this case, the
+    /// tile dirty rect is applied to the clip for each tile as it's drawn.
+    Multi,
+}
+
 /// A Texture that has been initialized by the `device` module and is ready to
 /// be used.
 struct ActiveTexture {
     texture: Texture,
     saved_index: Option<SavedTargetIndex>,
 }
 
 /// Helper struct for resolving device Textures for use during rendering passes.
@@ -1834,16 +1862,24 @@ pub struct Renderer {
 
     /// The set of documents which we've seen a publish for since last render.
     documents_seen: FastHashSet<DocumentId>,
 
     #[cfg(feature = "capture")]
     read_fbo: FBOId,
     #[cfg(feature = "replay")]
     owned_external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
+
+    /// The current presentation config, affecting how WR composites into the
+    /// final scene.
+    present_config: Option<PresentConfig>,
+
+    /// If true, partial present state has been reset and everything needs to
+    /// be drawn on the next render.
+    force_redraw: bool,
 }
 
 #[derive(Debug)]
 pub enum RendererError {
     Shader(ShaderError),
     Thread(std::io::Error),
     Resource(ResourceCacheError),
     MaxTextureSize,
@@ -2105,16 +2141,17 @@ impl Renderer {
             gpu_supports_fast_clears: options.gpu_supports_fast_clears,
             gpu_supports_advanced_blend: ext_blend_equation_advanced,
             advanced_blend_is_coherent: ext_blend_equation_advanced_coherent,
             batch_lookback_count: options.batch_lookback_count,
             background_color: options.clear_color,
         };
         info!("WR {:?}", config);
 
+        let present_config = options.present_config.take();
         let device_pixel_ratio = options.device_pixel_ratio;
         let debug_flags = options.debug_flags;
         let payload_rx_for_backend = payload_rx.to_mpsc_receiver();
         let size_of_op = options.size_of_op;
         let enclosing_size_of_op = options.enclosing_size_of_op;
         let make_size_of_ops =
             move || size_of_op.map(|o| MallocSizeOfOps::new(o, enclosing_size_of_op));
         let recorder = options.recorder;
@@ -2337,16 +2374,18 @@ impl Renderer {
             #[cfg(feature = "replay")]
             owned_external_images: FastHashMap::default(),
             notifications: Vec::new(),
             device_size: None,
             zoom_debug_texture: None,
             cursor_position: DeviceIntPoint::zero(),
             shared_texture_cache_cleared: false,
             documents_seen: FastHashSet::default(),
+            present_config,
+            force_redraw: true,
         };
 
         // We initially set the flags to default and then now call set_debug_flags
         // to ensure any potential transition when enabling a flag is run.
         renderer.set_debug_flags(debug_flags);
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
@@ -2807,16 +2846,22 @@ impl Renderer {
         let gpu_profiles = self.gpu_profiles.drain(..).collect();
         (cpu_profiles, gpu_profiles)
     }
 
     pub fn notify_slow_frame(&mut self) {
         self.slow_frame_indicator.changed();
     }
 
+    /// Reset the current partial present state. This forces the entire framebuffer
+    /// to be refreshed next time `render` is called.
+    pub fn force_redraw(&mut self) {
+        self.force_redraw = true;
+    }
+
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [`generate_frame()`][webrender_api::Transaction::generate_frame].
     pub fn render(
         &mut self,
         device_size: DeviceIntSize,
     ) -> Result<RenderResults, Vec<RendererError>> {
         self.device_size = Some(device_size);
@@ -2919,32 +2964,30 @@ impl Renderer {
                 assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id,
                     "Received frame depends on a later GPU cache epoch ({:?}) than one we received last via `UpdateGpuCache` ({:?})",
                     frame.gpu_cache_frame_id, self.gpu_cache_frame_id);
 
                 self.draw_frame(
                     frame,
                     device_size,
                     cpu_frame_id,
-                    &mut results.stats,
+                    &mut results,
                     doc_index == 0,
                 );
 
                 if let Some(_) = device_size {
                     self.draw_frame_debug_items(&frame.debug_items);
                 }
                 if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
                     frame_profiles.push(frame.profile_counters.clone());
                 }
 
                 let dirty_regions =
                     mem::replace(&mut frame.recorded_dirty_regions, Vec::new());
                 results.recorded_dirty_regions.extend(dirty_regions);
-                let dirty_rects = mem::replace(&mut frame.dirty_rects, Vec::new());
-                results.dirty_rects.extend(dirty_rects);
 
                 // If we're the last document, don't call end_pass here, because we'll
                 // be moving on to drawing the debug overlays. See the comment above
                 // the end_pass call in draw_frame about debug draw overlays
                 // for a bit more context.
                 if doc_index != last_document_index {
                     self.texture_resolver.end_pass(&mut self.device, None, None);
                 }
@@ -3833,16 +3876,17 @@ impl Renderer {
             self.device.disable_scissor();
         }
     }
 
     /// Draw a list of tiles to the framebuffer
     fn draw_tile_list<'a, I: Iterator<Item = &'a CompositeTile>>(
         &mut self,
         tiles_iter: I,
+        partial_present_mode: Option<PartialPresentMode>,
         stats: &mut RendererStats,
     ) {
         let mut current_textures = BatchTextures::no_texture();
         let mut instances = Vec::new();
 
         for tile in tiles_iter {
             // Work out the draw params based on the tile surface
             let (texture, layer, color) = match tile.surface {
@@ -3853,32 +3897,45 @@ impl Renderer {
                     (TextureSource::Dummy, 0.0, ColorF::BLACK)
                 }
                 CompositeTileSurface::Texture { texture_id, texture_layer } => {
                     (texture_id, texture_layer as f32, ColorF::WHITE)
                 }
             };
             let textures = BatchTextures::color(texture);
 
+            // Determine a clip rect to apply to this tile, depending on what
+            // the partial present mode is.
+            let partial_clip_rect = match partial_present_mode {
+                Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect,
+                Some(PartialPresentMode::Multi) => tile.dirty_rect,
+                None => tile.rect,
+            };
+
+            let clip_rect = match partial_clip_rect.intersection(&tile.clip_rect) {
+                Some(rect) => rect,
+                None => continue,
+            };
+
             // Flush this batch if the textures aren't compatible
             if !current_textures.is_compatible_with(&textures) {
                 self.draw_instanced_batch(
                     &instances,
                     VertexArrayKind::Composite,
                     &current_textures,
                     stats,
                 );
                 instances.clear();
             }
             current_textures = textures;
 
             // Create the instance and add to current batch
             let instance = CompositeInstance::new(
                 tile.rect,
-                tile.clip_rect,
+                clip_rect,
                 color.premultiplied(),
                 layer,
                 tile.z_id,
             );
             instances.push(instance);
         }
 
         // Flush the last batch
@@ -3893,67 +3950,150 @@ impl Renderer {
     }
 
     /// Composite picture cache tiles into the framebuffer. This is currently
     /// the only way that picture cache tiles get drawn. In future, the tiles
     /// will often be handed to the OS compositor, and this method will be
     /// rarely used.
     fn composite(
         &mut self,
-        composite_config: &CompositeConfig,
+        composite_state: &CompositeState,
+        clear_framebuffer: bool,
         draw_target: DrawTarget,
         projection: &default::Transform3D<f32>,
-        stats: &mut RendererStats,
+        results: &mut RenderResults,
     ) {
         let _gm = self.gpu_profile.start_marker("framebuffer");
         let _timer = self.gpu_profile.start_timer(GPU_TAG_COMPOSITE);
 
         self.device.bind_draw_target(draw_target);
         self.device.enable_depth();
+        self.device.enable_depth_write();
+
+        // Determine the partial present mode for this frame, which is used during
+        // framebuffer clears and calculating the clip rect for each tile that is drawn.
+        let mut partial_present_mode = None;
+
+        if let Some(PresentConfig::PartialPresent { max_dirty_rects }) = self.present_config {
+            // We can only use partial present if we have valid dirty rects and the
+            // client hasn't reset partial present state since last frame.
+            if composite_state.dirty_rects_are_valid && !self.force_redraw {
+                let mut dirty_rect_count = 0;
+                let mut combined_dirty_rect = DeviceRect::zero();
+
+                // Work out how many dirty rects WR produced, and if that's more than
+                // what the device supports.
+                for tile in composite_state.opaque_tiles.iter().chain(composite_state.alpha_tiles.iter()) {
+                    if !tile.dirty_rect.is_empty() {
+                        dirty_rect_count += 1;
+                        results.dirty_rects.push(tile.dirty_rect.to_i32());
+                        combined_dirty_rect = combined_dirty_rect.union(&tile.dirty_rect);
+                    }
+                }
+
+                let mode = if dirty_rect_count > max_dirty_rects {
+                    // In this case, WR produced more dirty rects than what the device supports.
+                    // As a simple way to handle this, just union all the dirty rects into a
+                    // single dirty rect.
+                    // TODO(gw): In future, maybe we can handle this case better by trying to
+                    //           coalesce dirty rects into a smaller number rather than just
+                    //           falling back to a single dirty rect.
+                    let combined_dirty_rect = combined_dirty_rect.round();
+                    results.dirty_rects.clear();
+                    results.dirty_rects.push(combined_dirty_rect.to_i32());
+                    PartialPresentMode::Single {
+                        dirty_rect: combined_dirty_rect,
+                    }
+                } else {
+                    PartialPresentMode::Multi
+                };
+
+                partial_present_mode = Some(mode);
+            }
+
+            self.force_redraw = false;
+        }
+
+        // Clear the framebuffer, if required
+        if clear_framebuffer {
+            let clear_color = self.clear_color.map(|color| color.to_array());
+
+            match partial_present_mode {
+                Some(PartialPresentMode::Single { dirty_rect }) => {
+                    // We have a single dirty rect, so clear only that
+                    self.device.clear_target(clear_color,
+                                             Some(1.0),
+                                             Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32())));
+                }
+                Some(PartialPresentMode::Multi) => {
+                    // We have a dirty rect per tile, so clear each of them
+                    let opaque_iter = composite_state.opaque_tiles.iter();
+                    let clear_iter = composite_state.clear_tiles.iter();
+                    let alpha_iter = composite_state.alpha_tiles.iter();
+
+                    for tile in opaque_iter.chain(clear_iter.chain(alpha_iter)) {
+                        if !tile.dirty_rect.is_empty() {
+                            self.device.clear_target(clear_color,
+                                                     Some(1.0),
+                                                     Some(draw_target.to_framebuffer_rect(tile.dirty_rect.to_i32())));
+                        }
+                    }
+                }
+                None => {
+                    // Partial present is disabled, so clear the entire framebuffer
+                    self.device.clear_target(clear_color,
+                                             Some(1.0),
+                                             None);
+                }
+            }
+        }
 
         self.shaders.borrow_mut().composite.bind(
             &mut self.device,
             &projection,
             &mut self.renderer_errors
         );
 
         // Draw opaque tiles first, front-to-back to get maxmum
         // z-reject efficiency.
-        if !composite_config.opaque_tiles.is_empty() {
+        if !composite_state.opaque_tiles.is_empty() {
             let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
             self.device.enable_depth_write();
             self.set_blend(false, FramebufferKind::Main);
             self.draw_tile_list(
-                composite_config.opaque_tiles.iter().rev(),
-                stats,
+                composite_state.opaque_tiles.iter().rev(),
+                partial_present_mode,
+                &mut results.stats,
             );
             self.gpu_profile.finish_sampler(opaque_sampler);
         }
 
-        if !composite_config.clear_tiles.is_empty() {
+        if !composite_state.clear_tiles.is_empty() {
             let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
             self.device.disable_depth_write();
             self.set_blend(true, FramebufferKind::Main);
             self.device.set_blend_mode_premultiplied_dest_out();
             self.draw_tile_list(
-                composite_config.clear_tiles.iter(),
-                stats,
+                composite_state.clear_tiles.iter(),
+                partial_present_mode,
+                &mut results.stats,
             );
             self.gpu_profile.finish_sampler(transparent_sampler);
         }
 
         // Draw alpha tiles
-        if !composite_config.alpha_tiles.is_empty() {
+        if !composite_state.alpha_tiles.is_empty() {
             let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
             self.device.disable_depth_write();
             self.set_blend(true, FramebufferKind::Main);
             self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main);
             self.draw_tile_list(
-                composite_config.alpha_tiles.iter(),
-                stats,
+                composite_state.alpha_tiles.iter(),
+                partial_present_mode,
+                &mut results.stats,
             );
             self.gpu_profile.finish_sampler(transparent_sampler);
         }
     }
 
     fn draw_color_target(
         &mut self,
         draw_target: DrawTarget,
@@ -4698,17 +4838,17 @@ impl Renderer {
         debug_assert!(self.texture_resolver.prev_pass_color.is_none());
     }
 
     fn draw_frame(
         &mut self,
         frame: &mut Frame,
         device_size: Option<DeviceIntSize>,
         frame_id: GpuFrameId,
-        stats: &mut RendererStats,
+        results: &mut RenderResults,
         clear_framebuffer: bool,
     ) {
         // These markers seem to crash a lot on Android, see bug 1559834
         #[cfg(not(target_os = "android"))]
         let _gm = self.gpu_profile.start_marker("draw frame");
 
         if frame.passes.is_empty() {
             frame.has_been_rendered = true;
@@ -4734,17 +4874,17 @@ impl Renderer {
                 &TextureSource::PrevPassColor,
                 TextureSampler::PrevPassColor,
                 &mut self.device,
             );
 
             match pass.kind {
                 RenderPassKind::MainFramebuffer { ref main_target, .. } => {
                     if let Some(device_size) = device_size {
-                        stats.color_target_count += 1;
+                        results.stats.color_target_count += 1;
 
                         let offset = frame.content_origin.to_f32();
                         let size = frame.device_rect.size.to_f32();
                         let projection = Transform3D::ortho(
                             offset.x,
                             offset.x + size.width,
                             offset.y + size.height,
                             offset.y,
@@ -4755,42 +4895,36 @@ impl Renderer {
                         let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32);
                         let mut fb_rect = frame.device_rect * fb_scale;
                         fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height;
 
                         let draw_target = DrawTarget::Default {
                             rect: fb_rect,
                             total_size: device_size * fb_scale,
                         };
-                        if clear_framebuffer {
-                            self.device.bind_draw_target(draw_target);
-                            self.device.enable_depth_write();
-                            self.device.clear_target(self.clear_color.map(|color| color.to_array()),
-                                                     Some(1.0),
-                                                     None);
-                        }
 
                         if self.enable_picture_caching {
                             self.composite(
-                                &frame.composite_config,
+                                &frame.composite_state,
+                                clear_framebuffer,
                                 draw_target,
                                 &projection,
-                                stats,
+                                results,
                             );
                         } else {
                             self.draw_color_target(
                                 draw_target,
                                 main_target,
                                 frame.content_origin,
                                 None,
                                 None,
                                 &frame.render_tasks,
                                 &projection,
                                 frame_id,
-                                stats,
+                                &mut results.stats,
                             );
                         }
                     }
                 }
                 RenderPassKind::OffScreen {
                     ref mut alpha,
                     ref mut color,
                     ref mut texture_cache,
@@ -4804,23 +4938,23 @@ impl Renderer {
                     // skipped this time.
                     if !frame.has_been_rendered {
                         for (&(texture_id, target_index), target) in texture_cache {
                             self.draw_texture_cache_target(
                                 &texture_id,
                                 target_index,
                                 target,
                                 &frame.render_tasks,
-                                stats,
+                                &mut results.stats,
                             );
                         }
 
                         // Draw picture caching tiles for this pass.
                         for picture_target in picture_cache {
-                            stats.color_target_count += 1;
+                            results.stats.color_target_count += 1;
 
                             let (texture, _) = self.texture_resolver
                                 .resolve(&picture_target.texture)
                                 .expect("bug");
                             let draw_target = DrawTarget::from_texture(
                                 texture,
                                 picture_target.layer,
                                 true,
@@ -4836,23 +4970,23 @@ impl Renderer {
                             );
 
                             self.draw_picture_cache_target(
                                 picture_target,
                                 draw_target,
                                 frame.content_origin,
                                 &projection,
                                 &frame.render_tasks,
-                                stats,
+                                &mut results.stats,
                             );
                         }
                     }
 
                     for (target_index, target) in alpha.targets.iter().enumerate() {
-                        stats.alpha_target_count += 1;
+                        results.stats.alpha_target_count += 1;
                         let draw_target = DrawTarget::from_texture(
                             &alpha_tex.as_ref().unwrap().texture,
                             target_index,
                             false,
                         );
 
                         let projection = Transform3D::ortho(
                             0.0,
@@ -4863,22 +4997,22 @@ impl Renderer {
                             ORTHO_FAR_PLANE,
                         );
 
                         self.draw_alpha_target(
                             draw_target,
                             target,
                             &projection,
                             &frame.render_tasks,
-                            stats,
+                            &mut results.stats,
                         );
                     }
 
                     for (target_index, target) in color.targets.iter().enumerate() {
-                        stats.color_target_count += 1;
+                        results.stats.color_target_count += 1;
                         let draw_target = DrawTarget::from_texture(
                             &color_tex.as_ref().unwrap().texture,
                             target_index,
                             target.needs_depth(),
                         );
 
                         let projection = Transform3D::ortho(
                             0.0,
@@ -4899,17 +5033,17 @@ impl Renderer {
                             draw_target,
                             target,
                             frame.content_origin,
                             Some([0.0, 0.0, 0.0, 0.0]),
                             clear_depth,
                             &frame.render_tasks,
                             &projection,
                             frame_id,
-                            stats,
+                            &mut results.stats,
                         );
                     }
 
                     // Only end the pass here and invalidate previous textures for
                     // off-screen targets. Deferring return of the inputs to the
                     // frame buffer until the implicit end_pass in end_frame allows
                     // debug draw overlays to be added without triggering a copy
                     // resolve stage in mobile / tiled GPUs.
@@ -5730,16 +5864,18 @@ pub struct RendererOptions {
     pub allow_texture_swizzling: bool,
     /// Number of batches to look back in history for adding the current
     /// transparent instance into.
     pub batch_lookback_count: usize,
     /// Start the debug server for this renderer.
     pub start_debug_server: bool,
     /// Output the source of the shader with the given name.
     pub dump_shader_source: Option<String>,
+    /// An optional presentation config for compositor integration.
+    pub present_config: Option<PresentConfig>,
 }
 
 impl Default for RendererOptions {
     fn default() -> Self {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
@@ -5781,16 +5917,17 @@ impl Default for RendererOptions {
             allow_texture_swizzling: true,
             batch_lookback_count: DEFAULT_BATCH_LOOKBACK_COUNT,
             // For backwards compatibility we set this to true by default, so
             // that if the debugger feature is enabled, the debug server will
             // be started automatically. Users can explicitly disable this as
             // needed.
             start_debug_server: true,
             dump_shader_source: None,
+            present_config: None,
         }
     }
 }
 
 pub trait DebugServer {
     fn send(&mut self, _message: String);
 }
 
--- a/gfx/wr/webrender/src/util.rs
+++ b/gfx/wr/webrender/src/util.rs
@@ -1159,59 +1159,8 @@ pub fn clamp_to_scale_factor(val: f32, r
 /// Rounds a value up to the nearest multiple of mul
 pub fn round_up_to_multiple(val: usize, mul: NonZeroUsize) -> usize {
     match val % mul.get() {
         0 => val,
         rem => val - rem + mul.get(),
     }
 }
 
-/// A helper function to construct a rect from a (x0,y0) and (x1,y1) pair
-#[inline]
-fn rect_from_points_f<U>(x0: f32,
-                         y0: f32,
-                         x1: f32,
-                         y1: f32) -> Rect<f32, U> {
-    Rect::new(Point2D::new(x0, y0),
-              Size2D::new(x1 - x0, y1 - y0))
-}
-
-/// Subtract `other` from `rect`, and write the outputs into `results`.
-pub fn subtract_rect<U>(rect: &Rect<f32, U>,
-                        other: &Rect<f32, U>,
-                        results: &mut Vec<Rect<f32, U>>) {
-    results.clear();
-
-    let int = rect.intersection(other);
-    match int {
-        Some(int) => {
-            let rx0 = rect.origin.x;
-            let ry0 = rect.origin.y;
-            let rx1 = rx0 + rect.size.width;
-            let ry1 = ry0 + rect.size.height;
-
-            let ox0 = int.origin.x;
-            let oy0 = int.origin.y;
-            let ox1 = ox0 + int.size.width;
-            let oy1 = oy0 + int.size.height;
-
-            let r = rect_from_points_f(rx0, ry0, ox0, ry1);
-            if r.size.width > 0.0 && r.size.height > 0.0 {
-                results.push(r);
-            }
-            let r = rect_from_points_f(ox0, ry0, ox1, oy0);
-            if r.size.width > 0.0 && r.size.height > 0.0 {
-                results.push(r);
-            }
-            let r = rect_from_points_f(ox0, oy1, ox1, ry1);
-            if r.size.width > 0.0 && r.size.height > 0.0 {
-                results.push(r);
-            }
-            let r = rect_from_points_f(ox1, ry0, rx1, ry1);
-            if r.size.width > 0.0 && r.size.height > 0.0 {
-                results.push(r);
-            }
-        }
-        None => {
-            results.push(*rect);
-        }
-    }
-}
--- a/intl/Encoding.h
+++ b/intl/Encoding.h
@@ -1039,18 +1039,18 @@ class Decoder final {
    *
    * Does not change the state of the decoder.
    *
    * Do not use this unless you are supporting SpiderMonkey-style string
    * storage optimizations.
    */
   inline mozilla::Maybe<size_t> Latin1ByteCompatibleUpTo(
       Span<const uint8_t> aBuffer) const {
-    size_t upTo = decoder_latin1_byte_compatible_up_to(
-        this, aBuffer.Elements(), aBuffer.Length());
+    size_t upTo = decoder_latin1_byte_compatible_up_to(this, aBuffer.Elements(),
+                                                       aBuffer.Length());
     if (upTo == MaxValue<size_t>::value) {
       return mozilla::Nothing();
     }
     return mozilla::Some(upTo);
   }
 
  private:
   Decoder() = delete;
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -3776,18 +3776,17 @@ static const JSFunctionSpec array_method
     /* Future additions */
     JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1, 0),
     JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0, 0),
 
     JS_FS_END};
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_INLINABLE_FN("isArray", array_isArray, 1, 0, ArrayIsArray),
-    JS_SELF_HOSTED_FN("from", "ArrayFrom", 3, 0),
-    JS_FN("of", array_of, 0, 0),
+    JS_SELF_HOSTED_FN("from", "ArrayFrom", 3, 0), JS_FN("of", array_of, 0, 0),
 
     JS_FS_END};
 
 const JSPropertySpec array_static_props[] = {
     JS_SELF_HOSTED_SYM_GET(species, "$ArraySpecies", 0), JS_PS_END};
 
 static inline bool ArrayConstructorImpl(JSContext* cx, CallArgs& args,
                                         bool isConstructor) {
--- a/js/src/builtin/streams/ReadableStream.cpp
+++ b/js/src/builtin/streams/ReadableStream.cpp
@@ -17,17 +17,17 @@
 #include "builtin/Array.h"                   // js::NewDenseFullyAllocatedArray
 #include "builtin/streams/ClassSpecMacro.h"  // JS_STREAMS_CLASS_SPEC
 #include "builtin/streams/MiscellaneousOperations.h"  // js::MakeSizeAlgorithmFromSizeFunction, js::ValidateAndNormalizeHighWaterMark, js::ReturnPromiseRejectedWithPendingError
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController
 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"  // js::SetUpReadableStreamDefaultControllerFromUnderlyingSource
 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStreamCancel
 #include "builtin/streams/ReadableStreamOperations.h"  // js::ReadableStreamTee
 #include "builtin/streams/ReadableStreamReader.h"  // js::CreateReadableStreamDefaultReader, js::ForAuthorCodeBool
-#include "js/CallArgs.h"                           // JS::CallArgs{,FromVp}
+#include "js/CallArgs.h"  // JS::CallArgs{,FromVp}
 #include "js/Class.h"  // JSCLASS_PRIVATE_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
 #include "js/PropertySpec.h"  // JS{Function,Property}Spec, JS_FN, JS_PSG, JS_{FS,PS}_END
 #include "js/RootingAPI.h"        // JS::Handle, JS::Rooted, js::CanGC
 #include "js/Stream.h"            // JS::ReadableStream{Mode,UnderlyingSource}
 #include "js/Value.h"             // JS::Value
 #include "vm/JSContext.h"         // JSContext
 #include "vm/JSObject.h"          // js::GetPrototypeFromBuiltinConstructor
 #include "vm/NativeObject.h"      // js::PlainObject
--- a/js/src/builtin/streams/ReadableStreamController.h
+++ b/js/src/builtin/streams/ReadableStreamController.h
@@ -8,20 +8,20 @@
 
 #ifndef builtin_streams_ReadableStreamController_h
 #define builtin_streams_ReadableStreamController_h
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 
 #include <stdint.h>  // uint32_t
 
-#include "builtin/streams/ReadableStream.h"  // js::ReadableStream
+#include "builtin/streams/ReadableStream.h"    // js::ReadableStream
 #include "builtin/streams/StreamController.h"  // js::StreamController
-#include "js/Class.h"                        // JSClass, js::ClassSpec
-#include "js/RootingAPI.h"                   // JS::Handle
+#include "js/Class.h"                          // JSClass, js::ClassSpec
+#include "js/RootingAPI.h"                     // JS::Handle
 #include "js/Stream.h"  // JS::ReadableStreamUnderlyingSource
 #include "js/Value.h"  // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue
 #include "vm/List.h"   // js::ListObject
 #include "vm/NativeObject.h"  // js::NativeObject
 
 namespace js {
 
 class ReadableStreamController : public StreamController {
--- a/js/src/builtin/streams/ReadableStreamDefaultController.cpp
+++ b/js/src/builtin/streams/ReadableStreamDefaultController.cpp
@@ -7,29 +7,29 @@
 /* Class ReadableStreamDefaultController. */
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsapi.h"        // JS_ReportErrorNumberASCII
 #include "jsfriendapi.h"  // js::GetErrorMessage, JSMSG_*, js::AssertSameCompartment
 
-#include "builtin/Promise.h"  // js::PromiseObject
+#include "builtin/Promise.h"                 // js::PromiseObject
 #include "builtin/streams/ClassSpecMacro.h"  // JS_STREAMS_CLASS_SPEC
 #include "builtin/streams/MiscellaneousOperations.h"  // js::IsMaybeWrapped, js::PromiseCall
-#include "builtin/streams/PullIntoDescriptor.h"       // js::PullIntoDescriptor
+#include "builtin/streams/PullIntoDescriptor.h"  // js::PullIntoDescriptor
 #include "builtin/streams/QueueWithSizes.h"  // js::{DequeueValue,ResetQueue}
 #include "builtin/streams/ReadableStream.h"  // js::ReadableStream, js::SetUpExternalReadableByteStreamController
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::CheckReadableStreamControllerCanCloseOrEnqueue, js::ReadableStreamControllerCancelSteps, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler
 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"  // js::ReadableStreamController{CallPullIfNeeded,ClearAlgorithms,Error,GetDesiredSizeUnchecked}, js::ReadableStreamDefaultController{Close,Enqueue}
 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult}
 #include "builtin/streams/ReadableStreamOperations.h"  // js::ReadableStreamTee_Cancel
 #include "builtin/streams/ReadableStreamReader.h"  // js::ReadableStream{,Default}Reader
-#include "builtin/streams/StreamController.h"      // js::StreamController
-#include "builtin/streams/TeeState.h"              // js::TeeState
+#include "builtin/streams/StreamController.h"  // js::StreamController
+#include "builtin/streams/TeeState.h"          // js::TeeState
 #include "gc/Heap.h"
 #include "js/ArrayBuffer.h"  // JS::NewArrayBuffer
 #include "js/Class.h"        // js::ClassSpec
 #include "js/PropertySpec.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 
--- a/js/src/builtin/streams/ReadableStreamDefaultReader.cpp
+++ b/js/src/builtin/streams/ReadableStreamDefaultReader.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsapi.h"        // JS_ReportErrorNumberASCII
 #include "jsfriendapi.h"  // js::GetErrorMessage
 
 #include "builtin/streams/ClassSpecMacro.h"  // JS_STREAMS_CLASS_SPEC
 #include "builtin/streams/MiscellaneousOperations.h"  // js::ReturnPromiseRejectedWithPendingError
-#include "builtin/streams/ReadableStream.h"           // js::ReadableStream
+#include "builtin/streams/ReadableStream.h"  // js::ReadableStream
 #include "builtin/streams/ReadableStreamReader.h"  // js::ReadableStream{,Default}Reader
 #include "js/CallArgs.h"                           // JS::CallArgs{,FromVp}
 #include "js/Class.h"                              // JSClass, JS_NULL_CLASS_OPS
 #include "js/RootingAPI.h"                         // JS::Handle, JS::Rooted
 
 #include "vm/Compartment-inl.h"   // js::UnwrapAndTypeCheckThis
 #include "vm/JSObject-inl.h"      // js::NewObjectWithClassProto
 #include "vm/NativeObject-inl.h"  // js::ThrowIfNotConstructing
--- a/js/src/builtin/streams/ReadableStreamInternals.cpp
+++ b/js/src/builtin/streams/ReadableStreamInternals.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include <stdint.h>  // uint32_t
 
 #include "jsfriendapi.h"  // js::AssertSameCompartment
 
-#include "builtin/Promise.h"  // js::PromiseObject
+#include "builtin/Promise.h"                           // js::PromiseObject
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStreamController{,CancelSteps}
 #include "builtin/streams/ReadableStreamReader.h"  // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool
 #include "gc/AllocKind.h"  // js::gc::AllocKind
 #include "js/CallArgs.h"   // JS::CallArgs{,FromVp}
 #include "js/GCAPI.h"      // JS::AutoSuppressGCAnalysis
 #include "js/Promise.h"  // JS::CallOriginalPromiseThen, JS::{Resolve,Reject}Promise
 #include "js/Result.h"      // JS_TRY_VAR_OR_RETURN_NULL
 #include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
--- a/js/src/builtin/streams/ReadableStreamOperations.cpp
+++ b/js/src/builtin/streams/ReadableStreamOperations.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsapi.h"  // JS_SetPrivate
 
 #include "builtin/Array.h"  // js::NewDenseFullyAllocatedArray
 #include "builtin/Promise.h"  // js::PromiseObject, js::RejectPromiseWithPendingError
-#include "builtin/streams/ReadableStream.h"        // js::ReadableStream
+#include "builtin/streams/ReadableStream.h"  // js::ReadableStream
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStream{,Default}Controller
 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"  // js::ReadableStreamDefaultController{Close,Enqueue}, js::ReadableStreamControllerError, js::SourceAlgorithms
 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStreamCancel
 #include "builtin/streams/ReadableStreamReader.h"  // js::CreateReadableStreamDefaultReader, js::ReadableStream{,Default}Reader, js::ReadableStreamDefaultReaderRead
 #include "builtin/streams/TeeState.h"              // js::TeeState
 #include "js/CallArgs.h"                           // JS::CallArgs{,FromVp}
 #include "js/Promise.h"  // JS::CallOriginalPromiseThen, JS::AddPromiseReactions
 #include "js/RootingAPI.h"        // JS::{,Mutable}Handle, JS::Rooted
--- a/js/src/builtin/streams/ReadableStreamReader-inl.h
+++ b/js/src/builtin/streams/ReadableStreamReader-inl.h
@@ -10,19 +10,19 @@
 #include "builtin/streams/ReadableStreamReader.h"
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsfriendapi.h"  // JS_IsDeadWrapper
 
 #include "builtin/streams/ReadableStream.h"  // js::ReadableStream
-#include "js/Proxy.h"         // js::IsProxy
-#include "js/RootingAPI.h"    // JS::Handle
-#include "vm/NativeObject.h"  // js::NativeObject::getFixedSlot
+#include "js/Proxy.h"                        // js::IsProxy
+#include "js/RootingAPI.h"                   // JS::Handle
+#include "vm/NativeObject.h"                 // js::NativeObject::getFixedSlot
 
 #include "vm/Compartment-inl.h"  // js::UnwrapInternalSlot
 
 namespace js {
 
 /**
  * Returns the stream associated with the given reader.
  */
--- a/js/src/builtin/streams/ReadableStreamReader.cpp
+++ b/js/src/builtin/streams/ReadableStreamReader.cpp
@@ -10,24 +10,24 @@
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT{,_IF}
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsfriendapi.h"  // JS_ReportErrorNumberASCII, js::GetErrorMessage
 
 #include "builtin/Promise.h"  // js::PromiseObject
 #include "builtin/Stream.h"  // js::ReadableStreamController, js::ReadableStreamControllerPullSteps
-#include "builtin/streams/ReadableStream.h"  // js::ReadableStream
+#include "builtin/streams/ReadableStream.h"            // js::ReadableStream
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStreamController
 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStream{Cancel,CreateReadResult}
-#include "js/RootingAPI.h"    // JS::Handle, JS::Rooted
-#include "js/Value.h"         // JS::Value, JS::UndefinedHandleValue
-#include "vm/Interpreter.h"   // js::GetAndClearException
-#include "vm/JSContext.h"     // JSContext
-#include "vm/Runtime.h"       // JSRuntime
+#include "js/RootingAPI.h"                            // JS::Handle, JS::Rooted
+#include "js/Value.h"        // JS::Value, JS::UndefinedHandleValue
+#include "vm/Interpreter.h"  // js::GetAndClearException
+#include "vm/JSContext.h"    // JSContext
+#include "vm/Runtime.h"      // JSRuntime
 
 #include "vm/Compartment-inl.h"  // JS::Compartment::wrap, js::UnwrapInternalSlot
 #include "vm/List-inl.h"         // js::StoreNewListInFixedSlot
 #include "vm/Realm-inl.h"        // js::AutoRealm
 
 using js::ReadableStreamController;
 using js::UnwrapStreamFromReader;
 
--- a/js/src/builtin/streams/StreamAPI.cpp
+++ b/js/src/builtin/streams/StreamAPI.cpp
@@ -11,24 +11,24 @@
 
 #include <stdint.h>  // uint32_t, uintptr_t
 
 #include "jsapi.h"        // js::AssertHeapIsIdle, JS_ReportErrorNumberASCII
 #include "jsfriendapi.h"  // JS_GetArrayBufferViewData, js::IsObjectInContextCompartment, js::GetErrorMessage, JSMSG_*
 #include "jstypes.h"      // JS_{FRIEND,PUBLIC}_API
 
 #include "builtin/Stream.h"  // js::ReadableByteStreamController{,Close}, js::ReadableStreamDefaultController{,Close}, js::StreamController
-#include "builtin/streams/ReadableStream.h"            // js::ReadableStream
+#include "builtin/streams/ReadableStream.h"  // js::ReadableStream
 #include "builtin/streams/ReadableStreamController.h"  // js::CheckReadableStreamControllerCanCloseOrEnqueue
 #include "builtin/streams/ReadableStreamDefaultControllerOperations.h"  // js::ReadableStreamController{Error,GetDesiredSizeUnchecked}, js::SetUpReadableStreamDefaultControllerFromUnderlyingSource
 #include "builtin/streams/ReadableStreamInternals.h"  // js::ReadableStream{Cancel,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader}
 #include "builtin/streams/ReadableStreamOperations.h"  // js::ReadableStreamTee
 #include "builtin/streams/ReadableStreamReader.h"  // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool
-#include "builtin/streams/StreamController.h"      // js::StreamController
-#include "gc/Zone.h"        // JS::Zone
+#include "builtin/streams/StreamController.h"  // js::StreamController
+#include "gc/Zone.h"                           // JS::Zone
 #include "js/GCAPI.h"       // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis
 #include "js/RootingAPI.h"  // JS::{,Mutable}Handle, JS::Rooted
 #include "js/Stream.h"      // JS::ReadableStreamUnderlyingSource
 #include "js/Value.h"       // JS::{,Object,Undefined}Value
 #include "vm/ArrayBufferViewObject.h"  // js::ArrayBufferViewObject
 #include "vm/JSContext.h"              // JSContext, CHECK_THREAD
 #include "vm/JSObject.h"               // JSObject
 #include "vm/NativeObject.h"           // js::PlainObject
--- a/js/src/builtin/streams/StreamController-inl.h
+++ b/js/src/builtin/streams/StreamController-inl.h
@@ -7,17 +7,17 @@
 /* Base stream controller inlines. */
 
 #ifndef builtin_streams_StreamController_inl_h
 #define builtin_streams_StreamController_inl_h
 
 #include "builtin/streams/StreamController.h"  // js::StreamController
 #include "builtin/streams/ReadableStreamController.h"  // js::Readable{ByteStream,StreamDefault}Controller
 #include "builtin/streams/WritableStreamDefaultController.h"  // js::WritableStreamDefaultController
-#include "vm/JSObject.h"                               // JSObject
+#include "vm/JSObject.h"                                      // JSObject
 
 template <>
 inline bool JSObject::is<js::StreamController>() const {
   return is<js::ReadableStreamDefaultController>() ||
          is<js::ReadableByteStreamController>() ||
          is<js::WritableStreamDefaultController>();
 }
 
--- a/js/src/builtin/streams/StreamController.h
+++ b/js/src/builtin/streams/StreamController.h
@@ -4,19 +4,19 @@
  * 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/. */
 
 /* Base class for readable and writable stream controllers. */
 
 #ifndef builtin_streams_StreamController_h
 #define builtin_streams_StreamController_h
 
-#include "js/Value.h"     // JS::Value, JS::NumberValue
-#include "vm/JSObject.h"  // JSObject
-#include "vm/List.h"   // js::ListObject
+#include "js/Value.h"         // JS::Value, JS::NumberValue
+#include "vm/JSObject.h"      // JSObject
+#include "vm/List.h"          // js::ListObject
 #include "vm/NativeObject.h"  // js::NativeObject
 
 namespace js {
 
 /**
  * Common base class of both readable and writable stream controllers.
  */
 class StreamController : public NativeObject {
--- a/js/src/builtin/streams/TeeState.cpp
+++ b/js/src/builtin/streams/TeeState.cpp
@@ -3,21 +3,21 @@
  * 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/. */
 
 /* Stream teeing state. */
 
 #include "builtin/streams/TeeState.h"
 
-#include "builtin/Promise.h"  // js::PromiseObject
+#include "builtin/Promise.h"                 // js::PromiseObject
 #include "builtin/streams/ReadableStream.h"  // js::ReadableStream
-#include "js/Class.h"         // JSClass, JSCLASS_HAS_RESERVED_SLOTS
-#include "js/RootingAPI.h"    // JS::Handle, JS::Rooted
-#include "vm/JSContext.h"     // JSContext
+#include "js/Class.h"       // JSClass, JSCLASS_HAS_RESERVED_SLOTS
+#include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
+#include "vm/JSContext.h"   // JSContext
 
 #include "vm/JSObject-inl.h"  // js::NewBuiltinClassInstance
 
 using js::ReadableStream;
 using js::TeeState;
 
 using JS::Handle;
 using JS::Int32Value;
--- a/js/src/builtin/streams/TeeState.h
+++ b/js/src/builtin/streams/TeeState.h
@@ -8,19 +8,19 @@
 
 #ifndef builtin_streams_TeeState_h
 #define builtin_streams_TeeState_h
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 
 #include <stdint.h>  // uint32_t
 
-#include "builtin/Promise.h"  // js::PromiseObject
+#include "builtin/Promise.h"                           // js::PromiseObject
 #include "builtin/streams/ReadableStreamController.h"  // js::ReadableStreamDefaultController
-#include "js/Class.h"         // JSClass
+#include "js/Class.h"                                  // JSClass
 #include "js/Value.h"         // JS::{Int32,Object}Value
 #include "vm/NativeObject.h"  // js::NativeObject
 
 namespace js {
 
 /**
  * TeeState objects implement the local variables in Streams spec 3.3.9
  * ReadableStreamTee, which are accessed by several algorithms.
--- a/js/src/builtin/streams/WritableStream.cpp
+++ b/js/src/builtin/streams/WritableStream.cpp
@@ -24,17 +24,17 @@
 #include "js/RootingAPI.h"        // JS::Handle, JS::Rooted
 #include "js/Value.h"             // JS::{,Object}Value
 #include "vm/JSContext.h"         // JSContext
 #include "vm/JSObject.h"          // js::GetPrototypeFromBuiltinConstructor
 #include "vm/NativeObject.h"      // js::PlainObject
 #include "vm/ObjectOperations.h"  // js::GetProperty
 #include "vm/Realm.h"             // JS::Realm
 
-#include "vm/Compartment-inl.h"  // js::UnwrapAndTypeCheckThis
+#include "vm/Compartment-inl.h"   // js::UnwrapAndTypeCheckThis
 #include "vm/JSObject-inl.h"      // js::NewBuiltinClassInstance
 #include "vm/NativeObject-inl.h"  // js::ThrowIfNotConstructing
 
 using js::CreateWritableStreamDefaultWriter;
 using js::UnwrapAndTypeCheckThis;
 using js::WritableStream;
 
 using JS::CallArgs;
--- a/js/src/builtin/streams/WritableStreamDefaultControllerOperations.cpp
+++ b/js/src/builtin/streams/WritableStreamDefaultControllerOperations.cpp
@@ -14,23 +14,23 @@
 #include "jsapi.h"  // JS_ReportErrorASCII
 
 #include "builtin/Promise.h"  // js::PromiseObject
 #include "builtin/streams/MiscellaneousOperations.h"  // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop
 #include "builtin/streams/QueueWithSizes.h"  // js::ResetQueue
 #include "builtin/streams/WritableStream.h"  // js::WritableStream
 #include "builtin/streams/WritableStreamDefaultController.h"  // js::WritableStreamDefaultController
 #include "builtin/streams/WritableStreamOperations.h"  // js::WritableStream{DealWithRejection,FinishErroring,UpdateBackpressure}
-#include "js/CallArgs.h"                     // JS::CallArgs{,FromVp}
-#include "js/RootingAPI.h"                   // JS::Handle, JS::Rooted
-#include "js/Value.h"                        // JS::{,Object}Value
-#include "vm/JSContext.h"                    // JSContext
-#include "vm/JSObject.h"                     // JSObject
-#include "vm/List.h"                         // js::ListObject
-#include "vm/Runtime.h"                      // JSAtomState
+#include "js/CallArgs.h"    // JS::CallArgs{,FromVp}
+#include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
+#include "js/Value.h"       // JS::{,Object}Value
+#include "vm/JSContext.h"   // JSContext
+#include "vm/JSObject.h"    // JSObject
+#include "vm/List.h"        // js::ListObject
+#include "vm/Runtime.h"     // JSAtomState
 
 #include "builtin/streams/HandlerFunction-inl.h"  // js::TargetFromHandler
 #include "vm/JSObject-inl.h"  // js::NewBuiltinClassInstance, js::NewObjectWithClassProto
 
 using JS::CallArgs;
 using JS::CallArgsFromVp;
 using JS::Handle;
 using JS::ObjectValue;
--- a/js/src/builtin/streams/WritableStreamOperations.cpp
+++ b/js/src/builtin/streams/WritableStreamOperations.cpp
@@ -10,21 +10,21 @@
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsapi.h"  // JS_ReportErrorASCII, JS_SetPrivate
 
 #include "builtin/streams/WritableStream.h"  // js::WritableStream
 #include "builtin/streams/WritableStreamDefaultController.h"  // js::WritableStreamDefaultController, js::WritableStream::controller
-#include "js/RootingAPI.h"                   // JS::Handle, JS::Rooted
-#include "js/Value.h"                        // JS::Value, JS::ObjecValue
+#include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
+#include "js/Value.h"       // JS::Value, JS::ObjecValue
 
-#include "vm/JSObject-inl.h"                 // js::NewObjectWithClassProto
-#include "vm/List-inl.h"                     // js::StoreNewListInFixedSlot
+#include "vm/JSObject-inl.h"  // js::NewObjectWithClassProto
+#include "vm/List-inl.h"      // js::StoreNewListInFixedSlot
 
 using js::WritableStream;
 
 using JS::Handle;
 using JS::ObjectValue;
 using JS::Rooted;
 using JS::Value;
 
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -845,17 +845,17 @@ ResumeMode DebugAPI::slowPathOnResumeFra
 /* static */
 ResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx, const CallArgs& args,
                                           CallReason reason) {
   RootedValue rval(cx);
   ResumeMode resumeMode = Debugger::dispatchHook(
       cx,
       [cx](Debugger* dbg) -> bool {
         return dbg == cx->insideDebuggerEvaluationWithOnNativeCallHook &&
-          dbg->getHook(Debugger::OnNativeCall);
+               dbg->getHook(Debugger::OnNativeCall);
       },
       [&](Debugger* dbg) -> ResumeMode {
         return dbg->fireNativeCall(cx, args, reason, &rval);
       });
 
   switch (resumeMode) {
     case ResumeMode::Continue:
       break;
@@ -3883,17 +3883,18 @@ void DebugAPI::sweepAll(JSFreeOp* fop) {
     // Debugger when the generator is resumed. When a Debugger.Frame gets GC'd,
     // generatorObserverCount needs to be decremented. It's much easier to do
     // this when we know that all parties involved - the Debugger.Frame, the
     // generator object, and the JSScript - have not yet been finalized.
     //
     // Since DebugAPI::sweepAll is called after everything is marked, but before
     // anything has been finalized, this is the perfect place to drop the count.
     if (dbg->zone()->isGCSweeping()) {
-      for (Debugger::GeneratorWeakMap::Enum e(dbg->generatorFrames); !e.empty(); e.popFront()) {
+      for (Debugger::GeneratorWeakMap::Enum e(dbg->generatorFrames); !e.empty();
+           e.popFront()) {
         DebuggerFrame* frameObj = &e.front().value()->as<DebuggerFrame>();
         if (IsAboutToBeFinalizedUnbarriered(&frameObj)) {
           frameObj->clearGenerator(fop, dbg, &e);
         }
       }
     }
 
     // Detach dying debuggers and debuggees from each other. Since this
--- a/js/src/debugger/Debugger.h
+++ b/js/src/debugger/Debugger.h
@@ -327,23 +327,23 @@ class DebuggerWeakMap : private WeakMap<
   using Entry = typename Base::Entry;
   using Ptr = typename Base::Ptr;
   using AddPtr = typename Base::AddPtr;
   using Range = typename Base::Range;
   using Lookup = typename Base::Lookup;
 
   // Expose WeakMap public interface.
 
-  using Base::zone;
   using Base::all;
   using Base::has;
   using Base::lookup;
   using Base::lookupForAdd;
   using Base::remove;
   using Base::trace;
+  using Base::zone;
 #ifdef DEBUG
   using Base::hasEntry;
 #endif
 
   class Enum : public Base::Enum {
    public:
     explicit Enum(DebuggerWeakMap& map) : Base::Enum(map) {}
   };
@@ -1407,24 +1407,24 @@ bool ParseEvalOptions(JSContext* cx, Han
 Result<Completion> DebuggerGenericEval(
     JSContext* cx, const mozilla::Range<const char16_t> chars,
     HandleObject bindings, const EvalOptions& options, Debugger* dbg,
     HandleObject envArg, FrameIter* iter);
 
 bool ParseResumptionValue(JSContext* cx, HandleValue rval,
                           ResumeMode& resumeMode, MutableHandleValue vp);
 
-#define JS_DEBUG_PSG(Name, Getter)                        \
+#define JS_DEBUG_PSG(Name, Getter) \
   JS_PSG(Name, CallData::ToNative<&CallData::Getter>, 0)
 
-#define JS_DEBUG_PSGS(Name, Getter, Setter)               \
-  JS_PSGS(Name, CallData::ToNative<&CallData::Getter>,  \
+#define JS_DEBUG_PSGS(Name, Getter, Setter)            \
+  JS_PSGS(Name, CallData::ToNative<&CallData::Getter>, \
           CallData::ToNative<&CallData::Setter>, 0)
 
-#define JS_DEBUG_FN(Name, Method, NumArgs)                \
+#define JS_DEBUG_FN(Name, Method, NumArgs) \
   JS_FN(Name, CallData::ToNative<&CallData::Method>, NumArgs, 0)
 
 } /* namespace js */
 
 namespace JS {
 
 template <>
 struct DeletePolicy<js::Debugger>
--- a/js/src/debugger/DebuggerMemory.cpp
+++ b/js/src/debugger/DebuggerMemory.cpp
@@ -132,17 +132,18 @@ struct MOZ_STACK_CLASS DebuggerMemory::C
   using Method = bool (CallData::*)();
 
   template <Method MyMethod>
   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
 };
 
 template <DebuggerMemory::CallData::Method MyMethod>
 /* static */
-bool DebuggerMemory::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
+bool DebuggerMemory::CallData::ToNative(JSContext* cx, unsigned argc,
+                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   Rooted<DebuggerMemory*> memory(cx, DebuggerMemory::checkThis(cx, args));
   if (!memory) {
     return false;
   }
 
   CallData data(cx, args, memory);
--- a/js/src/debugger/Environment.cpp
+++ b/js/src/debugger/Environment.cpp
@@ -134,21 +134,22 @@ struct MOZ_STACK_CLASS DebuggerEnvironme
   using Method = bool (CallData::*)();
 
   template <Method MyMethod>
   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
 };
 
 template <DebuggerEnvironment::CallData::Method MyMethod>
 /* static */
-bool DebuggerEnvironment::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
+bool DebuggerEnvironment::CallData::ToNative(JSContext* cx, unsigned argc,
+                                             Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  RootedDebuggerEnvironment environment(cx,
-      DebuggerEnvironment_checkThis(cx, args));
+  RootedDebuggerEnvironment environment(
+      cx, DebuggerEnvironment_checkThis(cx, args));
   if (!environment) {
     return false;
   }
 
   CallData data(cx, args, environment);
   return (data.*MyMethod)();
 }
 
@@ -376,21 +377,19 @@ const JSPropertySpec DebuggerEnvironment
     JS_DEBUG_PSG("parent", parentGetter),
     JS_DEBUG_PSG("object", objectGetter),
     JS_DEBUG_PSG("callee", calleeGetter),
     JS_DEBUG_PSG("inspectable", inspectableGetter),
     JS_DEBUG_PSG("optimizedOut", optimizedOutGetter),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerEnvironment::methods_[] = {
-    JS_DEBUG_FN("names", namesMethod, 0),
-    JS_DEBUG_FN("find", findMethod, 1),
+    JS_DEBUG_FN("names", namesMethod, 0), JS_DEBUG_FN("find", findMethod, 1),
     JS_DEBUG_FN("getVariable", getVariableMethod, 1),
-    JS_DEBUG_FN("setVariable", setVariableMethod, 2),
-    JS_FS_END};
+    JS_DEBUG_FN("setVariable", setVariableMethod, 2), JS_FS_END};
 
 /* static */
 NativeObject* DebuggerEnvironment::initClass(JSContext* cx,
                                              Handle<GlobalObject*> global,
                                              HandleObject dbgCtor) {
   return InitClass(cx, dbgCtor, nullptr, &DebuggerEnvironment::class_,
                    construct, 0, properties_, methods_, nullptr, nullptr);
 }
--- a/js/src/debugger/Frame.cpp
+++ b/js/src/debugger/Frame.cpp
@@ -1176,18 +1176,18 @@ template <DebuggerFrame::CallData::Metho
 /* static */
 bool DebuggerFrame::CallData::ToNative(JSContext* cx, unsigned argc,
                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // All accessors/methods require a live frame, except for the live getter.
   bool checkLive = MyMethod != &CallData::liveGetter;
 
-  RootedDebuggerFrame frame(cx, DebuggerFrame::check(cx, args.thisv(),
-                                                     checkLive));
+  RootedDebuggerFrame frame(cx,
+                            DebuggerFrame::check(cx, args.thisv(), checkLive));
   if (!frame) {
     return false;
   }
 
   CallData data(cx, args, frame);
   return (data.*MyMethod)();
 }
 
@@ -1312,18 +1312,18 @@ static bool DebuggerArguments_getArg(JSC
   }
   if (argsobj->getClass() != &DebuggerArguments::class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Arguments",
                               "getArgument", argsobj->getClass()->name);
     return false;
   }
 
-  RootedValue framev(cx,
-      argsobj->as<NativeObject>().getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));
+  RootedValue framev(cx, argsobj->as<NativeObject>().getReservedSlot(
+                             JSSLOT_DEBUGARGUMENTS_FRAME));
   RootedDebuggerFrame thisobj(cx, DebuggerFrame::check(cx, framev, true));
   if (!thisobj) {
     return false;
   }
 
   FrameIter frameIter(*thisobj->frameIterData());
   AbstractFramePtr frame = frameIter.abstractFramePtr();
 
@@ -1610,18 +1610,17 @@ const JSPropertySpec DebuggerFrame::prop
     JS_DEBUG_PSG("type", typeGetter),
     JS_DEBUG_PSG("implementation", implementationGetter),
     JS_DEBUG_PSGS("onStep", onStepGetter, onStepSetter),
     JS_DEBUG_PSGS("onPop", onPopGetter, onPopSetter),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerFrame::methods_[] = {
     JS_DEBUG_FN("eval", evalMethod, 1),
-    JS_DEBUG_FN("evalWithBindings", evalWithBindingsMethod, 1),
-    JS_FS_END};
+    JS_DEBUG_FN("evalWithBindings", evalWithBindingsMethod, 1), JS_FS_END};
 
 JSObject* js::IdVectorToArray(JSContext* cx, Handle<IdVector> ids) {
   Rooted<ValueVector> vals(cx, ValueVector(cx));
   if (!vals.growBy(ids.length())) {
     return nullptr;
   }
 
   for (size_t i = 0, len = ids.length(); i < len; i++) {
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -21,49 +21,49 @@
 
 #include "builtin/Array.h"       // for NewDenseCopiedArray
 #include "debugger/Debugger.h"   // for Completion, Debugger
 #include "debugger/NoExecute.h"  // for LeaveDebuggeeNoExecute
 #include "debugger/Script.h"     // for DebuggerScript
 #include "debugger/Source.h"     // for DebuggerSource
 #include "gc/Barrier.h"          // for ImmutablePropertyNamePtr
 #include "gc/Rooting.h"          // for RootedDebuggerObject
-#include "gc/Tracer.h"       // for TraceManuallyBarrieredCrossCompartmentEdge
-#include "js/CompilationAndEvaluation.h" //  for Compile
-#include "js/Conversions.h"  // for ToObject
-#include "js/HeapAPI.h"      // for IsInsideNursery
-#include "js/Promise.h"      // for PromiseState
-#include "js/Proxy.h"        // for PropertyDescriptor
-#include "js/StableStringChars.h"        // for AutoStableStringChars
-#include "proxy/ScriptedProxyHandler.h"  // for ScriptedProxyHandler
-#include "vm/ArgumentsObject.h"          // for ARGS_LENGTH_MAX
-#include "vm/ArrayObject.h"              // for ArrayObject
-#include "vm/BytecodeUtil.h"             // for JSDVG_SEARCH_STACK
-#include "vm/Compartment.h"              // for Compartment
-#include "vm/EnvironmentObject.h"        // for GetDebugEnvironmentForFunction
-#include "vm/ErrorObject.h"              // for JSObject::is, ErrorObject
-#include "vm/GlobalObject.h"             // for JSObject::is, GlobalObject
-#include "vm/Instrumentation.h"          // for RealmInstrumentation
-#include "vm/Interpreter.h"              // for Call
-#include "vm/JSAtom.h"                   // for Atomize, js_apply_str
-#include "vm/JSContext.h"                // for JSContext, ReportValueError
-#include "vm/JSFunction.h"               // for JSFunction
-#include "vm/JSScript.h"                 // for JSScript
-#include "vm/NativeObject.h"             // for NativeObject, JSObject::is
-#include "vm/ObjectGroup.h"              // for GenericObject, NewObjectKind
-#include "vm/ObjectOperations.h"         // for DefineProperty
-#include "vm/Realm.h"                    // for AutoRealm, ErrorCopier, Realm
-#include "vm/Runtime.h"                  // for JSAtomState
-#include "vm/SavedFrame.h"               // for SavedFrame
-#include "vm/Scope.h"                    // for PositionalFormalParameterIter
-#include "vm/SelfHosting.h"              // for GetClonedSelfHostedFunctionName
-#include "vm/Shape.h"                    // for Shape
-#include "vm/Stack.h"                    // for InvokeArgs
-#include "vm/StringType.h"               // for JSAtom, PropertyName
-#include "vm/WrapperObject.h"            // for JSObject::is, WrapperObject
+#include "gc/Tracer.h"  // for TraceManuallyBarrieredCrossCompartmentEdge
+#include "js/CompilationAndEvaluation.h"  //  for Compile
+#include "js/Conversions.h"               // for ToObject
+#include "js/HeapAPI.h"                   // for IsInsideNursery
+#include "js/Promise.h"                   // for PromiseState
+#include "js/Proxy.h"                     // for PropertyDescriptor
+#include "js/StableStringChars.h"         // for AutoStableStringChars
+#include "proxy/ScriptedProxyHandler.h"   // for ScriptedProxyHandler
+#include "vm/ArgumentsObject.h"           // for ARGS_LENGTH_MAX
+#include "vm/ArrayObject.h"               // for ArrayObject
+#include "vm/BytecodeUtil.h"              // for JSDVG_SEARCH_STACK
+#include "vm/Compartment.h"               // for Compartment
+#include "vm/EnvironmentObject.h"         // for GetDebugEnvironmentForFunction
+#include "vm/ErrorObject.h"               // for JSObject::is, ErrorObject
+#include "vm/GlobalObject.h"              // for JSObject::is, GlobalObject
+#include "vm/Instrumentation.h"           // for RealmInstrumentation
+#include "vm/Interpreter.h"               // for Call
+#include "vm/JSAtom.h"                    // for Atomize, js_apply_str
+#include "vm/JSContext.h"                 // for JSContext, ReportValueError
+#include "vm/JSFunction.h"                // for JSFunction
+#include "vm/JSScript.h"                  // for JSScript
+#include "vm/NativeObject.h"              // for NativeObject, JSObject::is
+#include "vm/ObjectGroup.h"               // for GenericObject, NewObjectKind
+#include "vm/ObjectOperations.h"          // for DefineProperty
+#include "vm/Realm.h"                     // for AutoRealm, ErrorCopier, Realm
+#include "vm/Runtime.h"                   // for JSAtomState
+#include "vm/SavedFrame.h"                // for SavedFrame
+#include "vm/Scope.h"                     // for PositionalFormalParameterIter
+#include "vm/SelfHosting.h"               // for GetClonedSelfHostedFunctionName
+#include "vm/Shape.h"                     // for Shape
+#include "vm/Stack.h"                     // for InvokeArgs
+#include "vm/StringType.h"                // for JSAtom, PropertyName
+#include "vm/WrapperObject.h"             // for JSObject::is, WrapperObject
 
 #include "vm/Compartment-inl.h"       // for Compartment::wrap
 #include "vm/JSAtom-inl.h"            // for ValueToId
 #include "vm/JSObject-inl.h"          // for GetObjectClassName, InitClass
 #include "vm/NativeObject-inl.h"      // for NativeObject::global
 #include "vm/ObjectOperations-inl.h"  // for DeleteProperty, GetProperty
 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
 
@@ -214,17 +214,18 @@ struct MOZ_STACK_CLASS DebuggerObject::C
   using Method = bool (CallData::*)();
 
   template <Method MyMethod>
   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
 };
 
 template <DebuggerObject::CallData::Method MyMethod>
 /* static */
-bool DebuggerObject::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
+bool DebuggerObject::CallData::ToNative(JSContext* cx, unsigned argc,
+                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   RootedDebuggerObject obj(cx, DebuggerObject_checkThis(cx, args));
   if (!obj) {
     return false;
   }
 
   CallData data(cx, args, obj);
@@ -1151,18 +1152,17 @@ static bool CopyStringToVector(JSContext
   if (!chars.appendN(0, flat->length() + 1)) {
     return false;
   }
   CopyChars(chars.begin(), *flat);
   return true;
 }
 
 bool DebuggerObject::CallData::createSource() {
-  if (!args.requireAtLeast(
-          cx, "Debugger.Object.prototype.createSource", 1)) {
+  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.createSource", 1)) {
     return false;
   }
 
   if (!DebuggerObject::requireGlobal(cx, object)) {
     return false;
   }
 
   RootedObject options(cx, ToObject(cx, args[0]));
@@ -1223,17 +1223,17 @@ bool DebuggerObject::CallData::createSou
     JS_ReportErrorASCII(cx, "URL must be a narrow string");
     return false;
   }
 
   Vector<Latin1Char> urlChars(cx);
   if (!CopyStringToVector(cx, url, urlChars)) {
     return false;
   }
-  compileOptions.setFile((const char*) urlChars.begin());
+  compileOptions.setFile((const char*)urlChars.begin());
 
   Vector<char16_t> sourceMapURLChars(cx);
   if (sourceMapURL) {
     if (!CopyStringToVector(cx, sourceMapURL, sourceMapURLChars)) {
       return false;
     }
     compileOptions.setSourceMapURL(sourceMapURLChars.begin());
   }
@@ -1288,18 +1288,17 @@ bool DebuggerObject::CallData::makeDebug
     return false;
   }
 
   return DebuggerObject::makeDebuggeeNativeFunction(cx, object, args[0],
                                                     args.rval());
 }
 
 bool DebuggerObject::CallData::isSameNativeMethod() {
-  if (!args.requireAtLeast(
-          cx, "Debugger.Object.prototype.isSameNative", 1)) {
+  if (!args.requireAtLeast(cx, "Debugger.Object.prototype.isSameNative", 1)) {
     return false;
   }
 
   return DebuggerObject::isSameNative(cx, object, args[0], args.rval());
 }
 
 bool DebuggerObject::CallData::unsafeDereferenceMethod() {
   RootedObject result(cx);
@@ -1466,18 +1465,18 @@ const JSFunctionSpec DebuggerObject::met
     JS_DEBUG_FN("asEnvironment", asEnvironmentMethod, 0),
     JS_DEBUG_FN("forceLexicalInitializationByName",
                 forceLexicalInitializationByNameMethod, 1),
     JS_DEBUG_FN("executeInGlobal", executeInGlobalMethod, 1),
     JS_DEBUG_FN("executeInGlobalWithBindings",
                 executeInGlobalWithBindingsMethod, 2),
     JS_DEBUG_FN("createSource", createSource, 1),
     JS_DEBUG_FN("makeDebuggeeValue", makeDebuggeeValueMethod, 1),
-    JS_DEBUG_FN("makeDebuggeeNativeFunction",
-                makeDebuggeeNativeFunctionMethod, 1),
+    JS_DEBUG_FN("makeDebuggeeNativeFunction", makeDebuggeeNativeFunctionMethod,
+                1),
     JS_DEBUG_FN("isSameNative", isSameNativeMethod, 1),
     JS_DEBUG_FN("unsafeDereference", unsafeDereferenceMethod, 0),
     JS_DEBUG_FN("unwrap", unwrapMethod, 0),
     JS_DEBUG_FN("setInstrumentation", setInstrumentationMethod, 2),
     JS_DEBUG_FN("setInstrumentationActive", setInstrumentationActiveMethod, 1),
     JS_FS_END};
 
 /* static */
@@ -2451,18 +2450,18 @@ bool DebuggerObject::isSameNative(JSCont
   RootedFunction fun(cx, EnsureNativeFunction(value));
   if (!fun) {
     RootedAtom selfHostedName(cx, MaybeGetSelfHostedFunctionName(value));
     if (!selfHostedName) {
       JS_ReportErrorASCII(cx, "Need native function");
       return false;
     }
 
-    result.setBoolean(
-        selfHostedName == MaybeGetSelfHostedFunctionName(referentValue));
+    result.setBoolean(selfHostedName ==
+                      MaybeGetSelfHostedFunctionName(referentValue));
     return true;
   }
 
   RootedFunction referentFun(cx, EnsureNativeFunction(referentValue));
   result.setBoolean(referentFun && referentFun->native() == fun->native());
   return true;
 }
 
--- a/js/src/debugger/Script.cpp
+++ b/js/src/debugger/Script.cpp
@@ -208,17 +208,20 @@ struct MOZ_STACK_CLASS DebuggerScript::C
   JSContext* cx;
   const CallArgs& args;
 
   HandleDebuggerScript obj;
   Rooted<DebuggerScriptReferent> referent;
   RootedScript script;
 
   CallData(JSContext* cx, const CallArgs& args, HandleDebuggerScript obj)
-      : cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()),
+      : cx(cx),
+        args(args),
+        obj(obj),
+        referent(cx, obj->getReferent()),
         script(cx) {}
 
   MOZ_MUST_USE bool ensureScriptMaybeLazy() {
     if (!referent.is<JSScript*>() && !referent.is<LazyScript*>()) {
       ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
                        args.thisv(), nullptr, "a JS script");
       return false;
     }
@@ -278,17 +281,18 @@ struct MOZ_STACK_CLASS DebuggerScript::C
   using Method = bool (CallData::*)();
 
   template <Method MyMethod>
   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
 };
 
 template <DebuggerScript::CallData::Method MyMethod>
 /* static */
-bool DebuggerScript::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
+bool DebuggerScript::CallData::ToNative(JSContext* cx, unsigned argc,
+                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   RootedDebuggerScript obj(cx, DebuggerScript::check(cx, args.thisv()));
   if (!obj) {
     return false;
   }
 
   CallData data(cx, args, obj);
@@ -2457,17 +2461,17 @@ bool DebuggerScript::construct(JSContext
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                             "Debugger.Script");
   return false;
 }
 
 const JSPropertySpec DebuggerScript::properties_[] = {
     JS_DEBUG_PSG("isGeneratorFunction", getIsGeneratorFunction),
     JS_DEBUG_PSG("isAsyncFunction", getIsAsyncFunction),
-    JS_DEBUG_PSG("isFunction",  getIsFunction),
+    JS_DEBUG_PSG("isFunction", getIsFunction),
     JS_DEBUG_PSG("isModule", getIsModule),
     JS_DEBUG_PSG("displayName", getDisplayName),
     JS_DEBUG_PSG("url", getUrl),
     JS_DEBUG_PSG("startLine", getStartLine),
     JS_DEBUG_PSG("startColumn", getStartColumn),
     JS_DEBUG_PSG("lineCount", getLineCount),
     JS_DEBUG_PSG("source", getSource),
     JS_DEBUG_PSG("sourceStart", getSourceStart),
@@ -2475,26 +2479,27 @@ const JSPropertySpec DebuggerScript::pro
     JS_DEBUG_PSG("mainOffset", getMainOffset),
     JS_DEBUG_PSG("global", getGlobal),
     JS_DEBUG_PSG("format", getFormat),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerScript::methods_[] = {
     JS_DEBUG_FN("getChildScripts", getChildScripts, 0),
     JS_DEBUG_FN("getPossibleBreakpoints", getPossibleBreakpoints, 0),
-    JS_DEBUG_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets, 0),
+    JS_DEBUG_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets,
+                0),
     JS_DEBUG_FN("setBreakpoint", setBreakpoint, 2),
     JS_DEBUG_FN("getBreakpoints", getBreakpoints, 1),
     JS_DEBUG_FN("clearBreakpoint", clearBreakpoint, 1),
     JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0),
     JS_DEBUG_FN("isInCatchScope", isInCatchScope, 1),
     JS_DEBUG_FN("getOffsetMetadata", getOffsetMetadata, 1),
     JS_DEBUG_FN("getOffsetsCoverage", getOffsetsCoverage, 0),
-    JS_DEBUG_FN("getSuccessorOffsets",
-                getSuccessorOrPredecessorOffsets<true>, 1),
+    JS_DEBUG_FN("getSuccessorOffsets", getSuccessorOrPredecessorOffsets<true>,
+                1),
     JS_DEBUG_FN("getPredecessorOffsets",
                 getSuccessorOrPredecessorOffsets<false>, 1),
     JS_DEBUG_FN("getEffectfulOffsets", getEffectfulOffsets, 1),
     JS_DEBUG_FN("setInstrumentationId", setInstrumentationId, 1),
 
     // The following APIs are deprecated due to their reliance on the
     // under-defined 'entrypoint' concept. Make use of getPossibleBreakpoints,
     // getPossibleBreakpointOffsets, or getOffsetMetadata instead.
--- a/js/src/debugger/Source.cpp
+++ b/js/src/debugger/Source.cpp
@@ -15,30 +15,30 @@
 #include <utility>   // for move
 
 #include "jsapi.h"        // for JS_ReportErrorNumberASCII
 #include "jsfriendapi.h"  // for GetErrorMessage, JS_NewUint8Array
 
 #include "debugger/Debugger.h"  // for DebuggerSourceReferent, Debugger
 #include "debugger/Script.h"    // for DebuggerScript
 #include "gc/Tracer.h"  // for TraceManuallyBarrieredCrossCompartmentEdge
-#include "js/CompilationAndEvaluation.h" // for Compile
-#include "js/StableStringChars.h"  // for AutoStableStringChars
-#include "vm/BytecodeUtil.h"       // for JSDVG_SEARCH_STACK
-#include "vm/JSContext.h"          // for JSContext (ptr only)
-#include "vm/JSObject.h"           // for JSObject, RequireObject
-#include "vm/JSScript.h"           // for ScriptSource, ScriptSourceObject
-#include "vm/ObjectGroup.h"        // for TenuredObject
-#include "vm/StringType.h"         // for NewStringCopyZ, JSString (ptr only)
-#include "vm/TypedArrayObject.h"   // for TypedArrayObject, JSObject::is
-#include "wasm/WasmCode.h"         // for Metadata
-#include "wasm/WasmDebug.h"        // for DebugState
-#include "wasm/WasmInstance.h"     // for Instance
-#include "wasm/WasmJS.h"           // for WasmInstanceObject
-#include "wasm/WasmTypes.h"        // for Bytes, RootedWasmInstanceObject
+#include "js/CompilationAndEvaluation.h"  // for Compile
+#include "js/StableStringChars.h"         // for AutoStableStringChars
+#include "vm/BytecodeUtil.h"              // for JSDVG_SEARCH_STACK
+#include "vm/JSContext.h"                 // for JSContext (ptr only)
+#include "vm/JSObject.h"                  // for JSObject, RequireObject
+#include "vm/JSScript.h"          // for ScriptSource, ScriptSourceObject
+#include "vm/ObjectGroup.h"       // for TenuredObject
+#include "vm/StringType.h"        // for NewStringCopyZ, JSString (ptr only)
+#include "vm/TypedArrayObject.h"  // for TypedArrayObject, JSObject::is
+#include "wasm/WasmCode.h"        // for Metadata
+#include "wasm/WasmDebug.h"       // for DebugState
+#include "wasm/WasmInstance.h"    // for Instance
+#include "wasm/WasmJS.h"          // for WasmInstanceObject
+#include "wasm/WasmTypes.h"       // for Bytes, RootedWasmInstanceObject
 
 #include "vm/JSObject-inl.h"      // for InitClass
 #include "vm/NativeObject-inl.h"  // for NewNativeObjectWithGivenProto
 
 namespace js {
 class GlobalObject;
 }
 
@@ -179,17 +179,18 @@ struct MOZ_STACK_CLASS DebuggerSource::C
   using Method = bool (CallData::*)();
 
   template <Method MyMethod>
   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
 };
 
 template <DebuggerSource::CallData::Method MyMethod>
 /* static */
-bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
+bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc,
+                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   RootedDebuggerSource obj(cx, DebuggerSource::check(cx, args.thisv()));
   if (!obj) {
     return false;
   }
 
   CallData data(cx, args, obj);
@@ -506,18 +507,18 @@ bool DebuggerSource::CallData::getIntrod
 
   return true;
 }
 
 ScriptSourceObject* EnsureSourceObject(JSContext* cx,
                                        HandleDebuggerSource obj) {
   if (!obj->getReferent().is<ScriptSourceObject*>()) {
     RootedValue v(cx, ObjectValue(*obj));
-    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
-                     v, nullptr, "a JS source");
+    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, v,
+                     nullptr, "a JS source");
     return nullptr;
   }
   return obj->getReferent().as<ScriptSourceObject*>();
 }
 
 bool DebuggerSource::CallData::setSourceMapURL() {
   RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
   if (!sourceObject) {
@@ -671,10 +672,9 @@ const JSPropertySpec DebuggerSource::pro
     JS_DEBUG_PSG("introductionScript", getIntroductionScript),
     JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),
     JS_DEBUG_PSG("introductionType", getIntroductionType),
     JS_DEBUG_PSG("elementAttributeName", getElementProperty),
     JS_DEBUG_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL),
     JS_PS_END};
 
 const JSFunctionSpec DebuggerSource::methods_[] = {
-    JS_DEBUG_FN("reparse", reparse, 0),
-    JS_FS_END};
+    JS_DEBUG_FN("reparse", reparse, 0), JS_FS_END};
--- a/js/src/devtools/rootAnalysis/t/virtual/source.cpp
+++ b/js/src/devtools/rootAnalysis/t/virtual/source.cpp
@@ -34,19 +34,25 @@ class Base {
   static float testAnnotations() ANNOTATE("static func");
 
   // Similar, though sixgill currently completely ignores parameter annotations.
   static double testParamAnnotations(Cell& ANNOTATE("param annotation")
                                          ANNOTATE("second param annot") cell)
       ANNOTATE("static func") ANNOTATE("second func");
 };
 
-float Base::testAnnotations() { asm(""); return 1.1; }
+float Base::testAnnotations() {
+  asm("");
+  return 1.1;
+}
 
-double Base::testParamAnnotations(Cell& cell) { asm(""); return 1.2; }
+double Base::testParamAnnotations(Cell& cell) {
+  asm("");
+  return 1.2;
+}
 
 class Super : public Base {
  public:
   virtual void noneGC() = 0;
   virtual void allGC() = 0;
 };
 
 void bar() { GC(); }
--- a/js/src/frontend/BinAST-macros.h
+++ b/js/src/frontend/BinAST-macros.h
@@ -7,56 +7,67 @@
 #ifndef frontend_BinAST_macros_h
 #define frontend_BinAST_macros_h
 
 #include "vm/JSContext.h"
 
 // Evaluate an expression EXPR, checking that the result is not falsy.
 //
 // Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
-#define BINJS_TRY(EXPR)                            \
-  do {                                             \
-    if (!EXPR) return cx_->alreadyReportedError(); \
+#define BINJS_TRY(EXPR)                   \
+  do {                                    \
+    if (MOZ_UNLIKELY(!(EXPR))) {          \
+      return cx_->alreadyReportedError(); \
+    }                                     \
   } while (false)
 
 // Evaluate an expression EXPR, checking that the result is not falsy.
 // In case of success, assign the result to VAR.
 //
 // Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
-#define BINJS_TRY_VAR(VAR, EXPR)                  \
-  do {                                            \
-    VAR = EXPR;                                   \
-    if (!VAR) return cx_->alreadyReportedError(); \
+#define BINJS_TRY_VAR(VAR, EXPR)          \
+  do {                                    \
+    VAR = (EXPR);                         \
+    if (MOZ_UNLIKELY(!(VAR))) {           \
+      return cx_->alreadyReportedError(); \
+    }                                     \
   } while (false)
 
 // Evaluate an expression EXPR, checking that the result is not falsy.
 // In case of success, assign the result to a new variable VAR.
 //
 // Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
-#define BINJS_TRY_DECL(VAR, EXPR) \
-  auto VAR = EXPR;                \
-  if (!VAR) return cx_->alreadyReportedError();
+#define BINJS_TRY_DECL(VAR, EXPR)       \
+  auto VAR = (EXPR);                    \
+  if (MOZ_UNLIKELY(!(VAR))) {           \
+    return cx_->alreadyReportedError(); \
+  }
 
-#define BINJS_TRY_EMPL(VAR, EXPR)                            \
-  do {                                                       \
-    auto _tryEmplResult = EXPR;                              \
-    if (!_tryEmplResult) return cx_->alreadyReportedError(); \
-    VAR.emplace(_tryEmplResult.unwrap());                    \
+#define BINJS_TRY_EMPL(VAR, EXPR)           \
+  do {                                      \
+    auto _tryEmplResult = (EXPR);           \
+    if (MOZ_UNLIKELY(!_tryEmplResult)) {    \
+      return cx_->alreadyReportedError();   \
+    }                                       \
+    (VAR).emplace(_tryEmplResult.unwrap()); \
   } while (false)
 
 #define BINJS_MOZ_TRY_EMPLACE(VAR, EXPR)                 \
   do {                                                   \
-    auto _tryEmplResult = EXPR;                          \
-    if (_tryEmplResult.isErr())                          \
+    auto _tryEmplResult = (EXPR);                        \
+    if (MOZ_UNLIKELY(_tryEmplResult.isErr())) {          \
       return ::mozilla::Err(_tryEmplResult.unwrapErr()); \
-    VAR.emplace(_tryEmplResult.unwrap());                \
+    }                                                    \
+    (VAR).emplace(_tryEmplResult.unwrap());              \
   } while (false)
 
 // Evaluate an expression EXPR, checking that the result is a success.
 // In case of success, unwrap and assign the result to a new variable VAR.
 //
 // In case of error, propagate the error.
-#define BINJS_MOZ_TRY_DECL(VAR, EXPR)                            \
-  auto _##VAR = EXPR;                                            \
-  if (_##VAR.isErr()) return ::mozilla::Err(_##VAR.unwrapErr()); \
+#define BINJS_MOZ_TRY_DECL(VAR, EXPR)          \
+  auto _##VAR = (EXPR);                        \
+  if (MOZ_UNLIKELY(_##VAR.isErr())) {          \
+    return ::mozilla::Err(_##VAR.unwrapErr()); \
+  }                                            \
   auto VAR = _##VAR.unwrap();
 
 #endif  // frontend_BinAST_macros_h
--- a/js/src/frontend/BinAST.yaml
+++ b/js/src/frontend/BinAST.yaml
@@ -113,24 +113,24 @@ hpp:
 
       template<typename Tok>
       class BinASTParser : public BinASTParserPerTokenizer<Tok> {
        public:
         using Base = BinASTParserPerTokenizer<Tok>;
 
         using Tokenizer = Tok;
 
-        using BinASTFields = typename Tokenizer::BinASTFields;
         using AutoList = typename Tokenizer::AutoList;
         using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
         using Chars = typename Tokenizer::Chars;
-        using Context = typename BinASTTokenReaderBase::Context;
         using ListContext = typename BinASTTokenReaderBase::ListContext;
         using FieldContext = typename BinASTTokenReaderBase::FieldContext;
         using RootContext = typename BinASTTokenReaderBase::RootContext;
+        using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
+        using FieldOrListContext = BinASTTokenReaderBase::FieldOrListContext;
 
        public:
         // Auto-generated types.
         using AssertedDeclaredKind = binast::AssertedDeclaredKind;
         using BinaryOperator = binast::BinaryOperator;
         using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
         using UnaryOperator = binast::UnaryOperator;
         using UpdateOperator = binast::UpdateOperator;
@@ -654,17 +654,17 @@ Block:
     BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, statements));
 
 BreakStatement:
   fields:
     label:
       block:
         replace: |
           RootedAtom label(cx_);
-          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(BinASTInterfaceAndField::BreakStatement__Label))));
+          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(BinASTInterfaceAndField::BreakStatement__Label)));
 
   build: |
     if (label) {
       if (!IsIdentifier(label)) {
         return raiseError("Invalid identifier");
       }
     }
 
@@ -798,17 +798,17 @@ ConditionalExpression:
                    handler_.newConditional(test, consequent, alternate));
 
 ContinueStatement:
   fields:
     label:
       block:
         replace: |
           RootedAtom label(cx_);
-          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(BinASTInterfaceAndField::ContinueStatement__Label))));
+          MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(BinASTInterfaceAndField::ContinueStatement__Label)));
 
   build: |
     if (label) {
       if (!IsIdentifier(label)) {
         return raiseError("ContinueStatement - Label MUST be an identifier");
       }
     }
 
@@ -1305,17 +1305,17 @@ LiteralPropertyName:
     }
 
 LiteralRegExpExpression:
   fields:
     flags:
       block:
         replace: |
           RegExpFlags reflags = JS::RegExpFlag::NoFlags;
-          auto flagsContext = Context(FieldContext(BinASTInterfaceAndField::LiteralRegExpExpression__Flags));
+          auto flagsContext = FieldContext(BinASTInterfaceAndField::LiteralRegExpExpression__Flags);
           if (mozilla::IsSame<Tok, BinASTTokenReaderContext>::value) {
             // Hack: optimized `readChars` is not implemented for `BinASTTokenReaderContext`.
             RootedAtom flags(cx_);
             MOZ_TRY_VAR(flags, tokenizer_->readAtom(flagsContext));
             if (!this->parseRegExpFlags(flags, &reflags)) {
               return raiseError("Invalid regexp flags");
             }
           } else {
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -48,47 +48,48 @@ bool operator==(const typename Tok::Char
 /*
 AssertedMaybePositionalParameterName ::= AssertedParameterName
     AssertedPositionalParameterName
     AssertedRestParameterName
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedMaybePositionalParameterName(
     AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
+    MutableHandle<GCVector<JSAtom*>> positionalParams,
+    const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(
-      result, parseSumAssertedMaybePositionalParameterName(
-                  start, kind, fields, scopeKind, positionalParams, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result,
+                     parseSumAssertedMaybePositionalParameterName(
+                         start, kind, scopeKind, positionalParams, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseSumAssertedMaybePositionalParameterName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
+    const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams,
+    const ListContext& context) {
   Ok result;
   switch (kind) {
     case BinASTKind::AssertedParameterName:
       return raiseError(
           "FIXME: Not implemented yet in this preview release "
           "(AssertedParameterName)");
     case BinASTKind::AssertedPositionalParameterName:
-      MOZ_TRY_VAR(result, parseInterfaceAssertedPositionalParameterName(
-                              start, kind, fields, scopeKind, positionalParams,
-                              context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceAssertedPositionalParameterName(
+                      start, kind, scopeKind, positionalParams, context));
       break;
     case BinASTKind::AssertedRestParameterName:
       return raiseError(
           "FIXME: Not implemented yet in this preview release "
           "(AssertedRestParameterName)");
     default:
       return raiseInvalidKind("AssertedMaybePositionalParameterName", kind);
   }
@@ -99,56 +100,54 @@ JS::Result<Ok> BinASTParser<Tok>::parseS
 AssignmentTarget ::= ArrayAssignmentTarget
     AssignmentTargetIdentifier
     ComputedMemberAssignmentTarget
     ObjectAssignmentTarget
     StaticMemberAssignmentTarget
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseAssignmentTarget(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result,
-                     parseSumAssignmentTarget(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumAssignmentTarget(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumAssignmentTarget(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayAssignmentTarget:
-      MOZ_TRY_VAR(result, parseInterfaceArrayAssignmentTarget(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceArrayAssignmentTarget(start, kind, context));
       break;
     case BinASTKind::AssignmentTargetIdentifier:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(start, kind,
+                                                                   context));
       break;
     case BinASTKind::ComputedMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     case BinASTKind::ObjectAssignmentTarget:
-      MOZ_TRY_VAR(result, parseInterfaceObjectAssignmentTarget(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceObjectAssignmentTarget(start, kind, context));
       break;
     case BinASTKind::StaticMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     default:
       return raiseInvalidKind("AssignmentTarget", kind);
   }
   return result;
 }
 
 /*
@@ -156,105 +155,103 @@ AssignmentTargetOrForInOfBinding ::= Arr
     AssignmentTargetIdentifier
     ComputedMemberAssignmentTarget
     ForInOfBinding
     ObjectAssignmentTarget
     StaticMemberAssignmentTarget
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseAssignmentTargetOrForInOfBinding(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumAssignmentTargetOrForInOfBinding(
-                                 start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(
+      result, parseSumAssignmentTargetOrForInOfBinding(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumAssignmentTargetOrForInOfBinding(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayAssignmentTarget:
-      MOZ_TRY_VAR(result, parseInterfaceArrayAssignmentTarget(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceArrayAssignmentTarget(start, kind, context));
       break;
     case BinASTKind::AssignmentTargetIdentifier:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(start, kind,
+                                                                   context));
       break;
     case BinASTKind::ComputedMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     case BinASTKind::ForInOfBinding:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceForInOfBinding(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceForInOfBinding(start, kind, context));
       break;
     case BinASTKind::ObjectAssignmentTarget:
-      MOZ_TRY_VAR(result, parseInterfaceObjectAssignmentTarget(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceObjectAssignmentTarget(start, kind, context));
       break;
     case BinASTKind::StaticMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     default:
       return raiseInvalidKind("AssignmentTargetOrForInOfBinding", kind);
   }
   return result;
 }
 
 /*
 Binding ::= ArrayBinding
     BindingIdentifier
     ObjectBinding
 */
 template <typename Tok>
-JS::Result<ParseNode*> BinASTParser<Tok>::parseBinding(const Context& context) {
+JS::Result<ParseNode*> BinASTParser<Tok>::parseBinding(
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumBinding(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumBinding(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumBinding(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayBinding:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayBinding(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayBinding(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::BindingIdentifier:
-      MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ObjectBinding:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectBinding(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectBinding(
+                              start, kind, FieldOrListContext(context)));
       break;
     default:
       return raiseInvalidKind("Binding", kind);
   }
   return result;
 }
 
 /*
@@ -288,163 +285,162 @@ Expression ::= ArrayExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseExpression(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumExpression(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumExpression(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AssignmentExpression:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AwaitExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceAwaitExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::BinaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceBinaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CallExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceCallExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ClassExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceClassExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CompoundAssignmentExpression:
       MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ComputedMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ConditionalExpression:
-      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::IdentifierExpression:
-      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralBooleanExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralInfinityExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNullExpression:
-      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNumericExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralRegExpExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralStringExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceNewExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewTargetExpression:
-      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ObjectExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::StaticMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::TemplateExpression:
-      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields,
-                                                           context));
+      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ThisExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceThisExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UnaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUnaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UpdateExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUpdateExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceYieldExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldStarExpression:
-      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     default:
       return raiseInvalidKind("Expression", kind);
   }
   return result;
 }
 
 /*
@@ -479,168 +475,166 @@ ExpressionOrSpreadElement ::= ArrayExpre
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseExpressionOrSpreadElement(
-    const Context& context) {
+    const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(
-      result, parseSumExpressionOrSpreadElement(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result,
+                     parseSumExpressionOrSpreadElement(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpressionOrSpreadElement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AssignmentExpression:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AwaitExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceAwaitExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::BinaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceBinaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CallExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceCallExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ClassExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceClassExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CompoundAssignmentExpression:
       MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ComputedMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ConditionalExpression:
-      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::IdentifierExpression:
-      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralBooleanExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralInfinityExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNullExpression:
-      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNumericExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralRegExpExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralStringExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceNewExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewTargetExpression:
-      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ObjectExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::SpreadElement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceSpreadElement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceSpreadElement(start, kind, context));
       break;
     case BinASTKind::StaticMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::TemplateExpression:
-      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields,
-                                                           context));
+      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ThisExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceThisExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UnaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUnaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UpdateExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUpdateExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceYieldExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldStarExpression:
-      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     default:
       return raiseInvalidKind("ExpressionOrSpreadElement", kind);
   }
   return result;
 }
 
 /*
@@ -675,312 +669,309 @@ ExpressionOrSuper ::= ArrayExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseExpressionOrSuper(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result,
-                     parseSumExpressionOrSuper(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumExpressionOrSuper(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpressionOrSuper(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AssignmentExpression:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AwaitExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceAwaitExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::BinaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceBinaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CallExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceCallExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ClassExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceClassExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CompoundAssignmentExpression:
       MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ComputedMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ConditionalExpression:
-      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::IdentifierExpression:
-      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralBooleanExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralInfinityExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNullExpression:
-      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNumericExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralRegExpExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralStringExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceNewExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewTargetExpression:
-      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ObjectExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::StaticMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::Super:
-      MOZ_TRY_VAR(result, parseInterfaceSuper(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceSuper(start, kind, context));
       break;
     case BinASTKind::TemplateExpression:
-      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields,
-                                                           context));
+      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ThisExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceThisExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UnaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUnaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UpdateExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUpdateExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceYieldExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldStarExpression:
-      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     default:
       return raiseInvalidKind("ExpressionOrSuper", kind);
   }
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumExpressionOrVariableDeclaration(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AssignmentExpression:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::AwaitExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceAwaitExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::BinaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceBinaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CallExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceCallExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ClassExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceClassExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::CompoundAssignmentExpression:
       MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ComputedMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ConditionalExpression:
-      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::EagerFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::IdentifierExpression:
-      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyArrowExpressionWithFunctionBody:
       MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LazyFunctionExpression:
       MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralBooleanExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralInfinityExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNullExpression:
-      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind,
-                                                              fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralNumericExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralRegExpExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::LiteralStringExpression:
       MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceNewExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::NewTargetExpression:
-      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ObjectExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::StaticMemberExpression:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
-                              start, kind, fields, context));
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::TemplateExpression:
-      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields,
-                                                           context));
+      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::ThisExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceThisExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UnaryExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUnaryExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::UpdateExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceUpdateExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::VariableDeclaration:
-      MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldExpression:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceYieldExpression(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     case BinASTKind::YieldStarExpression:
-      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
+                              start, kind, FieldOrListContext(context)));
       break;
     default:
       return raiseInvalidKind("ExpressionOrVariableDeclaration", kind);
   }
   return result;
 }
 
 /*
@@ -990,249 +981,235 @@ ObjectProperty ::= DataProperty
     EagerSetter
     LazyGetter
     LazyMethod
     LazySetter
     ShorthandProperty
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseObjectProperty(
-    const Context& context) {
+    const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result,
-                     parseSumObjectProperty(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumObjectProperty(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumObjectProperty(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::DataProperty:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceDataProperty(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceDataProperty(start, kind, context));
       break;
     case BinASTKind::EagerGetter:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceEagerGetter(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceEagerGetter(start, kind, context));
       break;
     case BinASTKind::EagerMethod:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceEagerMethod(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceEagerMethod(start, kind, context));
       break;
     case BinASTKind::EagerSetter:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceEagerSetter(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceEagerSetter(start, kind, context));
       break;
     case BinASTKind::LazyGetter:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceLazyGetter(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLazyGetter(start, kind, context));
       break;
     case BinASTKind::LazyMethod:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceLazyMethod(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLazyMethod(start, kind, context));
       break;
     case BinASTKind::LazySetter:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceLazySetter(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceLazySetter(start, kind, context));
       break;
     case BinASTKind::ShorthandProperty:
-      MOZ_TRY_VAR(result, parseInterfaceShorthandProperty(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceShorthandProperty(start, kind, context));
       break;
     default:
       return raiseInvalidKind("ObjectProperty", kind);
   }
   return result;
 }
 
 /*
 Parameter ::= ArrayBinding
     BindingIdentifier
     BindingWithInitializer
     ObjectBinding
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseParameter(
-    const Context& context) {
+    const FieldOrListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumParameter(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumParameter(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumParameter(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ArrayBinding:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceArrayBinding(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceArrayBinding(start, kind, context));
       break;
     case BinASTKind::BindingIdentifier:
-      MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceBindingIdentifier(start, kind, context));
       if (!pc_->positionalFormalParameterNames().append(
               result->template as<NameNode>().atom())) {
         return raiseOOM();
       }
       if (pc_->isFunctionBox()) {
         pc_->functionBox()->length++;
       }
       break;
     case BinASTKind::BindingWithInitializer:
-      MOZ_TRY_VAR(result, parseInterfaceBindingWithInitializer(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceBindingWithInitializer(start, kind, context));
       break;
     case BinASTKind::ObjectBinding:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceObjectBinding(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceObjectBinding(start, kind, context));
       break;
     default:
       return raiseInvalidKind("Parameter", kind);
   }
   return result;
 }
 
 /*
 Program ::= Module
     Script
 */
 template <typename Tok>
-JS::Result<ParseNode*> BinASTParser<Tok>::parseProgram(const Context& context) {
+JS::Result<ParseNode*> BinASTParser<Tok>::parseProgram(
+    const RootContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumProgram(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumProgram(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumProgram(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const RootContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::Module:
-      MOZ_TRY_VAR(result, parseInterfaceModule(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceModule(start, kind, context));
       break;
     case BinASTKind::Script:
-      MOZ_TRY_VAR(result, parseInterfaceScript(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceScript(start, kind, context));
       break;
     default:
       return raiseInvalidKind("Program", kind);
   }
   return result;
 }
 
 /*
 PropertyName ::= ComputedPropertyName
     LiteralPropertyName
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parsePropertyName(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result,
-                     parseSumPropertyName(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumPropertyName(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumPropertyName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::ComputedPropertyName:
-      MOZ_TRY_VAR(result, parseInterfaceComputedPropertyName(start, kind,
-                                                             fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceComputedPropertyName(start, kind, context));
       break;
     case BinASTKind::LiteralPropertyName:
-      MOZ_TRY_VAR(result, parseInterfaceLiteralPropertyName(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceLiteralPropertyName(start, kind, context));
       break;
     default:
       return raiseInvalidKind("PropertyName", kind);
   }
   return result;
 }
 
 /*
 SimpleAssignmentTarget ::= AssignmentTargetIdentifier
     ComputedMemberAssignmentTarget
     StaticMemberAssignmentTarget
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSimpleAssignmentTarget(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(
-      result, parseSumSimpleAssignmentTarget(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result,
+                     parseSumSimpleAssignmentTarget(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumSimpleAssignmentTarget(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::AssignmentTargetIdentifier:
-      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceAssignmentTargetIdentifier(start, kind,
+                                                                   context));
       break;
     case BinASTKind::ComputedMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceComputedMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     case BinASTKind::StaticMemberAssignmentTarget:
       MOZ_TRY_VAR(result, parseInterfaceStaticMemberAssignmentTarget(
-                              start, kind, fields, context));
+                              start, kind, context));
       break;
     default:
       return raiseInvalidKind("SimpleAssignmentTarget", kind);
   }
   return result;
 }
 
 /*
@@ -1258,229 +1235,202 @@ Statement ::= Block
     TryCatchStatement
     TryFinallyStatement
     VariableDeclaration
     WhileStatement
     WithStatement
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseStatement(
-    const Context& context) {
+    const FieldOrListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
   const auto start = tokenizer_->offset();
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-
-  BINJS_MOZ_TRY_DECL(result, parseSumStatement(start, kind, fields, context));
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+
+  BINJS_MOZ_TRY_DECL(result, parseSumStatement(start, kind, context));
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseSumStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   ParseNode* result;
   switch (kind) {
     case BinASTKind::Block:
-      MOZ_TRY_VAR(result, parseInterfaceBlock(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBlock(start, kind, context));
       break;
     case BinASTKind::BreakStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceBreakStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceBreakStatement(start, kind, context));
       break;
     case BinASTKind::ClassDeclaration:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceClassDeclaration(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceClassDeclaration(start, kind, context));
       break;
     case BinASTKind::ContinueStatement:
-      MOZ_TRY_VAR(result, parseInterfaceContinueStatement(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceContinueStatement(start, kind, context));
       break;
     case BinASTKind::DebuggerStatement:
-      MOZ_TRY_VAR(result, parseInterfaceDebuggerStatement(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceDebuggerStatement(start, kind, context));
       break;
     case BinASTKind::DoWhileStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceDoWhileStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceDoWhileStatement(start, kind, context));
       break;
     case BinASTKind::EagerFunctionDeclaration:
-      MOZ_TRY_VAR(result, parseInterfaceEagerFunctionDeclaration(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceEagerFunctionDeclaration(start, kind, context));
       break;
     case BinASTKind::EmptyStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceEmptyStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceEmptyStatement(start, kind, context));
       break;
     case BinASTKind::ExpressionStatement:
-      MOZ_TRY_VAR(result, parseInterfaceExpressionStatement(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceExpressionStatement(start, kind, context));
       break;
     case BinASTKind::ForInStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceForInStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceForInStatement(start, kind, context));
       break;
     case BinASTKind::ForOfStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceForOfStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceForOfStatement(start, kind, context));
       break;
     case BinASTKind::ForStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceForStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceForStatement(start, kind, context));
       break;
     case BinASTKind::IfStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceIfStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceIfStatement(start, kind, context));
       break;
     case BinASTKind::LabelledStatement:
-      MOZ_TRY_VAR(result, parseInterfaceLabelledStatement(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceLabelledStatement(start, kind, context));
       break;
     case BinASTKind::LazyFunctionDeclaration:
-      MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceLazyFunctionDeclaration(start, kind, context));
       break;
     case BinASTKind::ReturnStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceReturnStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceReturnStatement(start, kind, context));
       break;
     case BinASTKind::SwitchStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceSwitchStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceSwitchStatement(start, kind, context));
       break;
     case BinASTKind::SwitchStatementWithDefault:
-      MOZ_TRY_VAR(result, parseInterfaceSwitchStatementWithDefault(
-                              start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceSwitchStatementWithDefault(start, kind,
+                                                                   context));
       break;
     case BinASTKind::ThrowStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceThrowStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceThrowStatement(start, kind, context));
       break;
     case BinASTKind::TryCatchStatement:
-      MOZ_TRY_VAR(result, parseInterfaceTryCatchStatement(start, kind, fields,
-                                                          context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceTryCatchStatement(start, kind, context));
       break;
     case BinASTKind::TryFinallyStatement:
-      MOZ_TRY_VAR(result, parseInterfaceTryFinallyStatement(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceTryFinallyStatement(start, kind, context));
       break;
     case BinASTKind::VariableDeclaration:
-      MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(start, kind, fields,
-                                                            context));
+      MOZ_TRY_VAR(result,
+                  parseInterfaceVariableDeclaration(start, kind, context));
       break;
     case BinASTKind::WhileStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceWhileStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceWhileStatement(start, kind, context));
       break;
     case BinASTKind::WithStatement:
-      MOZ_TRY_VAR(result,
-                  parseInterfaceWithStatement(start, kind, fields, context));
+      MOZ_TRY_VAR(result, parseInterfaceWithStatement(start, kind, context));
       break;
     default:
       return raiseInvalidKind("Statement", kind);
   }
   return result;
 }
 
 // ----- Interfaces (autogenerated, by lexicographical order)
 // When fields have a non-trivial type, implementation is deanonymized and
 // delegated to another parser.
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayAssignmentTarget(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(ArrayAssignmentTarget)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayBinding(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (ArrayBinding)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ArrayExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Elements};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
-  BINJS_MOZ_TRY_DECL(
-      elements,
-      parseListOfOptionalExpressionOrSpreadElement(Context(
-          FieldContext(BinASTInterfaceAndField::ArrayExpression__Elements))));
+  BINJS_MOZ_TRY_DECL(elements,
+                     parseListOfOptionalExpressionOrSpreadElement(FieldContext(
+                         BinASTInterfaceAndField::ArrayExpression__Elements)));
 
   if (elements->empty()) {
     elements->setHasNonConstInitializer();
   }
   auto result = elements;
   return result;
 }
 
 /*
  interface AssertedBlockScope : Node {
     FrozenArray<AssertedDeclaredName> declaredNames;
     bool hasDirectEval;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedBlockScope(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedBlockScope) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedBlockScope)) {
     return raiseInvalidKind("AssertedBlockScope", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceAssertedBlockScope(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceAssertedBlockScope(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBlockScope(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBlockScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
-                                          BinASTField::HasDirectEval};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Block;
 
   MOZ_TRY(parseListOfAssertedDeclaredName(
       scopeKind,
-      Context(FieldContext(
-          BinASTInterfaceAndField::AssertedBlockScope__DeclaredNames))));
+      FieldContext(
+          BinASTInterfaceAndField::AssertedBlockScope__DeclaredNames)));
 
   BINJS_MOZ_TRY_DECL(
       hasDirectEval,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedBlockScope__HasDirectEval))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedBlockScope__HasDirectEval)));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1492,55 +1442,48 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 /*
  interface AssertedBoundName : Node {
     [IdentifierName] string name;
     bool isCaptured;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedBoundName(
-    AssertedScopeKind scopeKind, const Context& context) {
+    AssertedScopeKind scopeKind, const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedBoundName) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedBoundName)) {
     return raiseInvalidKind("AssertedBoundName", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedBoundName(
-                                 start, kind, fields, scopeKind, context));
+  BINJS_MOZ_TRY_DECL(
+      result, parseInterfaceAssertedBoundName(start, kind, scopeKind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    AssertedScopeKind scopeKind, const Context& context) {
+    const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
+    const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBoundName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Name,
-                                          BinASTField::IsCaptured};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const bool allowDuplicateName = false;
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
-                        BinASTInterfaceAndField::AssertedBoundName__Name))));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
+                        BinASTInterfaceAndField::AssertedBoundName__Name)));
 
   BINJS_MOZ_TRY_DECL(
-      isCaptured,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedBoundName__IsCaptured))));
+      isCaptured, tokenizer_->readBool(FieldContext(
+                      BinASTInterfaceAndField::AssertedBoundName__IsCaptured)));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1548,56 +1491,49 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 /*
  interface AssertedBoundNamesScope : Node {
     FrozenArray<AssertedBoundName> boundNames;
     bool hasDirectEval;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedBoundNamesScope(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedBoundNamesScope) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedBoundNamesScope)) {
     return raiseInvalidKind("AssertedBoundNamesScope", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedBoundNamesScope(
-                                 start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(
+      result, parseInterfaceAssertedBoundNamesScope(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundNamesScope(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedBoundNamesScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::BoundNames,
-                                          BinASTField::HasDirectEval};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Catch;
 
   MOZ_TRY(parseListOfAssertedBoundName(
       scopeKind,
-      Context(FieldContext(
-          BinASTInterfaceAndField::AssertedBoundNamesScope__BoundNames))));
+      FieldContext(
+          BinASTInterfaceAndField::AssertedBoundNamesScope__BoundNames)));
 
   BINJS_MOZ_TRY_DECL(
       hasDirectEval,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedBoundNamesScope__HasDirectEval))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedBoundNamesScope__HasDirectEval)));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1610,64 +1546,58 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
  interface AssertedDeclaredName : Node {
     [IdentifierName] string name;
     AssertedDeclaredKind kind;
     bool isCaptured;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedDeclaredName(
-    AssertedScopeKind scopeKind, const Context& context) {
+    AssertedScopeKind scopeKind, const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedDeclaredName) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedDeclaredName)) {
     return raiseInvalidKind("AssertedDeclaredName", kind);
   }
   const auto start = tokenizer_->offset();
   BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedDeclaredName(
-                                 start, kind, fields, scopeKind, context));
+                                 start, kind, scopeKind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedDeclaredName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    AssertedScopeKind scopeKind, const Context& context) {
+    const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
+    const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedDeclaredName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {BinASTField::Name, BinASTField::Kind,
-                                          BinASTField::IsCaptured};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const bool allowDuplicateName = false;
 
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
-                        BinASTInterfaceAndField::AssertedDeclaredName__Name))));
-
-  BINJS_MOZ_TRY_DECL(
-      kind_, parseAssertedDeclaredKind(Context(FieldContext(
-                 BinASTInterfaceAndField::AssertedDeclaredName__Kind))));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
+                        BinASTInterfaceAndField::AssertedDeclaredName__Name)));
+
+  BINJS_MOZ_TRY_DECL(kind_,
+                     parseAssertedDeclaredKind(FieldContext(
+                         BinASTInterfaceAndField::AssertedDeclaredName__Kind)));
   if (kind_ == AssertedDeclaredKind::NonConstLexical) {
     return raiseError("Let is not supported in this preview release");
   }
   if (kind_ == AssertedDeclaredKind::ConstLexical) {
     return raiseError("Const is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
       isCaptured,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedDeclaredName__IsCaptured))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedDeclaredName__IsCaptured)));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1676,101 +1606,89 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
  interface AssertedParameterScope : Node {
     FrozenArray<AssertedMaybePositionalParameterName> paramNames;
     bool hasDirectEval;
     bool isSimpleParameterList;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedParameterScope(
-    MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
+    MutableHandle<GCVector<JSAtom*>> positionalParams,
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedParameterScope) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedParameterScope)) {
     return raiseInvalidKind("AssertedParameterScope", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceAssertedParameterScope(start, kind, fields,
-                                                   positionalParams, context));
+  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedParameterScope(
+                                 start, kind, positionalParams, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedParameterScope(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
+    const size_t start, const BinASTKind kind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams,
+    const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedParameterScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {BinASTField::ParamNames,
-                                          BinASTField::HasDirectEval,
-                                          BinASTField::IsSimpleParameterList};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Parameter;
 
   MOZ_TRY(parseListOfAssertedMaybePositionalParameterName(
       scopeKind, positionalParams,
-      Context(FieldContext(
-          BinASTInterfaceAndField::AssertedParameterScope__ParamNames))));
+      FieldContext(
+          BinASTInterfaceAndField::AssertedParameterScope__ParamNames)));
 
   BINJS_MOZ_TRY_DECL(
       hasDirectEval,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedParameterScope__HasDirectEval))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedParameterScope__HasDirectEval)));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   BINJS_MOZ_TRY_DECL(isSimpleParameterList,
-                     tokenizer_->readBool(Context(FieldContext(
+                     tokenizer_->readBool(FieldContext(
                          BinASTInterfaceAndField::
-                             AssertedParameterScope__IsSimpleParameterList))));
+                             AssertedParameterScope__IsSimpleParameterList)));
   (void)isSimpleParameterList;
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
   }
   auto result = Ok();
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedPositionalParameterName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams, const Context& context) {
+    const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams,
+    const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedPositionalParameterName);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {BinASTField::Index, BinASTField::Name,
-                                          BinASTField::IsCaptured};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   bool allowDuplicateName = !pc_->sc()->strict();
 
   BINJS_MOZ_TRY_DECL(
       index,
-      tokenizer_->readUnsignedLong(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedPositionalParameterName__Index))));
+      tokenizer_->readUnsignedLong(FieldContext(
+          BinASTInterfaceAndField::AssertedPositionalParameterName__Index)));
 
   RootedAtom name(cx_);
   MOZ_TRY_VAR(
       name,
-      tokenizer_->readIdentifierName(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedPositionalParameterName__Name))));
+      tokenizer_->readIdentifierName(FieldContext(
+          BinASTInterfaceAndField::AssertedPositionalParameterName__Name)));
   // `positionalParams` vector can be shorter than the actual
   // parameter length. Resize on demand.
   // (see also ListOfAssertedMaybePositionalParameterName)
   size_t prevLength = positionalParams.get().length();
   if (index >= prevLength) {
     // This is implementation limit, which is not in the spec.
     if (index >= ARGNO_LIMIT - 1) {
       return raiseError("AssertedPositionalParameterName.index is too big");
@@ -1784,19 +1702,19 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 
   if (positionalParams.get()[index]) {
     return raiseError(
         "AssertedPositionalParameterName has duplicate entry for the same "
         "index");
   }
   positionalParams.get()[index] = name;
   BINJS_MOZ_TRY_DECL(isCaptured,
-                     tokenizer_->readBool(Context(FieldContext(
+                     tokenizer_->readBool(FieldContext(
                          BinASTInterfaceAndField::
-                             AssertedPositionalParameterName__IsCaptured))));
+                             AssertedPositionalParameterName__IsCaptured)));
   ParseContext::Scope* scope;
   DeclarationKind declKind;
   MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
   MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                        allowDuplicateName));
   auto result = Ok();
   return result;
 }
@@ -1804,56 +1722,49 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 /*
  interface AssertedScriptGlobalScope : Node {
     FrozenArray<AssertedDeclaredName> declaredNames;
     bool hasDirectEval;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedScriptGlobalScope(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedScriptGlobalScope) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedScriptGlobalScope)) {
     return raiseInvalidKind("AssertedScriptGlobalScope", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedScriptGlobalScope(
-                                 start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(
+      result, parseInterfaceAssertedScriptGlobalScope(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedScriptGlobalScope(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedScriptGlobalScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
-                                          BinASTField::HasDirectEval};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Global;
 
   MOZ_TRY(parseListOfAssertedDeclaredName(
       scopeKind,
-      Context(FieldContext(
-          BinASTInterfaceAndField::AssertedScriptGlobalScope__DeclaredNames))));
+      FieldContext(
+          BinASTInterfaceAndField::AssertedScriptGlobalScope__DeclaredNames)));
 
   BINJS_MOZ_TRY_DECL(
       hasDirectEval,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedScriptGlobalScope__HasDirectEval))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedScriptGlobalScope__HasDirectEval)));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
@@ -1865,153 +1776,127 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
 /*
  interface AssertedVarScope : Node {
     FrozenArray<AssertedDeclaredName> declaredNames;
     bool hasDirectEval;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseAssertedVarScope(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::AssertedVarScope) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::AssertedVarScope)) {
     return raiseInvalidKind("AssertedVarScope", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceAssertedVarScope(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceAssertedVarScope(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedVarScope(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssertedVarScope);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::DeclaredNames,
-                                          BinASTField::HasDirectEval};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto scopeKind = AssertedScopeKind::Var;
 
   MOZ_TRY(parseListOfAssertedDeclaredName(
       scopeKind,
-      Context(FieldContext(
-          BinASTInterfaceAndField::AssertedVarScope__DeclaredNames))));
+      FieldContext(BinASTInterfaceAndField::AssertedVarScope__DeclaredNames)));
 
   BINJS_MOZ_TRY_DECL(
       hasDirectEval,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::AssertedVarScope__HasDirectEval))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::AssertedVarScope__HasDirectEval)));
   if (hasDirectEval) {
     pc_->sc()->setHasDirectEval();
     pc_->sc()->setBindingsAccessedDynamically();
   }
   if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
     // In non-strict mode code, direct calls to eval can
     // add variables to the call object.
     pc_->functionBox()->setHasExtensibleScope();
   }
   auto result = Ok();
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceAssignmentExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssignmentExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Binding,
-                                          BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      binding, parseAssignmentTarget(Context(FieldContext(
-                   BinASTInterfaceAndField::AssignmentExpression__Binding))));
+      binding, parseAssignmentTarget(FieldContext(
+                   BinASTInterfaceAndField::AssignmentExpression__Binding)));
 
   BINJS_MOZ_TRY_DECL(
       expression,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::AssignmentExpression__Expression))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::AssignmentExpression__Expression)));
 
   BINJS_TRY_DECL(result, handler_.newAssignment(ParseNodeKind::AssignExpr,
                                                 binding, expression));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceAssignmentTargetIdentifier(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::AssignmentTargetIdentifier);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Name};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom name(cx_);
   MOZ_TRY_VAR(name,
-              tokenizer_->readIdentifierName(Context(FieldContext(
-                  BinASTInterfaceAndField::AssignmentTargetIdentifier__Name))));
+              tokenizer_->readIdentifierName(FieldContext(
+                  BinASTInterfaceAndField::AssignmentTargetIdentifier__Name)));
 
   BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                                pc_->innermostScope()->id()));
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceAwaitExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (AwaitExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBinaryExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::BinaryExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::Operator, BinASTField::Left, BinASTField::Right};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
+  BINJS_MOZ_TRY_DECL(operator_,
+                     parseBinaryOperator(FieldContext(
+                         BinASTInterfaceAndField::BinaryExpression__Operator)));
 
   BINJS_MOZ_TRY_DECL(
-      operator_, parseBinaryOperator(Context(FieldContext(
-                     BinASTInterfaceAndField::BinaryExpression__Operator))));
-
-  BINJS_MOZ_TRY_DECL(left,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::BinaryExpression__Left))));
+      left, parseExpression(
+                FieldContext(BinASTInterfaceAndField::BinaryExpression__Left)));
 
   BINJS_MOZ_TRY_DECL(right,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::BinaryExpression__Right))));
+                     parseExpression(FieldContext(
+                         BinASTInterfaceAndField::BinaryExpression__Right)));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case BinaryOperator::Comma:
       pnk = ParseNodeKind::CommaExpr;
       break;
     case BinaryOperator::LogicalOr:
       pnk = ParseNodeKind::OrExpr;
@@ -2105,129 +1990,114 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
 /*
  interface BindingIdentifier : Node {
     [IdentifierName] string name;
  }
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseBindingIdentifier(
-    const Context& context) {
+    const FieldOrListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::BindingIdentifier) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::BindingIdentifier)) {
     return raiseInvalidKind("BindingIdentifier", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceBindingIdentifier(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceBindingIdentifier(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingIdentifier(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::BindingIdentifier);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Name};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
-                        BinASTInterfaceAndField::BindingIdentifier__Name))));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
+                        BinASTInterfaceAndField::BindingIdentifier__Name)));
 
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingWithInitializer(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(BindingWithInitializer)");
 }
 
 /*
  interface Block : Node {
     AssertedBlockScope scope;
     FrozenArray<Statement> statements;
  }
 */
 template <typename Tok>
-JS::Result<ParseNode*> BinASTParser<Tok>::parseBlock(const Context& context) {
+JS::Result<ParseNode*> BinASTParser<Tok>::parseBlock(
+    const FieldOrListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::Block) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::Block)) {
     return raiseInvalidKind("Block", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result, parseInterfaceBlock(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result, parseInterfaceBlock(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBlock(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::Block);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Scope,
-                                          BinASTField::Statements};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::Block);
   ParseContext::Scope currentScope(cx_, pc_, usedNames_);
   BINJS_TRY(currentScope.init(pc_));
 
   MOZ_TRY(parseAssertedBlockScope(
-      Context(FieldContext(BinASTInterfaceAndField::Block__Scope))));
+      FieldContext(BinASTInterfaceAndField::Block__Scope)));
 
   BINJS_MOZ_TRY_DECL(statements,
-                     parseListOfStatement(Context(FieldContext(
-                         BinASTInterfaceAndField::Block__Statements))));
+                     parseListOfStatement(FieldContext(
+                         BinASTInterfaceAndField::Block__Statements)));
 
   MOZ_TRY(checkClosedVars(currentScope));
   BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
   BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, statements));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBreakStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::BreakStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Label};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(
-                         BinASTInterfaceAndField::BreakStatement__Label))));
+  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(
+                         BinASTInterfaceAndField::BreakStatement__Label)));
 
   if (label) {
     if (!IsIdentifier(label)) {
       return raiseError("Invalid identifier");
     }
   }
 
   auto validity =
@@ -2246,34 +2116,28 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY_DECL(result, handler_.newBreakStatement(
                              label ? label->asPropertyName() : nullptr,
                              tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceCallExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::CallExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Callee,
-                                          BinASTField::Arguments};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(callee,
-                     parseExpressionOrSuper(Context(FieldContext(
-                         BinASTInterfaceAndField::CallExpression__Callee))));
+                     parseExpressionOrSuper(FieldContext(
+                         BinASTInterfaceAndField::CallExpression__Callee)));
 
   BINJS_MOZ_TRY_DECL(arguments,
-                     parseArguments(Context(FieldContext(
-                         BinASTInterfaceAndField::CallExpression__Arguments))));
+                     parseArguments(FieldContext(
+                         BinASTInterfaceAndField::CallExpression__Arguments)));
 
   auto op = JSOP_CALL;
 
   // Try to optimize funcall and funapply at the bytecode level
   if (PropertyName* prop = handler_.maybeDottedProperty(callee)) {
     if (prop == cx_->names().apply) {
       op = JSOP_FUNAPPLY;
       if (pc_->isFunctionBox()) {
@@ -2305,113 +2169,99 @@ JS::Result<ParseNode*> BinASTParser<Tok>
  interface CatchClause : Node {
     AssertedBoundNamesScope bindingScope;
     Binding binding;
     Block body;
  }
 */
 template <typename Tok>
 JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseCatchClause(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::CatchClause) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::CatchClause)) {
     return raiseInvalidKind("CatchClause", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result,
-                     parseInterfaceCatchClause(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result, parseInterfaceCatchClause(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseInterfaceCatchClause(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::CatchClause);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::BindingScope, BinASTField::Binding, BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::Catch);
   ParseContext::Scope currentScope(cx_, pc_, usedNames_);
   BINJS_TRY(currentScope.init(pc_));
 
-  MOZ_TRY(parseAssertedBoundNamesScope(Context(
-      FieldContext(BinASTInterfaceAndField::CatchClause__BindingScope))));
+  MOZ_TRY(parseAssertedBoundNamesScope(
+      FieldContext(BinASTInterfaceAndField::CatchClause__BindingScope)));
 
   BINJS_MOZ_TRY_DECL(binding,
-                     parseBinding(Context(FieldContext(
-                         BinASTInterfaceAndField::CatchClause__Binding))));
+                     parseBinding(FieldContext(
+                         BinASTInterfaceAndField::CatchClause__Binding)));
   if (!currentScope.lookupDeclaredName(
           binding->template as<NameNode>().atom())) {
     return raiseError("Missing catch variable in scope");
   }
-  BINJS_MOZ_TRY_DECL(body, parseBlock(Context(FieldContext(
+  BINJS_MOZ_TRY_DECL(body, parseBlock(FieldOrListContext(FieldContext(
                                BinASTInterfaceAndField::CatchClause__Body))));
 
   MOZ_TRY(checkClosedVars(currentScope));
   BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
   BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, body));
   BINJS_TRY(handler_.setupCatchScope(result, binding, body));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceClassDeclaration(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (ClassDeclaration)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceClassExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (ClassExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceCompoundAssignmentExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::CompoundAssignmentExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::Operator, BinASTField::Binding, BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       operator_,
-      parseCompoundAssignmentOperator(Context(FieldContext(
-          BinASTInterfaceAndField::CompoundAssignmentExpression__Operator))));
+      parseCompoundAssignmentOperator(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Operator)));
 
   BINJS_MOZ_TRY_DECL(
       binding,
-      parseSimpleAssignmentTarget(Context(FieldContext(
-          BinASTInterfaceAndField::CompoundAssignmentExpression__Binding))));
+      parseSimpleAssignmentTarget(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Binding)));
 
   BINJS_MOZ_TRY_DECL(
       expression,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::CompoundAssignmentExpression__Expression))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::CompoundAssignmentExpression__Expression)));
 
   ParseNodeKind pnk;
   switch (operator_) {
     case CompoundAssignmentOperator::PlusAssign:
       pnk = ParseNodeKind::AddAssignExpr;
       break;
     case CompoundAssignmentOperator::MinusAssign:
       pnk = ParseNodeKind::SubAssignExpr;
@@ -2449,124 +2299,99 @@ BinASTParser<Tok>::parseInterfaceCompoun
   }
   BINJS_TRY_DECL(result, handler_.newAssignment(pnk, binding, expression));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ComputedMemberAssignmentTarget);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Object,
-                                          BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       object,
-      parseExpressionOrSuper(Context(FieldContext(
-          BinASTInterfaceAndField::ComputedMemberAssignmentTarget__Object))));
+      parseExpressionOrSuper(FieldContext(
+          BinASTInterfaceAndField::ComputedMemberAssignmentTarget__Object)));
 
   BINJS_MOZ_TRY_DECL(expression,
-                     parseExpression(Context(FieldContext(
+                     parseExpression(FieldContext(
                          BinASTInterfaceAndField::
-                             ComputedMemberAssignmentTarget__Expression))));
+                             ComputedMemberAssignmentTarget__Expression)));
 
   BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                      tokenizer_->offset()));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceComputedMemberExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ComputedMemberExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Object,
-                                          BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      object, parseExpressionOrSuper(Context(FieldContext(
-                  BinASTInterfaceAndField::ComputedMemberExpression__Object))));
+      object, parseExpressionOrSuper(FieldContext(
+                  BinASTInterfaceAndField::ComputedMemberExpression__Object)));
 
   BINJS_MOZ_TRY_DECL(
       expression,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::ComputedMemberExpression__Expression))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::ComputedMemberExpression__Expression)));
 
   BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                      tokenizer_->offset()));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceComputedPropertyName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(ComputedPropertyName)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceConditionalExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ConditionalExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::Test, BinASTField::Consequent, BinASTField::Alternate};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      test, parseExpression(Context(FieldContext(
-                BinASTInterfaceAndField::ConditionalExpression__Test))));
+      test, parseExpression(FieldContext(
+                BinASTInterfaceAndField::ConditionalExpression__Test)));
 
   BINJS_MOZ_TRY_DECL(
       consequent,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::ConditionalExpression__Consequent))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::ConditionalExpression__Consequent)));
 
   BINJS_MOZ_TRY_DECL(
       alternate,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::ConditionalExpression__Alternate))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::ConditionalExpression__Alternate)));
 
   BINJS_TRY_DECL(result, handler_.newConditional(test, consequent, alternate));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceContinueStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ContinueStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Label};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(Context(FieldContext(
-                         BinASTInterfaceAndField::ContinueStatement__Label))));
+  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(
+                         BinASTInterfaceAndField::ContinueStatement__Label)));
 
   if (label) {
     if (!IsIdentifier(label)) {
       return raiseError("ContinueStatement - Label MUST be an identifier");
     }
   }
 
   auto validity =
@@ -2585,33 +2410,26 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY_DECL(result, handler_.newContinueStatement(
                              label ? label->asPropertyName() : nullptr,
                              tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDataProperty(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::DataProperty);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Name,
-                                          BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
-                               BinASTInterfaceAndField::DataProperty__Name))));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
+                               BinASTInterfaceAndField::DataProperty__Name)));
 
   BINJS_MOZ_TRY_DECL(expression,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::DataProperty__Expression))));
+                     parseExpression(FieldContext(
+                         BinASTInterfaceAndField::DataProperty__Expression)));
 
   if (!handler_.isUsableAsObjectPropertyName(name)) {
     return raiseError("DataProperty key kind");
   }
 
   ParseNode* result;
   if (name->template is<NameNode>() &&
       name->template as<NameNode>().atom() == cx_->names().proto) {
@@ -2621,156 +2439,137 @@ JS::Result<ParseNode*> BinASTParser<Tok>
     BINJS_TRY_VAR(result, handler_.newObjectMethodOrPropertyDefinition(
                               name, expression, AccessorType::None));
   }
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDebuggerStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (DebuggerStatement)");
 }
 
 /*
  interface Directive : Node {
     string rawValue;
  }
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseDirective(
-    const Context& context) {
+    const ListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::Directive) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::Directive)) {
     return raiseInvalidKind("Directive", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(result,
-                     parseInterfaceDirective(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result, parseInterfaceDirective(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDirective(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::Directive);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::RawValue};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom rawValue(cx_);
-  MOZ_TRY_VAR(rawValue, tokenizer_->readAtom(Context(FieldContext(
-                            BinASTInterfaceAndField::Directive__RawValue))));
+  MOZ_TRY_VAR(rawValue, tokenizer_->readAtom(FieldContext(
+                            BinASTInterfaceAndField::Directive__RawValue)));
 
   TokenPos pos = tokenizer_->pos(start);
   BINJS_TRY_DECL(result, handler_.newStringLiteral(rawValue, pos));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDoWhileStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::DoWhileStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Test, BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::DoLoop);
 
-  BINJS_MOZ_TRY_DECL(test,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::DoWhileStatement__Test))));
+  BINJS_MOZ_TRY_DECL(
+      test, parseExpression(
+                FieldContext(BinASTInterfaceAndField::DoWhileStatement__Test)));
 
   BINJS_MOZ_TRY_DECL(body,
-                     parseStatement(Context(FieldContext(
+                     parseStatement(FieldOrListContext(FieldContext(
                          BinASTInterfaceAndField::DoWhileStatement__Body))));
 
   BINJS_TRY_DECL(
       result, handler_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(EagerArrowExpressionWithExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithFunctionBody(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(EagerArrowExpressionWithFunctionBody)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionDeclaration(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerFunctionDeclaration);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[6] = {
-      BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
-      BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Statement;
 
   BINJS_MOZ_TRY_DECL(
       isAsync,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionDeclaration__IsAsync))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__IsAsync)));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
       isGenerator,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionDeclaration__IsGenerator))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__IsGenerator)));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
-      name, parseBindingIdentifier(Context(FieldContext(
+      name, parseBindingIdentifier(FieldOrListContext(FieldContext(
                 BinASTInterfaceAndField::EagerFunctionDeclaration__Name))));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(FieldContext(
-                  BinASTInterfaceAndField::EagerFunctionDeclaration__Length))));
+      length, tokenizer_->readUnsignedLong(FieldContext(
+                  BinASTInterfaceAndField::EagerFunctionDeclaration__Length)));
 
   BINJS_MOZ_TRY_DECL(
       directives,
-      parseListOfDirective(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionDeclaration__Directives))));
+      parseListOfDirective(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionDeclaration__Directives)));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2788,72 +2587,64 @@ BinASTParser<Tok>::parseInterfaceEagerFu
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseFunctionOrMethodContents(
       length, &params, &body,
-      Context(FieldContext(
+      FieldOrRootContext(FieldContext(
           BinASTInterfaceAndField::EagerFunctionDeclaration__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   uint32_t nargs = params->count();
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, kind, funbox));
   MOZ_TRY(setFunctionParametersAndBody(result, params, bodyScope));
   MOZ_TRY(finishEagerFunction(funbox, nargs));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerFunctionExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[6] = {
-      BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
-      BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Expression;
 
   BINJS_MOZ_TRY_DECL(
-      isAsync,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionExpression__IsAsync))));
+      isAsync, tokenizer_->readBool(FieldContext(
+                   BinASTInterfaceAndField::EagerFunctionExpression__IsAsync)));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
       isGenerator,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionExpression__IsGenerator))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__IsGenerator)));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
-      name, parseOptionalBindingIdentifier(Context(FieldContext(
-                BinASTInterfaceAndField::EagerFunctionExpression__Name))));
+      name, parseOptionalBindingIdentifier(FieldContext(
+                BinASTInterfaceAndField::EagerFunctionExpression__Name)));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(FieldContext(
-                  BinASTInterfaceAndField::EagerFunctionExpression__Length))));
+      length, tokenizer_->readUnsignedLong(FieldContext(
+                  BinASTInterfaceAndField::EagerFunctionExpression__Length)));
 
   BINJS_MOZ_TRY_DECL(
       directives,
-      parseListOfDirective(Context(FieldContext(
-          BinASTInterfaceAndField::EagerFunctionExpression__Directives))));
+      parseListOfDirective(FieldContext(
+          BinASTInterfaceAndField::EagerFunctionExpression__Directives)));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2871,54 +2662,47 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseFunctionExpressionContents(
       length, &params, &body,
-      Context(FieldContext(
+      FieldOrRootContext(FieldContext(
           BinASTInterfaceAndField::EagerFunctionExpression__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   uint32_t nargs = params->count();
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, kind, funbox));
   MOZ_TRY(setFunctionParametersAndBody(result, params, bodyScope));
   MOZ_TRY(finishEagerFunction(funbox, nargs));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerGetter(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerGetter);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::Name, BinASTField::Directives, BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Setter;
   const bool isGenerator = false;
   const bool isAsync = false;
   const auto accessorType = AccessorType::Getter;
   const uint32_t length = 0;
 
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
-                               BinASTInterfaceAndField::EagerGetter__Name))));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
+                               BinASTInterfaceAndField::EagerGetter__Name)));
 
   BINJS_MOZ_TRY_DECL(directives,
-                     parseListOfDirective(Context(FieldContext(
-                         BinASTInterfaceAndField::EagerGetter__Directives))));
+                     parseListOfDirective(FieldContext(
+                         BinASTInterfaceAndField::EagerGetter__Directives)));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -2936,70 +2720,62 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseGetterContents(
       length, &params, &body,
-      Context(FieldContext(BinASTInterfaceAndField::EagerGetter__Contents))));
+      FieldContext(BinASTInterfaceAndField::EagerGetter__Contents)));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   uint32_t nargs = params->count();
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, kind, funbox));
   MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   MOZ_TRY(finishEagerFunction(funbox, nargs));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerMethod(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerMethod);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[6] = {
-      BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
-      BinASTField::Length,  BinASTField::Directives,  BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Method;
   const auto accessorType = AccessorType::None;
 
   BINJS_MOZ_TRY_DECL(isAsync,
-                     tokenizer_->readBool(Context(FieldContext(
-                         BinASTInterfaceAndField::EagerMethod__IsAsync))));
+                     tokenizer_->readBool(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__IsAsync)));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(isGenerator,
-                     tokenizer_->readBool(Context(FieldContext(
-                         BinASTInterfaceAndField::EagerMethod__IsGenerator))));
+                     tokenizer_->readBool(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__IsGenerator)));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
-                               BinASTInterfaceAndField::EagerMethod__Name))));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
+                               BinASTInterfaceAndField::EagerMethod__Name)));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(
-                  FieldContext(BinASTInterfaceAndField::EagerMethod__Length))));
+      length, tokenizer_->readUnsignedLong(
+                  FieldContext(BinASTInterfaceAndField::EagerMethod__Length)));
 
   BINJS_MOZ_TRY_DECL(directives,
-                     parseListOfDirective(Context(FieldContext(
-                         BinASTInterfaceAndField::EagerMethod__Directives))));
+                     parseListOfDirective(FieldContext(
+                         BinASTInterfaceAndField::EagerMethod__Directives)));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -3017,59 +2793,52 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseFunctionOrMethodContents(
       length, &params, &body,
-      Context(FieldContext(BinASTInterfaceAndField::EagerMethod__Contents))));
+      FieldOrRootContext(
+          FieldContext(BinASTInterfaceAndField::EagerMethod__Contents))));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   uint32_t nargs = params->count();
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, kind, funbox));
   MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   MOZ_TRY(finishEagerFunction(funbox, nargs));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerSetter(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EagerSetter);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[4] = {
-      BinASTField::Name, BinASTField::Length, BinASTField::Directives,
-      BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Setter;
   const bool isGenerator = false;
   const bool isAsync = false;
   const auto accessorType = AccessorType::Setter;
 
-  BINJS_MOZ_TRY_DECL(name, parsePropertyName(Context(FieldContext(
-                               BinASTInterfaceAndField::EagerSetter__Name))));
+  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
+                               BinASTInterfaceAndField::EagerSetter__Name)));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(
-                  FieldContext(BinASTInterfaceAndField::EagerSetter__Length))));
+      length, tokenizer_->readUnsignedLong(
+                  FieldContext(BinASTInterfaceAndField::EagerSetter__Length)));
 
   BINJS_MOZ_TRY_DECL(directives,
-                     parseListOfDirective(Context(FieldContext(
-                         BinASTInterfaceAndField::EagerSetter__Directives))));
+                     parseListOfDirective(FieldContext(
+                         BinASTInterfaceAndField::EagerSetter__Directives)));
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
                                       isAsync ? FunctionAsyncKind::AsyncFunction
                                               : FunctionAsyncKind::SyncFunction,
                                       syntax,
                                       (syntax != FunctionSyntaxKind::Setter &&
@@ -3087,85 +2856,72 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   MOZ_ASSERT(pc_->isFunctionBox());
 
   ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
   BINJS_TRY(lexicalScope.init(pc_));
   ListNode* params;
   ListNode* body;
   MOZ_TRY(parseSetterContents(
       length, &params, &body,
-      Context(FieldContext(BinASTInterfaceAndField::EagerSetter__Contents))));
+      FieldContext(BinASTInterfaceAndField::EagerSetter__Contents)));
   MOZ_TRY(prependDirectivesToBody(body, directives));
   uint32_t nargs = params->count();
 
   BINJS_TRY_DECL(lexicalScopeData,
                  NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
   BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
   BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, kind, funbox));
   MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
   BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, method, accessorType));
   MOZ_TRY(finishEagerFunction(funbox, nargs));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEmptyStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::EmptyStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
   BINJS_TRY_DECL(result, handler_.newEmptyStatement(tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceExpressionStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ExpressionStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       expression,
-      parseExpression(Context(FieldContext(
-          BinASTInterfaceAndField::ExpressionStatement__Expression))));
+      parseExpression(FieldContext(
+          BinASTInterfaceAndField::ExpressionStatement__Expression)));
 
   BINJS_TRY_DECL(result, handler_.newExprStatement(expression));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInOfBinding(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ForInOfBinding);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Kind,
-                                          BinASTField::Binding};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   AutoVariableDeclarationKind kindGuard(this);
 
   BINJS_MOZ_TRY_DECL(
-      kind_, parseVariableDeclarationKind(Context(
-                 FieldContext(BinASTInterfaceAndField::ForInOfBinding__Kind))));
+      kind_, parseVariableDeclarationKind(
+                 FieldContext(BinASTInterfaceAndField::ForInOfBinding__Kind)));
 
   BINJS_MOZ_TRY_DECL(binding,
-                     parseBinding(Context(FieldContext(
-                         BinASTInterfaceAndField::ForInOfBinding__Binding))));
+                     parseBinding(FieldContext(
+                         BinASTInterfaceAndField::ForInOfBinding__Binding)));
 
   // Restored by `kindGuard`.
   variableDeclarationKind_ = kind_;
   MOZ_TRY(
       checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
   ParseNodeKind pnk;
   switch (kind_) {
     case VariableDeclarationKind::Var:
@@ -3179,44 +2935,37 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_TRY_DECL(result,
                  handler_.newDeclarationList(pnk, tokenizer_->pos(start)));
   handler_.addList(result, binding);
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ForInStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {BinASTField::Left, BinASTField::Right,
-                                          BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::ForInLoop);
 
   // Implicit scope around the `for`, used to store `for (let x in  ...)`
   // or `for (const x in ...)`-style declarations. Detail on the
   // declaration is stored as part of `scope`.
   ParseContext::Scope scope(cx_, pc_, usedNames_);
   BINJS_TRY(scope.init(pc_));
 
-  BINJS_MOZ_TRY_DECL(
-      left, parseAssignmentTargetOrForInOfBinding(Context(
-                FieldContext(BinASTInterfaceAndField::ForInStatement__Left))));
-
-  BINJS_MOZ_TRY_DECL(right,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::ForInStatement__Right))));
+  BINJS_MOZ_TRY_DECL(left, parseAssignmentTargetOrForInOfBinding(FieldContext(
+                               BinASTInterfaceAndField::ForInStatement__Left)));
 
   BINJS_MOZ_TRY_DECL(
-      body, parseStatement(Context(
+      right, parseExpression(
+                 FieldContext(BinASTInterfaceAndField::ForInStatement__Right)));
+
+  BINJS_MOZ_TRY_DECL(
+      body, parseStatement(FieldOrListContext(
                 FieldContext(BinASTInterfaceAndField::ForInStatement__Body))));
 
   BINJS_TRY_DECL(forHead,
                  handler_.newForInOrOfHead(ParseNodeKind::ForIn, left, right,
                                            tokenizer_->pos(start)));
   ParseNode* result;
   BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                  /* iflags = */ 0));
@@ -3225,55 +2974,48 @@ JS::Result<ParseNode*> BinASTParser<Tok>
     BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, pc_));
     BINJS_TRY_VAR(result, handler_.newLexicalScope(*bindings, result));
   }
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForOfStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (ForOfStatement)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ForStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[4] = {BinASTField::Init, BinASTField::Test,
-                                          BinASTField::Update,
-                                          BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   ParseContext::Statement stmt(pc_, StatementKind::ForLoop);
 
   // Implicit scope around the `for`, used to store `for (let x; ...; ...)`
   // or `for (const x; ...; ...)`-style declarations. Detail on the
   // declaration is stored as part of `BINJS_Scope`.
   ParseContext::Scope scope(cx_, pc_, usedNames_);
   BINJS_TRY(scope.init(pc_));
 
   BINJS_MOZ_TRY_DECL(
-      init, parseOptionalExpressionOrVariableDeclaration(Context(
-                FieldContext(BinASTInterfaceAndField::ForStatement__Init))));
-
-  BINJS_MOZ_TRY_DECL(test, parseOptionalExpression(Context(FieldContext(
-                               BinASTInterfaceAndField::ForStatement__Test))));
-
-  BINJS_MOZ_TRY_DECL(update,
-                     parseOptionalExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::ForStatement__Update))));
-
-  BINJS_MOZ_TRY_DECL(body, parseStatement(Context(FieldContext(
+      init, parseOptionalExpressionOrVariableDeclaration(
+                FieldContext(BinASTInterfaceAndField::ForStatement__Init)));
+
+  BINJS_MOZ_TRY_DECL(test, parseOptionalExpression(FieldContext(
+                               BinASTInterfaceAndField::ForStatement__Test)));
+
+  BINJS_MOZ_TRY_DECL(
+      update, parseOptionalExpression(
+                  FieldContext(BinASTInterfaceAndField::ForStatement__Update)));
+
+  BINJS_MOZ_TRY_DECL(body, parseStatement(FieldOrListContext(FieldContext(
                                BinASTInterfaceAndField::ForStatement__Body))));
 
   BINJS_TRY_DECL(
       forHead, handler_.newForHead(init, test, update, tokenizer_->pos(start)));
   ParseNode* result;
   BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                  /* iflags = */ 0));
 
@@ -3287,53 +3029,46 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 /*
  interface FormalParameters : Node {
     FrozenArray<Parameter> items;
     Binding? rest;
  }
 */
 template <typename Tok>
 JS::Result<ListNode*> BinASTParser<Tok>::parseFormalParameters(
-    const Context& context) {
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::FormalParameters) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::FormalParameters)) {
     return raiseInvalidKind("FormalParameters", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceFormalParameters(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceFormalParameters(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<ListNode*> BinASTParser<Tok>::parseInterfaceFormalParameters(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::FormalParameters);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Items,
-                                          BinASTField::Rest};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(items,
-                     parseListOfParameter(Context(FieldContext(
-                         BinASTInterfaceAndField::FormalParameters__Items))));
-
-  BINJS_MOZ_TRY_DECL(rest,
-                     parseOptionalBinding(Context(FieldContext(
-                         BinASTInterfaceAndField::FormalParameters__Rest))));
+                     parseListOfParameter(FieldContext(
+                         BinASTInterfaceAndField::FormalParameters__Items)));
+
+  BINJS_MOZ_TRY_DECL(
+      rest, parseOptionalBinding(
+                FieldContext(BinASTInterfaceAndField::FormalParameters__Rest)));
 
   auto result = items;
   if (rest) {
     return raiseError(
         "Rest parameter is not supported in this preview release");
   }
   return result;
 }
@@ -3346,87 +3081,77 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     FormalParameters params;
     AssertedVarScope bodyScope;
     FunctionBody body;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseFunctionExpressionContents(
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
-    const Context& context) {
+    const FieldOrRootContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::FunctionExpressionContents) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::FunctionExpressionContents)) {
     return raiseInvalidKind("FunctionExpressionContents", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceFunctionExpressionContents(
-                  start, kind, fields, funLength, paramsOut, bodyOut, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceFunctionExpressionContents(
+                         start, kind, funLength, paramsOut, bodyOut, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionExpressionContents(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, uint32_t funLength,
+    ListNode** paramsOut, ListNode** bodyOut,
+    const FieldOrRootContext& context) {
   MOZ_ASSERT(kind == BinASTKind::FunctionExpressionContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[6] = {BinASTField::IsFunctionNameCaptured,
-                                          BinASTField::IsThisCaptured,
-                                          BinASTField::ParameterScope,
-                                          BinASTField::Params,
-                                          BinASTField::BodyScope,
-                                          BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       isFunctionNameCaptured,
-      tokenizer_->readBool(Context(FieldContext(
+      tokenizer_->readBool(FieldContext(
           BinASTInterfaceAndField::
-              FunctionExpressionContents__IsFunctionNameCaptured))));
+              FunctionExpressionContents__IsFunctionNameCaptured)));
   // Per spec, isFunctionNameCaptured can be true for anonymous
   // function.  Check isFunctionNameCaptured only for named
   // function.
   if (pc_->functionBox()->isNamedLambda() && isFunctionNameCaptured) {
     captureFunctionName();
   }
   BINJS_MOZ_TRY_DECL(isThisCaptured,
-                     tokenizer_->readBool(Context(FieldContext(
+                     tokenizer_->readBool(FieldContext(
                          BinASTInterfaceAndField::
-                             FunctionExpressionContents__IsThisCaptured))));
+                             FunctionExpressionContents__IsThisCaptured)));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
   MOZ_TRY(parseAssertedParameterScope(
       &positionalParams,
-      Context(FieldContext(BinASTInterfaceAndField::
-                               FunctionExpressionContents__ParameterScope))));
+      FieldContext(BinASTInterfaceAndField::
+                       FunctionExpressionContents__ParameterScope)));
 
   BINJS_MOZ_TRY_DECL(
       params,
-      parseFormalParameters(Context(FieldContext(
-          BinASTInterfaceAndField::FunctionExpressionContents__Params))));
+      parseFormalParameters(FieldContext(
+          BinASTInterfaceAndField::FunctionExpressionContents__Params)));
   MOZ_TRY(checkFunctionLength(funLength));
   MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
-  MOZ_TRY(parseAssertedVarScope(Context(FieldContext(
-      BinASTInterfaceAndField::FunctionExpressionContents__BodyScope))));
+  MOZ_TRY(parseAssertedVarScope(FieldContext(
+      BinASTInterfaceAndField::FunctionExpressionContents__BodyScope)));
 
   BINJS_MOZ_TRY_DECL(
-      body, parseFunctionBody(Context(FieldContext(
-                BinASTInterfaceAndField::FunctionExpressionContents__Body))));
+      body, parseFunctionBody(FieldContext(
+                BinASTInterfaceAndField::FunctionExpressionContents__Body)));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
@@ -3436,315 +3161,273 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
     FormalParameters params;
     AssertedVarScope bodyScope;
     FunctionBody body;
  }
 */
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseFunctionOrMethodContents(
     uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
-    const Context& context) {
+    const FieldOrRootContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::FunctionOrMethodContents) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::FunctionOrMethodContents)) {
     return raiseInvalidKind("FunctionOrMethodContents", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceFunctionOrMethodContents(
-                  start, kind, fields, funLength, paramsOut, bodyOut, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceFunctionOrMethodContents(
+                         start, kind, funLength, paramsOut, bodyOut, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionOrMethodContents(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, uint32_t funLength,
+    ListNode** paramsOut, ListNode** bodyOut,
+    const FieldOrRootContext& context) {
   MOZ_ASSERT(kind == BinASTKind::FunctionOrMethodContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[5] = {
-      BinASTField::IsThisCaptured, BinASTField::ParameterScope,
-      BinASTField::Params, BinASTField::BodyScope, BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       isThisCaptured,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::FunctionOrMethodContents__IsThisCaptured))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::FunctionOrMethodContents__IsThisCaptured)));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
   MOZ_TRY(parseAssertedParameterScope(
       &positionalParams,
-      Context(FieldContext(
-          BinASTInterfaceAndField::FunctionOrMethodContents__ParameterScope))));
+      FieldContext(
+          BinASTInterfaceAndField::FunctionOrMethodContents__ParameterScope)));
 
   BINJS_MOZ_TRY_DECL(
-      params, parseFormalParameters(Context(FieldContext(
-                  BinASTInterfaceAndField::FunctionOrMethodContents__Params))));
+      params, parseFormalParameters(FieldContext(
+                  BinASTInterfaceAndField::FunctionOrMethodContents__Params)));
   MOZ_TRY(checkFunctionLength(funLength));
   MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
-  MOZ_TRY(parseAssertedVarScope(Context(FieldContext(
-      BinASTInterfaceAndField::FunctionOrMethodContents__BodyScope))));
+  MOZ_TRY(parseAssertedVarScope(FieldContext(
+      BinASTInterfaceAndField::FunctionOrMethodContents__BodyScope)));
 
   BINJS_MOZ_TRY_DECL(
-      body, parseFunctionBody(Context(FieldContext(
-                BinASTInterfaceAndField::FunctionOrMethodContents__Body))));
+      body, parseFunctionBody(FieldContext(
+                BinASTInterfaceAndField::FunctionOrMethodContents__Body)));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
  interface GetterContents : Node {
     bool isThisCaptured;
     AssertedVarScope bodyScope;
     FunctionBody body;
  }
 */
 template <typename Tok>
-JS::Result<Ok> BinASTParser<Tok>::parseGetterContents(uint32_t funLength,
-                                                      ListNode** paramsOut,
-                                                      ListNode** bodyOut,
-                                                      const Context& context) {
+JS::Result<Ok> BinASTParser<Tok>::parseGetterContents(
+    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
+    const FieldContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::GetterContents) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::GetterContents)) {
     return raiseInvalidKind("GetterContents", kind);
   }
   const auto start = tokenizer_->offset();
   BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceGetterContents(start, kind, fields, funLength,
-                                           paramsOut, bodyOut, context));
+      result, parseInterfaceGetterContents(start, kind, funLength, paramsOut,
+                                           bodyOut, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<Ok> BinASTParser<Tok>::parseInterfaceGetterContents(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, uint32_t funLength,
+    ListNode** paramsOut, ListNode** bodyOut, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::GetterContents);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::IsThisCaptured, BinASTField::BodyScope, BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
       isThisCaptured,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::GetterContents__IsThisCaptured))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::GetterContents__IsThisCaptured)));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
-  MOZ_TRY(parseAssertedVarScope(Context(
-      FieldContext(BinASTInterfaceAndField::GetterContents__BodyScope))));
+  MOZ_TRY(parseAssertedVarScope(
+      FieldContext(BinASTInterfaceAndField::GetterContents__BodyScope)));
 
   BINJS_TRY_DECL(params, handler_.newParamsBody(tokenizer_->pos(start)));
-  BINJS_MOZ_TRY_DECL(
-      body, parseFunctionBody(Context(
-                FieldContext(BinASTInterfaceAndField::GetterContents__Body))));
+  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(FieldContext(
+                               BinASTInterfaceAndField::GetterContents__Body)));
 
   *paramsOut = params;
   *bodyOut = body;
   auto result = Ok();
   return result;
 }
 
 /*
  interface IdentifierExpression : Node {
     [IdentifierName] string name;
  }
 */
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseIdentifierExpression(
-    const Context& context) {
+    const FieldOrListContext& context) {
   BinASTKind kind;
-  BinASTFields fields(cx_);
   AutoTaggedTuple guard(*tokenizer_);
 
-  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, context, guard));
-  if (kind != BinASTKind::IdentifierExpression) {
+  guard.init();
+  MOZ_TRY(tokenizer_->enterTaggedTuple(kind, context));
+  if (MOZ_UNLIKELY(kind != BinASTKind::IdentifierExpression)) {
     return raiseInvalidKind("IdentifierExpression", kind);
   }
   const auto start = tokenizer_->offset();
-  BINJS_MOZ_TRY_DECL(
-      result, parseInterfaceIdentifierExpression(start, kind, fields, context));
+  BINJS_MOZ_TRY_DECL(result,
+                     parseInterfaceIdentifierExpression(start, kind, context));
   MOZ_TRY(guard.done());
 
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIdentifierExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::IdentifierExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Name};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom name(cx_);
-  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(Context(FieldContext(
-                        BinASTInterfaceAndField::IdentifierExpression__Name))));
+  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
+                        BinASTInterfaceAndField::IdentifierExpression__Name)));
 
   BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                                pc_->innermostScope()->id()));
   BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                           tokenizer_->pos(start), cx_));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIfStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::IfStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[3] = {
-      BinASTField::Test, BinASTField::Consequent, BinASTField::Alternate};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
-  BINJS_MOZ_TRY_DECL(test, parseExpression(Context(FieldContext(
-                               BinASTInterfaceAndField::IfStatement__Test))));
+  BINJS_MOZ_TRY_DECL(test, parseExpression(FieldContext(
+                               BinASTInterfaceAndField::IfStatement__Test)));
 
   BINJS_MOZ_TRY_DECL(consequent,
-                     parseStatement(Context(FieldContext(
+                     parseStatement(FieldOrListContext(FieldContext(
                          BinASTInterfaceAndField::IfStatement__Consequent))));
 
   BINJS_MOZ_TRY_DECL(alternate,
-                     parseOptionalStatement(Context(FieldContext(
-                         BinASTInterfaceAndField::IfStatement__Alternate))));
+                     parseOptionalStatement(FieldContext(
+                         BinASTInterfaceAndField::IfStatement__Alternate)));
 
   BINJS_TRY_DECL(result,
                  handler_.newIfStatement(start, test, consequent, alternate));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLabelledStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LabelledStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Label,
-                                          BinASTField::Body};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom label(cx_);
-  MOZ_TRY_VAR(label, tokenizer_->readAtom(Context(FieldContext(
-                         BinASTInterfaceAndField::LabelledStatement__Label))));
+  MOZ_TRY_VAR(label, tokenizer_->readAtom(FieldContext(
+                         BinASTInterfaceAndField::LabelledStatement__Label)));
   if (!IsIdentifier(label)) {
     return raiseError("Invalid identifier");
   }
   ParseContext::LabelStatement stmt(pc_, label);
   BINJS_MOZ_TRY_DECL(body,
-                     parseStatement(Context(FieldContext(
+                     parseStatement(FieldOrListContext(FieldContext(
                          BinASTInterfaceAndField::LabelledStatement__Body))));
 
   BINJS_TRY_DECL(result, handler_.newLabeledStatement(label->asPropertyName(),
                                                       body, start));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(LazyArrowExpressionWithExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithFunctionBody(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(LazyArrowExpressionWithFunctionBody)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionDeclaration(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LazyFunctionDeclaration);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[7] = {
-      BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
-      BinASTField::Length,  BinASTField::Directives,  BinASTField::ContentsSkip,
-      BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Statement;
 
   BINJS_MOZ_TRY_DECL(
-      isAsync,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionDeclaration__IsAsync))));
+      isAsync, tokenizer_->readBool(FieldContext(
+                   BinASTInterfaceAndField::LazyFunctionDeclaration__IsAsync)));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
       isGenerator,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionDeclaration__IsGenerator))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__IsGenerator)));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
-      name, parseBindingIdentifier(Context(FieldContext(
+      name, parseBindingIdentifier(FieldOrListContext(FieldContext(
                 BinASTInterfaceAndField::LazyFunctionDeclaration__Name))));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(FieldContext(
-                  BinASTInterfaceAndField::LazyFunctionDeclaration__Length))));
+      length, tokenizer_->readUnsignedLong(FieldContext(
+                  BinASTInterfaceAndField::LazyFunctionDeclaration__Length)));
 
   BINJS_MOZ_TRY_DECL(
       directives,
-      parseListOfDirective(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionDeclaration__Directives))));
+      parseListOfDirective(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__Directives)));
 
   BINJS_MOZ_TRY_DECL(
       contentsSkip,
-      tokenizer_->readSkippableSubTree(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionDeclaration__ContentsSkip))));
+      tokenizer_->readSkippableSubTree(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionDeclaration__ContentsSkip)));
   // Don't parse the contents until we delazify.
 
   // TODO: This will become incorrect in the face of ES6 features.
   uint32_t nargs = length;
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
@@ -3759,61 +3442,53 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   auto skipStart = contentsSkip.startOffset();
   auto skipEnd = skipStart + contentsSkip.length();
   MOZ_TRY(finishLazyFunction(funbox, nargs, skipStart, skipEnd));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LazyFunctionExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[7] = {
-      BinASTField::IsAsync, BinASTField::IsGenerator, BinASTField::Name,
-      BinASTField::Length,  BinASTField::Directives,  BinASTField::ContentsSkip,
-      BinASTField::Contents};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   const auto syntax = FunctionSyntaxKind::Expression;
 
   BINJS_MOZ_TRY_DECL(
-      isAsync, tokenizer_->readBool(Context(FieldContext(
-                   BinASTInterfaceAndField::LazyFunctionExpression__IsAsync))));
+      isAsync, tokenizer_->readBool(FieldContext(
+                   BinASTInterfaceAndField::LazyFunctionExpression__IsAsync)));
   if (isAsync) {
     return raiseError(
         "Async function is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
       isGenerator,
-      tokenizer_->readBool(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionExpression__IsGenerator))));
+      tokenizer_->readBool(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__IsGenerator)));
   if (isGenerator) {
     return raiseError("Generator is not supported in this preview release");
   }
   BINJS_MOZ_TRY_DECL(
-      name, parseOptionalBindingIdentifier(Context(FieldContext(
-                BinASTInterfaceAndField::LazyFunctionExpression__Name))));
+      name, parseOptionalBindingIdentifier(FieldContext(
+                BinASTInterfaceAndField::LazyFunctionExpression__Name)));
 
   BINJS_MOZ_TRY_DECL(
-      length, tokenizer_->readUnsignedLong(Context(FieldContext(
-                  BinASTInterfaceAndField::LazyFunctionExpression__Length))));
+      length, tokenizer_->readUnsignedLong(FieldContext(
+                  BinASTInterfaceAndField::LazyFunctionExpression__Length)));
 
   BINJS_MOZ_TRY_DECL(
       directives,
-      parseListOfDirective(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionExpression__Directives))));
+      parseListOfDirective(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__Directives)));
 
   BINJS_MOZ_TRY_DECL(
       contentsSkip,
-      tokenizer_->readSkippableSubTree(Context(FieldContext(
-          BinASTInterfaceAndField::LazyFunctionExpression__ContentsSkip))));
+      tokenizer_->readSkippableSubTree(FieldContext(
+          BinASTInterfaceAndField::LazyFunctionExpression__ContentsSkip)));
   // Don't parse the contents until we delazify.
 
   // TODO: This will become incorrect in the face of ES6 features.
   uint32_t nargs = length;
 
   BINJS_MOZ_TRY_DECL(funbox,
                      buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                   : GeneratorKind::NotGenerator,
@@ -3828,154 +3503,127 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   auto skipStart = contentsSkip.startOffset();
   auto skipEnd = skipStart + contentsSkip.length();
   MOZ_TRY(finishLazyFunction(funbox, nargs, skipStart, skipEnd));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyGetter(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (LazyGetter)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyMethod(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (LazyMethod)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazySetter(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const ListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (LazySetter)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralBooleanExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralBooleanExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Value};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      value, tokenizer_->readBool(Context(FieldContext(
-                 BinASTInterfaceAndField::LiteralBooleanExpression__Value))));
+      value, tokenizer_->readBool(FieldContext(
+                 BinASTInterfaceAndField::LiteralBooleanExpression__Value)));
 
   BINJS_TRY_DECL(result,
                  handler_.newBooleanLiteral(value, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralInfinityExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(LiteralInfinityExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralNullExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralNullExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
-  MOZ_TRY(tokenizer_->checkFields0(kind, fields));
 
   BINJS_TRY_DECL(result, handler_.newNullLiteral(tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceLiteralNumericExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralNumericExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Value};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      value, tokenizer_->readDouble(Context(FieldContext(
-                 BinASTInterfaceAndField::LiteralNumericExpression__Value))));
+      value, tokenizer_->readDouble(FieldContext(
+                 BinASTInterfaceAndField::LiteralNumericExpression__Value)));
 
   BINJS_TRY_DECL(result, handler_.newNumber(value, DecimalPoint::HasDecimal,
                                             tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralPropertyName(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralPropertyName);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Value};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom value(cx_);
-  MOZ_TRY_VAR(value,
-              tokenizer_->readAtom(Context(FieldContext(
-                  BinASTInterfaceAndField::LiteralPropertyName__Value))));
+  MOZ_TRY_VAR(value, tokenizer_->readAtom(FieldContext(
+                         BinASTInterfaceAndField::LiteralPropertyName__Value)));
 
   ParseNode* result;
   uint32_t index;
   if (value->isIndex(&index)) {
     BINJS_TRY_VAR(result,
                   handler_.newNumber(index, NoDecimal,
                                      TokenPos(start, tokenizer_->offset())));
   } else {
     BINJS_TRY_VAR(result, handler_.newObjectLiteralPropertyName(
                               value, tokenizer_->pos(start)));
   }
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralRegExpExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralRegExpExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Pattern,
-                                          BinASTField::Flags};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom pattern(cx_);
   MOZ_TRY_VAR(pattern,
-              tokenizer_->readAtom(Context(FieldContext(
-                  BinASTInterfaceAndField::LiteralRegExpExpression__Pattern))));
+              tokenizer_->readAtom(FieldContext(
+                  BinASTInterfaceAndField::LiteralRegExpExpression__Pattern)));
   RegExpFlags reflags = JS::RegExpFlag::NoFlags;
-  auto flagsContext = Context(
-      FieldContext(BinASTInterfaceAndField::LiteralRegExpExpression__Flags));
+  auto flagsContext =
+      FieldContext(BinASTInterfaceAndField::LiteralRegExpExpression__Flags);
   if (mozilla::IsSame<Tok, BinASTTokenReaderContext>::value) {
     // Hack: optimized `readChars` is not implemented for
     // `BinASTTokenReaderContext`.
     RootedAtom flags(cx_);
     MOZ_TRY_VAR(flags, tokenizer_->readAtom(flagsContext));
     if (!this->parseRegExpFlags(flags, &reflags)) {
       return raiseError("Invalid regexp flags");
     }
@@ -3993,167 +3641,137 @@ JS::Result<ParseNode*> BinASTParser<Tok>
 
   BINJS_TRY_DECL(result,
                  handler_.newRegExp(reobj, tokenizer_->pos(start), *this));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralStringExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::LiteralStringExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Value};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   RootedAtom value(cx_);
   MOZ_TRY_VAR(value,
-              tokenizer_->readAtom(Context(FieldContext(
-                  BinASTInterfaceAndField::LiteralStringExpression__Value))));
+              tokenizer_->readAtom(FieldContext(
+                  BinASTInterfaceAndField::LiteralStringExpression__Value)));
 
   BINJS_TRY_DECL(result,
                  handler_.newStringLiteral(value, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceModule(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const RootContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (Module)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceNewExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::NewExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[2] = {BinASTField::Callee,
-                                          BinASTField::Arguments};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(callee,
-                     parseExpression(Context(FieldContext(
-                         BinASTInterfaceAndField::NewExpression__Callee))));
+                     parseExpression(FieldContext(
+                         BinASTInterfaceAndField::NewExpression__Callee)));
 
   BINJS_MOZ_TRY_DECL(arguments,
-                     parseArguments(Context(FieldContext(
-                         BinASTInterfaceAndField::NewExpression__Arguments))));
+                     parseArguments(FieldContext(
+                         BinASTInterfaceAndField::NewExpression__Arguments)));
 
   BINJS_TRY_DECL(result,
                  handler_.newNewExpression(tokenizer_->pos(start).begin, callee,
                                            arguments, /* isSpread = */ false));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceNewTargetExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(NewTargetExpression)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectAssignmentTarget(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind, const FieldContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release "
       "(ObjectAssignmentTarget)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectBinding(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (ObjectBinding)");
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectExpression(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ObjectExpression);
   BINJS_TRY(CheckRecursionLimit(cx_));
 
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Properties};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
-
   BINJS_MOZ_TRY_DECL(
-      properties, parseListOfObjectProperty(Context(FieldContext(
-                      BinASTInterfaceAndField::ObjectExpression__Properties))));
+      properties, parseListOfObjectProperty(FieldContext(
+                      BinASTInterfaceAndField::ObjectExpression__Properties)));
 
   auto result = properties;
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceReturnStatement(
-    const size_t start, const BinASTKind kind, const BinASTFields& fields,
-    const Context& context) {
+    const size_t start, const BinASTKind kind,
+    const FieldOrListContext& context) {
   MOZ_ASSERT(kind == BinASTKind::ReturnStatement);
   BINJS_TRY(CheckRecursionLimit(cx_));
-
-#if defined(DEBUG)
-  const BinASTField expected_fields[1] = {BinASTField::Expression};
-  MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
-#endif  // defined(DEBUG)
   if (!pc_->isFunctionBox()) {
     // Return statements are permitted only inside functions.
     return raiseInvalidKind("Toplevel Statement", kind);
   }
 
   pc_->functionBox()->usesReturn = true;
 
   BINJS_MOZ_TRY_DECL(
-      expression, parseOptionalExpression(Context(FieldContext(
-                      BinASTInterfaceAndField::ReturnStatement__Expression))));
+      expression, parseOptionalExpression(FieldContext(
+                      BinASTInterfaceAndField::ReturnStatement__Expression)));
 
   BINJS_TRY_DECL(
       result, handler_.newReturnStatement(expression, tokenizer_->pos(start)));
   return result;
 }
 
 template <typename Tok>
 JS::Result<