Four places walked an option's ancestors to work out which select or optgroup it belongs to, each with its own copy of the boundary and nested-optgroup rules: HTMLOptionsCollection::IsValidOption, HTMLOptionElement's disabled state and cached nearest ancestor select, and HTMLSelectElement::IsOptionDisabled. This change generalises HTMLOptionElement::ComputeNearestAncestorSelect into a single HTMLSelectElement::ComputeNearestAncestors, which takes any node and returns both the nearest ancestor select and the optgroup that groups it, and refactors all four callers to use it. IsOptionListBoundary moves alongside it, so that the whole option list concept lives in HTMLSelectElement. Differential Revision: https://phabricator.services.mozilla.com/D322996
90 lines
3.0 KiB
C++
90 lines
3.0 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/dom/HTMLOptGroupElement.h"
|
|
|
|
#include "mozilla/EventDispatcher.h"
|
|
#include "mozilla/dom/HTMLOptGroupElementBinding.h"
|
|
#include "mozilla/dom/HTMLOptionElement.h"
|
|
#include "mozilla/dom/HTMLSelectElement.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIFrame.h"
|
|
|
|
NS_IMPL_NS_NEW_HTML_ELEMENT(OptGroup)
|
|
|
|
namespace mozilla::dom {
|
|
|
|
/**
|
|
* The implementation of <optgroup>
|
|
*/
|
|
|
|
HTMLOptGroupElement::HTMLOptGroupElement(
|
|
already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo)
|
|
: nsGenericHTMLElement(std::move(aNodeInfo)) {
|
|
// We start off enabled
|
|
AddStatesSilently(ElementState::ENABLED);
|
|
}
|
|
|
|
HTMLOptGroupElement::~HTMLOptGroupElement() = default;
|
|
|
|
NS_IMPL_ELEMENT_CLONE(HTMLOptGroupElement)
|
|
|
|
void HTMLOptGroupElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|
aVisitor.mCanHandle = false;
|
|
nsGenericHTMLElement::GetEventTargetParent(aVisitor);
|
|
}
|
|
|
|
HTMLSelectElement* HTMLOptGroupElement::GetSelect() const {
|
|
return HTMLSelectElement::FromNodeOrNull(GetParentNode());
|
|
}
|
|
|
|
void HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
bool aNotify) {
|
|
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
|
|
ElementState disabledStates;
|
|
if (aValue) {
|
|
disabledStates |= ElementState::DISABLED;
|
|
} else {
|
|
disabledStates |= ElementState::ENABLED;
|
|
}
|
|
|
|
ElementState oldDisabledStates = State() & ElementState::DISABLED_STATES;
|
|
ElementState changedStates = disabledStates ^ oldDisabledStates;
|
|
|
|
if (!changedStates.IsEmpty()) {
|
|
ToggleStates(changedStates, aNotify);
|
|
|
|
// Descendant options have their :disabled state depending on our
|
|
// disabled attribute. Walk descendants, skipping boundary elements
|
|
// whose children are not affected by this optgroup's disabled state.
|
|
for (nsIContent* node = GetFirstChild(); node;) {
|
|
if (auto* opt = HTMLOptionElement::FromNode(node)) {
|
|
opt->OptGroupDisabledChanged(true);
|
|
node = node->GetNextNonChildNode(this);
|
|
continue;
|
|
}
|
|
if (HTMLSelectElement::IsOptionListBoundary(*node) ||
|
|
node->IsHTMLElement(nsGkAtoms::optgroup)) {
|
|
node = node->GetNextNonChildNode(this);
|
|
continue;
|
|
}
|
|
node = node->GetNextNode(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nsGenericHTMLElement::AfterSetAttr(
|
|
aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
|
|
}
|
|
|
|
JSObject* HTMLOptGroupElement::WrapNode(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return HTMLOptGroupElement_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
} // namespace mozilla::dom
|