tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLFormControlsCollection.cpp (10542B)


      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 "RadioNodeList.h"
     10 #include "jsfriendapi.h"
     11 #include "mozilla/FlushType.h"
     12 #include "mozilla/dom/BindingUtils.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/Element.h"
     15 #include "mozilla/dom/HTMLFormControlsCollectionBinding.h"
     16 #include "mozilla/dom/HTMLFormElement.h"
     17 #include "nsGenericHTMLElement.h"  // nsGenericHTMLFormElement
     18 #include "nsIFormControl.h"
     19 #include "nsQueryObject.h"
     20 
     21 namespace mozilla::dom {
     22 
     23 /* static */
     24 bool HTMLFormControlsCollection::ShouldBeInElements(
     25    const nsIFormControl* aFormControl) {
     26  // For backwards compatibility (with 4.x and IE) we must not add
     27  // <input type=image> elements to the list of form controls in a
     28  // form.
     29 
     30  switch (aFormControl->ControlType()) {
     31    case FormControlType::ButtonButton:
     32    case FormControlType::ButtonReset:
     33    case FormControlType::ButtonSubmit:
     34    case FormControlType::InputButton:
     35    case FormControlType::InputCheckbox:
     36    case FormControlType::InputColor:
     37    case FormControlType::InputEmail:
     38    case FormControlType::InputFile:
     39    case FormControlType::InputHidden:
     40    case FormControlType::InputReset:
     41    case FormControlType::InputPassword:
     42    case FormControlType::InputRadio:
     43    case FormControlType::InputSearch:
     44    case FormControlType::InputSubmit:
     45    case FormControlType::InputText:
     46    case FormControlType::InputTel:
     47    case FormControlType::InputUrl:
     48    case FormControlType::InputNumber:
     49    case FormControlType::InputRange:
     50    case FormControlType::InputDate:
     51    case FormControlType::InputTime:
     52    case FormControlType::InputMonth:
     53    case FormControlType::InputWeek:
     54    case FormControlType::InputDatetimeLocal:
     55    case FormControlType::Select:
     56    case FormControlType::Textarea:
     57    case FormControlType::Fieldset:
     58    case FormControlType::Object:
     59    case FormControlType::Output:
     60    case FormControlType::FormAssociatedCustomElement:
     61      return true;
     62 
     63    // These form control types are not supposed to end up in the
     64    // form.elements array
     65    // XXXbz maybe we should just return aType != InputImage or something
     66    // instead of the big switch?
     67    case FormControlType::InputImage:
     68      break;
     69  }
     70  return false;
     71 }
     72 
     73 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement* aForm)
     74    : mForm(aForm),
     75      mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH) {}
     76 
     77 HTMLFormControlsCollection::~HTMLFormControlsCollection() {
     78  mForm = nullptr;
     79  Clear();
     80 }
     81 
     82 void HTMLFormControlsCollection::DropFormReference() {
     83  mForm = nullptr;
     84  Clear();
     85 }
     86 
     87 void HTMLFormControlsCollection::Clear() {
     88  // Null out childrens' pointer to me.  No refcounting here.
     89  for (nsGenericHTMLFormElement* element : mElements.AsSpan()) {
     90    nsCOMPtr<nsIFormControl> formControl = nsIFormControl::FromNode(element);
     91    MOZ_ASSERT(formControl);
     92    formControl->ClearForm(false, false);
     93  }
     94  mElements.Clear();
     95 
     96  for (nsGenericHTMLFormElement* element : mNotInElements.AsSpan()) {
     97    nsCOMPtr<nsIFormControl> formControl = nsIFormControl::FromNode(element);
     98    MOZ_ASSERT(formControl);
     99    formControl->ClearForm(false, false);
    100  }
    101  mNotInElements.Clear();
    102 
    103  mNameLookupTable.Clear();
    104 }
    105 
    106 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection)
    107 
    108 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection)
    109  // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
    110  // so may result in crashes because of inconsistent null-checking after the
    111  // object gets unlinked.
    112  tmp->Clear();
    113  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    114 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection)
    116  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable)
    117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    118 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection)
    119  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    120 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    121 
    122 // XPConnect interface list for HTMLFormControlsCollection
    123 NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection)
    124  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
    125  NS_INTERFACE_TABLE(HTMLFormControlsCollection, nsIHTMLCollection)
    126  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection)
    127 NS_INTERFACE_MAP_END
    128 
    129 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection)
    130 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection)
    131 
    132 // nsIHTMLCollection interfac
    133 
    134 uint32_t HTMLFormControlsCollection::Length() { return mElements.Length(); }
    135 
    136 nsISupports* HTMLFormControlsCollection::NamedItemInternal(
    137    const nsAString& aName) {
    138  return mNameLookupTable.GetWeak(aName);
    139 }
    140 
    141 nsresult HTMLFormControlsCollection::AddElementToTable(
    142    nsGenericHTMLFormElement* aChild, const nsAString& aName) {
    143  const auto* formControl = nsIFormControl::FromNode(aChild);
    144  MOZ_ASSERT(formControl);
    145  if (!ShouldBeInElements(formControl)) {
    146    return NS_OK;
    147  }
    148 
    149  return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
    150 }
    151 
    152 nsresult HTMLFormControlsCollection::IndexOfContent(nsIContent* aContent,
    153                                                    int32_t* aIndex) {
    154  // Note -- not a DOM method; callers should handle flushing themselves
    155 
    156  NS_ENSURE_ARG_POINTER(aIndex);
    157  *aIndex = mElements.IndexOf(aContent);
    158  return NS_OK;
    159 }
    160 
    161 nsresult HTMLFormControlsCollection::RemoveElementFromTable(
    162    nsGenericHTMLFormElement* aChild, const nsAString& aName) {
    163  const auto* formControl = nsIFormControl::FromNode(aChild);
    164  MOZ_ASSERT(formControl);
    165  if (!ShouldBeInElements(formControl)) {
    166    return NS_OK;
    167  }
    168 
    169  return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
    170 }
    171 
    172 nsresult HTMLFormControlsCollection::GetSortedControls(
    173    nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const {
    174  aControls.Clear();
    175 
    176  // Merge the elements list and the not in elements list. Both lists are
    177  // already sorted.
    178  auto elements = mElements.AsSpan();
    179  auto notInElements = mNotInElements.AsSpan();
    180  uint32_t elementsLen = elements.Length();
    181  uint32_t notInElementsLen = notInElements.Length();
    182  aControls.SetCapacity(elementsLen + notInElementsLen);
    183 
    184  uint32_t elementsIdx = 0;
    185  uint32_t notInElementsIdx = 0;
    186 
    187  nsContentUtils::NodeIndexCache indexCache;
    188  while (elementsIdx < elementsLen || notInElementsIdx < notInElementsLen) {
    189    // Check whether we're done with mElements
    190    if (elementsIdx == elementsLen) {
    191      NS_ASSERTION(notInElementsIdx < notInElementsLen,
    192                   "Should have remaining not-in-elements");
    193      // Append the remaining mNotInElements elements
    194      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    195      // pretended earlier.
    196      aControls.AppendElements(notInElements.From(notInElementsIdx));
    197      break;
    198    }
    199    // Check whether we're done with mNotInElements
    200    if (notInElementsIdx == notInElementsLen) {
    201      NS_ASSERTION(elementsIdx < elementsLen,
    202                   "Should have remaining in-elements");
    203      // Append the remaining mElements elements
    204      // XXX(Bug 1631371) Check if this should use a fallible operation as it
    205      // pretended earlier.
    206      aControls.AppendElements(elements.From(elementsIdx));
    207      break;
    208    }
    209    // Both lists have elements left.
    210    NS_ASSERTION(elements[elementsIdx] && notInElements[notInElementsIdx],
    211                 "Should have remaining elements");
    212    // Determine which of the two elements should be ordered
    213    // first and add it to the end of the list.
    214    nsGenericHTMLFormElement* elementToAdd;
    215    if (nsContentUtils::CompareTreePosition<TreeKind::DOM>(
    216            elements[elementsIdx], notInElements[notInElementsIdx], mForm,
    217            &indexCache) < 0) {
    218      elementToAdd = elements[elementsIdx];
    219      ++elementsIdx;
    220    } else {
    221      elementToAdd = notInElements[notInElementsIdx];
    222      ++notInElementsIdx;
    223    }
    224    // Add the first element to the list.
    225    // XXX(Bug 1631371) Check if this should use a fallible operation as it
    226    // pretended earlier.
    227    aControls.AppendElement(elementToAdd);
    228  }
    229 
    230  NS_ASSERTION(aControls.Length() == elementsLen + notInElementsLen,
    231               "Not all form controls were added to the sorted list");
    232  return NS_OK;
    233 }
    234 
    235 Element* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex) {
    236  return mElements.SafeElementAt(aIndex, nullptr);
    237 }
    238 
    239 /* virtual */
    240 nsINode* HTMLFormControlsCollection::GetParentObject() { return mForm; }
    241 
    242 /* virtual */
    243 Element* HTMLFormControlsCollection::GetFirstNamedElement(
    244    const nsAString& aName, bool& aFound) {
    245  Nullable<OwningRadioNodeListOrElement> maybeResult;
    246  NamedGetter(aName, aFound, maybeResult);
    247  if (!aFound) {
    248    return nullptr;
    249  }
    250  MOZ_ASSERT(!maybeResult.IsNull());
    251  const OwningRadioNodeListOrElement& result = maybeResult.Value();
    252  if (result.IsElement()) {
    253    return result.GetAsElement().get();
    254  }
    255  if (result.IsRadioNodeList()) {
    256    RadioNodeList& nodelist = result.GetAsRadioNodeList();
    257    return nodelist.Item(0)->AsElement();
    258  }
    259  MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
    260  return nullptr;
    261 }
    262 
    263 void HTMLFormControlsCollection::NamedGetter(
    264    const nsAString& aName, bool& aFound,
    265    Nullable<OwningRadioNodeListOrElement>& aResult) {
    266  nsISupports* item = NamedItemInternal(aName);
    267  if (!item) {
    268    aFound = false;
    269    return;
    270  }
    271  aFound = true;
    272  if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
    273    aResult.SetValue().SetAsElement() = element;
    274    return;
    275  }
    276  if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
    277    aResult.SetValue().SetAsRadioNodeList() = nodelist;
    278    return;
    279  }
    280  MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
    281 }
    282 
    283 void HTMLFormControlsCollection::GetSupportedNames(nsTArray<nsString>& aNames) {
    284  // Just enumerate mNameLookupTable.  This won't guarantee order, but
    285  // that's OK, because the HTML5 spec doesn't define an order for
    286  // this enumeration.
    287  AppendToArray(aNames, mNameLookupTable.Keys());
    288 }
    289 
    290 /* virtual */
    291 JSObject* HTMLFormControlsCollection::WrapObject(
    292    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    293  return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto);
    294 }
    295 
    296 }  // namespace mozilla::dom