Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLFormControlsCollection.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/dom/HTMLFormControlsCollection.h"
8
9
#include "mozilla/FlushType.h"
10
#include "mozilla/dom/BindingUtils.h"
11
#include "mozilla/dom/Element.h"
12
#include "mozilla/dom/HTMLFormControlsCollectionBinding.h"
13
#include "mozilla/dom/HTMLFormElement.h"
14
#include "nsGenericHTMLElement.h" // nsGenericHTMLFormElement
15
#include "nsIDocument.h"
16
#include "nsIFormControl.h"
17
#include "RadioNodeList.h"
18
#include "jsfriendapi.h"
19
20
namespace mozilla {
21
namespace dom {
22
23
/* static */ bool
24
HTMLFormControlsCollection::ShouldBeInElements(nsIFormControl* aFormControl)
25
0
{
26
0
  // For backwards compatibility (with 4.x and IE) we must not add
27
0
  // <input type=image> elements to the list of form controls in a
28
0
  // form.
29
0
30
0
  switch (aFormControl->ControlType()) {
31
0
  case NS_FORM_BUTTON_BUTTON :
32
0
  case NS_FORM_BUTTON_RESET :
33
0
  case NS_FORM_BUTTON_SUBMIT :
34
0
  case NS_FORM_INPUT_BUTTON :
35
0
  case NS_FORM_INPUT_CHECKBOX :
36
0
  case NS_FORM_INPUT_COLOR :
37
0
  case NS_FORM_INPUT_EMAIL :
38
0
  case NS_FORM_INPUT_FILE :
39
0
  case NS_FORM_INPUT_HIDDEN :
40
0
  case NS_FORM_INPUT_RESET :
41
0
  case NS_FORM_INPUT_PASSWORD :
42
0
  case NS_FORM_INPUT_RADIO :
43
0
  case NS_FORM_INPUT_SEARCH :
44
0
  case NS_FORM_INPUT_SUBMIT :
45
0
  case NS_FORM_INPUT_TEXT :
46
0
  case NS_FORM_INPUT_TEL :
47
0
  case NS_FORM_INPUT_URL :
48
0
  case NS_FORM_INPUT_NUMBER :
49
0
  case NS_FORM_INPUT_RANGE :
50
0
  case NS_FORM_INPUT_DATE :
51
0
  case NS_FORM_INPUT_TIME :
52
0
  case NS_FORM_INPUT_MONTH :
53
0
  case NS_FORM_INPUT_WEEK :
54
0
  case NS_FORM_INPUT_DATETIME_LOCAL :
55
0
  case NS_FORM_SELECT :
56
0
  case NS_FORM_TEXTAREA :
57
0
  case NS_FORM_FIELDSET :
58
0
  case NS_FORM_OBJECT :
59
0
  case NS_FORM_OUTPUT :
60
0
    return true;
61
0
  }
62
0
63
0
  // These form control types are not supposed to end up in the
64
0
  // form.elements array
65
0
  //
66
0
  // NS_FORM_INPUT_IMAGE
67
0
  //
68
0
  // XXXbz maybe we should just check for that type here instead of the big
69
0
  // switch?
70
0
71
0
  return false;
72
0
}
73
74
HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement* aForm)
75
  : mForm(aForm)
76
  // Initialize the elements list to have an initial capacity
77
  // of 8 to reduce allocations on small forms.
78
  , mElements(8)
79
  , mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH)
80
0
{
81
0
}
82
83
HTMLFormControlsCollection::~HTMLFormControlsCollection()
84
0
{
85
0
  mForm = nullptr;
86
0
  Clear();
87
0
}
88
89
void
90
HTMLFormControlsCollection::DropFormReference()
91
0
{
92
0
  mForm = nullptr;
93
0
  Clear();
94
0
}
95
96
void
97
HTMLFormControlsCollection::Clear()
98
0
{
99
0
  // Null out childrens' pointer to me.  No refcounting here
100
0
  for (int32_t i = mElements.Length() - 1; i >= 0; i--) {
101
0
    mElements[i]->ClearForm(false, false);
102
0
  }
103
0
  mElements.Clear();
104
0
105
0
  for (int32_t i = mNotInElements.Length() - 1; i >= 0; i--) {
106
0
    mNotInElements[i]->ClearForm(false, false);
107
0
  }
108
0
  mNotInElements.Clear();
109
0
110
0
  mNameLookupTable.Clear();
111
0
}
112
113
void
114
HTMLFormControlsCollection::FlushPendingNotifications()
115
0
{
116
0
  if (mForm) {
117
0
    nsIDocument* doc = mForm->GetUncomposedDoc();
118
0
    if (doc) {
119
0
      doc->FlushPendingNotifications(FlushType::Content);
120
0
    }
121
0
  }
122
0
}
123
124
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection)
125
126
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection)
127
0
  // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
