From 79c7489bb0cfcb828b1e67164c5593142bfe3351 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Thu, 7 Mar 2024 12:24:06 +0100 Subject: [PATCH] Issue #2472 - Part 3: Refactor Fullscreen stack to Top Layer stack Fullscreen stack isn't part of the spec anymore; it's been changed to a more generic version called Top Layer stack, which is being used by both full-screen APIs and dialog elements. This patch refactors it to Top Layer stack so that it can be reused for dialog elements. Top Layer stack spec: https://fullscreen.spec.whatwg.org/#new-stacking-layer --- dom/base/DocumentOrShadowRoot.cpp | 2 +- dom/base/nsDocument.cpp | 204 ++++++++++++++++++----------- dom/base/nsDocument.h | 43 +++--- dom/base/nsIDocument.h | 10 +- layout/generic/nsViewportFrame.cpp | 4 +- 5 files changed, 163 insertions(+), 100 deletions(-) diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp index f420ff6a28..2850bdb2d6 100644 --- a/dom/base/DocumentOrShadowRoot.cpp +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -169,7 +169,7 @@ DocumentOrShadowRoot::GetFullscreenElement() return nullptr; } - Element* element = AsNode().OwnerDoc()->FullScreenStackTop(); + Element* element = AsNode().OwnerDoc()->GetUnretargetedFullScreenElement(); NS_ASSERTION(!element || element->State().HasState(NS_EVENT_STATE_FULL_SCREEN), "Fullscreen element should have fullscreen styles applied"); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 0c6950e422..68dfd7eebd 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -8481,7 +8481,7 @@ nsDocument::OnPageHide(bool aPersisted, EnumerateActivityObservers(NotifyActivityChanged, nullptr); ClearPendingFullscreenRequests(this); - if (FullScreenStackTop()) { + if (GetUnretargetedFullScreenElement()) { // 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 @@ -10112,6 +10112,19 @@ nsDocument::GetFullscreenRoot() return root; } +size_t +nsDocument::CountFullscreenElements() const { + size_t count = 0; + for (const nsWeakPtr& ptr : mTopLayer) { + if (nsCOMPtr elem = do_QueryReferent(ptr)) { + if (elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + count++; + } + } + } + return count; +} + void nsDocument::SetFullscreenRoot(nsIDocument* aRoot) { @@ -10175,7 +10188,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc) static bool CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) { - if (aDoc->FullScreenStackTop()) { + if (aDoc->GetUnretargetedFullScreenElement()) { uint32_t* count = static_cast(aData); (*count)++; } @@ -10195,7 +10208,7 @@ nsDocument::IsFullscreenLeaf() { // A fullscreen leaf document is fullscreen, and has no fullscreen // subdocuments. - if (!FullScreenStackTop()) { + if (!GetUnretargetedFullScreenElement()) { return false; } return CountFullscreenSubDocuments(this) == 0; @@ -10204,11 +10217,11 @@ nsDocument::IsFullscreenLeaf() static bool ResetFullScreen(nsIDocument* aDocument, void* aData) { - if (aDocument->FullScreenStackTop()) { + if (aDocument->GetUnretargetedFullScreenElement()) { NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, "Should have at most 1 fullscreen subdocument."); static_cast(aDocument)->CleanupFullscreenState(); - NS_ASSERTION(!aDocument->FullScreenStackTop(), + NS_ASSERTION(!aDocument->GetUnretargetedFullScreenElement(), "Should reset full-screen"); auto changed = reinterpret_cast*>(aData); changed->AppendElement(aDocument); @@ -10256,7 +10269,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) UnlockPointer(); nsCOMPtr root = aMaybeNotARootDoc->GetFullscreenRoot(); - if (!root || !root->FullScreenStackTop()) { + if (!root || !root->GetUnretargetedFullScreenElement()) { // If a document was detached before exiting from fullscreen, it is // possible that the root had left fullscreen state. In this case, // we would not get anything from the ResetFullScreen() call. Root's @@ -10285,7 +10298,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) DispatchFullScreenChange(changed[changed.Length() - i - 1]); } - NS_ASSERTION(!root->FullScreenStackTop(), + NS_ASSERTION(!root->GetUnretargetedFullScreenElement(), "Fullscreen root should no longer be a fullscreen doc..."); // Move the top-level window out of fullscreen mode. @@ -10302,7 +10315,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData) nsIDocument** result = static_cast(aData); *result = aDoc; return false; - } else if (aDoc->FullScreenStackTop()) { + } else if (aDoc->GetUnretargetedFullScreenElement()) { aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); } return true; @@ -10321,7 +10334,7 @@ GetFullscreenLeaf(nsIDocument* aDoc) nsIDocument* root = nsContentUtils::GetRootDocument(aDoc); // Check that the root is actually fullscreen so we don't waste time walking // around its descendants. - if (!root->FullScreenStackTop()) { + if (!root->GetUnretargetedFullScreenElement()) { return nullptr; } GetFullscreenLeaf(root, &leaf); @@ -10331,10 +10344,10 @@ GetFullscreenLeaf(nsIDocument* aDoc) void nsDocument::RestorePreviousFullScreenState() { - NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(), + NS_ASSERTION(!GetUnretargetedFullScreenElement() || !FullscreenRoots::IsEmpty(), "Should have at least 1 fullscreen root when fullscreen!"); - if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) { + if (!GetUnretargetedFullScreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) { return; } @@ -10350,10 +10363,10 @@ nsDocument::RestorePreviousFullScreenState() // Collect all ancestor documents which we are going to change. for (; doc; doc = doc->GetParentDocument()) { nsDocument* theDoc = static_cast(doc); - MOZ_ASSERT(!theDoc->mFullScreenStack.IsEmpty(), + Element* top = theDoc->GetUnretargetedFullScreenElement(); + MOZ_ASSERT(!top, "Ancestor of fullscreen document must also be in fullscreen"); if (doc != this) { - Element* top = theDoc->FullScreenStackTop(); if (top->IsHTMLElement(nsGkAtoms::iframe)) { if (static_cast(top)->FullscreenFlag()) { // If this is an iframe, and it explicitly requested @@ -10363,14 +10376,14 @@ nsDocument::RestorePreviousFullScreenState() } } exitDocs.AppendElement(theDoc); - if (theDoc->mFullScreenStack.Length() > 1) { + if (theDoc->CountFullscreenElements() > 1) { break; } } nsDocument* lastDoc = exitDocs.LastElement(); - if (!lastDoc->GetParentDocument() && - lastDoc->mFullScreenStack.Length() == 1) { + size_t fullscreenCount = lastDoc->CountFullscreenElements(); + if (!lastDoc->GetParentDocument() && fullscreenCount == 1) { // If we are fully exiting fullscreen, don't touch anything here, // just wait for the window to get out from fullscreen first. AskWindowToExitFullscreen(this); @@ -10387,8 +10400,8 @@ nsDocument::RestorePreviousFullScreenState() // The last document will either rollback one fullscreen element, or // completely exit from the fullscreen state as well. nsIDocument* newFullscreenDoc; - if (lastDoc->mFullScreenStack.Length() > 1) { - lastDoc->FullScreenStackPop(); + if (fullscreenCount > 1) { + lastDoc->UnsetFullscreenElement(); newFullscreenDoc = lastDoc; } else { lastDoc->CleanupFullscreenState(); @@ -10482,93 +10495,131 @@ ClearFullscreenStateOnElement(Element* aElement) void nsDocument::CleanupFullscreenState() { - // Iterate the fullscreen stack and clear the fullscreen states. + // Iterate the top layer and clear the fullscreen states. // Since we also need to clear the fullscreen-ancestor state, and // currently fullscreen elements can only be placed in hierarchy // order in the stack, reversely iterating the stack could be more // efficient. NOTE that fullscreen-ancestor state would be removed // in bug 1199529, and the elements may not in hierarchy order // after bug 1195213. - for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) { - if (nsCOMPtr element = do_QueryReferent(weakPtr)) { - ClearFullscreenStateOnElement(element); + mTopLayer.RemoveElementsBy([&](const nsWeakPtr& weakPtr) { + nsCOMPtr element(do_QueryReferent(weakPtr)); + if (!element || !element->IsInComposedDoc() || + element->OwnerDoc() != this) { + return true; } - } - mFullScreenStack.Clear(); + + if (element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + ClearFullscreenStateOnElement(element); + return true; + } + return false; + }); + mFullscreenRoot = nullptr; UpdateViewportScrollbarOverrideForFullscreen(this); } -bool -nsDocument::FullScreenStackPush(Element* aElement) -{ - NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()"); - Element* top = FullScreenStackTop(); - if (top == aElement || !aElement) { - return false; - } - EventStateManager::SetFullScreenState(aElement, true); - mFullScreenStack.AppendElement(do_GetWeakReference(aElement)); - NS_ASSERTION(FullScreenStackTop() == aElement, "Should match"); +void +nsDocument::UnsetFullscreenElement() { + Element* removedElement = TopLayerPop([](Element* element) -> bool { + return element->State().HasState(NS_EVENT_STATE_FULL_SCREEN); + }); + + MOZ_ASSERT(removedElement->State().HasState(NS_EVENT_STATE_FULL_SCREEN)); + ClearFullscreenStateOnElement(removedElement); UpdateViewportScrollbarOverrideForFullscreen(this); +} + +bool +nsDocument::SetFullscreenElement(Element* aElement) { + if (TopLayerPush(aElement)) { + EventStateManager::SetFullScreenState(aElement, true); + UpdateViewportScrollbarOverrideForFullscreen(this); + return true; + } + return false; +} + +bool +nsDocument::TopLayerPush(Element* aElement) { + NS_ASSERTION(aElement, "Must pass non-null to TopLayerPush()"); + auto predictFunc = [&aElement](Element* element) { + return element == aElement; + }; + TopLayerPop(predictFunc); + + mTopLayer.AppendElement(do_GetWeakReference(aElement)); + NS_ASSERTION(GetTopLayerTop() == aElement, "Should match"); return true; } -void -nsDocument::FullScreenStackPop() -{ - if (mFullScreenStack.IsEmpty()) { - return; +Element* +nsDocument::TopLayerPop(FunctionRef aPredicateFunc) { + if (mTopLayer.IsEmpty()) { + return nullptr; } - ClearFullscreenStateOnElement(FullScreenStackTop()); - - // Remove top element. Note the remaining top element in the stack - // will not have full-screen style bits set, so we will need to restore - // them on the new top element before returning. - uint32_t last = mFullScreenStack.Length() - 1; - mFullScreenStack.RemoveElementAt(last); + // Remove the topmost element that qualifies aPredicate; This + // is required is because the top layer contains not only + // fullscreen elements, but also dialog elements. + Element* removedElement; + for (auto i : Reversed(MakeRange(mTopLayer.Length()))) { + nsCOMPtr element(do_QueryReferent(mTopLayer[i])); + if (element && aPredicateFunc(element)) { + removedElement = element; + mTopLayer.RemoveElementAt(i); + break; + } + } // Pop from the stack null elements (references to elements which have // been GC'd since they were added to the stack) and elements which are // no longer in this document. - while (!mFullScreenStack.IsEmpty()) { - Element* element = FullScreenStackTop(); + while (!mTopLayer.IsEmpty()) { + Element* element = GetTopLayerTop(); if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) { - NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN), - "Should have already removed full-screen styles"); - uint32_t last = mFullScreenStack.Length() - 1; - mFullScreenStack.RemoveElementAt(last); + mTopLayer.RemoveElementAt(mTopLayer.Length() - 1); } else { // The top element of the stack is now an in-doc element. Return here. break; } } - UpdateViewportScrollbarOverrideForFullscreen(this); + return removedElement; } Element* -nsDocument::FullScreenStackTop() -{ - if (mFullScreenStack.IsEmpty()) { +nsDocument::GetTopLayerTop() { + if (mTopLayer.IsEmpty()) { return nullptr; } - uint32_t last = mFullScreenStack.Length() - 1; - nsCOMPtr element(do_QueryReferent(mFullScreenStack[last])); - NS_ASSERTION(element, "Should have full-screen element!"); - NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc"); - NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); + uint32_t last = mTopLayer.Length() - 1; + nsCOMPtr element(do_QueryReferent(mTopLayer[last])); + NS_ASSERTION(element, "Should have a top layer element!"); + NS_ASSERTION(element->IsInComposedDoc(), "Top layer element should be in doc"); + NS_ASSERTION(element->OwnerDoc() == this, "Top layer element should be in this doc"); return element; } -/* virtual */ nsTArray -nsDocument::GetFullscreenStack() const -{ +Element* +nsDocument::GetUnretargetedFullScreenElement() { + for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) { + nsCOMPtr element(do_QueryReferent(weakPtr)); + // Per spec, the fullscreen element is the topmost element in the document's + // top layer of which the fullscreen flag is set, if any, and null otherwise. + if (element && element->State().HasState(NS_EVENT_STATE_FULL_SCREEN)) { + return element; + } + } + return nullptr; +} + +nsTArray +nsDocument::GetTopLayer() const { nsTArray elements; - for (const nsWeakPtr& ptr : mFullScreenStack) { + for (const nsWeakPtr& ptr : mTopLayer) { if (nsCOMPtr elem = do_QueryReferent(ptr)) { - MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)); elements.AppendElement(elem); } } @@ -10683,7 +10734,8 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement, { NS_ASSERTION(aElement, "Must pass non-null element to nsDocument::RequestFullScreen"); - if (!aElement || aElement == FullScreenStackTop()) { + Element* fullscreenElement = GetUnretargetedFullScreenElement(); + if (!aElement || aElement == fullscreenElement) { return false; } if (!aElement->IsInComposedDoc()) { @@ -10712,9 +10764,9 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement, } //XXXsmaug Note, we don't follow the latest fullscreen spec here. // This whole check could be probably removed. - if (FullScreenStackTop() && + if (fullscreenElement && !nsContentUtils::ContentIsHostIncludingDescendantOf(aElement, - FullScreenStackTop())) { + fullscreenElement)) { // If this document is full-screen, only grant full-screen requests from // a descendant of the current full-screen element. DispatchFullscreenError("FullscreenDeniedNotDescendant"); @@ -10871,7 +10923,7 @@ ShouldApplyFullscreenDirectly(nsIDocument* aDoc, // If we are in the content process, we can apply the fullscreen // state directly only if we have been in DOM fullscreen, because // otherwise we always need to notify the chrome. - return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement(); + return !!nsContentUtils::GetRootDocument(aDoc)->GetUnretargetedFullScreenElement(); } else { // If we are in the chrome process, and the window has not been in // fullscreen, we certainly need to make that fullscreen first. @@ -10994,8 +11046,8 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest) // Set the full-screen element. This sets the full-screen style on the // element, and the full-screen-ancestor styles on ancestors of the element // in this document. - DebugOnly x = FullScreenStackPush(elem); - NS_ASSERTION(x, "Full-screen state of requesting doc should always change!"); + DebugOnly x = SetFullscreenElement(elem); + MOZ_ASSERT(x, "Full-screen state of requesting doc should always change!"); // Set the iframe fullscreen flag. if (elem->IsHTMLElement(nsGkAtoms::iframe)) { static_cast(elem)->SetFullscreenFlag(true); @@ -11017,14 +11069,14 @@ nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest) } nsIDocument* parent = child->GetParentDocument(); Element* element = parent->FindContentForSubDocument(child)->AsElement(); - if (static_cast(parent)->FullScreenStackPush(element)) { + if (static_cast(parent)->SetFullscreenElement(element)) { changed.AppendElement(parent); child = parent; } else { // We've reached either the root, or a point in the doctree where the // new full-screen element container is the same as the previous // full-screen element's container. No more changes need to be made - // to the full-screen stacks of documents further up the tree. + // to the top layer of documents further up the tree. break; } } @@ -11294,7 +11346,7 @@ PointerLockRequest::Run() } // If it is neither user input initiated, nor requested in fullscreen, // it should be rejected. - if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) { + if (!error && !mUserInputOrChromeCaller && !doc->GetUnretargetedFullScreenElement()) { error = "PointerLockDeniedNotInputDriven"; } if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) { diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 22157500a0..bb05a4fb9b 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -56,6 +56,7 @@ #include "imgIRequest.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStates.h" +#include "mozilla/FunctionRef.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PendingAnimationTracker.h" #include "mozilla/dom/DOMImplementation.h" @@ -77,6 +78,7 @@ #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2) #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3) +using namespace mozilla; class nsDOMStyleSheetSetList; class nsDocument; @@ -638,7 +640,7 @@ public: using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName; using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS; using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName; - + // nsIDOMEventTarget virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; @@ -850,7 +852,7 @@ public: virtual Element* FindImageMap(const nsAString& aNormalizedMapName) override; - virtual nsTArray GetFullscreenStack() const override; + virtual nsTArray GetTopLayer() const override; virtual void AsyncRequestFullScreen( mozilla::UniquePtr&& aRequest) override; virtual void RestorePreviousFullScreenState() override; @@ -860,6 +862,7 @@ public: virtual nsresult RemoteFrameFullscreenReverted() override; virtual nsIDocument* GetFullscreenRoot() override; + virtual size_t CountFullscreenElements() const override; virtual void SetFullscreenRoot(nsIDocument* aRoot) override; // Returns the size of the mBlockedTrackingNodes array. (nsIDocument.h) @@ -896,23 +899,29 @@ public: // to move this document into full-screen mode if allowed. void RequestFullScreen(mozilla::UniquePtr&& aRequest); - // Removes all elements from the full-screen stack, removing full-scren - // styles from the top element in the stack. + // Removes all elements with the full-screen flag set from the top layer, and + // clears their full-screen flag. void CleanupFullscreenState(); - // Pushes aElement onto the full-screen stack, and removes full-screen styles - // from the former full-screen stack top, and its ancestors, and applies the - // styles to aElement. aElement becomes the new "full-screen element". - bool FullScreenStackPush(Element* aElement); + // Pushes aElement onto the top layer + bool TopLayerPush(Element* aElement); - // Remove the top element from the full-screen stack. Removes the full-screen - // styles from the former top element, and applies them to the new top - // element, if there is one. - void FullScreenStackPop(); + // Removes the topmost element which have aPredicate return true from the top + // layer. The removed element, if any, is returned. + Element* TopLayerPop(FunctionRef aPredicateFunc); + + // Pops the fullscreen element from the top layer and clears its + // fullscreen flag. + void UnsetFullscreenElement(); + + // Pushes the given element into the top of top layer and set fullscreen + // flag. + bool SetFullscreenElement(Element* aElement); // Returns the top element from the full-screen stack. - Element* FullScreenStackTop() override; - + Element* GetTopLayerTop() override; + // Return the fullscreen element in the top layer + Element* GetUnretargetedFullScreenElement() override; // DOM-exposed fullscreen API bool FullscreenEnabled() override; @@ -1193,10 +1202,8 @@ protected: // is a weak reference to avoid leaks due to circular references. nsWeakPtr mScopeObject; - // Stack of full-screen elements. When we request full-screen we push the - // full-screen element onto this stack, and when we cancel full-screen we - // pop one off this stack, restoring the previous full-screen state - nsTArray mFullScreenStack; + // Stack of top layer elements. + nsTArray mTopLayer; // The root of the doc tree in which this document is in. This is only // non-null when this document is in fullscreen mode. diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 90c0668e2e..81b5b51c8e 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -33,6 +33,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/CORSMode.h" #include "mozilla/dom/DocumentOrShadowRoot.h" +#include "mozilla/FunctionRef.h" #include "mozilla/LinkedList.h" #include "mozilla/StyleSheet.h" #include "mozilla/TimeStamp.h" @@ -1273,9 +1274,9 @@ public: virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0; /** - * Returns all elements in the fullscreen stack in the insertion order. + * Returns all elements in the top layer in the insertion order. */ - virtual nsTArray GetFullscreenStack() const = 0; + virtual nsTArray GetTopLayer() const = 0; /** * Asynchronously requests that the document make aElement the fullscreen @@ -1331,6 +1332,8 @@ public: */ virtual nsIDocument* GetFullscreenRoot() = 0; + virtual size_t CountFullscreenElements() const = 0; + /** * Sets the fullscreen root to aRoot. This stores a weak reference to aRoot * in this document. @@ -2607,7 +2610,8 @@ public: nsIURI* GetDocumentURIObject() const; // Not const because all the full-screen goop is not const virtual bool FullscreenEnabled() = 0; - virtual Element* FullScreenStackTop() = 0; + virtual Element* GetTopLayerTop() = 0; + virtual Element* GetUnretargetedFullScreenElement() = 0; bool Fullscreen() { return !!GetFullscreenElement(); diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index ec933b84e3..ab3bfc88c3 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -123,8 +123,8 @@ ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder, nsDisplayList* aList) { nsIDocument* doc = PresContext()->Document(); - nsTArray fullscreenStack = doc->GetFullscreenStack(); - for (Element* elem : fullscreenStack) { + nsTArray toplayer = doc->GetTopLayer(); + for (Element* elem : toplayer) { if (nsIFrame* frame = elem->GetPrimaryFrame()) { // There are two cases where an element in fullscreen is not in // the top layer: