Bug 1624829 - Use FunctionRef for various Enumerate* callbacks. r=edgar
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 26 Mar 2020 12:44:47 +0000
changeset 520545 18f5ed3410b9db480e5fe20d431ef29dd25056cd
parent 520544 9911fb33115594e5bd957b67f1d8b0219efaae9f
child 520546 6b86d0dfff95a0a836ca60cf1691deab8c9d056f
push id37253
push usernerli@mozilla.com
push dateThu, 26 Mar 2020 21:36:52 +0000
treeherdermozilla-central@c644dd16e2cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedgar
bugs1624829
milestone76.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1624829 - Use FunctionRef for various Enumerate* callbacks. r=edgar This avoids a bunch of ugly casts and void pointers, without much overhead (unlike std::function or such). Differential Revision: https://phabricator.services.mozilla.com/D68182
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsGlobalWindowOuter.cpp
layout/base/PresShell.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsRefreshDriver.cpp
layout/generic/nsPluginFrame.cpp
layout/generic/nsPluginFrame.h
layout/generic/nsSubDocumentFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/nsDisplayList.cpp
layout/printing/nsPrintJob.cpp
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -755,27 +755,26 @@ Document* ExternalResourceMap::RequestRe
     AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
   } else {
     load.forget(aPendingLoad);
   }
 
   return nullptr;
 }
 
-void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback,
-                                             void* aData) {
+void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback) {
   nsTArray<RefPtr<Document>> docs(mMap.Count());
   for (const auto& entry : mMap) {
     if (Document* doc = entry.GetData()->mDocument) {
       docs.AppendElement(doc);
     }
   }
 
   for (auto& doc : docs) {
-    if (aCallback(*doc, aData) == CallState::Stop) {
+    if (aCallback(*doc) == CallState::Stop) {
       return;
     }
   }
 }
 
 void ExternalResourceMap::Traverse(
     nsCycleCollectionTraversalCallback* aCallback) const {
   // mPendingLoads will get cleared out as the requests complete, so
@@ -6825,54 +6824,50 @@ void Document::SetScopeObject(nsIGlobalO
 
       MOZ_ASSERT_IF(
           mNodeInfoManager->GetArenaAllocator(),
           mNodeInfoManager->GetArenaAllocator() == mDocGroup->ArenaAllocator());
     }
   }
 }
 
-static void CheckIfContainsEMEContent(nsISupports* aSupports,
-                                      void* aContainsEME) {
-  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
-  if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
-    bool* contains = static_cast<bool*>(aContainsEME);
-    if (mediaElem->GetMediaKeys()) {
-      *contains = true;
-    }
-  }
-}
-
 bool Document::ContainsEMEContent() {
   bool containsEME = false;
-  EnumerateActivityObservers(CheckIfContainsEMEContent,
-                             static_cast<void*>(&containsEME));
+
+  auto check = [&containsEME](nsISupports* aSupports) {
+    nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
+    if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
+      if (mediaElem->GetMediaKeys()) {
+        containsEME = true;
+      }
+    }
+  };
+
+  EnumerateActivityObservers(check);
   return containsEME;
 }
 
-static void CheckIfContainsMSEContent(nsISupports* aSupports,
-                                      void* aContainsMSE) {
-  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
-  if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
-    bool* contains = static_cast<bool*>(aContainsMSE);
-    RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
-    if (ms) {
-      *contains = true;
-    }
-  }
-}
-
 bool Document::ContainsMSEContent() {
   bool containsMSE = false;
-  EnumerateActivityObservers(CheckIfContainsMSEContent,
-                             static_cast<void*>(&containsMSE));
+
+  auto check = [&containsMSE](nsISupports* aSupports) {
+    nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
+    if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
+      RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
+      if (ms) {
+        containsMSE = true;
+      }
+    }
+  };
+
+  EnumerateActivityObservers(check);
   return containsMSE;
 }
 
-static void NotifyActivityChanged(nsISupports* aSupports, void* aUnused) {
+static void NotifyActivityChanged(nsISupports* aSupports) {
   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
     mediaElem->NotifyOwnerDocumentActivityChanged();
   }
   nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(
       do_QueryInterface(aSupports));
   if (objectLoadingContent) {
     nsObjectLoadingContent* olc =
@@ -6914,17 +6909,17 @@ void Document::SetContainer(nsDocShell* 
     mDocumentContainer = aContainer;
   } else {
     mDocumentContainer = WeakPtr<nsDocShell>();
   }
 
   mInChromeDocShell =
       aContainer && aContainer->GetBrowsingContext()->IsChrome();
 
-  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
+  EnumerateActivityObservers(NotifyActivityChanged);
 
   // IsTopLevelWindowInactive depends on the docshell, so
   // update the cached value now that it's available.
   UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, false);
   if (!aContainer) {
     return;
   }
 
@@ -7059,17 +7054,17 @@ void Document::SetScriptGlobalObject(
   // MediaDecoder decides whether starting decoding is decided based on
   // document's visibility. When the MediaDecoder is created,
   // Document::SetScriptGlobalObject() is not yet called and document is
   // hidden state. Therefore the MediaDecoder decides that decoding is
   // not yet necessary. But soon after Document::SetScriptGlobalObject()
   // call, the document becomes not hidden. At the time, MediaDecoder needs
   // to know it and needs to start updating decoding.
   if (oldState != mVisibilityState) {
-    EnumerateActivityObservers(NotifyActivityChanged, nullptr);
+    EnumerateActivityObservers(NotifyActivityChanged);
   }
 
   // The global in the template contents owner document should be the same.
   if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
     mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
   }
 
   if (!mMaybeServiceWorkerControlled && mDocumentContainer &&
@@ -8624,19 +8619,18 @@ Document* Document::RequestExternalResou
     return mDisplayDocument->RequestExternalResource(
         aURI, aReferrerInfo, aRequestingNode, aPendingLoad);
   }
 
   return mExternalResourceMap.RequestResource(
       aURI, aReferrerInfo, aRequestingNode, this, aPendingLoad);
 }
 
-void Document::EnumerateExternalResources(SubDocEnumFunc aCallback,
-                                          void* aData) {
-  mExternalResourceMap.EnumerateResources(aCallback, aData);
+void Document::EnumerateExternalResources(SubDocEnumFunc aCallback) {
+  mExternalResourceMap.EnumerateResources(aCallback);
 }
 
 SMILAnimationController* Document::GetAnimationController() {
   // We create the animation controller lazily because most documents won't want
   // one and only SVG documents and the like will call this
   if (mAnimationController) return mAnimationController;
   // Refuse to create an Animation Controller for data documents.
   if (mLoadedAsData) return nullptr;
@@ -10091,22 +10085,22 @@ void Document::FlushPendingNotifications
 void Document::FlushExternalResources(FlushType aType) {
   NS_ASSERTION(
       aType >= FlushType::Style,
       "should only need to flush for style or higher in external resources");
   if (GetDisplayDocument()) {
     return;
   }
 
-  EnumerateExternalResources(
-      [](Document& aDoc, void* aData) -> CallState {
-        aDoc.FlushPendingNotifications(*static_cast<FlushType*>(aData));
-        return CallState::Continue;
-      },
-      &aType);
+  auto flush = [aType](Document& aDoc) {
+    aDoc.FlushPendingNotifications(aType);
+    return CallState::Continue;
+  };
+
+  EnumerateExternalResources(flush);
 }
 
 void Document::SetXMLDeclaration(const char16_t* aVersion,
                                  const char16_t* aEncoding,
                                  const int32_t aStandalone) {
   if (!aVersion || *aVersion == '\0') {
     mXMLDeclarationBits = 0;
     return;
@@ -10321,32 +10315,32 @@ void Document::Sanitize() {
     HTMLFormElement* form = HTMLFormElement::FromNode(nodes->Item(i));
     if (!form) continue;
 
     form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value);
     if (value.LowerCaseEqualsLiteral("off")) form->Reset();
   }
 }
 
-void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback, void* aData) {
+void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback) {
   if (!mSubDocuments) {
     return;
   }
 
   // PLDHashTable::Iterator can't handle modifications while iterating so we
   // copy all entries to an array first before calling any callbacks.
   AutoTArray<RefPtr<Document>, 8> subdocs;
   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
     if (Document* subdoc = entry->mSubDocument) {
       subdocs.AppendElement(subdoc);
     }
   }
   for (auto& subdoc : subdocs) {
-    if (aCallback(*subdoc, aData) == CallState::Stop) {
+    if (aCallback(*subdoc) == CallState::Stop) {
       break;
     }
   }
 }
 
 void Document::CollectDescendantDocuments(
     nsTArray<RefPtr<Document>>& aDescendants, nsDocTestFunc aCallback) const {
   if (!mSubDocuments) {
@@ -10592,17 +10586,17 @@ void Document::Destroy() {
 }
 
 void Document::RemovedFromDocShell() {
   mEditingState = EditingState::eOff;
 
   if (mRemovedFromDocShell) return;
 
   mRemovedFromDocShell = true;
-  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
+  EnumerateActivityObservers(NotifyActivityChanged);
 
   for (nsIContent* child = GetFirstChild(); child;
        child = child->GetNextSibling()) {
     child->SaveSubtreeState();
   }
 
   nsIDocShell* docShell = GetDocShell();
   if (docShell) {
@@ -10818,22 +10812,16 @@ void Document::DispatchPageTransition(Ev
   event->SetTarget(this);
   if (aOnlySystemGroup) {
     event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent = true;
   }
   EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, nullptr,
                                     nullptr);
 }
 
-static CallState NotifyPageShow(Document& aDocument, void* aData) {
-  const bool* aPersistedPtr = static_cast<const bool*>(aData);
-  aDocument.OnPageShow(*aPersistedPtr, nullptr);
-  return CallState::Continue;
-}
-
 void Document::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget,
                           bool aOnlySystemGroup) {
   const bool inFrameLoaderSwap = !!aDispatchStartTarget;
   MOZ_DIAGNOSTIC_ASSERT(
       inFrameLoaderSwap ==
       (mDocumentContainer && mDocumentContainer->InFrameSwap()));
 
   Element* root = GetRootElement();
@@ -10857,18 +10845,23 @@ void Document::OnPageShow(bool aPersiste
     // Set mIsShowing before firing events, in case those event handlers
     // move us around.
     mIsShowing = true;
     mVisible = true;
 
     UpdateVisibilityState();
   }
 
-  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
-  EnumerateExternalResources(NotifyPageShow, &aPersisted);
+  EnumerateActivityObservers(NotifyActivityChanged);
+
+  auto notifyExternal = [aPersisted](Document& aExternalResource) {
+    aExternalResource.OnPageShow(aPersisted, nullptr);
+    return CallState::Continue;
+  };
+  EnumerateExternalResources(notifyExternal);
 
   if (mAnimationController) {
     mAnimationController->OnPageShow();
   }
 
   if (!mIsBeingUsedAsImage) {
     // Dispatch observer notification to notify observers page is shown.
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@@ -10884,22 +10877,16 @@ void Document::OnPageShow(bool aPersiste
     if (!target) {
       target = do_QueryInterface(GetWindow());
     }
     DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"),
                            inFrameLoaderSwap, aPersisted, aOnlySystemGroup);
   }
 }
 
-static CallState NotifyPageHide(Document& aDocument, void* aData) {
-  const bool* aPersistedPtr = static_cast<const bool*>(aData);
-  aDocument.OnPageHide(*aPersistedPtr, nullptr);
-  return CallState::Continue;
-}
-
 static void DispatchFullscreenChange(Document& aDocument, nsINode* aTarget) {
   if (nsPresContext* presContext = aDocument.GetPresContext()) {
     auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
         FullscreenEventType::Change, &aDocument, aTarget);
     presContext->RefreshDriver()->ScheduleFullscreenEvent(
         std::move(pendingEvent));
   }
 }
@@ -10968,18 +10955,22 @@ void Document::OnPageHide(bool aPersiste
                              inFrameLoaderSwap, aPersisted, aOnlySystemGroup);
     }
   }
 
   if (!inFrameLoaderSwap) {
     UpdateVisibilityState();
   }
 
