Coverage Report

Created: 2018-09-25 14:53

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