128
0
  // so may result in crashes because of inconsistent null-checking after the
129
0
  // object gets unlinked.
130
0
  tmp->Clear();
131
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
132
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
133
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection)
134
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable)
135
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
136
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection)
137
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
138
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
139
140
// XPConnect interface list for HTMLFormControlsCollection
141
0
NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection)
142
0
  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
143
0
  NS_INTERFACE_TABLE(HTMLFormControlsCollection,
144
0
                     nsIHTMLCollection)
145
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection)
146
0
NS_INTERFACE_MAP_END
147
148
149
NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection)
150
NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection)
151
152
153
// nsIHTMLCollection interface
154
155
uint32_t
156
HTMLFormControlsCollection::Length()
157
0
{
158
0
  FlushPendingNotifications();
159
0
  return mElements.Length();
160
0
}
161
162
nsISupports*
163
HTMLFormControlsCollection::NamedItemInternal(const nsAString& aName,
164
                                              bool aFlushContent)
165
0
{
166
0
  if (aFlushContent) {
167
0
    FlushPendingNotifications();
168
0
  }
169
0
170
0
  return mNameLookupTable.GetWeak(aName);
171
0
}
172
173
nsresult
174
HTMLFormControlsCollection::AddElementToTable(nsGenericHTMLFormElement* aChild,
175
                                              const nsAString& aName)
176
0
{
177
0
  if (!ShouldBeInElements(aChild)) {
178
0
    return NS_OK;
179
0
  }
180
0
181
0
  return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
182
0
}
183
184
nsresult
185
HTMLFormControlsCollection::IndexOfControl(nsIFormControl* aControl,
186
                                           int32_t* aIndex)
187
0
{
188
0
  // Note -- not a DOM method; callers should handle flushing themselves
189
0
190
0
  NS_ENSURE_ARG_POINTER(aIndex);
191
0
192
0
  *aIndex = mElements.IndexOf(aControl);
193
0
194
0
  return NS_OK;
195
0
}
196
197
nsresult
198
HTMLFormControlsCollection::RemoveElementFromTable(
199
  nsGenericHTMLFormElement* aChild, const nsAString& aName)