-  EnumerateExternalResources(NotifyPageHide, &aPersisted);
-  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
+  auto notifyExternal = [aPersisted](Document& aExternalResource) {
+    aExternalResource.OnPageHide(aPersisted, nullptr);
+    return CallState::Continue;
+  };
+  EnumerateExternalResources(notifyExternal);
+  EnumerateActivityObservers(NotifyActivityChanged);
 
   ClearPendingFullscreenRequests(this);
   if (FullscreenStackTop()) {
     // If this document was fullscreen, we should exit fullscreen in this
     // doctree branch. This ensures that if the user navigates while in
     // fullscreen mode we don't leave its still visible ancestor documents
     // in fullscreen mode. So exit fullscreen in the document's fullscreen
     // root document, as this will exit fullscreen in all the root's
@@ -11300,30 +11291,29 @@ void Document::GetReadyState(nsAString& 
     case READYSTATE_COMPLETE:
       aReadyState.AssignLiteral(u"complete");
       break;
     default:
       aReadyState.AssignLiteral(u"uninitialized");
   }
 }
 
-static CallState SuppressEventHandlingInDocument(Document& aDocument,
-                                                 void* aData) {
-  aDocument.SuppressEventHandling(*static_cast<uint32_t*>(aData));
-  return CallState::Continue;
-}
-
 void Document::SuppressEventHandling(uint32_t aIncrease) {
   mEventsSuppressed += aIncrease;
   UpdateFrameRequestCallbackSchedulingState();
   for (uint32_t i = 0; i < aIncrease; ++i) {
     ScriptLoader()->AddExecuteBlocker();
   }
 
-  EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
+  auto suppressInSubDoc = [aIncrease](Document& aSubDoc) {
+    aSubDoc.SuppressEventHandling(aIncrease);
+    return CallState::Continue;
+  };
+
+  EnumerateSubDocuments(suppressInSubDoc);
 }
 
 static void FireOrClearDelayedEvents(nsTArray<nsCOMPtr<Document>>& aDocuments,
                                      bool aFireEvents) {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm) return;
 
   for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
@@ -11623,33 +11613,32 @@ class nsDelayedEventDispatcher : public 
     FireOrClearDelayedEvents(mDocuments, true);
     return NS_OK;
   }
 
  private:
   nsTArray<nsCOMPtr<Document>> mDocuments;
 };
 
-static CallState GetAndUnsuppressSubDocuments(Document& aDocument,
-                                              void* aData) {
+static void GetAndUnsuppressSubDocuments(Document& aDocument, nsTArray<nsCOMPtr<Document>>& aDocuments) {
   if (aDocument.EventHandlingSuppressed() > 0) {
     aDocument.DecreaseEventSuppression();
     aDocument.ScriptLoader()->RemoveExecuteBlocker();
   }
-
-  auto* docs = static_cast<nsTArray<nsCOMPtr<Document>>*>(aData);
-
-  docs->AppendElement(&aDocument);
-  aDocument.EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
-  return CallState::Continue;
+  aDocuments.AppendElement(&aDocument);
+  auto recurse = [&aDocuments](Document& aSubDoc) {
+    GetAndUnsuppressSubDocuments(aSubDoc, aDocuments);
+    return CallState::Continue;
+  };
+  aDocument.EnumerateSubDocuments(recurse);
 }
 
 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
   nsTArray<nsCOMPtr<Document>> documents;
-  GetAndUnsuppressSubDocuments(*this, &documents);
+  GetAndUnsuppressSubDocuments(*this, documents);
 
   if (aFireEvents) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
     Dispatch(TaskCategory::Other, ded.forget());
   } else {
     FireOrClearDelayedEvents(documents, false);
   }
