tor-browser

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

RadioGroupContainer.cpp (7195B)


      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/RadioGroupContainer.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/dom/HTMLInputElement.h"
     11 #include "mozilla/dom/TreeOrderedArrayInlines.h"
     12 #include "nsIFrame.h"
     13 
     14 namespace mozilla::dom {
     15 
     16 /**
     17 * A struct that holds all the information about a radio group.
     18 */
     19 struct nsRadioGroupStruct {
     20  nsRadioGroupStruct()
     21      : mRequiredRadioCount(0), mGroupSuffersFromValueMissing(false) {}
     22 
     23  /**
     24   * A strong pointer to the currently selected radio button.
     25   */
     26  RefPtr<HTMLInputElement> mSelectedRadioButton;
     27  TreeOrderedArray<RefPtr<HTMLInputElement>> mRadioButtons;
     28  uint32_t mRequiredRadioCount;
     29  bool mGroupSuffersFromValueMissing;
     30 };
     31 
     32 RadioGroupContainer::RadioGroupContainer() = default;
     33 
     34 RadioGroupContainer::~RadioGroupContainer() {
     35  for (const auto& group : mRadioGroups) {
     36    for (const auto& button : group.GetData()->mRadioButtons.AsSpan()) {
     37      // When the radio group container is being cycle-collected, any remaining
     38      // connected buttons will also be in the process of being cycle-collected.
     39      // Here, we unset the button's reference to the container so that when it
     40      // is collected it does not attempt to remove itself from a potentially
     41      // already deleted radio group.
     42      button->DisconnectRadioGroupContainer();
     43    }
     44  }
     45 }
     46 
     47 /* static */
     48 void RadioGroupContainer::Traverse(RadioGroupContainer* tmp,
     49                                   nsCycleCollectionTraversalCallback& cb) {
     50  for (const auto& entry : tmp->mRadioGroups) {
     51    nsRadioGroupStruct* radioGroup = entry.GetWeak();
     52    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
     53        cb, "mRadioGroups entry->mSelectedRadioButton");
     54    cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
     55 
     56    for (auto& button : radioGroup->mRadioButtons.AsSpan()) {
     57      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
     58          cb, "mRadioGroups entry->mRadioButtons[i]");
     59      cb.NoteXPCOMChild(ToSupports(button));
     60    }
     61  }
     62 }
     63 
     64 size_t RadioGroupContainer::SizeOfIncludingThis(
     65    MallocSizeOf aMallocSizeOf) const {
     66  return aMallocSizeOf(this) + mRadioGroups.SizeOfExcludingThis(aMallocSizeOf);
     67 }
     68 
     69 void RadioGroupContainer::SetCurrentRadioButton(const nsAString& aName,
     70                                                HTMLInputElement* aRadio) {
     71  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
     72  radioGroup->mSelectedRadioButton = aRadio;
     73 }
     74 
     75 HTMLInputElement* RadioGroupContainer::GetCurrentRadioButton(
     76    const nsAString& aName) {
     77  return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
     78 }
     79 
     80 nsresult RadioGroupContainer::GetNextRadioButton(
     81    const nsAString& aName, const bool aPrevious,
     82    HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut) {
     83  *aRadioOut = nullptr;
     84 
     85  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
     86 
     87  // Return the radio button relative to the focused radio button.
     88  // If no radio is focused, get the radio relative to the selected one.
     89  RefPtr<HTMLInputElement> currentRadio;
     90  if (aFocusedRadio) {
     91    currentRadio = aFocusedRadio;
     92  } else {
     93    currentRadio = radioGroup->mSelectedRadioButton;
     94    if (!currentRadio) {
     95      return NS_ERROR_FAILURE;
     96    }
     97  }
     98  int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
     99  if (index < 0) {
    100    return NS_ERROR_FAILURE;
    101  }
    102 
    103  int32_t numRadios = static_cast<int32_t>(radioGroup->mRadioButtons.Length());
    104  RefPtr<HTMLInputElement> radio;
    105  do {
    106    if (aPrevious) {
    107      if (--index < 0) {
    108        index = numRadios - 1;
    109      }
    110    } else if (++index >= numRadios) {
    111      index = 0;
    112    }
    113    radio = radioGroup->mRadioButtons.ElementAt(index);
    114  } while ((radio->Disabled() || !radio->GetPrimaryFrame() ||
    115            !radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) &&
    116           radio != currentRadio);
    117 
    118  radio.forget(aRadioOut);
    119  return NS_OK;
    120 }
    121 
    122 HTMLInputElement* RadioGroupContainer::GetFirstRadioButton(
    123    const nsAString& aName) {
    124  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
    125  for (HTMLInputElement* radio : radioGroup->mRadioButtons.AsSpan()) {
    126    if (!radio->Disabled() && radio->GetPrimaryFrame() &&
    127        radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) {
    128      return radio;
    129    }
    130  }
    131  return nullptr;
    132 }
    133 
    134 void RadioGroupContainer::AddToRadioGroup(const nsAString& aName,
    135                                          HTMLInputElement* aRadio,
    136                                          nsIContent* aAncestor) {
    137  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
    138  radioGroup->mRadioButtons.Insert(*aRadio, aAncestor);
    139  if (aRadio->IsRequired()) {
    140    radioGroup->mRequiredRadioCount++;
    141  }
    142 }
    143 
    144 void RadioGroupContainer::RemoveFromRadioGroup(const nsAString& aName,
    145                                               HTMLInputElement* aRadio) {
    146  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
    147  MOZ_ASSERT(
    148      radioGroup->mRadioButtons.Contains(aRadio),
    149      "Attempting to remove radio button from group it is not a part of!");
    150 
    151  radioGroup->mRadioButtons.RemoveElement(*aRadio);
    152 
    153  if (aRadio->IsRequired()) {
    154    MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
    155               "mRequiredRadioCount about to wrap below 0!");
    156    radioGroup->mRequiredRadioCount--;
    157  }
    158 }
    159 
    160 uint32_t RadioGroupContainer::GetRequiredRadioCount(
    161    const nsAString& aName) const {
    162  nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
    163  return radioGroup ? radioGroup->mRequiredRadioCount : 0;
    164 }
    165 
    166 void RadioGroupContainer::RadioRequiredWillChange(const nsAString& aName,
    167                                                  bool aRequiredAdded) {
    168  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
    169 
    170  if (aRequiredAdded) {
    171    radioGroup->mRequiredRadioCount++;
    172  } else {
    173    MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
    174               "mRequiredRadioCount about to wrap below 0!");
    175    radioGroup->mRequiredRadioCount--;
    176  }
    177 }
    178 
    179 bool RadioGroupContainer::GetValueMissingState(const nsAString& aName) const {
    180  nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
    181  return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
    182 }
    183 
    184 void RadioGroupContainer::SetValueMissingState(const nsAString& aName,
    185                                               bool aValue) {
    186  nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
    187  radioGroup->mGroupSuffersFromValueMissing = aValue;
    188 }
    189 
    190 nsRadioGroupStruct* RadioGroupContainer::GetRadioGroup(
    191    const nsAString& aName) const {
    192  nsRadioGroupStruct* radioGroup = nullptr;
    193  mRadioGroups.Get(aName, &radioGroup);
    194  return radioGroup;
    195 }
    196 
    197 nsRadioGroupStruct* RadioGroupContainer::GetOrCreateRadioGroup(
    198    const nsAString& aName) {
    199  return mRadioGroups.GetOrInsertNew(aName);
    200 }
    201 
    202 Span<const RefPtr<HTMLInputElement>> RadioGroupContainer::GetButtonsInGroup(
    203    nsRadioGroupStruct* aGroup) const {
    204  return aGroup->mRadioButtons.AsSpan();
    205 }
    206 
    207 }  // namespace mozilla::dom