200
0
{
201
0
  if (!ShouldBeInElements(aChild)) {
202
0
    return NS_OK;
203
0
  }
204
0
205
0
  return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
206
0
}
207
208
nsresult
209
HTMLFormControlsCollection::GetSortedControls(
210
  nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const
211
0
{
212
#ifdef DEBUG
213
  HTMLFormElement::AssertDocumentOrder(mElements, mForm);
214
  HTMLFormElement::AssertDocumentOrder(mNotInElements, mForm);
215
#endif
216
217
0
  aControls.Clear();
218
0
219
0
  // Merge the elements list and the not in elements list. Both lists are
220
0
  // already sorted.
221
0
  uint32_t elementsLen = mElements.Length();
222
0
  uint32_t notInElementsLen = mNotInElements.Length();
223
0
  aControls.SetCapacity(elementsLen + notInElementsLen);
224
0
225
0
  uint32_t elementsIdx = 0;
226
0
  uint32_t notInElementsIdx = 0;
227
0
228
0
  while (elementsIdx < elementsLen || notInElementsIdx < notInElementsLen) {
229
0
    // Check whether we're done with mElements
230
0
    if (elementsIdx == elementsLen) {
231
0
      NS_ASSERTION(notInElementsIdx < notInElementsLen,
232
0
                   "Should have remaining not-in-elements");
233
0
      // Append the remaining mNotInElements elements
234
0
      if (!aControls.AppendElements(mNotInElements.Elements() +
235
0
                                      notInElementsIdx,
236
0
                                    notInElementsLen -
237
0
                                      notInElementsIdx)) {
238
0
        return NS_ERROR_OUT_OF_MEMORY;
239
0
      }
240
0
      break;
241
0
    }
242
0
    // Check whether we're done with mNotInElements
243
0
    if (notInElementsIdx == notInElementsLen) {
244
0
      NS_ASSERTION(elementsIdx < elementsLen,
245
0
                   "Should have remaining in-elements");
246
0
      // Append the remaining mElements elements
247
0
      if (!aControls.AppendElements(mElements.Elements() +
248
0
                                      elementsIdx,
249
0
                                    elementsLen -
250
0
                                      elementsIdx)) {
251
0
        return NS_ERROR_OUT_OF_MEMORY;
252
0
      }
253
0
      break;
254
0
    }
255
0
    // Both lists have elements left.
256
0
    NS_ASSERTION(mElements[elementsIdx] &&
257
0
                 mNotInElements[notInElementsIdx],
258
0
                 "Should have remaining elements");
259
0
    // Determine which of the two elements should be ordered
260
0
    // first and add it to the end of the list.
261
0
    nsGenericHTMLFormElement* elementToAdd;
262
0
    if (HTMLFormElement::CompareFormControlPosition(
263
0
          mElements[elementsIdx], mNotInElements[notInElementsIdx], mForm) < 0) {
264
0
      elementToAdd = mElements[elementsIdx];
265
0
      ++elementsIdx;
266
0
    } else {
267
0
      elementToAdd = mNotInElements[notInElementsIdx];
268
0
      ++notInElementsIdx;
269
0
    }
270
0
    // Add the first element to the list.
271
0
    if (!aControls.AppendElement(elementToAdd)) {
272
0
      return NS_ERROR_OUT_OF_MEMORY;
273
0
    }
274
0
  }
275
0
276
0
  NS_ASSERTION(aControls.Length() == elementsLen + notInElementsLen,
277
0
               "Not all form controls were added to the sorted list");
278
#ifdef DEBUG
279
  HTMLFormElement::AssertDocumentOrder(aControls, mForm);
280
#endif
281
282
0
  return NS_OK;
283
0
}
284
285
Element*
286
HTMLFormControlsCollection::GetElementAt(uint32_t aIndex)
287
0
{
288
0
  FlushPendingNotifications();
289
0
290
0
  return mElements.SafeElementAt(aIndex, nullptr);
291
0
}
292
293
/* virtual */ nsINode*
294
HTMLFormControlsCollection::GetParentObject()
295
0
{
296
0
  return mForm;
297
0
}
298
299
/* virtual */ Element*
300
HTMLFormControlsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
301
0
{
302
0
  Nullable<OwningRadioNodeListOrElement> maybeResult;
303
0
  NamedGetter(aName, aFound, maybeResult);
304
0
  if (!aFound) {
305
0
    return nullptr;
306
0
  }
307
0
  MOZ_ASSERT(!maybeResult.IsNull());
308
0
  const OwningRadioNodeListOrElement& result = maybeResult.Value();
309
0
  if (result.IsElement()) {
310
0
    return result.GetAsElement().get();
311
0
  }
312
0
  if (result.IsRadioNodeList()) {
313
0
    RadioNodeList& nodelist = result.GetAsRadioNodeList();
314
0
    return nodelist.Item(0)->AsElement();
315
0
  }
316
0
  MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
317
0
  return nullptr;
318
0
}
319
320
void
321
HTMLFormControlsCollection::NamedGetter(const nsAString& aName,
322
                                        bool& aFound,
323
                                        Nullable<OwningRadioNodeListOrElement>& aResult)
324
0
{
325
0
  nsISupports* item = NamedItemInternal(aName, true);
326
0
  if (!item) {
327
0
    aFound = false;
328
0
    return;
329
0
  }
330
0
  aFound = true;
331
0
  if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
332
0
    aResult.SetValue().SetAsElement() = element;
333
0
    return;
334
0
  }
335
0
  if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
336
0
    aResult.SetValue().SetAsRadioNodeList() = nodelist;
337
0
    return;
338
0
  }
339
0
  MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
340
0
}
341
342
void
343
HTMLFormControlsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
344
0
{
345
0
  FlushPendingNotifications();
346
0
  // Just enumerate mNameLookupTable.  This won't guarantee order, but
347
0
  // that's OK, because the HTML5 spec doesn't define an order for
348
0
  // this enumeration.
349
0
  for (auto iter = mNameLookupTable.Iter(); !iter.Done(); iter.Next()) {
350
0
    aNames.AppendElement(iter.Key());
351
0
  }
352
0
}
353
354
/* virtual */ JSObject*
355
HTMLFormControlsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
356
0
{
357
0
  return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto);
358
0
}
359
360
} // namespace dom
361
} // namespace mozilla