@@ -11700,23 +11689,21 @@ void Document::FireOrClearPostMessageEve
     for (PostMessageEvent* event : events) {
       event->Run();
     }
   }
 }
 
 void Document::SetSuppressedEventListener(EventListener* aListener) {
   mSuppressedEventListener = aListener;
-  EnumerateSubDocuments(
-      [](Document& aDocument, void* aData) {
-        aDocument.SetSuppressedEventListener(
-            static_cast<EventListener*>(aData));
-        return CallState::Continue;
-      },
-      aListener);
+  auto setOnSubDocs = [&](Document& aDocument) {
+    aDocument.SetSuppressedEventListener(aListener);
+    return CallState::Continue;
+  };
+  EnumerateSubDocuments(setOnSubDocs);
 }
 
 nsISupports* Document::GetCurrentContentSink() {
   return mParser ? mParser->GetContentSink() : nullptr;
 }
 
 Document* Document::GetTemplateContentsOwner() {
   if (!mTemplateContentsOwner) {
@@ -11965,28 +11952,28 @@ bool Document::UnregisterActivityObserve
   if (!entry) {
     return false;
   }
   mActivityObservers->RemoveEntry(entry);
   return true;
 }
 
 void Document::EnumerateActivityObservers(
-    ActivityObserverEnumerator aEnumerator, void* aData) {
+    ActivityObserverEnumerator aEnumerator) {
   if (!mActivityObservers) {
     return;
   }
 
   nsTArray<nsCOMPtr<nsISupports>> observers(mActivityObservers->Count());
   for (auto iter = mActivityObservers->ConstIter(); !iter.Done(); iter.Next()) {
     observers.AppendElement(iter.Get()->GetKey());
   }
 
   for (auto& observer : observers) {
-    aEnumerator(observer.get(), aData);
+    aEnumerator(observer.get());
   }
 }
 
 void Document::RegisterPendingLinkUpdate(Link* aLink) {
   if (aLink->HasPendingLinkUpdate()) {
     return;
   }
 
@@ -12257,30 +12244,26 @@ void Document::WarnOnceAbout(
 
 mozilla::dom::ImageTracker* Document::ImageTracker() {
   if (!mImageTracker) {
     mImageTracker = new mozilla::dom::ImageTracker;
   }
   return mImageTracker;
 }
 
-static CallState AllSubDocumentPluginEnum(Document& aDocument, void* userArg) {
-  nsTArray<nsIObjectLoadingContent*>* plugins =
-      reinterpret_cast<nsTArray<nsIObjectLoadingContent*>*>(userArg);
-  MOZ_ASSERT(plugins);
-  aDocument.GetPlugins(*plugins);
-  return CallState::Continue;
-}
-
 void Document::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) {
   aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
   for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) {
     aPlugins.AppendElement(iter.Get()->GetKey());
   }
-  EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
+  auto recurse = [&aPlugins] (Document& aSubDoc) {
+    aSubDoc.GetPlugins(aPlugins);
+    return CallState::Continue;
+  };
+  EnumerateSubDocuments(recurse);
 }
 
 void Document::ScheduleSVGUseElementShadowTreeUpdate(
     SVGUseElement& aUseElement) {
   MOZ_ASSERT(aUseElement.IsInComposedDoc());
 
   mSVGUseElementsNeedingShadowTreeUpdate.PutEntry(&aUseElement);
 
@@ -13058,82 +13041,72 @@ void Document::AsyncExitFullscreen(Docum
   nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
   if (aDoc) {
     aDoc->Dispatch(TaskCategory::Other, exit.forget());
   } else {
     NS_DispatchToCurrentThread(exit.forget());
   }
 }
 
-static CallState CountFullscreenSubDocuments(Document& aDoc, void* aData) {
-  if (aDoc.FullscreenStackTop()) {
-    uint32_t* count = static_cast<uint32_t*>(aData);
-    (*count)++;
-  }
-  return CallState::Continue;
-}
-
 static uint32_t CountFullscreenSubDocuments(Document& aDoc) {
   uint32_t count = 0;
-  aDoc.EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
+  // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
+  auto subDoc = [&count](Document& aSubDoc) {
+    if (aSubDoc.FullscreenStackTop()) {
+      count++;
+    }
+    return CallState::Continue;
+  };
+  aDoc.EnumerateSubDocuments(subDoc);
   return count;
 }
 
 bool Document::IsFullscreenLeaf() {
   // A fullscreen leaf document is fullscreen, and has no fullscreen
   // subdocuments.
   if (!FullscreenStackTop()) {
     return false;
   }
   return CountFullscreenSubDocuments(*this) == 0;
 }
 
-static bool GetFullscreenLeaf(Document& aDoc, void* aData) {
+static Document* GetFullscreenLeaf(Document& aDoc) {
   if (aDoc.IsFullscreenLeaf()) {
-    Document** result = static_cast<Document**>(aData);
-    *result = &aDoc;
-    return false;
-  }
-  if (aDoc.FullscreenStackTop()) {
-    aDoc.EnumerateSubDocuments(
-        [](Document& aDocument, void* aData) {
-          return GetFullscreenLeaf(aDocument, aData) ? CallState::Continue
-                                                     : CallState::Stop;
-        },
-        aData);
-  }
-  return true;
+    return &aDoc;
+  }
+  if (!aDoc.FullscreenStackTop()) {
+    return nullptr;
+  }
+  Document* leaf = nullptr;
+  auto recurse = [&leaf](Document& aSubDoc) {
+    leaf = GetFullscreenLeaf(aSubDoc);
+    return leaf ? CallState::Stop : CallState::Continue;
+  };
+  aDoc.EnumerateSubDocuments(recurse);
+  return leaf;
 }
 
 static Document* GetFullscreenLeaf(Document* aDoc) {
-  Document* leaf = nullptr;
-  GetFullscreenLeaf(*aDoc, &leaf);
-  if (leaf) {
+  if (Document* leaf = GetFullscreenLeaf(*aDoc)) {
     return leaf;
   }
   // Otherwise we could be either in a non-fullscreen doc tree, or we're
   // below the fullscreen doc. Start the search from the root.
   Document* root = nsContentUtils::GetRootDocument(aDoc);
-  // Check that the root is actually fullscreen so we don't waste time walking
-  // around its descendants.
-  if (!root->FullscreenStackTop()) {
-    return nullptr;
-  }
-  GetFullscreenLeaf(*root, &leaf);
-  return leaf;
-}
-
-static CallState ResetFullscreen(Document& aDocument, void* aData) {
+  return GetFullscreenLeaf(*root);
+}
+
+static CallState ResetFullscreen(Document& aDocument) {
   if (Element* fsElement = aDocument.FullscreenStackTop()) {
     NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
                  "Should have at most 1 fullscreen subdocument.");
     aDocument.CleanupFullscreenState();
     NS_ASSERTION(!aDocument.FullscreenStackTop(), "Should reset fullscreen");
     DispatchFullscreenChange(aDocument, fsElement);
-    aDocument.EnumerateSubDocuments(ResetFullscreen, nullptr);
+    aDocument.EnumerateSubDocuments(ResetFullscreen);
   }
   return CallState::Continue;
 }
 
 // Since Document::ExitFullscreenInDocTree() could be called from
 // Element::UnbindFromTree() where it is not safe to synchronously run
 // script. This runnable is the script part of that function.
 class ExitFullscreenScriptRunnable : public Runnable {
@@ -13193,17 +13166,17 @@ void Document::ExitFullscreenInDocTree(D
 
   // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
   // See ExitFullscreenScriptRunnable::Run for details. We have to
   // record it here because we don't have such information after we
   // reset the fullscreen state below.
   Document* fullscreenLeaf = GetFullscreenLeaf(root);
 
   // Walk the tree of fullscreen documents, and reset their fullscreen state.
-  ResetFullscreen(*root, nullptr);
+  ResetFullscreen(*root);
 
   NS_ASSERTION(!root->FullscreenStackTop(),
                "Fullscreen root should no longer be a fullscreen doc...");
 
   // Move the top-level window out of fullscreen mode.
   FullscreenRoots::Remove(root);
 
   nsContentUtils::AddScriptRunner(
@@ -14118,17 +14091,17 @@ void Document::UnlockPointer(Document* a
 
 void Document::UpdateVisibilityState() {
   dom::VisibilityState oldState = mVisibilityState;
   mVisibilityState = ComputeVisibilityState();
   if (oldState != mVisibilityState) {
     nsContentUtils::DispatchTrustedEvent(this, ToSupports(this),
                                          NS_LITERAL_STRING("visibilitychange"),
                                          CanBubble::eYes, Cancelable::eNo);
-    EnumerateActivityObservers(NotifyActivityChanged, nullptr);
+    EnumerateActivityObservers(NotifyActivityChanged);
   }
 
   if (mVisibilityState == dom::VisibilityState::Visible) {
     MaybeActiveMediaComponents();
   }
 }
 
 VisibilityState Document::ComputeVisibilityState() const {
@@ -14590,22 +14563,20 @@ void Document::ReportUseCounters() {
       for (auto iter = mSubDocuments->ConstIter(); !iter.Done(); iter.Next()) {
         auto* entry = static_cast<SubDocMapEntry*>(iter.Get());
         entry->mSubDocument->ReportUseCounters();
       }
     }
     if (Document* doc = GetLatestStaticClone()) {
       doc->ReportUseCounters();
     }
-    EnumerateExternalResources(
-        [](Document& aDoc, void*) -> CallState {
-          aDoc.ReportUseCounters();
-          return CallState::Continue;
-        },
-        nullptr);
+    EnumerateExternalResources([](Document& aDoc) {
+      aDoc.ReportUseCounters();
+      return CallState::Continue;
+    });
   }
 
   PropagateUseCountersToPage();
 
   if (Telemetry::HistogramUseCounterCount > 0 &&
       (IsContentDocument() || IsResourceDoc())) {
     nsCOMPtr<nsIURI> uri;
     NodePrincipal()->GetURI(getter_AddRefs(uri));
@@ -14765,25 +14736,22 @@ void Document::NotifyIntersectionObserve
 DOMIntersectionObserver& Document::EnsureLazyLoadImageObserver() {
   if (!mLazyLoadImageObserver) {
     mLazyLoadImageObserver =
         DOMIntersectionObserver::CreateLazyLoadObserver(*this);
   }
   return *mLazyLoadImageObserver;
 }
 
-static CallState NotifyLayerManagerRecreatedCallback(Document& aDocument,
-                                                     void*) {
-  aDocument.NotifyLayerManagerRecreated();
-  return CallState::Continue;
-}
-
 void Document::NotifyLayerManagerRecreated() {
-  EnumerateActivityObservers(NotifyActivityChanged, nullptr);
-  EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr);
+  EnumerateActivityObservers(NotifyActivityChanged);
+  EnumerateSubDocuments([] (Document& aSubDoc) {
+    aSubDoc.NotifyLayerManagerRecreated();
+    return CallState::Continue;
+  });
 }
 
 XPathEvaluator* Document::XPathEvaluator() {
   if (!mXPathEvaluator) {
     mXPathEvaluator.reset(new dom::XPathEvaluator(this));
   }
   return mXPathEvaluator.get();
 }
@@ -14858,40 +14826,42 @@ already_AddRefed<Element> Document::Crea
   DebugOnly<nsresult> rv =
       NS_NewHTMLElement(getter_AddRefs(element), nodeInfo.forget(),
                         mozilla::dom::NOT_FROM_PARSER);
 
   MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
   return element.forget();
 }
 
-static CallState MarkDocumentTreeToBeInSyncOperation(Document& aDoc,
-                                                     void* aData) {
-  auto* documents = static_cast<nsTArray<nsCOMPtr<Document>>*>(aData);
+static CallState MarkDocumentTreeToBeInSyncOperation(
+    Document& aDoc, nsTArray<RefPtr<Document>>& aDocuments) {
   aDoc.SetIsInSyncOperation(true);
   if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc.GetInnerWindow()) {
     window->TimeoutManager().BeginSyncOperation();
   }
-  documents->AppendElement(&aDoc);
-  aDoc.EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
+  aDocuments.AppendElement(&aDoc);
+  auto recurse = [&aDocuments](Document& aSubDoc) {
+    return MarkDocumentTreeToBeInSyncOperation(aSubDoc, aDocuments);
+  };
+  aDoc.EnumerateSubDocuments(recurse);
   return CallState::Continue;
 }
 
 nsAutoSyncOperation::nsAutoSyncOperation(Document* aDoc) {
   mMicroTaskLevel = 0;
   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
   if (ccjs) {
     mMicroTaskLevel = ccjs->MicroTaskLevel();
     ccjs->SetMicroTaskLevel(0);
   }
   if (aDoc) {
     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
       if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetInProcessTop()) {
         if (RefPtr<Document> doc = top->GetExtantDoc()) {
-          MarkDocumentTreeToBeInSyncOperation(*doc, &mDocuments);
+          MarkDocumentTreeToBeInSyncOperation(*doc, mDocuments);
         }
       }
     }
   }
 }
 
 nsAutoSyncOperation::~nsAutoSyncOperation() {
   for (RefPtr<Document>& doc : mDocuments) {
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -4,17 +4,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/. */
 #ifndef mozilla_dom_Document_h___
 #define mozilla_dom_Document_h___
 
 #include "mozilla/EventStates.h"  // for EventStates
 #include "mozilla/FlushType.h"    // for enum
 #include "mozilla/MozPromise.h"   // for MozPromise
-#include "mozilla/Saturate.h"     // for SaturateUint32
+#include "mozilla/FunctionRef.h"  // for FunctionRef
 #include "nsAutoPtr.h"            // for member
 #include "nsCOMArray.h"           // for member
 #include "nsCompatibility.h"      // for member
 #include "nsCOMPtr.h"             // for member
 #include "nsICookieJarSettings.h"
 #include "nsGkAtoms.h"           // for static class members
 #include "nsNameSpaceManager.h"  // for static class members
 #include "nsIApplicationCache.h"
@@ -291,17 +291,17 @@ class DocHeaderData {
   ~DocHeaderData(void) { delete mNext; }
 
   RefPtr<nsAtom> mField;
   nsString mData;
   DocHeaderData* mNext;
 };
 
 class ExternalResourceMap {
-  typedef CallState (*SubDocEnumFunc)(Document& aDocument, void* aData);
+  using SubDocEnumFunc = FunctionRef<CallState(Document&)>;
 
  public:
   /**
    * A class that represents an external resource load that has begun but
    * doesn't have a document yet.  Observers can be registered on this object,
    * and will be notified after the document is created.  Observers registered
    * after the document has been created will NOT be notified.  When observers
    * are notified, the subject will be the newly-created document, the topic
@@ -334,17 +334,17 @@ class ExternalResourceMap {
                             nsINode* aRequestingNode,
                             Document* aDisplayDocument,
                             ExternalResourceLoad** aPendingLoad);
 
   /**
    * Enumerate the resource documents.  See
    * Document::EnumerateExternalResources.
    */
-  void EnumerateResources(SubDocEnumFunc aCallback, void* aData);
+  void EnumerateResources(SubDocEnumFunc aCallback);
 
   /**
    * Traverse ourselves for cycle-collection
    */
   void Traverse(nsCycleCollectionTraversalCallback* aCallback) const;
 
   /**
    * Shut ourselves down (used for cycle-collection unlink), as well
@@ -2271,18 +2271,18 @@ class Document : public nsINode,
   void Sanitize();
 
   /**
    * Enumerate all subdocuments.
    * The enumerator callback should return CallState::Continue to continue
    * enumerating, or CallState::Stop to stop.  This will never get passed a null
    * aDocument.
    */
-  typedef CallState (*SubDocEnumFunc)(Document&, void* aData);
-  void EnumerateSubDocuments(SubDocEnumFunc aCallback, void* aData);
+  using SubDocEnumFunc = FunctionRef<CallState(Document&)>;
+  void EnumerateSubDocuments(SubDocEnumFunc aCallback);
 
   /**
    * Collect all the descendant documents for which |aCalback| returns true.
    * The callback function must not mutate any state for the given document.
    */
   typedef bool (*nsDocTestFunc)(const Document* aDocument);
   void CollectDescendantDocuments(nsTArray<RefPtr<Document>>& aDescendants,
                                   nsDocTestFunc aCallback) const;
@@ -2585,17 +2585,17 @@ class Document : public nsINode,
                                     ExternalResourceLoad** aPendingLoad);
 
   /**
    * Enumerate the external resource documents associated with this document.
    * The enumerator callback should return CallState::Continue to continue
    * enumerating, or CallState::Stop to stop.  This callback will never get
    * passed a null aDocument.
    */
-  void EnumerateExternalResources(SubDocEnumFunc aCallback, void* aData);
+  void EnumerateExternalResources(SubDocEnumFunc aCallback);
 
   dom::ExternalResourceMap& ExternalResourceMap() {
     return mExternalResourceMap;
   }
 
   /**
    * Return whether the document is currently showing (in the sense of
    * OnPageShow() having been called already and OnPageHide() not having been
@@ -2653,19 +2653,18 @@ class Document : public nsINode,
    * Register/Unregister the ActivityObserver into mActivityObservers to listen
    * the document's activity changes such as OnPageHide, visibility, activity.
    * The ActivityObserver objects can be nsIObjectLoadingContent or
    * nsIDocumentActivity or HTMLMEdiaElement.
    */
   void RegisterActivityObserver(nsISupports* aSupports);
   bool UnregisterActivityObserver(nsISupports* aSupports);
   // Enumerate all the observers in mActivityObservers by the aEnumerator.
-  typedef void (*ActivityObserverEnumerator)(nsISupports*, void*);
-  void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
-                                  void* aData);
+  using ActivityObserverEnumerator = FunctionRef<void(nsISupports*)>;
+  void EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator);
 
   // Indicates whether mAnimationController has been (lazily) initialized.
   // If this returns true, we're promising that GetAnimationController()
   // will have a non-null return value.
   bool HasAnimationController() { return !!mAnimationController; }
 
   // Getter for this document's SMIL Animation Controller. Performs lazy
   // initialization, if this document supports animation and if
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -6660,26 +6660,26 @@ void nsGlobalWindowOuter::ActivateOrDeac
     }
 
     if (topLevelWindow) {
       topLevelWindow->SetActive(aActivate);
     }
   }
 }
 
-static CallState NotifyDocumentTree(Document& aDocument, void*) {
-  aDocument.EnumerateSubDocuments(NotifyDocumentTree, nullptr);
+static CallState NotifyDocumentTree(Document& aDocument) {
+  aDocument.EnumerateSubDocuments(NotifyDocumentTree);
   aDocument.UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, true);
   return CallState::Continue;
 }
 
 void nsGlobalWindowOuter::SetActive(bool aActive) {
   nsPIDOMWindowOuter::SetActive(aActive);
   if (mDoc) {
-    NotifyDocumentTree(*mDoc, nullptr);
+    NotifyDocumentTree(*mDoc);
   }
 }
 
 bool nsGlobalWindowOuter::IsTopLevelWindowActive() {
   nsCOMPtr<nsIDocShellTreeItem> treeItem(GetDocShell());
   if (!treeItem) {
     return false;
   }
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6403,25 +6403,25 @@ nsIFrame* PresShell::EventHandler::GetNe
   nsIFrame* frame = nullptr;
   if (view) {
     frame = view->GetFrame();
   }
 
   return frame;
 }
 
-static CallState FlushThrottledStyles(Document& aDocument, void* aData) {
+static CallState FlushThrottledStyles(Document& aDocument) {
   PresShell* presShell = aDocument.GetPresShell();
   if (presShell && presShell->IsVisible()) {
     if (nsPresContext* presContext = presShell->GetPresContext()) {
       presContext->RestyleManager()->UpdateOnlyAnimationStyles();
     }
   }
 
-  aDocument.EnumerateSubDocuments(FlushThrottledStyles, nullptr);
+  aDocument.EnumerateSubDocuments(FlushThrottledStyles);
   return CallState::Continue;
 }
 
 bool PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const {
   bool rv =
       mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
   if (aEvent) {
     rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
@@ -7331,17 +7331,17 @@ nsIFrame* PresShell::EventHandler::Maybe
   Document* rootDocument = rootPresShell->GetDocument();
   if (NS_WARN_IF(!rootDocument)) {
     return nullptr;
   }
 
   AutoWeakFrame weakFrameForPresShell(aFrameForPresShell);
   {  // scope for scriptBlocker.
     nsAutoScriptBlocker scriptBlocker;
-    FlushThrottledStyles(*rootDocument, nullptr);
+    FlushThrottledStyles(*rootDocument);
   }
 
   if (weakFrameForPresShell.IsAlive()) {
     return aFrameForPresShell;
   }
 
   return GetNearestFrameContainingPresShell(mPresShell);
 }
@@ -8954,45 +8954,44 @@ nsresult PresShell::AddOverrideStyleShee
   return NS_OK;
 }
 
 nsresult PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet) {
   StyleSet()->RemoveStyleSheet(*aSheet);
   return NS_OK;
 }
 
-static void FreezeElement(nsISupports* aSupports, void* /* unused */) {
-  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
-  if (olc) {
+static void FreezeElement(nsISupports* aSupports) {
+  if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aSupports)) {
     olc->StopPluginInstance();
   }
 }
 
-static CallState FreezeSubDocument(Document& aDocument, void*) {
+static CallState FreezeSubDocument(Document& aDocument) {
   if (PresShell* presShell = aDocument.GetPresShell()) {
     presShell->Freeze();
   }
   return CallState::Continue;
 }
 
 void PresShell::Freeze() {
   mUpdateApproximateFrameVisibilityEvent.Revoke();
 
   MaybeReleaseCapturingContent();
 
-  mDocument->EnumerateActivityObservers(FreezeElement, nullptr);
+  mDocument->EnumerateActivityObservers(FreezeElement);
 
   if (mCaret) {
     SetCaretEnabled(false);
   }
 
   mPaintingSuppressed = true;
 
   if (mDocument) {
-    mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
+    mDocument->EnumerateSubDocuments(FreezeSubDocument);
   }
 
   nsPresContext* presContext = GetPresContext();
   if (presContext) {
     presContext->DisableInteractionTimeRecording();
     if (presContext->RefreshDriver()->GetPresContext() == presContext) {
       presContext->RefreshDriver()->Freeze();
     }
@@ -9024,41 +9023,36 @@ void PresShell::FireOrClearDelayedEvents
       ev->Dispatch();
     }
     if (!doc->EventHandlingSuppressed()) {
       mDelayedEvents.Clear();
     }
   }
 }
 
-static void ThawElement(nsISupports* aSupports, void* aShell) {
-  nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
-  if (olc) {
-    olc->AsyncStartPluginInstance();
-  }
-}
-
-static CallState ThawSubDocument(Document& aDocument, void* aData) {
-  if (PresShell* presShell = aDocument.GetPresShell()) {
-    presShell->Thaw();
-  }
-  return CallState::Continue;
-}
-
 void PresShell::Thaw() {
   nsPresContext* presContext = GetPresContext();
   if (presContext &&
       presContext->RefreshDriver()->GetPresContext() == presContext) {
     presContext->RefreshDriver()->Thaw();
   }
 
-  mDocument->EnumerateActivityObservers(ThawElement, this);
+  mDocument->EnumerateActivityObservers([](nsISupports* aSupports) {
+    if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aSupports)) {
+      olc->AsyncStartPluginInstance();
+    }
+  });
 
   if (mDocument) {
-    mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
+    mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
+      if (PresShell* presShell = aSubDoc.GetPresShell()) {
+        presShell->Thaw();
+      }
+      return CallState::Continue;
+    });
   }
 
   // Get the activeness of our presshell, as this might have changed
   // while we were in the bfcache
   QueryIsActive();
 
   // We're now unfrozen
   mFrozen = false;
@@ -10523,35 +10517,24 @@ void PresShell::QueryIsActive() {
     // Even though in theory the docshell here could be "Inactive and
     // Foreground", thus implying aIsHidden=false for SetIsActive(),
     // this is a newly created PresShell so we'd like to invalidate anyway
     // upon being made active to ensure that the contents get painted.
     if (NS_SUCCEEDED(rv)) SetIsActive(isActive);
   }
 }
 
-// Helper for propagating mIsActive changes to external resources
-static CallState SetExternalResourceIsActive(Document& aDocument,
-                                             void* aClosure) {
-  if (PresShell* presShell = aDocument.GetPresShell()) {
-    presShell->SetIsActive(*static_cast<bool*>(aClosure));
-  }
-  return CallState::Continue;
-}
-
-static void SetPluginIsActive(nsISupports* aSupports, void* aClosure) {
+static void SetPluginIsActive(nsISupports* aSupports, bool aIsActive) {
   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   if (!content) {
     return;
   }
 
-  nsIFrame* frame = content->GetPrimaryFrame();
-  nsIObjectFrame* objectFrame = do_QueryFrame(frame);
-  if (objectFrame) {
-    objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
+  if (nsIObjectFrame* objectFrame = do_QueryFrame(content->GetPrimaryFrame())) {
+    objectFrame->SetIsDocumentActive(aIsActive);
   }
 }
 
 nsresult PresShell::SetIsActive(bool aIsActive) {
   MOZ_ASSERT(mDocument, "should only be called with a document");
 
 #if defined(MOZ_WIDGET_ANDROID)
   const bool changed = mIsActive != aIsActive;
@@ -10560,20 +10543,32 @@ nsresult PresShell::SetIsActive(bool aIs
   mIsActive = aIsActive;
 
   nsPresContext* presContext = GetPresContext();
   if (presContext &&
       presContext->RefreshDriver()->GetPresContext() == presContext) {
     presContext->RefreshDriver()->SetThrottled(!mIsActive);
   }
 
-  // Propagate state-change to my resource documents' PresShells
-  mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
-                                        &aIsActive);
-  mDocument->EnumerateActivityObservers(SetPluginIsActive, &aIsActive);
+  {
+    // Propagate state-change to my resource documents' PresShells
+    auto recurse = [aIsActive](Document& aResourceDoc) {
+      if (PresShell* presShell = aResourceDoc.GetPresShell()) {
+        presShell->SetIsActive(aIsActive);
+      }
+      return CallState::Continue;
+    };
+    mDocument->EnumerateExternalResources(recurse);
+  }
+  {
+    auto visitPlugin = [aIsActive](nsISupports* aSupports) {
+      SetPluginIsActive(aSupports, aIsActive);
+    };
+    mDocument->EnumerateActivityObservers(visitPlugin);
+  }
   nsresult rv = UpdateImageLockingState();
 #ifdef ACCESSIBILITY
   if (aIsActive) {
     if (nsAccessibilityService* accService =
             PresShell::GetAccessibilityService()) {
       accService->PresShellActivated(this);
     }
   }
@@ -11299,26 +11294,24 @@ PresShell::EventHandler::HandlingTimeAcc
             mHandlingStartTime);
       }
       return;
     default:
       return;
   }
 }
 
-static CallState EndPaintHelper(Document& aDocument, void* aData) {
-  if (PresShell* presShell = aDocument.GetPresShell()) {
-    presShell->EndPaint();
-  }
-  return CallState::Continue;
-}
-
 void PresShell::EndPaint() {
   ClearPendingVisualScrollUpdate();
 
   if (mDocument) {
-    mDocument->EnumerateSubDocuments(EndPaintHelper, nullptr);
+    mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
+      if (PresShell* presShell = aSubDoc.GetPresShell()) {
+        presShell->EndPaint();
+      }
+      return CallState::Continue;
+    });
   }
 }
 
 void PresShell::PingPerTickTelemetry(FlushType aFlushType) {
   mLayoutTelemetry.PingPerTickTelemetry(aFlushType);
 }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -301,33 +301,32 @@ class nsDocumentViewer final : public ns
   // nsIContentViewerEdit
   NS_DECL_NSICONTENTVIEWEREDIT
 
 #ifdef NS_PRINTING
   // nsIWebBrowserPrint
   NS_DECL_NSIWEBBROWSERPRINT
 #endif
 
-  typedef void (*CallChildFunc)(nsDocumentViewer* aViewer, void* aClosure);
-  void CallChildren(CallChildFunc aFunc, void* aClosure);
-
-  typedef void (*PresContextFunc)(nsPresContext*, void* aClosure);
+  using CallChildFunc = FunctionRef<void(nsDocumentViewer*)>;
+  void CallChildren(CallChildFunc aFunc);
+
+  using PresContextFunc = FunctionRef<void(nsPresContext*)>;
   /**
    * Calls a `CallChildFunc` on all children, a `PresContextFunc`
    * on all external documents' pres contexts  of our document, and then
    * finally on _this_ pres context, in that order.
    *
    * The children function is expected to call this function reentrantly, and
    * thus the `PresContextFunc` won't be called for the children's pres context
    * directly here.
    *
    * FIXME(emilio): Better name for this appreciated.
    */
-  void PropagateToPresContextsHelper(CallChildFunc, PresContextFunc,
-                                     void* aClosure);
+  void PropagateToPresContextsHelper(CallChildFunc, PresContextFunc);
 
   // nsIDocumentViewerPrint Printing Methods
   NS_DECL_NSIDOCUMENTVIEWERPRINT
 
   void EmulateMediumInternal(nsAtom*);
 
   using ColorSchemeOverride = Maybe<StylePrefersColorScheme>;
   void EmulatePrefersColorSchemeInternal(const ColorSchemeOverride&);
