Line data Source code
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/. */
6 :
7 : #include "mozilla/BasicEvents.h"
8 : #include "mozilla/EventDispatcher.h"
9 : #include "mozilla/EventStates.h"
10 : #include "mozilla/dom/HTMLFieldSetElement.h"
11 : #include "mozilla/dom/HTMLFieldSetElementBinding.h"
12 : #include "nsContentList.h"
13 : #include "nsQueryObject.h"
14 :
15 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(FieldSet)
16 :
17 : namespace mozilla {
18 : namespace dom {
19 :
20 0 : HTMLFieldSetElement::HTMLFieldSetElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
21 : : nsGenericHTMLFormElement(aNodeInfo, NS_FORM_FIELDSET)
22 : , mElements(nullptr)
23 : , mFirstLegend(nullptr)
24 0 : , mInvalidElementsCount(0)
25 : {
26 : // <fieldset> is always barred from constraint validation.
27 0 : SetBarredFromConstraintValidation(true);
28 :
29 : // We start out enabled and valid.
30 0 : AddStatesSilently(NS_EVENT_STATE_ENABLED | NS_EVENT_STATE_VALID);
31 0 : }
32 :
33 0 : HTMLFieldSetElement::~HTMLFieldSetElement()
34 : {
35 0 : uint32_t length = mDependentElements.Length();
36 0 : for (uint32_t i = 0; i < length; ++i) {
37 0 : mDependentElements[i]->ForgetFieldSet(this);
38 : }
39 0 : }
40 :
41 : // nsISupports
42 :
43 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement, nsGenericHTMLFormElement,
44 : mValidity, mElements)
45 :
46 0 : NS_IMPL_ADDREF_INHERITED(HTMLFieldSetElement, Element)
47 0 : NS_IMPL_RELEASE_INHERITED(HTMLFieldSetElement, Element)
48 :
49 : // QueryInterface implementation for HTMLFieldSetElement
50 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement)
51 0 : NS_INTERFACE_TABLE_INHERITED(HTMLFieldSetElement,
52 : nsIDOMHTMLFieldSetElement,
53 : nsIConstraintValidation)
54 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElement)
55 :
56 0 : NS_IMPL_ELEMENT_CLONE(HTMLFieldSetElement)
57 :
58 :
59 0 : NS_IMPL_BOOL_ATTR(HTMLFieldSetElement, Disabled, disabled)
60 0 : NS_IMPL_STRING_ATTR(HTMLFieldSetElement, Name, name)
61 :
62 : // nsIConstraintValidation
63 0 : NS_IMPL_NSICONSTRAINTVALIDATION(HTMLFieldSetElement)
64 :
65 : bool
66 0 : HTMLFieldSetElement::IsDisabledForEvents(EventMessage aMessage)
67 : {
68 0 : return IsElementDisabledForEvents(aMessage, nullptr);
69 : }
70 :
71 : // nsIContent
72 : nsresult
73 0 : HTMLFieldSetElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
74 : {
75 : // Do not process any DOM events if the element is disabled.
76 0 : aVisitor.mCanHandle = false;
77 0 : if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
78 0 : return NS_OK;
79 : }
80 :
81 0 : return nsGenericHTMLFormElement::GetEventTargetParent(aVisitor);
82 : }
83 :
84 : nsresult
85 0 : HTMLFieldSetElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
86 : const nsAttrValue* aValue,
87 : const nsAttrValue* aOldValue, bool aNotify)
88 : {
89 0 : if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled &&
90 0 : nsINode::GetFirstChild()) {
91 0 : if (!mElements) {
92 : mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
93 0 : true);
94 : }
95 :
96 0 : uint32_t length = mElements->Length(true);
97 0 : for (uint32_t i=0; i<length; ++i) {
98 0 : static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
99 0 : ->FieldSetDisabledChanged(aNotify);
100 : }
101 : }
102 :
103 0 : return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
104 0 : aValue, aOldValue, aNotify);
105 : }
106 :
107 : // nsIDOMHTMLFieldSetElement
108 :
109 : NS_IMETHODIMP
110 0 : HTMLFieldSetElement::GetForm(nsIDOMHTMLFormElement** aForm)
111 : {
112 0 : return nsGenericHTMLFormElement::GetForm(aForm);
113 : }
114 :
115 : NS_IMETHODIMP
116 0 : HTMLFieldSetElement::GetType(nsAString& aType)
117 : {
118 0 : aType.AssignLiteral("fieldset");
119 0 : return NS_OK;
120 : }
121 :
122 : /* static */
123 : bool
124 0 : HTMLFieldSetElement::MatchListedElements(Element* aElement, int32_t aNamespaceID,
125 : nsIAtom* aAtom, void* aData)
126 : {
127 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aElement);
128 0 : return formControl;
129 : }
130 :
131 : NS_IMETHODIMP
132 0 : HTMLFieldSetElement::GetElements(nsIDOMHTMLCollection** aElements)
133 : {
134 0 : NS_ADDREF(*aElements = Elements());
135 0 : return NS_OK;
136 : }
137 :
138 : nsIHTMLCollection*
139 0 : HTMLFieldSetElement::Elements()
140 : {
141 0 : if (!mElements) {
142 : mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
143 0 : true);
144 : }
145 :
146 0 : return mElements;
147 : }
148 :
149 : // nsIFormControl
150 :
151 : nsresult
152 0 : HTMLFieldSetElement::Reset()
153 : {
154 0 : return NS_OK;
155 : }
156 :
157 : NS_IMETHODIMP
158 0 : HTMLFieldSetElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
159 : {
160 0 : return NS_OK;
161 : }
162 :
163 : nsresult
164 0 : HTMLFieldSetElement::InsertChildAt(nsIContent* aChild, uint32_t aIndex,
165 : bool aNotify)
166 : {
167 0 : bool firstLegendHasChanged = false;
168 :
169 0 : if (aChild->IsHTMLElement(nsGkAtoms::legend)) {
170 0 : if (!mFirstLegend) {
171 0 : mFirstLegend = aChild;
172 : // We do not want to notify the first time mFirstElement is set.
173 : } else {
174 : // If mFirstLegend is before aIndex, we do not change it.
175 : // Otherwise, mFirstLegend is now aChild.
176 0 : if (int32_t(aIndex) <= IndexOf(mFirstLegend)) {
177 0 : mFirstLegend = aChild;
178 0 : firstLegendHasChanged = true;
179 : }
180 : }
181 : }
182 :
183 0 : nsresult rv = nsGenericHTMLFormElement::InsertChildAt(aChild, aIndex, aNotify);
184 0 : NS_ENSURE_SUCCESS(rv, rv);
185 :
186 0 : if (firstLegendHasChanged) {
187 0 : NotifyElementsForFirstLegendChange(aNotify);
188 : }
189 :
190 0 : return rv;
191 : }
192 :
193 : void
194 0 : HTMLFieldSetElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
195 : {
196 0 : bool firstLegendHasChanged = false;
197 :
198 0 : if (mFirstLegend && (GetChildAt(aIndex) == mFirstLegend)) {
199 : // If we are removing the first legend we have to found another one.
200 0 : nsIContent* child = mFirstLegend->GetNextSibling();
201 0 : mFirstLegend = nullptr;
202 0 : firstLegendHasChanged = true;
203 :
204 0 : for (; child; child = child->GetNextSibling()) {
205 0 : if (child->IsHTMLElement(nsGkAtoms::legend)) {
206 0 : mFirstLegend = child;
207 0 : break;
208 : }
209 : }
210 : }
211 :
212 0 : nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
213 :
214 0 : if (firstLegendHasChanged) {
215 0 : NotifyElementsForFirstLegendChange(aNotify);
216 : }
217 0 : }
218 :
219 : void
220 0 : HTMLFieldSetElement::AddElement(nsGenericHTMLFormElement* aElement)
221 : {
222 0 : mDependentElements.AppendElement(aElement);
223 :
224 : // If the element that we are adding aElement is a fieldset, then all the
225 : // invalid elements in aElement are also invalid elements of this.
226 0 : HTMLFieldSetElement* fieldSet = FromContent(aElement);
227 0 : if (fieldSet) {
228 0 : for (int32_t i = 0; i < fieldSet->mInvalidElementsCount; i++) {
229 0 : UpdateValidity(false);
230 : }
231 0 : return;
232 : }
233 :
234 : // We need to update the validity of the fieldset.
235 0 : nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
236 0 : if (cvElmt &&
237 0 : cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
238 0 : UpdateValidity(false);
239 : }
240 :
241 : #if DEBUG
242 0 : int32_t debugInvalidElementsCount = 0;
243 0 : for (uint32_t i = 0; i < mDependentElements.Length(); i++) {
244 0 : HTMLFieldSetElement* fieldSet = FromContent(mDependentElements[i]);
245 0 : if (fieldSet) {
246 0 : debugInvalidElementsCount += fieldSet->mInvalidElementsCount;
247 0 : continue;
248 : }
249 : nsCOMPtr<nsIConstraintValidation>
250 0 : cvElmt = do_QueryObject(mDependentElements[i]);
251 0 : if (cvElmt &&
252 0 : cvElmt->IsCandidateForConstraintValidation() &&
253 0 : !(cvElmt->IsValid())) {
254 0 : debugInvalidElementsCount += 1;
255 : }
256 : }
257 0 : MOZ_ASSERT(debugInvalidElementsCount == mInvalidElementsCount);
258 : #endif
259 : }
260 :
261 : void
262 0 : HTMLFieldSetElement::RemoveElement(nsGenericHTMLFormElement* aElement)
263 : {
264 0 : mDependentElements.RemoveElement(aElement);
265 :
266 : // If the element that we are removing aElement is a fieldset, then all the
267 : // invalid elements in aElement are also removed from this.
268 0 : HTMLFieldSetElement* fieldSet = FromContent(aElement);
269 0 : if (fieldSet) {
270 0 : for (int32_t i = 0; i < fieldSet->mInvalidElementsCount; i++) {
271 0 : UpdateValidity(true);
272 : }
273 0 : return;
274 : }
275 :
276 : // We need to update the validity of the fieldset.
277 0 : nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aElement);
278 0 : if (cvElmt &&
279 0 : cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
280 0 : UpdateValidity(true);
281 : }
282 :
283 : #if DEBUG
284 0 : int32_t debugInvalidElementsCount = 0;
285 0 : for (uint32_t i = 0; i < mDependentElements.Length(); i++) {
286 0 : HTMLFieldSetElement* fieldSet = FromContent(mDependentElements[i]);
287 0 : if (fieldSet) {
288 0 : debugInvalidElementsCount += fieldSet->mInvalidElementsCount;
289 0 : continue;
290 : }
291 : nsCOMPtr<nsIConstraintValidation>
292 0 : cvElmt = do_QueryObject(mDependentElements[i]);
293 0 : if (cvElmt &&
294 0 : cvElmt->IsCandidateForConstraintValidation() &&
295 0 : !(cvElmt->IsValid())) {
296 0 : debugInvalidElementsCount += 1;
297 : }
298 : }
299 0 : MOZ_ASSERT(debugInvalidElementsCount == mInvalidElementsCount);
300 : #endif
301 : }
302 :
303 : void
304 0 : HTMLFieldSetElement::NotifyElementsForFirstLegendChange(bool aNotify)
305 : {
306 : /**
307 : * NOTE: this could be optimized if only call when the fieldset is currently
308 : * disabled.
309 : * This should also make sure that mElements is set when we happen to be here.
310 : * However, this method shouldn't be called very often in normal use cases.
311 : */
312 0 : if (!mElements) {
313 : mElements = new nsContentList(this, MatchListedElements, nullptr, nullptr,
314 0 : true);
315 : }
316 :
317 0 : uint32_t length = mElements->Length(true);
318 0 : for (uint32_t i = 0; i < length; ++i) {
319 0 : static_cast<nsGenericHTMLFormElement*>(mElements->Item(i))
320 0 : ->FieldSetFirstLegendChanged(aNotify);
321 : }
322 0 : }
323 :
324 : void
325 0 : HTMLFieldSetElement::UpdateValidity(bool aElementValidity)
326 : {
327 0 : if (aElementValidity) {
328 0 : --mInvalidElementsCount;
329 : } else {
330 0 : ++mInvalidElementsCount;
331 : }
332 :
333 0 : MOZ_ASSERT(mInvalidElementsCount >= 0);
334 :
335 : // The fieldset validity has just changed if:
336 : // - there are no more invalid elements ;
337 : // - or there is one invalid elmement and an element just became invalid.
338 0 : if (!mInvalidElementsCount || (mInvalidElementsCount == 1 && !aElementValidity)) {
339 0 : UpdateState(true);
340 : }
341 :
342 : // We should propagate the change to the fieldset parent chain.
343 0 : if (mFieldSet) {
344 0 : mFieldSet->UpdateValidity(aElementValidity);
345 : }
346 :
347 0 : return;
348 : }
349 :
350 : EventStates
351 0 : HTMLFieldSetElement::IntrinsicState() const
352 : {
353 0 : EventStates state = nsGenericHTMLFormElement::IntrinsicState();
354 :
355 0 : if (mInvalidElementsCount) {
356 0 : state |= NS_EVENT_STATE_INVALID;
357 : } else {
358 0 : state |= NS_EVENT_STATE_VALID;
359 : }
360 :
361 0 : return state;
362 : }
363 :
364 : JSObject*
365 0 : HTMLFieldSetElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
366 : {
367 0 : return HTMLFieldSetElementBinding::Wrap(aCx, this, aGivenProto);
368 : }
369 :
370 : } // namespace dom
371 : } // namespace mozilla
|