Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / html / HTMLFieldSetElement.cpp
blob828bed9c8d1a6f481fa903dec1babf677b7cd5d2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/BasicEvents.h"
8 #include "mozilla/EventDispatcher.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/StaticPrefs_dom.h"
11 #include "mozilla/dom/CustomElementRegistry.h"
12 #include "mozilla/dom/HTMLFieldSetElement.h"
13 #include "mozilla/dom/HTMLFieldSetElementBinding.h"
14 #include "nsContentList.h"
15 #include "nsQueryObject.h"
17 NS_IMPL_NS_NEW_HTML_ELEMENT(FieldSet)
19 namespace mozilla::dom {
21 HTMLFieldSetElement::HTMLFieldSetElement(
22 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
23 : nsGenericHTMLFormControlElement(std::move(aNodeInfo),
24 FormControlType::Fieldset),
25 mElements(nullptr),
26 mFirstLegend(nullptr),
27 mInvalidElementsCount(0) {
28 // <fieldset> is always barred from constraint validation.
29 SetBarredFromConstraintValidation(true);
31 // We start out enabled and valid.
32 AddStatesSilently(ElementState::ENABLED | ElementState::VALID);
35 HTMLFieldSetElement::~HTMLFieldSetElement() {
36 uint32_t length = mDependentElements.Length();
37 for (uint32_t i = 0; i < length; ++i) {
38 mDependentElements[i]->ForgetFieldSet(this);
42 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement,
43 nsGenericHTMLFormControlElement, mValidity,
44 mElements)
46 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement,
47 nsGenericHTMLFormControlElement,
48 nsIConstraintValidation)
50 NS_IMPL_ELEMENT_CLONE(HTMLFieldSetElement)
52 bool HTMLFieldSetElement::IsDisabledForEvents(WidgetEvent* aEvent) {
53 if (StaticPrefs::dom_forms_fieldset_disable_only_descendants_enabled()) {
54 return false;
56 return IsElementDisabledForEvents(aEvent, nullptr);
59 // nsIContent
60 void HTMLFieldSetElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
61 // Do not process any DOM events if the element is disabled.
62 aVisitor.mCanHandle = false;
63 if (IsDisabledForEvents(aVisitor.mEvent)) {
64 return;
67 nsGenericHTMLFormControlElement::GetEventTargetParent(aVisitor);
70 void HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
71 const nsAttrValue* aValue,
72 const nsAttrValue* aOldValue,
73 nsIPrincipal* aSubjectPrincipal,
74 bool aNotify) {
75 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
76 // This *has* to be called *before* calling FieldSetDisabledChanged on our
77 // controls, as they may depend on our disabled state.
78 UpdateDisabledState(aNotify);
81 return nsGenericHTMLFormControlElement::AfterSetAttr(
82 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
85 void HTMLFieldSetElement::GetType(nsAString& aType) const {
86 aType.AssignLiteral("fieldset");
89 /* static */
90 bool HTMLFieldSetElement::MatchListedElements(Element* aElement,
91 int32_t aNamespaceID,
92 nsAtom* aAtom, void* aData) {
93 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aElement);
94 return formControl;
97 nsIHTMLCollection* HTMLFieldSetElement::Elements() {
98 if (!mElements) {
99 mElements =
100 new nsContentList(this, MatchListedElements, nullptr, nullptr, true);
103 return mElements;
106 // nsIFormControl
108 nsresult HTMLFieldSetElement::Reset() { return NS_OK; }
110 void HTMLFieldSetElement::InsertChildBefore(nsIContent* aChild,
111 nsIContent* aBeforeThis,
112 bool aNotify, ErrorResult& aRv) {
113 bool firstLegendHasChanged = false;
115 if (aChild->IsHTMLElement(nsGkAtoms::legend)) {
116 if (!mFirstLegend) {
117 mFirstLegend = aChild;
118 // We do not want to notify the first time mFirstElement is set.
119 } else {
120 // If mFirstLegend is before aIndex, we do not change it.
121 // Otherwise, mFirstLegend is now aChild.
122 const Maybe<uint32_t> indexOfRef =
123 aBeforeThis ? ComputeIndexOf(aBeforeThis) : Some(GetChildCount());
124 const Maybe<uint32_t> indexOfFirstLegend = ComputeIndexOf(mFirstLegend);
125 if ((indexOfRef.isSome() && indexOfFirstLegend.isSome() &&
126 *indexOfRef <= *indexOfFirstLegend) ||
127 // XXX Keep the odd traditional behavior for now.
128 indexOfRef.isNothing()) {
129 mFirstLegend = aChild;
130 firstLegendHasChanged = true;
135 nsGenericHTMLFormControlElement::InsertChildBefore(aChild, aBeforeThis,
136 aNotify, aRv);
137 if (aRv.Failed()) {
138 return;
141 if (firstLegendHasChanged) {
142 NotifyElementsForFirstLegendChange(aNotify);
146 void HTMLFieldSetElement::RemoveChildNode(nsIContent* aKid, bool aNotify) {
147 bool firstLegendHasChanged = false;
149 if (mFirstLegend && aKid == mFirstLegend) {
150 // If we are removing the first legend we have to found another one.
151 nsIContent* child = mFirstLegend->GetNextSibling();
152 mFirstLegend = nullptr;
153 firstLegendHasChanged = true;
155 for (; child; child = child->GetNextSibling()) {
156 if (child->IsHTMLElement(nsGkAtoms::legend)) {
157 mFirstLegend = child;
158 break;
163 nsGenericHTMLFormControlElement::RemoveChildNode(aKid, aNotify);
165 if (firstLegendHasChanged) {
166 NotifyElementsForFirstLegendChange(aNotify);
170 void HTMLFieldSetElement::AddElement(nsGenericHTMLFormElement* aElement) {
171 mDependentElements.AppendElement(aElement);
173 // If the element that we are adding aElement is a fieldset, then all the
174 // invalid elements in aElement are also invalid elements of this.
175 HTMLFieldSetElement* fieldSet = FromNode(aElement);
176 if (fieldSet) {
177 for (int32_t i = 0; i < fieldSet->mInvalidElementsCount; i++) {
178 UpdateValidity(false);
180 return;
183 // If the element is a form-associated custom element, adding element might be
184 // caused by FACE upgrade which won't trigger mutation observer, so mark
185 // mElements dirty manually here.
186 CustomElementData* data = aElement->GetCustomElementData();
187 if (data && data->IsFormAssociated() && mElements) {
188 mElements->SetDirty();
191 // We need to update the validity of the fieldset.
192 nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
193 if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
194 !cvElmt->IsValid()) {
195 UpdateValidity(false);
198 #if DEBUG
199 int32_t debugInvalidElementsCount = 0;
200 for (uint32_t i = 0; i < mDependentElements.Length(); i++) {
201 HTMLFieldSetElement* fieldSet = FromNode(mDependentElements[i]);
202 if (fieldSet) {
203 debugInvalidElementsCount += fieldSet->mInvalidElementsCount;
204 continue;
206 nsCOMPtr<nsIConstraintValidation> cvElmt =
207 do_QueryObject(mDependentElements[i]);
208 if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
209 !(cvElmt->IsValid())) {
210 debugInvalidElementsCount += 1;
213 MOZ_ASSERT(debugInvalidElementsCount == mInvalidElementsCount);
214 #endif
217 void HTMLFieldSetElement::RemoveElement(nsGenericHTMLFormElement* aElement) {
218 mDependentElements.RemoveElement(aElement);
220 // If the element that we are removing aElement is a fieldset, then all the
221 // invalid elements in aElement are also removed from this.
222 HTMLFieldSetElement* fieldSet = FromNode(aElement);
223 if (fieldSet) {
224 for (int32_t i = 0; i < fieldSet->mInvalidElementsCount; i++) {
225 UpdateValidity(true);
227 return;
230 // We need to update the validity of the fieldset.
231 nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
232 if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
233 !cvElmt->IsValid()) {
234 UpdateValidity(true);
237 #if DEBUG
238 int32_t debugInvalidElementsCount = 0;
239 for (uint32_t i = 0; i < mDependentElements.Length(); i++) {
240 HTMLFieldSetElement* fieldSet = FromNode(mDependentElements[i]);
241 if (fieldSet) {
242 debugInvalidElementsCount += fieldSet->mInvalidElementsCount;
243 continue;
245 nsCOMPtr<nsIConstraintValidation> cvElmt =
246 do_QueryObject(mDependentElements[i]);
247 if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
248 !(cvElmt->IsValid())) {
249 debugInvalidElementsCount += 1;
252 MOZ_ASSERT(debugInvalidElementsCount == mInvalidElementsCount);
253 #endif
256 void HTMLFieldSetElement::UpdateDisabledState(bool aNotify) {
257 nsGenericHTMLFormControlElement::UpdateDisabledState(aNotify);
259 for (nsGenericHTMLFormElement* element : mDependentElements) {
260 element->FieldSetDisabledChanged(aNotify);
264 void HTMLFieldSetElement::NotifyElementsForFirstLegendChange(bool aNotify) {
266 * NOTE: this could be optimized if only call when the fieldset is currently
267 * disabled.
268 * This should also make sure that mElements is set when we happen to be here.
269 * However, this method shouldn't be called very often in normal use cases.
271 if (!mElements) {
272 mElements =
273 new nsContentList(this, MatchListedElements, nullptr, nullptr, true);
276 uint32_t length = mElements->Length(true);
277 for (uint32_t i = 0; i < length; ++i) {
278 static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
279 ->FieldSetFirstLegendChanged(aNotify);
283 void HTMLFieldSetElement::UpdateValidity(bool aElementValidity) {
284 if (aElementValidity) {
285 --mInvalidElementsCount;
286 } else {
287 ++mInvalidElementsCount;
290 MOZ_ASSERT(mInvalidElementsCount >= 0);
292 // The fieldset validity has just changed if:
293 // - there are no more invalid elements ;
294 // - or there is one invalid elmement and an element just became invalid.
295 if (!mInvalidElementsCount ||
296 (mInvalidElementsCount == 1 && !aElementValidity)) {
297 AutoStateChangeNotifier notifier(*this, true);
298 RemoveStatesSilently(ElementState::VALID | ElementState::INVALID);
299 AddStatesSilently(mInvalidElementsCount ? ElementState::INVALID
300 : ElementState::VALID);
303 // We should propagate the change to the fieldset parent chain.
304 if (mFieldSet) {
305 mFieldSet->UpdateValidity(aElementValidity);
309 JSObject* HTMLFieldSetElement::WrapNode(JSContext* aCx,
310 JS::Handle<JSObject*> aGivenProto) {
311 return HTMLFieldSetElement_Binding::Wrap(aCx, this, aGivenProto);
314 } // namespace mozilla::dom