@@ -504,35 +503,38 @@ class AutoPrintEventDispatcher {
   explicit AutoPrintEventDispatcher(Document* aTop) : mTop(aTop) {
     DispatchEventToWindowTree(NS_LITERAL_STRING("beforeprint"));
   }
   ~AutoPrintEventDispatcher() {
     DispatchEventToWindowTree(NS_LITERAL_STRING("afterprint"));
   }
 
  private:
+  static CallState CollectDocuments(Document& aDoc,
+                                    nsTArray<nsCOMPtr<Document>>& aDocs) {
+    aDocs.AppendElement(&aDoc);
+    auto recurse = [&aDocs](Document& aSubDoc) {
+      return CollectDocuments(aSubDoc, aDocs);
+    };
+    aDoc.EnumerateSubDocuments(recurse);
+    return CallState::Continue;
+  }
+
   void DispatchEventToWindowTree(const nsAString& aEvent) {
     nsTArray<nsCOMPtr<Document>> targets;
     if (mTop) {
-      CollectDocuments(*mTop, &targets);
+      CollectDocuments(*mTop, targets);
     }
     for (nsCOMPtr<Document>& doc : targets) {
       nsContentUtils::DispatchTrustedEvent(doc, doc->GetWindow(), aEvent,
                                            CanBubble::eNo, Cancelable::eNo,
                                            nullptr);
     }
   }
 
-  static CallState CollectDocuments(Document& aDocument, void* aData) {
-    static_cast<nsTArray<nsCOMPtr<Document>>*>(aData)->AppendElement(
-        &aDocument);
-    aDocument.EnumerateSubDocuments(CollectDocuments, aData);
-    return CallState::Continue;
-  }
-
   nsCOMPtr<Document> mTop;
 };
 
 }  // namespace mozilla
 
 class nsDocumentShownDispatcher : public Runnable {
  public:
   explicit nsDocumentShownDispatcher(nsCOMPtr<Document> aDocument)
@@ -2661,61 +2663,51 @@ NS_IMETHODIMP nsDocumentViewer::SetComma
   nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
   NS_ENSURE_STATE(root);
 
   root->SetPopupNode(aNode);
   return NS_OK;
 }
 
 void nsDocumentViewer::PropagateToPresContextsHelper(CallChildFunc aChildFunc,
-                                                     PresContextFunc aPcFunc,
-                                                     void* aClosure) {
-  CallChildren(aChildFunc, aClosure);
-
-  {
-    struct ResourceDocClosure {
-      PresContextFunc mFunc;
-      void* mParentClosure;
-    } resourceDocClosure{aPcFunc, aClosure};
-
-    if (mDocument) {
-      mDocument->EnumerateExternalResources(
-          [](Document& aDoc, void* aClosure) -> CallState {
-            auto* closure = static_cast<ResourceDocClosure*>(aClosure);
-            if (nsPresContext* pc = aDoc.GetPresContext()) {
-              closure->mFunc(pc, closure->mParentClosure);
-            }
-            return CallState::Continue;
-          },
-          &resourceDocClosure);
-    }
+                                                     PresContextFunc aPcFunc) {
+  CallChildren(aChildFunc);
+
+  if (mDocument) {
+    auto resourceDoc = [aPcFunc](Document& aResourceDoc) {
+      if (nsPresContext* pc = aResourceDoc.GetPresContext()) {
+        aPcFunc(pc);
+      }
+      return CallState::Continue;
+    };
+    mDocument->EnumerateExternalResources(resourceDoc);
   }
 
   if (mPresContext) {
-    aPcFunc(mPresContext, aClosure);
+    aPcFunc(mPresContext);
   }
 }
 
-void nsDocumentViewer::CallChildren(CallChildFunc aFunc, void* aClosure) {
+void nsDocumentViewer::CallChildren(CallChildFunc aFunc) {
   nsCOMPtr<nsIDocShell> docShell(mContainer);
-  if (docShell) {
-    int32_t i;
-    int32_t n;
-    docShell->GetInProcessChildCount(&n);
-    for (i = 0; i < n; i++) {
-      nsCOMPtr<nsIDocShellTreeItem> child;
-      docShell->GetInProcessChildAt(i, getter_AddRefs(child));
-      nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
-      NS_ASSERTION(childAsShell, "null child in docshell");
-      if (childAsShell) {
-        nsCOMPtr<nsIContentViewer> childCV;
-        childAsShell->GetContentViewer(getter_AddRefs(childCV));
-        if (childCV) {
-          (*aFunc)(static_cast<nsDocumentViewer*>(childCV.get()), aClosure);
-        }
+  if (!docShell) {
+    return;
+  }
+  int32_t n = 0;
+  docShell->GetInProcessChildCount(&n);
+  for (int32_t i = 0; i < n; i++) {
+    nsCOMPtr<nsIDocShellTreeItem> child;
+    docShell->GetInProcessChildAt(i, getter_AddRefs(child));
+    nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
+    NS_ASSERTION(childAsShell, "null child in docshell");
+    if (childAsShell) {
+      nsCOMPtr<nsIContentViewer> childCV;
+      childAsShell->GetContentViewer(getter_AddRefs(childCV));
+      if (childCV) {
+        aFunc(static_cast<nsDocumentViewer*>(childCV.get()));
       }
     }
   }
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::SetTextZoom(float aTextZoom) {
   // If we don't have a document, then we need to bail.
@@ -2725,24 +2717,23 @@ nsDocumentViewer::SetTextZoom(float aTex
 
   if (GetIsPrintPreview()) {
     return NS_OK;
   }
 
   bool textZoomChange = (mTextZoom != aTextZoom);
   mTextZoom = aTextZoom;
 
-  PropagateToPresContextsHelper(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->SetTextZoom(*static_cast<float*>(aClosure));
-      },
-      [](nsPresContext* aPc, void* aClosure) {
-        aPc->SetTextZoom(*static_cast<float*>(aClosure));
-      },
-      &aTextZoom);
+  auto childFn = [aTextZoom](nsDocumentViewer* aChild) {
+    aChild->SetTextZoom(aTextZoom);
+  };
+  auto presContextFn = [aTextZoom](nsPresContext* aPc) {
+    aPc->SetTextZoom(aTextZoom);
+  };
+  PropagateToPresContextsHelper(childFn, presContextFn);
 
   // Dispatch TextZoomChange event only if text zoom value has changed.
   if (textZoomChange) {
     nsContentUtils::DispatchChromeEvent(mDocument, ToSupports(mDocument),
                                         NS_LITERAL_STRING("TextZoomChange"),
                                         CanBubble::eYes, Cancelable::eYes);
   }
 
@@ -2806,24 +2797,23 @@ nsDocumentViewer::SetFullZoom(float aFul
   if (fullZoomChange) {
     nsContentUtils::DispatchChromeEvent(mDocument, ToSupports(mDocument),
                                         NS_LITERAL_STRING("PreFullZoomChange"),
                                         CanBubble::eYes, Cancelable::eYes);
   }
 
   mPageZoom = aFullZoom;
 
-  PropagateToPresContextsHelper(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->SetFullZoom(*static_cast<float*>(aClosure));
-      },
-      [](nsPresContext* aPc, void* aClosure) {
-        aPc->SetFullZoom(*static_cast<float*>(aClosure));
-      },
-      &aFullZoom);
+  auto childFn = [aFullZoom](nsDocumentViewer* aChild) {
+    aChild->SetFullZoom(aFullZoom);
+  };
+  auto presContextFn = [aFullZoom](nsPresContext* aPc) {
+    aPc->SetFullZoom(aFullZoom);
+  };
+  PropagateToPresContextsHelper(childFn, presContextFn);
 
   // Dispatch FullZoomChange event only if fullzoom value really has changed.
   if (fullZoomChange) {
     nsContentUtils::DispatchChromeEvent(mDocument, ToSupports(mDocument),
                                         NS_LITERAL_STRING("FullZoomChange"),
                                         CanBubble::eYes, Cancelable::eYes);
   }
 
@@ -2868,25 +2858,23 @@ NS_IMETHODIMP
 nsDocumentViewer::SetOverrideDPPX(float aDPPX) {
   // If we don't have a document, then we need to bail.
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
 
   mOverrideDPPX = aDPPX;
 
-  PropagateToPresContextsHelper(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->SetOverrideDPPX(*static_cast<float*>(aClosure));
-      },
-      [](nsPresContext* aPc, void* aClosure) {
-        aPc->SetOverrideDPPX(*static_cast<float*>(aClosure));
-      },
-      &aDPPX);
-
+  auto childFn = [aDPPX](nsDocumentViewer* aChild) {
+    aChild->SetOverrideDPPX(aDPPX);
+  };
+  auto presContextFn = [aDPPX](nsPresContext* aPc) {
+    aPc->SetOverrideDPPX(aDPPX);
+  };
+  PropagateToPresContextsHelper(childFn, presContextFn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::GetOverrideDPPX(float* aDPPX) {
   NS_ENSURE_ARG_POINTER(aDPPX);
 
   nsPresContext* pc = GetPresContext();
@@ -2895,43 +2883,42 @@ nsDocumentViewer::GetOverrideDPPX(float*
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::SetAuthorStyleDisabled(bool aStyleDisabled) {
   if (mPresShell) {
     mPresShell->SetAuthorStyleDisabled(aStyleDisabled);
   }
 
-  CallChildren(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->SetAuthorStyleDisabled(*static_cast<bool*>(aClosure));
-      },
-      &aStyleDisabled);
+  auto children = [aStyleDisabled](nsDocumentViewer* aChild) {
+    aChild->SetAuthorStyleDisabled(aStyleDisabled);
+  };
+
+  CallChildren(children);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::GetAuthorStyleDisabled(bool* aStyleDisabled) {
   if (mPresShell) {
     *aStyleDisabled = mPresShell->GetAuthorStyleDisabled();
   } else {
     *aStyleDisabled = false;
   }
   return NS_OK;
 }
 
 void nsDocumentViewer::EmulateMediumInternal(nsAtom* aMedia) {
-  PropagateToPresContextsHelper(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->EmulateMediumInternal(static_cast<nsAtom*>(aClosure));
-      },
-      [](nsPresContext* aPc, void* aClosure) {
-        aPc->EmulateMedium(static_cast<nsAtom*>(aClosure));
-      },
-      aMedia);
+  auto childFn = [&](nsDocumentViewer* aChild) {
+    aChild->EmulateMediumInternal(aMedia);
+  };
+  auto presContextFn = [&](nsPresContext* aPc) {
+    aPc->EmulateMedium(aMedia);
+  };
+  PropagateToPresContextsHelper(childFn, presContextFn);
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::EmulateMedium(const nsAString& aMediaType) {
   nsAutoString mediaType;
   nsContentUtils::ASCIIToLower(aMediaType, mediaType);
   RefPtr<nsAtom> media = NS_Atomize(mediaType);
 
@@ -2964,26 +2951,23 @@ nsDocumentViewer::EmulatePrefersColorSch
   };
 
   EmulatePrefersColorSchemeInternal(ToStyle(aScheme));
   return NS_OK;
 }
 
 void nsDocumentViewer::EmulatePrefersColorSchemeInternal(
     const ColorSchemeOverride& aOverride) {
-  PropagateToPresContextsHelper(
-      [](nsDocumentViewer* aChild, void* aClosure) {
-        aChild->EmulatePrefersColorSchemeInternal(
-            *static_cast<const ColorSchemeOverride*>(aClosure));
-      },
-      [](nsPresContext* aPc, void* aClosure) {
-        aPc->SetOverridePrefersColorScheme(
-            *static_cast<const ColorSchemeOverride*>(aClosure));
-      },
-      const_cast<ColorSchemeOverride*>(&aOverride));
+  auto childFn = [&aOverride](nsDocumentViewer* aChild) {
+    aChild->EmulatePrefersColorSchemeInternal(aOverride);
+  };
+  auto presContextFn = [&aOverride](nsPresContext* aPc) {
+    aPc->SetOverridePrefersColorScheme(aOverride);
+  };
+  PropagateToPresContextsHelper(childFn, presContextFn);
 }
 
 NS_IMETHODIMP nsDocumentViewer::GetForceCharacterSet(
     nsACString& aForceCharacterSet) {
   auto encoding = nsDocumentViewer::GetForceCharset();
   if (encoding) {
     encoding->Name(aForceCharacterSet);
   } else {
@@ -2991,22 +2975,16 @@ NS_IMETHODIMP nsDocumentViewer::GetForce
   }
   return NS_OK;
 }
 
 /* [noscript,notxpcom] Encoding getForceCharset (); */
 NS_IMETHODIMP_(const Encoding*)
 nsDocumentViewer::GetForceCharset() { return mForceCharacterSet; }
 
-static void SetChildForceCharacterSet(nsDocumentViewer* aChild,
-                                      void* aClosure) {
-  auto encoding = static_cast<const Encoding*>(aClosure);
-  aChild->SetForceCharset(encoding);
-}
-
 NS_IMETHODIMP
 nsDocumentViewer::SetForceCharacterSet(const nsACString& aForceCharacterSet) {
   // The empty string means no hint.
   const Encoding* encoding = nullptr;
   if (!aForceCharacterSet.IsEmpty()) {
     if (!(encoding = Encoding::ForLabel(aForceCharacterSet))) {
       // Reject unknown labels
       return NS_ERROR_INVALID_ARG;
@@ -3015,18 +2993,21 @@ nsDocumentViewer::SetForceCharacterSet(c
   nsDocumentViewer::SetForceCharset(encoding);
   return NS_OK;
 }
 
 /* [noscript,notxpcom] void setForceCharset (in Encoding aEncoding); */
 NS_IMETHODIMP_(void)
 nsDocumentViewer::SetForceCharset(const Encoding* aEncoding) {
   mForceCharacterSet = aEncoding;
+  auto childFn = [aEncoding](nsDocumentViewer* aChild) {
+    aChild->SetForceCharset(aEncoding);
+  };
   // now set the force char set on all children of mContainer
-  CallChildren(SetChildForceCharacterSet, (void*)aEncoding);
+  CallChildren(childFn);
 }
 
 NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSet(
     nsACString& aHintCharacterSet) {
   auto encoding = nsDocumentViewer::GetHintCharset();
   if (encoding) {
     encoding->Name(aHintCharacterSet);
   } else {
@@ -3045,40 +3026,31 @@ nsDocumentViewer::GetHintCharset() {
   // got a related value!
   // mHintCharsetSource = kCharsetUninitialized;
   return mHintCharset;
 }
 
 NS_IMETHODIMP nsDocumentViewer::GetHintCharacterSetSource(
     int32_t* aHintCharacterSetSource) {
   NS_ENSURE_ARG_POINTER(aHintCharacterSetSource);
-
   *aHintCharacterSetSource = mHintCharsetSource;
   return NS_OK;
 }
 
-static void SetChildHintCharacterSetSource(nsDocumentViewer* aChild,
-                                           void* aClosure) {
-  aChild->SetHintCharacterSetSource(NS_PTR_TO_INT32(aClosure));
-}
-
 NS_IMETHODIMP
 nsDocumentViewer::SetHintCharacterSetSource(int32_t aHintCharacterSetSource) {
   mHintCharsetSource = aHintCharacterSetSource;
-  // now set the hint char set source on all children of mContainer
-  CallChildren(SetChildHintCharacterSetSource,
-               NS_INT32_TO_PTR(aHintCharacterSetSource));
+  auto childFn = [aHintCharacterSetSource](nsDocumentViewer* aChild) {
+    aChild->SetHintCharacterSetSource(aHintCharacterSetSource);
+  };
+  // now set the force char set on all children of mContainer
+  CallChildren(childFn);
   return NS_OK;
 }
 
-static void SetChildHintCharacterSet(nsDocumentViewer* aChild, void* aClosure) {
-  auto encoding = static_cast<const Encoding*>(aClosure);
-  aChild->SetHintCharset(encoding);
-}
-
 NS_IMETHODIMP
 nsDocumentViewer::SetHintCharacterSet(const nsACString& aHintCharacterSet) {
   // The empty string means no hint.
   const Encoding* encoding = nullptr;
   if (!aHintCharacterSet.IsEmpty()) {
     if (!(encoding = Encoding::ForLabel(aHintCharacterSet))) {
       // Reject unknown labels
       return NS_ERROR_INVALID_ARG;
@@ -3087,50 +3059,47 @@ nsDocumentViewer::SetHintCharacterSet(co
   nsDocumentViewer::SetHintCharset(encoding);
   return NS_OK;
 }
 
 /* [noscript,notxpcom] void setHintCharset (in Encoding aEncoding); */
 NS_IMETHODIMP_(void)
 nsDocumentViewer::SetHintCharset(const Encoding* aEncoding) {
   mHintCharset = aEncoding;
-  // now set the hint char set on all children of mContainer
-  CallChildren(SetChildHintCharacterSet, (void*)aEncoding);
-}
-
-static void AppendChildSubtree(nsDocumentViewer* aChild, void* aClosure) {
-  nsTArray<nsCOMPtr<nsIContentViewer>>& array =
-      *static_cast<nsTArray<nsCOMPtr<nsIContentViewer>>*>(aClosure);
-  aChild->AppendSubtree(array);
+  auto childFn = [aEncoding](nsDocumentViewer* aChild) {
+    aChild->SetHintCharset(aEncoding);
+  };
+  // now set the force char set on all children of mContainer
+  CallChildren(childFn);
 }
 
 NS_IMETHODIMP nsDocumentViewer::AppendSubtree(
     nsTArray<nsCOMPtr<nsIContentViewer>>& aArray) {
   aArray.AppendElement(this);
-  CallChildren(AppendChildSubtree, &aArray);
+  auto childFn = [&aArray](nsDocumentViewer* aChild) {
+    aChild->AppendSubtree(aArray);
+  };
+  CallChildren(childFn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::PausePainting() {
-  CallChildren([](nsDocumentViewer* aChild, void*) { aChild->PausePainting(); },
-               nullptr);
+  CallChildren([](nsDocumentViewer* aChild) { aChild->PausePainting(); });
 
   if (PresShell* presShell = GetPresShell()) {
     presShell->PausePainting();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::ResumePainting() {
-  CallChildren(
-      [](nsDocumentViewer* aChild, void*) { aChild->ResumePainting(); },
-      nullptr);
+  CallChildren([](nsDocumentViewer* aChild) { aChild->ResumePainting(); });
 
   if (PresShell* presShell = GetPresShell()) {
     presShell->ResumePainting();
   }
 
   return NS_OK;
 }
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1390,30 +1390,16 @@ void nsPresContext::UIResolutionChanged(
 
 void nsPresContext::UIResolutionChangedSync() {
   if (!mPendingUIResolutionChanged) {
     mPendingUIResolutionChanged = true;
     UIResolutionChangedInternalScale(0.0);
   }
 }
 
-/* static */
-CallState nsPresContext::UIResolutionChangedSubdocumentCallback(
-    dom::Document& aDocument, void* aData) {
-  if (nsPresContext* pc = aDocument.GetPresContext()) {
-    // For subdocuments, we want to apply the parent's scale, because there
-    // are cases where the subdoc's device context is connected to a widget
-    // that has an out-of-date resolution (it's on a different screen, but
-    // currently hidden, and will not be updated until shown): bug 1249279.
-    double scale = *static_cast<double*>(aData);
-    pc->UIResolutionChangedInternalScale(scale);
-  }
-  return CallState::Continue;
-}
-
 static void NotifyTabUIResolutionChanged(nsIRemoteTab* aTab, void* aArg) {
   aTab->NotifyResolutionChanged();
 }
 
 static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
   nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
   RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
   if (!topLevelWin) {
@@ -1434,18 +1420,27 @@ void nsPresContext::UIResolutionChangedI
     AppUnitsPerDevPixelChanged();
   }
 
   // Recursively notify all remote leaf descendants of the change.
   if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
     NotifyChildrenUIResolutionChanged(window);
   }
 
-  mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
-                                   &aScale);
+  auto recurse = [aScale](dom::Document& aSubDoc) {
+    if (nsPresContext* pc = aSubDoc.GetPresContext()) {
+      // For subdocuments, we want to apply the parent's scale, because there
+      // are cases where the subdoc's device context is connected to a widget
+      // that has an out-of-date resolution (it's on a different screen, but
+      // currently hidden, and will not be updated until shown): bug 1249279.
+      pc->UIResolutionChangedInternalScale(aScale);
+    }
+    return CallState::Continue;
+  };
+  mDocument->EnumerateSubDocuments(recurse);
 }
 
 void nsPresContext::EmulateMedium(nsAtom* aMediaType) {
   MOZ_ASSERT(!aMediaType || aMediaType->IsAsciiLowercase());
   RefPtr<const nsAtom> oldMedium = Medium();
   mMediaEmulationData.mMedium = aMediaType;
 
   if (Medium() != oldMedium) {
@@ -1498,38 +1493,33 @@ void nsPresContext::PostRebuildAllStyleD
     return;
   }
   if (aRestyleHint.DefinitelyRecascadesAllSubtree()) {
     mUsesExChUnits = false;
   }
   RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
 }
 
-static CallState MediaFeatureValuesChangedAllDocumentsCallback(
-    Document& aDocument, void* aChange) {
-  auto* change = static_cast<const MediaFeatureChange*>(aChange);
-  if (nsPresContext* pc = aDocument.GetPresContext()) {
-    pc->MediaFeatureValuesChangedAllDocuments(*change);
-  }
-  return CallState::Continue;
-}
-
 void nsPresContext::MediaFeatureValuesChangedAllDocuments(
     const MediaFeatureChange& aChange) {
   // Handle the media feature value change in this document.
   MediaFeatureValuesChanged(aChange);
 
   // Propagate the media feature value change down to any SVG images the
   // document is using.
   mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange);
 
   // And then into any subdocuments.
-  mDocument->EnumerateSubDocuments(
-      MediaFeatureValuesChangedAllDocumentsCallback,
-      const_cast<MediaFeatureChange*>(&aChange));
+  auto recurse = [&aChange](dom::Document& aSubDoc) {
+    if (nsPresContext* pc = aSubDoc.GetPresContext()) {
+      pc->MediaFeatureValuesChangedAllDocuments(aChange);
+    }
+    return CallState::Continue;
+  };
+  mDocument->EnumerateSubDocuments(recurse);
 }
 
 void nsPresContext::FlushPendingMediaFeatureValuesChanged() {
   if (!mPendingMediaFeatureValuesChange) {
     return;
   }
 
   MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
@@ -1806,29 +1796,16 @@ void nsPresContext::FireDOMPaintEvent(
   // the chrome event handler, not the window), the window is still
   // logically the event target.
   event->SetTarget(eventTarget);
   event->SetTrusted(true);
   EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
                                     static_cast<Event*>(event), this, nullptr);
 }
 
-static CallState MayHavePaintEventListenerSubdocumentCallback(
-    Document& aDocument, void* aData) {
-  bool* result = static_cast<bool*>(aData);
-  if (nsPresContext* pc = aDocument.GetPresContext()) {
-    *result = pc->MayHavePaintEventListenerInSubDocument();
-
-    // If we found a paint event listener, then we can stop enumerating
-    // sub documents.
-    return !*result ? CallState::Continue : CallState::Stop;
-  }
-  return CallState::Continue;
-}
-
 static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
   if (!aInnerWindow) return false;
   if (aInnerWindow->HasPaintEventListeners()) return true;
 
   EventTarget* parentTarget = aInnerWindow->GetParentTarget();
   if (!parentTarget) return false;
 
   EventListenerManager* manager = nullptr;
@@ -1867,18 +1844,27 @@ bool nsPresContext::MayHavePaintEventLis
 }
 
 bool nsPresContext::MayHavePaintEventListenerInSubDocument() {
   if (MayHavePaintEventListener()) {
     return true;
   }
 
   bool result = false;
-  mDocument->EnumerateSubDocuments(MayHavePaintEventListenerSubdocumentCallback,
-                                   &result);
+  auto recurse = [&result](dom::Document& aSubDoc) {
+    if (nsPresContext* pc = aSubDoc.GetPresContext()) {
+      if (pc->MayHavePaintEventListenerInSubDocument()) {
+        result = true;
+        return CallState::Stop;
+      }
+    }
+    return CallState::Continue;
+  };
+
+  mDocument->EnumerateSubDocuments(recurse);
   return result;
 }
 
 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
                                        const nsIntRect& aRect) {
   // Prevent values from overflow after DevPixelsToAppUnits().
   //
   // DevPixelsTopAppUnits() will multiple a factor (60) to the value,
@@ -1972,40 +1958,16 @@ void nsPresContext::SetNotifySubDocInval
 }
 
 /* static */
 void nsPresContext::ClearNotifySubDocInvalidationData(
     ContainerLayer* aContainer) {
   aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
 }
 
-struct NotifyDidPaintSubdocumentCallbackClosure {
-  TransactionId mTransactionId;
-  const mozilla::TimeStamp& mTimeStamp;
-};
-/* static */
-CallState nsPresContext::NotifyDidPaintSubdocumentCallback(
-    dom::Document& aDocument, void* aData) {
-  auto* closure = static_cast<NotifyDidPaintSubdocumentCallbackClosure*>(aData);
-  if (nsPresContext* pc = aDocument.GetPresContext()) {
-    pc->NotifyDidPaintForSubtree(closure->mTransactionId, closure->mTimeStamp);
-  }
-  return CallState::Continue;
-}
-
-/* static */
-CallState nsPresContext::NotifyRevokingDidPaintSubdocumentCallback(
-    dom::Document& aDocument, void* aData) {
-  auto* closure = static_cast<NotifyDidPaintSubdocumentCallbackClosure*>(aData);
-  if (nsPresContext* pc = aDocument.GetPresContext()) {
-    pc->NotifyRevokingDidPaint(closure->mTransactionId);
-  }
-  return CallState::Continue;
-}
-
 class DelayedFireDOMPaintEvent : public Runnable {
  public:
   DelayedFireDOMPaintEvent(
       nsPresContext* aPresContext, nsTArray<nsRect>* aList,
       TransactionId aTransactionId,
       const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp())
       : mozilla::Runnable("DelayedFireDOMPaintEvent"),
         mPresContext(aPresContext),
@@ -2059,20 +2021,23 @@ void nsPresContext::NotifyRevokingDidPai
         this, &transaction->mInvalidations, transaction->mTransactionId,
         mozilla::TimeStamp());
     nsContentUtils::AddScriptRunner(ev);
     mTransactions.RemoveElementAt(0);
   } else {
     transaction->mIsWaitingForPreviousTransaction = true;
   }
 
-  NotifyDidPaintSubdocumentCallbackClosure closure = {aTransactionId,
-                                                      mozilla::TimeStamp()};
-  mDocument->EnumerateSubDocuments(
-      nsPresContext::NotifyRevokingDidPaintSubdocumentCallback, &closure);
+  auto recurse = [&aTransactionId](dom::Document& aSubDoc) {
+    if (nsPresContext* pc = aSubDoc.GetPresContext()) {
+      pc->NotifyRevokingDidPaint(aTransactionId);
+    }
+    return CallState::Continue;
+  };
+  mDocument->EnumerateSubDocuments(recurse);
 }
 
 void nsPresContext::NotifyDidPaintForSubtree(
     TransactionId aTransactionId, const mozilla::TimeStamp& aTimeStamp) {
   if (mFirstContentfulPaintTransactionId && !mHadContentfulPaintComposite) {
     if (aTransactionId >= *mFirstContentfulPaintTransactionId) {
       mHadContentfulPaintComposite = true;
       RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
@@ -2126,20 +2091,23 @@ void nsPresContext::NotifyDidPaintForSub
 
   if (!sent) {
     nsTArray<nsRect> dummy;
     nsCOMPtr<nsIRunnable> ev =
         new DelayedFireDOMPaintEvent(this, &dummy, aTransactionId, aTimeStamp);
     nsContentUtils::AddScriptRunner(ev);
   }
 
-  NotifyDidPaintSubdocumentCallbackClosure closure = {aTransactionId,
-                                                      aTimeStamp};
-  mDocument->EnumerateSubDocuments(
-      nsPresContext::NotifyDidPaintSubdocumentCallback, &closure);
+  auto recurse = [&aTransactionId, &aTimeStamp](dom::Document& aSubDoc) {
+    if (nsPresContext* pc = aSubDoc.GetPresContext()) {
+      pc->NotifyDidPaintForSubtree(aTransactionId, aTimeStamp);
+    }
+    return CallState::Continue;
+  };
+  mDocument->EnumerateSubDocuments(recurse);
 }
 
 already_AddRefed<nsITimer> nsPresContext::CreateTimer(
     nsTimerCallbackFunc aCallback, const char* aName, uint32_t aDelay) {
   nsCOMPtr<nsITimer> timer;
   NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback, this, aDelay,
                               nsITimer::TYPE_ONE_SHOT, aName,
                               Document()->EventTargetFor(TaskCategory::Other));
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1065,41 +1065,30 @@ class nsPresContext : public nsISupports
 
   // update device context's resolution from the widget
   void UIResolutionChangedInternal();
 
   // if aScale > 0.0, use it as resolution scale factor to the device context
   // (otherwise get it from the widget)
   void UIResolutionChangedInternalScale(double aScale);
 
-  // aData here is a pointer to a double that holds the CSS to device-pixel
-  // scale factor from the parent, which will be applied to the subdocument's
-  // device context instead of retrieving a scale from the widget.
-  static mozilla::CallState UIResolutionChangedSubdocumentCallback(
-      mozilla::dom::Document&, void* aData);
-
   void SetImgAnimations(nsIContent* aParent, uint16_t aMode);
   void SetSMILAnimations(mozilla::dom::Document* aDoc, uint16_t aNewMode,
                          uint16_t aOldMode);
 
   static void PreferenceChanged(const char* aPrefName, void* aSelf);
   void PreferenceChanged(const char* aPrefName);
 
   void UpdateAfterPreferencesChanged();
   void DispatchPrefChangedRunnableIfNeeded();
 
   void GetUserPreferences();
 
   void UpdateCharSet(NotNull<const Encoding*> aCharSet);
 
-  static mozilla::CallState NotifyDidPaintSubdocumentCallback(
-      mozilla::dom::Document&, void* aData);
-  static mozilla::CallState NotifyRevokingDidPaintSubdocumentCallback(
-      mozilla::dom::Document&, void* aData);
-
  public:
   // Used by the PresShell to force a reflow when some aspect of font info
   // has been updated, potentially affecting font selection and layout.
   void ForceReflowForFontInfoUpdate();
 
   /**
    * Checks for MozAfterPaint listeners on the document
    */
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1815,22 +1815,23 @@ void nsRefreshDriver::CancelIdleRunnable
   }
 
   if (sPendingIdleRunnables->IsEmpty()) {
     delete sPendingIdleRunnables;
     sPendingIdleRunnables = nullptr;
   }
 }
 
-static CallState ReduceAnimations(Document& aDocument, void* aData) {
-  if (aDocument.GetPresContext() &&
-      aDocument.GetPresContext()->EffectCompositor()->NeedsReducing()) {
-    aDocument.GetPresContext()->EffectCompositor()->ReduceAnimations();
+static CallState ReduceAnimations(Document& aDocument) {
+  if (nsPresContext* pc = aDocument.GetPresContext()) {
+    if (pc->EffectCompositor()->NeedsReducing()) {
+      pc->EffectCompositor()->ReduceAnimations();
+    }
   }
-  aDocument.EnumerateSubDocuments(ReduceAnimations, nullptr);
+  aDocument.EnumerateSubDocuments(ReduceAnimations);
   return CallState::Continue;
 }
 
 void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
   MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
              "Shouldn't have a JSContext on the stack");
 
   if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
@@ -2000,17 +2001,17 @@ void nsRefreshDriver::Tick(VsyncId aId, 
     // As per the "update animations and send events" procedure[1], we should
     // remove replaced animations and then run these microtasks before
     // dispatching the corresponding animation events.
     //
     // [1]
     // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
     if (i == 1) {
       nsAutoMicroTask mt;
-      ReduceAnimations(*mPresContext->Document(), nullptr);
+      ReduceAnimations(*mPresContext->Document());
     }
 
     // Check if running the microtask checkpoint caused the pres context to
     // be destroyed.
     if (i == 1 && (!mPresContext || !mPresContext->GetPresShell())) {
       StopTimer();
       return;
     }
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1626,17 +1626,17 @@ nsIObjectFrame* nsPluginFrame::GetNextOb
     outFrame = GetNextObjectFrame(aPresContext, child);
     if (outFrame) return outFrame;
   }
 
   return nullptr;
 }
 
 /*static*/
-void nsPluginFrame::BeginSwapDocShells(nsISupports* aSupports, void*) {
+void nsPluginFrame::BeginSwapDocShells(nsISupports* aSupports) {
   MOZ_ASSERT(aSupports, "null parameter");
   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   if (!content) {
     return;
   }
 
   // This function is called from a document content enumerator so we need
   // to filter out the nsPluginFrames and ignore the rest.
@@ -1645,17 +1645,17 @@ void nsPluginFrame::BeginSwapDocShells(n
 
   nsPluginFrame* objectFrame = static_cast<nsPluginFrame*>(obj);
   NS_ASSERTION(!objectFrame->mWidget || objectFrame->mWidget->GetParent(),
                "Plugin windows must not be toplevel");
   objectFrame->UnregisterPluginForGeometryUpdates();
 }
 
 /*static*/
-void nsPluginFrame::EndSwapDocShells(nsISupports* aSupports, void*) {
+void nsPluginFrame::EndSwapDocShells(nsISupports* aSupports) {
   MOZ_ASSERT(aSupports, "null parameter");
   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   if (!content) {
     return;
   }
 
   // This function is called from a document content enumerator so we need
   // to filter out the nsPluginFrames and ignore the rest.
--- a/layout/generic/nsPluginFrame.h
+++ b/layout/generic/nsPluginFrame.h
@@ -178,22 +178,22 @@ class nsPluginFrame final : public nsFra
   nsRect GetPaintedRect(const nsDisplayPlugin* aItem) const;
 
   /**
    * If aSupports has a nsPluginFrame, then prepare it for a DocShell swap.
    * @see nsSubDocumentFrame::BeginSwapDocShells.
    * There will be a call to EndSwapDocShells after we were moved to the
    * new view tree.
    */
-  static void BeginSwapDocShells(nsISupports* aSupports, void*);
+  static void BeginSwapDocShells(nsISupports* aSupports);
   /**
    * If aSupports has a nsPluginFrame, then set it up after a DocShell swap.
    * @see nsSubDocumentFrame::EndSwapDocShells.
    */
-  static void EndSwapDocShells(nsISupports* aSupports, void*);
+  static void EndSwapDocShells(nsISupports* aSupports);
 
   nsIWidget* GetWidget() override {
     if (!mInnerView) {
       return nullptr;
     }
     return mWidget;
   }
 
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -997,37 +997,36 @@ static void DestroyDisplayItemDataForFra
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       DestroyDisplayItemDataForFrames(childFrames.get());
     }
   }
 }
 
-static CallState BeginSwapDocShellsForDocument(Document& aDocument, void*) {
+static CallState BeginSwapDocShellsForDocument(Document& aDocument) {
   if (PresShell* presShell = aDocument.GetPresShell()) {
     // Disable painting while the views are detached, see bug 946929.
     presShell->SetNeverPainting(true);
 
     if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
       ::DestroyDisplayItemDataForFrames(rootFrame);
     }
   }
-  aDocument.EnumerateActivityObservers(nsPluginFrame::BeginSwapDocShells,
-                                       nullptr);
-  aDocument.EnumerateSubDocuments(BeginSwapDocShellsForDocument, nullptr);
+  aDocument.EnumerateActivityObservers(nsPluginFrame::BeginSwapDocShells);
+  aDocument.EnumerateSubDocuments(BeginSwapDocShellsForDocument);
   return CallState::Continue;
 }
 
 static nsView* BeginSwapDocShellsForViews(nsView* aSibling) {
   // Collect the removed sibling views in reverse order in 'removedViews'.
   nsView* removedViews = nullptr;
   while (aSibling) {
     if (Document* doc = ::GetDocumentFromView(aSibling)) {
-      ::BeginSwapDocShellsForDocument(*doc, nullptr);
+      ::BeginSwapDocShellsForDocument(*doc);
     }
     nsView* next = aSibling->GetNextSibling();
     aSibling->GetViewManager()->RemoveChild(aSibling);
     aSibling->SetNextSibling(removedViews);
     removedViews = aSibling;
     aSibling = next;
   }
   return removedViews;
@@ -1070,17 +1069,17 @@ nsresult nsSubDocumentFrame::BeginSwapDo
 
     ::InsertViewsInReverseOrder(ourRemovedViews, other->mInnerView);
     ::InsertViewsInReverseOrder(otherRemovedViews, mInnerView);
   }
   mFrameLoader.swap(other->mFrameLoader);
   return NS_OK;
 }
 
-static CallState EndSwapDocShellsForDocument(Document& aDocument, void*) {
+static CallState EndSwapDocShellsForDocument(Document& aDocument) {
   // Our docshell and view trees have been updated for the new hierarchy.
   // Now also update all nsDeviceContext::mWidget to that of the
   // container view in the new hierarchy.
   if (nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell()) {
     nsCOMPtr<nsIContentViewer> cv;
     ds->GetContentViewer(getter_AddRefs(cv));
     while (cv) {
       RefPtr<nsPresContext> pc = cv->GetPresContext();
@@ -1091,26 +1090,25 @@ static CallState EndSwapDocShellsForDocu
       if (dc) {
         nsView* v = cv->FindContainerView();
         dc->Init(v ? v->GetNearestWidget(nullptr) : nullptr);
       }
       cv = cv->GetPreviousViewer();
     }
   }
 
-  aDocument.EnumerateActivityObservers(nsPluginFrame::EndSwapDocShells,
-                                       nullptr);
-  aDocument.EnumerateSubDocuments(EndSwapDocShellsForDocument, nullptr);
+  aDocument.EnumerateActivityObservers(nsPluginFrame::EndSwapDocShells);
+  aDocument.EnumerateSubDocuments(EndSwapDocShellsForDocument);
   return CallState::Continue;
 }
 
 static void EndSwapDocShellsForViews(nsView* aSibling) {
   for (; aSibling; aSibling = aSibling->GetNextSibling()) {
     if (Document* doc = ::GetDocumentFromView(aSibling)) {
-      ::EndSwapDocShellsForDocument(*doc, nullptr);
+      ::EndSwapDocShellsForDocument(*doc);
     }
     nsIFrame* frame = aSibling->GetFrame();
     if (frame) {
       nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
       if (parent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
         nsIFrame::AddInPopupStateBitToDescendants(frame);
       } else {
         nsIFrame::RemoveInPopupStateBitFromDescendants(frame);
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -839,53 +839,16 @@ bool RetainedDisplayListBuilder::MergeDi
     previousItemIndex = merge.ProcessItemFromNewList(item, previousItemIndex);
   }
 
   *aOutList = merge.Finalize();
   aOutContainerASR = merge.mContainerASR;
   return merge.mResultIsModified;
 }
 
-static void TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-    nsDisplayListBuilder* aBuilder, nsTArray<nsIFrame*>* aModifiedFrames,
-    nsTArray<nsIFrame*>* aFramesWithProps, nsIFrame* aRootFrame) {
-  MOZ_ASSERT(aRootFrame);
-
-  RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame);
-
-  if (!data) {
-    return;
-  }
-
-  for (auto it = data->Iterator(); !it.Done(); it.Next()) {
-    nsIFrame* frame = it.Key();
-    const RetainedDisplayListData::FrameFlags& flags = it.Data();
-
-    if (flags & RetainedDisplayListData::FrameFlags::Modified) {
-      aModifiedFrames->AppendElement(frame);
-    }
-
-    if (flags & RetainedDisplayListData::FrameFlags::HasProps) {
-      aFramesWithProps->AppendElement(frame);
-    }
-
-    if (flags & RetainedDisplayListData::FrameFlags::HadWillChange) {
-      aBuilder->RemoveFromWillChangeBudgets(frame);
-    }
-  }
-
-  data->Clear();
-}
-
-struct CbData {
-  nsDisplayListBuilder* builder;
-  nsTArray<nsIFrame*>* modifiedFrames;
-  nsTArray<nsIFrame*>* framesWithProps;
-};
-
 static nsIFrame* GetRootFrameForPainting(nsDisplayListBuilder* aBuilder,
                                          Document& aDocument) {
   // Although this is the actual subdocument, it might not be
   // what painting uses. Walk up to the nsSubDocumentFrame owning
   // us, and then ask that which subdoc it's going to paint.
 
   PresShell* presShell = aDocument.GetPresShell();
   if (!presShell) {
@@ -917,44 +880,62 @@ static nsIFrame* GetRootFrameForPainting
   MOZ_ASSERT(subdocumentFrame);
   presShell = subdocumentFrame->GetSubdocumentPresShellForPainting(
       aBuilder->IsIgnoringPaintSuppression()
           ? nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION
           : 0);
   return presShell ? presShell->GetRootFrame() : nullptr;
 }
 
-static CallState SubDocEnumCb(Document& aDocument, void* aData) {
-  MOZ_ASSERT(aData);
+static void TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
+    nsDisplayListBuilder* aBuilder, nsTArray<nsIFrame*>* aModifiedFrames,
+    nsTArray<nsIFrame*>* aFramesWithProps, nsIFrame* aRootFrame,
+    Document& aDoc) {
+  MOZ_ASSERT(aRootFrame);
 
-  auto* data = static_cast<CbData*>(aData);
+  if (RetainedDisplayListData* data = GetRetainedDisplayListData(aRootFrame)) {
+    for (auto it = data->Iterator(); !it.Done(); it.Next()) {
+      nsIFrame* frame = it.Key();
+      const RetainedDisplayListData::FrameFlags& flags = it.Data();
+
+      if (flags & RetainedDisplayListData::FrameFlags::Modified) {
+        aModifiedFrames->AppendElement(frame);
+      }
 
-  if (nsIFrame* rootFrame = GetRootFrameForPainting(data->builder, aDocument)) {
-    TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-        data->builder, data->modifiedFrames, data->framesWithProps, rootFrame);
+      if (flags & RetainedDisplayListData::FrameFlags::HasProps) {
+        aFramesWithProps->AppendElement(frame);
+      }
+
+      if (flags & RetainedDisplayListData::FrameFlags::HadWillChange) {
+        aBuilder->RemoveFromWillChangeBudgets(frame);
+      }
+    }
 
-    if (Document* innerDoc = rootFrame->PresShell()->GetDocument()) {
-      innerDoc->EnumerateSubDocuments(SubDocEnumCb, aData);
+    data->Clear();
+  }
+
+  auto recurse = [&](Document& aSubDoc) {
+    if (nsIFrame* rootFrame = GetRootFrameForPainting(aBuilder, aSubDoc)) {
+      TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
+          aBuilder, aModifiedFrames, aFramesWithProps, rootFrame, aSubDoc);
     }
-  }
-  return CallState::Continue;
+    return CallState::Continue;
+  };
+  aDoc.EnumerateSubDocuments(recurse);
 }
 
 static void GetModifiedAndFramesWithProps(
     nsDisplayListBuilder* aBuilder, nsTArray<nsIFrame*>* aOutModifiedFrames,
     nsTArray<nsIFrame*>* aOutFramesWithProps) {
   nsIFrame* rootFrame = aBuilder->RootReferenceFrame();
   MOZ_ASSERT(rootFrame);
 
+  Document* rootDoc = rootFrame->PresContext()->Document();
   TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
-      aBuilder, aOutModifiedFrames, aOutFramesWithProps, rootFrame);
-
-  Document* rootdoc = rootFrame->PresContext()->Document();
-  CbData data = {aBuilder, aOutModifiedFrames, aOutFramesWithProps};
-  rootdoc->EnumerateSubDocuments(SubDocEnumCb, &data);
+      aBuilder, aOutModifiedFrames, aOutFramesWithProps, rootFrame, *rootDoc);
 }
 
 // ComputeRebuildRegion  debugging
 // #define CRR_DEBUG 1
 #if CRR_DEBUG
 #  define CRR_LOG(...) printf_stderr(__VA_ARGS__)
 #else
 #  define CRR_LOG(...)
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2781,39 +2781,34 @@ bool nsDisplayList::ComputeVisibilityFor
     }
     AppendToBottom(item);
   }
 
   mIsOpaque = !aVisibleRegion->Intersects(aListVisibleBounds);
   return anyVisible;
 }
 
-static CallState TriggerPendingAnimationsOnSubDocuments(Document& aDoc,
-                                                        void* aReadyTime) {
+static void TriggerPendingAnimations(Document& aDoc,
+                                     const TimeStamp& aReadyTime) {
+  MOZ_ASSERT(!aReadyTime.IsNull(),
+             "Animation ready time is not set. Perhaps we're using a layer"
+             " manager that doesn't update it");
   if (PendingAnimationTracker* tracker = aDoc.GetPendingAnimationTracker()) {
     PresShell* presShell = aDoc.GetPresShell();
     // If paint-suppression is in effect then we haven't finished painting
     // this document yet so we shouldn't start animations
     if (!presShell || !presShell->IsPaintingSuppressed()) {
-      const TimeStamp& readyTime = *static_cast<TimeStamp*>(aReadyTime);
-      tracker->TriggerPendingAnimationsOnNextTick(readyTime);
-    }
-  }
-  aDoc.EnumerateSubDocuments(TriggerPendingAnimationsOnSubDocuments,
-                             aReadyTime);
-  return CallState::Continue;
-}
-
-static void TriggerPendingAnimations(Document& aDocument,
-                                     const TimeStamp& aReadyTime) {
-  MOZ_ASSERT(!aReadyTime.IsNull(),
-             "Animation ready time is not set. Perhaps we're using a layer"
-             " manager that doesn't update it");
-  TriggerPendingAnimationsOnSubDocuments(aDocument,
-                                         const_cast<TimeStamp*>(&aReadyTime));
+      tracker->TriggerPendingAnimationsOnNextTick(aReadyTime);
+    }
+  }
+  auto recurse = [&aReadyTime](Document& aDoc) {
+    TriggerPendingAnimations(aDoc, aReadyTime);
+    return CallState::Continue;
+  };
+  aDoc.EnumerateSubDocuments(recurse);
 }
 
 LayerManager* nsDisplayListBuilder::GetWidgetLayerManager(nsView** aView) {
   if (aView) {
     *aView = RootReferenceFrame()->GetView();
   }
   if (RootReferenceFrame() !=
       nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) {
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -485,41 +485,44 @@ static nsresult EnsureSettingsHasPrinter
   rv = printSettingsService->GetDefaultPrinterName(printerName);
   if (NS_SUCCEEDED(rv) && !printerName.IsEmpty()) {
     rv = aPrintSettings->SetPrinterName(printerName);
   }
   return rv;
 #endif
 }
 
-static CallState DocHasPrintCallbackCanvas(Document& aDoc, void* aData) {
+static bool DocHasPrintCallbackCanvas(Document& aDoc) {
   Element* root = aDoc.GetRootElement();
   if (!root) {
-    return CallState::Continue;
+    return false;
   }
+  // FIXME(emilio): This doesn't account for shadow dom and it's unnecessarily
+  // inefficient. Though I guess it doesn't really matter.
   RefPtr<nsContentList> canvases =
       NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("canvas"));
   uint32_t canvasCount = canvases->Length(true);
   for (uint32_t i = 0; i < canvasCount; ++i) {
-    HTMLCanvasElement* canvas =
-        HTMLCanvasElement::FromNodeOrNull(canvases->Item(i, false));
+    auto* canvas = HTMLCanvasElement::FromNodeOrNull(canvases->Item(i, false));
     if (canvas && canvas->GetMozPrintCallback()) {
-      // This subdocument has a print callback. Set result and return false to
-      // stop iteration.
-      *static_cast<bool*>(aData) = true;
+      return true;
+    }
+  }
+
+  bool result = false;
+
+  auto checkSubDoc = [&result](Document& aSubDoc) {
+    if (DocHasPrintCallbackCanvas(aSubDoc)) {
+      result = true;
       return CallState::Stop;
     }
-  }
-  return CallState::Continue;
-}
-
-static bool AnySubdocHasPrintCallbackCanvas(Document& aDoc) {
-  bool result = false;
-  aDoc.EnumerateSubDocuments(DocHasPrintCallbackCanvas,
-                             static_cast<void*>(&result));
+    return CallState::Continue;
+  };
+
+  aDoc.EnumerateSubDocuments(checkSubDoc);
   return result;
 }
 
 //-------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(nsPrintJob, nsIWebProgressListener, nsISupportsWeakReference,
                   nsIObserver)
 
@@ -582,21 +585,17 @@ nsresult nsPrintJob::Initialize(nsIDocum
     if (nsCOMPtr<nsIWebBrowserChrome> wbc = window->GetWebBrowserChrome()) {
       // We only get this in order to skip opening the progress dialog when
       // the window is modal.  Once the platform code stops opening the
       // progress dialog (bug 1558907), we can get rid of this.
       wbc->IsWindowModal(&mIsForModalWindow);
     }
   }
 
-  bool hasMozPrintCallback = false;
-  DocHasPrintCallbackCanvas(*aOriginalDoc,
-                            static_cast<void*>(&hasMozPrintCallback));
-  mHasMozPrintCallback =
-      hasMozPrintCallback || AnySubdocHasPrintCallbackCanvas(*aOriginalDoc);
+  mHasMozPrintCallback = DocHasPrintCallbackCanvas(*aOriginalDoc);
 
   return NS_OK;
 }
 
 //-------------------------------------------------------
 nsresult nsPrintJob::Cancel() {
   if (mPrt && mPrt->mPrintSettings) {
     return mPrt->mPrintSettings->SetIsCancelled(true);