commit 6d5ada44aabf2066d1f4144e529b45da0fd13ad7
parent bc955825b5b449a15d3597f15bf966bd5f17624b
Author: Keith Cirkel <keithamus@users.noreply.github.com>
Date: Mon, 15 Dec 2025 22:54:19 +0000
Bug 1867743 - Part 6: Refactor popover methods to no longer hardcode to Auto mode r=dom-core,smaug
The methods CloseEntirePopoverList, HidePopoverStackUntil, AutoPopoverList,
GetTopmostAutoPopover, and IsAutoPopover all either take a popover list (of hint
or auto) or have equivalent methods (e.g. HintPopoverList). In order to
introduce popover=hint, this change refactors these methods to take a
PopoverAttributeState which determines the "opened in mode" to filter the
popover list by, ensuring that these methods can act on either Auto or Hint
popovers.
This change has no behavioural changes - everything should function the
same - but it is preparatory work for popover=hint.
Differential Revision: https://phabricator.services.mozilla.com/D276372
Diffstat:
8 files changed, 69 insertions(+), 45 deletions(-)
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
@@ -15353,7 +15353,7 @@ void Document::HandleEscKey() {
for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
if (RefPtr popoverHTMLEl = nsGenericHTMLElement::FromNodeOrNull(element)) {
- if (element->IsAutoPopover() && element->IsPopoverOpen()) {
+ if (element->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
popoverHTMLEl->HidePopover(IgnoreErrors());
return;
}
@@ -16059,13 +16059,15 @@ bool Document::TopLayerContains(Element& aElement) const {
}
// https://html.spec.whatwg.org/#close-entire-popover-list
-void Document::CloseEntirePopoverList(bool aFocusPreviousElement,
+void Document::CloseEntirePopoverList(PopoverAttributeState aMode,
+ bool aFocusPreviousElement,
bool aFireEvents) {
// 1. While popoverList is not empty:
- while (RefPtr<Element> topmost = GetTopmostAutoPopover()) {
+ // XXX: Rather than computing the list, find from top layer elements
+ while (RefPtr popover = GetTopmostPopoverOf(aMode)) {
// 1.1. Run the hide popover algorithm given popoverList's last item,
// focusPreviousElement, fireEvents, false, and null.
- HidePopover(*topmost, aFocusPreviousElement, aFireEvents,
+ HidePopover(*popover, aFocusPreviousElement, aFireEvents,
/* aSource */ nullptr, IgnoreErrors());
}
}
@@ -16096,7 +16098,8 @@ void Document::HideAllPopoversUntil(nsINode& aEndpoint,
// list, focusPreviousElement, and fireEvents.
// 5.2. Run close entire popover list given document's showing auto popover
// list, focusPreviousElement, and fireEvents.
- CloseEntirePopoverList(aFocusPreviousElement, aFireEvents);
+ CloseEntirePopoverList(PopoverAttributeState::Auto, aFocusPreviousElement,
+ aFireEvents);
// 5.3. Return.
return;
}
@@ -16115,15 +16118,17 @@ void Document::HideAllPopoversUntil(nsINode& aEndpoint,
// 9. Run hide popover stack until given endpoint, document's showing auto
// popover list, focusPreviousElement, and fireEvents.
- HidePopoverStackUntil(aEndpoint, aFocusPreviousElement, aFireEvents);
+ HidePopoverStackUntil(PopoverAttributeState::Auto, aEndpoint,
+ aFocusPreviousElement, aFireEvents);
}
// https://html.spec.whatwg.org/#hide-popover-stack-until
-void Document::HidePopoverStackUntil(nsINode& aEndpoint,
+void Document::HidePopoverStackUntil(PopoverAttributeState aMode,
+ nsINode& aEndpoint,
bool aFocusPreviousElement,
bool aFireEvents) {
auto needRepeatingHide = [&]() {
- auto autoList = AutoPopoverList();
+ auto autoList = PopoverListOf(aMode);
return autoList.Contains(&aEndpoint) &&
&aEndpoint != autoList.LastElement();
};
@@ -16138,7 +16143,7 @@ void Document::HidePopoverStackUntil(nsINode& aEndpoint,
RefPtr<const Element> lastToHide = nullptr;
bool foundEndpoint = false;
// 2.2. For each popover in popoverList:
- for (const Element* popover : AutoPopoverList()) {
+ for (const Element* popover : PopoverListOf(aMode)) {
// 2.2.1. If popover is endpoint, then break.
// todo(keithamus): Get this logic closer to spec.
if (popover == &aEndpoint) {
@@ -16152,7 +16157,8 @@ void Document::HidePopoverStackUntil(nsINode& aEndpoint,
// 2.3. If lastToHide is null, then return.
if (!foundEndpoint) {
- CloseEntirePopoverList(aFocusPreviousElement, fireEvents);
+ CloseEntirePopoverList(PopoverAttributeState::Auto, aFocusPreviousElement,
+ fireEvents);
return;
}
@@ -16163,7 +16169,8 @@ void Document::HidePopoverStackUntil(nsINode& aEndpoint,
// 2.4.2. Run the hide popover algorithm given the last item in
// popoverList, focusPreviousElement, fireEvents, false, and null.
- RefPtr<Element> topmost = GetTopmostAutoPopover();
+ RefPtr<Element> topmost =
+ GetTopmostPopoverOf(PopoverAttributeState::Auto);
if (!topmost) {
break;
}
@@ -16247,14 +16254,16 @@ void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
// See, https://github.com/whatwg/html/issues/9197
// If popoverHTMLEl is not on top, hide popovers again without firing
// events.
- if (NS_WARN_IF(GetTopmostAutoPopover() != popoverHTMLEl)) {
+ if (NS_WARN_IF(GetTopmostPopoverOf(PopoverAttributeState::Auto) !=
+ popoverHTMLEl)) {
HideAllPopoversUntil(*popoverHTMLEl, aFocusPreviousElement, false);
if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
nullptr, aRv)) {
return;
}
- MOZ_ASSERT(GetTopmostAutoPopover() == popoverHTMLEl,
- "popoverHTMLEl should be on top of auto popover list");
+ MOZ_ASSERT(
+ GetTopmostPopoverOf(PopoverAttributeState::Auto) == popoverHTMLEl,
+ "popoverHTMLEl should be on top of auto popover list");
}
}
@@ -16276,9 +16285,8 @@ void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
// auto popover list's last item is not element, then run hide all popovers
// until given element, focusPreviousElement, and false. Hide all popovers
// when beforetoggle shows a popover.
- if (popoverHTMLEl->IsAutoPopover() &&
- GetTopmostAutoPopover() != popoverHTMLEl &&
- popoverHTMLEl->PopoverOpen()) {
+ if (popoverHTMLEl->IsPopoverOpenedInMode(PopoverAttributeState::Auto) &&
+ GetTopmostPopoverOf(PopoverAttributeState::Auto) != popoverHTMLEl) {
HideAllPopoversUntil(*popoverHTMLEl, aFocusPreviousElement, false);
}
@@ -16332,11 +16340,11 @@ void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
}
}
-nsTArray<Element*> Document::AutoPopoverList() const {
+nsTArray<Element*> Document::PopoverListOf(PopoverAttributeState aMode) const {
nsTArray<Element*> elements;
for (const nsWeakPtr& ptr : mTopLayer) {
if (nsCOMPtr<Element> element = do_QueryReferent(ptr)) {
- if (element && element->IsAutoPopover() && element->IsPopoverOpen()) {
+ if (element && element->IsPopoverOpenedInMode(aMode)) {
elements.AppendElement(element);
}
}
@@ -16344,10 +16352,10 @@ nsTArray<Element*> Document::AutoPopoverList() const {
return elements;
}
-Element* Document::GetTopmostAutoPopover() const {
+Element* Document::GetTopmostPopoverOf(PopoverAttributeState aMode) const {
for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
- if (element && element->IsAutoPopover() && element->IsPopoverOpen()) {
+ if (element && element->IsPopoverOpenedInMode(aMode)) {
return element;
}
}
@@ -16743,7 +16751,8 @@ bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
Element* elem = aRequest->Element();
- RefPtr<nsINode> hideUntil = elem->GetTopmostPopoverAncestor(nullptr, false);
+ RefPtr<nsINode> hideUntil = elem->GetTopmostPopoverAncestor(
+ PopoverAttributeState::Auto, nullptr, false);
if (!hideUntil) {
hideUntil = OwnerDoc();
}
diff --git a/dom/base/Document.h b/dom/base/Document.h
@@ -270,6 +270,7 @@ class NodeFilter;
class NodeInfo;
class NodeIterator;
enum class OrientationType : uint8_t;
+enum class PopoverAttributeState : uint8_t;
class ProcessingInstruction;
class Promise;
class ScriptLoader;
@@ -3585,7 +3586,8 @@ class Document : public nsINode,
Nullable<Wireframe>&);
// https://html.spec.whatwg.org/#close-entire-popover-list
- MOZ_CAN_RUN_SCRIPT void CloseEntirePopoverList(bool aFocusPreviousElement,
+ MOZ_CAN_RUN_SCRIPT void CloseEntirePopoverList(PopoverAttributeState aMode,
+ bool aFocusPreviousElement,
bool aFireEvents);
// Hides all popovers until the given end point, see
@@ -3596,7 +3598,8 @@ class Document : public nsINode,
// Hides all popovers, until the given end point, see
// https://html.spec.whatwg.org/#hide-popover-stack-until
- MOZ_CAN_RUN_SCRIPT void HidePopoverStackUntil(nsINode& aEndpoint,
+ MOZ_CAN_RUN_SCRIPT void HidePopoverStackUntil(PopoverAttributeState aMode,
+ nsINode& aEndpoint,
bool aFocusPreviousElement,
bool aFireEvents);
@@ -3608,14 +3611,15 @@ class Document : public nsINode,
ErrorResult& aRv);
// Returns a list of all the elements in the Document's top layer whose
- // popover attribute is in the auto state.
+ // popover opened in mode is in the given state.
// See https://html.spec.whatwg.org/multipage/popover.html#auto-popover-list
- nsTArray<Element*> AutoPopoverList() const;
+ // See https://html.spec.whatwg.org/#showing-hint-popover-list
+ nsTArray<Element*> PopoverListOf(PopoverAttributeState aMode) const;
- // Return document's auto popover list's last element.
+ // Return document's popover list's last element of a particular mode.
// See
// https://html.spec.whatwg.org/multipage/popover.html#topmost-auto-popover
- Element* GetTopmostAutoPopover() const;
+ Element* GetTopmostPopoverOf(PopoverAttributeState aMode) const;
void AddPopoverToTopLayer(Element&);
void RemovePopoverFromTopLayer(Element&);
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
@@ -4994,10 +4994,10 @@ void Element::ClearServoData(Document* aDoc) {
}
}
-bool Element::IsAutoPopover() const {
+bool Element::IsPopoverOpenedInMode(PopoverAttributeState aMode) const {
const auto* htmlElement = nsGenericHTMLElement::FromNode(this);
- return htmlElement &&
- htmlElement->GetPopoverAttributeState() == PopoverAttributeState::Auto;
+ return htmlElement && htmlElement->PopoverOpen() &&
+ htmlElement->GetPopoverData()->GetOpenedInMode() == aMode;
}
bool Element::IsPopoverOpen() const {
@@ -5025,13 +5025,14 @@ nsGenericHTMLElement* Element::GetAssociatedPopover() const {
return nullptr;
}
-Element* Element::GetTopmostPopoverAncestor(const Element* aInvoker,
+Element* Element::GetTopmostPopoverAncestor(PopoverAttributeState aMode,
+ const Element* aInvoker,
bool isPopover) const {
const Element* newPopover = this;
nsTHashMap<nsPtrHashKey<const Element>, size_t> popoverPositions;
size_t index = 0;
- for (Element* popover : OwnerDoc()->AutoPopoverList()) {
+ for (Element* popover : OwnerDoc()->PopoverListOf(aMode)) {
popoverPositions.LookupOrInsert(popover, index++);
}
diff --git a/dom/base/Element.h b/dom/base/Element.h
@@ -150,6 +150,7 @@ class Optional;
enum class CallerType : uint32_t;
enum class ReferrerPolicy : uint8_t;
enum class FetchPriority : uint8_t;
+enum class PopoverAttributeState : uint8_t;
} // namespace dom
} // namespace mozilla
@@ -610,7 +611,7 @@ class Element : public FragmentOrElement {
return CreatePopoverData();
}
- bool IsAutoPopover() const;
+ bool IsPopoverOpenedInMode(PopoverAttributeState aMode) const;
bool IsPopoverOpen() const;
void SetAssociatedPopover(nsGenericHTMLElement& aPopover);
@@ -619,7 +620,8 @@ class Element : public FragmentOrElement {
/**
* https://html.spec.whatwg.org/multipage/popover.html#topmost-popover-ancestor
*/
- Element* GetTopmostPopoverAncestor(const Element* aInvoker,
+ Element* GetTopmostPopoverAncestor(PopoverAttributeState aMode,
+ const Element* aInvoker,
bool isPopover) const;
ElementAnimationData* GetAnimationData() const {
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
@@ -3554,7 +3554,7 @@ Element* nsINode::GetParentFlexElement() {
Element* nsINode::GetNearestInclusiveOpenPopover() const {
for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
- if (el->IsAutoPopover() && el->IsPopoverOpen()) {
+ if (el->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
return el;
}
}
@@ -3564,12 +3564,12 @@ Element* nsINode::GetNearestInclusiveOpenPopover() const {
Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const {
for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) {
if (auto* popover = el->GetEffectiveCommandForElement()) {
- if (popover->IsAutoPopover() && popover->IsPopoverOpen()) {
+ if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
return popover;
}
}
if (auto* popover = el->GetEffectivePopoverTargetElement()) {
- if (popover->IsAutoPopover() && popover->IsPopoverOpen()) {
+ if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
return popover;
}
}
@@ -3622,7 +3622,8 @@ Element* nsINode::GetTopmostClickedPopover() const {
if (!clickedPopover) {
return invokedPopover;
}
- auto autoPopoverList = clickedPopover->OwnerDoc()->AutoPopoverList();
+ auto autoPopoverList =
+ clickedPopover->OwnerDoc()->PopoverListOf(PopoverAttributeState::Auto);
for (Element* el : Reversed(autoPopoverList)) {
if (el == clickedPopover || el == invokedPopover) {
return el;
diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
@@ -68,6 +68,7 @@
#include "mozilla/dom/HTMLLabelElement.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/PointerEventHandler.h"
+#include "mozilla/dom/PopoverData.h"
#include "mozilla/dom/Record.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/UIEvent.h"
@@ -1529,7 +1530,8 @@ void EventStateManager::LightDismissOpenPopovers(WidgetEvent* aEvent,
return;
}
- Element* topmostPopover = aTargetContent->OwnerDoc()->GetTopmostAutoPopover();
+ Element* topmostPopover = aTargetContent->OwnerDoc()->GetTopmostPopoverOf(
+ PopoverAttributeState::Auto);
if (!topmostPopover) {
return;
}
diff --git a/dom/html/HTMLDialogElement.cpp b/dom/html/HTMLDialogElement.cpp
@@ -314,7 +314,8 @@ void HTMLDialogElement::Show(ErrorResult& aError) {
// 9. Let hideUntil be the result of running topmost popover ancestor given
// this, document's showing hint popover list, null, and false.
- RefPtr<nsINode> hideUntil = GetTopmostPopoverAncestor(nullptr, false);
+ RefPtr<nsINode> hideUntil =
+ GetTopmostPopoverAncestor(PopoverAttributeState::Auto, nullptr, false);
// 10. If hideUntil is null, then set hideUntil to the result of running
// topmost popover ancestor given this, document's showing auto popover list,
@@ -492,7 +493,8 @@ void HTMLDialogElement::ShowModal(Element* aSource, ErrorResult& aError) {
// 18. Let hideUntil be the result of running topmost popover ancestor given
// subject, document's showing hint popover list, null, and false.
- RefPtr<nsINode> hideUntil = GetTopmostPopoverAncestor(nullptr, false);
+ RefPtr<nsINode> hideUntil =
+ GetTopmostPopoverAncestor(PopoverAttributeState::Auto, nullptr, false);
// 19. If hideUntil is null, then set hideUntil to the result of running
// topmost popover ancestor given subject, document's showing auto popover
diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp
@@ -3486,7 +3486,8 @@ void nsGenericHTMLElement::ShowPopoverInternal(Element* aSource,
// 16.2. Let ancestor be the result of running the topmost popover ancestor
// algorithm given element, document's showing auto popover list, source,
// and true.
- RefPtr<nsINode> ancestor = GetTopmostPopoverAncestor(aSource, true);
+ RefPtr<nsINode> ancestor =
+ GetTopmostPopoverAncestor(PopoverAttributeState::Auto, aSource, true);
// 16.3. If ancestor is null, then set ancestor to document.
if (!ancestor) {
@@ -3545,13 +3546,15 @@ void nsGenericHTMLElement::ShowPopoverInternal(Element* aSource,
// 18.4. If the result of running topmost
// auto or hint popover on document is null, then set shouldRestoreFocus to
// true.
- shouldRestoreFocus = !document->GetTopmostAutoPopover();
+ shouldRestoreFocus =
+ !document->GetTopmostPopoverOf(PopoverAttributeState::Auto);
// 18.5. If stackToAppendTo is "auto":
if (stackToAppendTo == PopoverAttributeState::Auto) {
// 18.5.1. Assert: document's showing auto popover list does not contain
// element.
- MOZ_ASSERT(!document->AutoPopoverList().Contains(this));
+ MOZ_ASSERT(
+ !document->PopoverListOf(PopoverAttributeState::Auto).Contains(this));
// 18.5.2. Set element's opened in popover mode to "auto".
GetPopoverData()->SetOpenedInMode(PopoverAttributeState::Auto);