Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / RadioGroupContainer.cpp
blobe49cbef13c6bcb0fa3e7ce3f8011995465c293dc
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/. */
7 #include "mozilla/dom/HTMLInputElement.h"
8 #include "mozilla/dom/RadioGroupContainer.h"
9 #include "mozilla/dom/TreeOrderedArrayInlines.h"
10 #include "mozilla/Assertions.h"
11 #include "nsIFrame.h"
12 #include "nsIRadioVisitor.h"
13 #include "nsRadioVisitor.h"
15 namespace mozilla::dom {
17 /**
18 * A struct that holds all the information about a radio group.
20 struct nsRadioGroupStruct {
21 nsRadioGroupStruct()
22 : mRequiredRadioCount(0), mGroupSuffersFromValueMissing(false) {}
24 /**
25 * A strong pointer to the currently selected radio button.
27 RefPtr<HTMLInputElement> mSelectedRadioButton;
28 TreeOrderedArray<RefPtr<HTMLInputElement>> mRadioButtons;
29 uint32_t mRequiredRadioCount;
30 bool mGroupSuffersFromValueMissing;
33 RadioGroupContainer::RadioGroupContainer() = default;
35 RadioGroupContainer::~RadioGroupContainer() {
36 for (const auto& group : mRadioGroups) {
37 for (const auto& button : group.GetData()->mRadioButtons.AsList()) {
38 // When the radio group container is being cycle-collected, any remaining
39 // connected buttons will also be in the process of being cycle-collected.
40 // Here, we unset the button's reference to the container so that when it
41 // is collected it does not attempt to remove itself from a potentially
42 // already deleted radio group.
43 button->DisconnectRadioGroupContainer();
48 /* static */
49 void RadioGroupContainer::Traverse(RadioGroupContainer* tmp,
50 nsCycleCollectionTraversalCallback& cb) {
51 for (const auto& entry : tmp->mRadioGroups) {
52 nsRadioGroupStruct* radioGroup = entry.GetWeak();
53 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
54 cb, "mRadioGroups entry->mSelectedRadioButton");
55 cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
57 uint32_t i, count = radioGroup->mRadioButtons->Length();
58 for (i = 0; i < count; ++i) {
59 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
60 cb, "mRadioGroups entry->mRadioButtons[i]");
61 cb.NoteXPCOMChild(ToSupports(radioGroup->mRadioButtons->ElementAt(i)));
66 size_t RadioGroupContainer::SizeOfIncludingThis(
67 MallocSizeOf aMallocSizeOf) const {
68 return aMallocSizeOf(this) + mRadioGroups.SizeOfExcludingThis(aMallocSizeOf);
71 nsresult RadioGroupContainer::WalkRadioGroup(const nsAString& aName,
72 nsIRadioVisitor* aVisitor) {
73 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
75 for (HTMLInputElement* button : radioGroup->mRadioButtons.AsList()) {
76 if (!aVisitor->Visit(button)) {
77 return NS_OK;
81 return NS_OK;
84 void RadioGroupContainer::WalkRadioGroup(const nsAString& aName,
85 const VisitCallback& aCallback) {
86 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
87 for (HTMLInputElement* button : radioGroup->mRadioButtons.AsList()) {
88 if (!aCallback(button)) {
89 return;
94 void RadioGroupContainer::SetCurrentRadioButton(const nsAString& aName,
95 HTMLInputElement* aRadio) {
96 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
97 radioGroup->mSelectedRadioButton = aRadio;
100 HTMLInputElement* RadioGroupContainer::GetCurrentRadioButton(
101 const nsAString& aName) {
102 return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
105 nsresult RadioGroupContainer::GetNextRadioButton(
106 const nsAString& aName, const bool aPrevious,
107 HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut) {
108 *aRadioOut = nullptr;
110 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
112 // Return the radio button relative to the focused radio button.
113 // If no radio is focused, get the radio relative to the selected one.
114 RefPtr<HTMLInputElement> currentRadio;
115 if (aFocusedRadio) {
116 currentRadio = aFocusedRadio;
117 } else {
118 currentRadio = radioGroup->mSelectedRadioButton;
119 if (!currentRadio) {
120 return NS_ERROR_FAILURE;
123 int32_t index = radioGroup->mRadioButtons->IndexOf(currentRadio);
124 if (index < 0) {
125 return NS_ERROR_FAILURE;
128 int32_t numRadios = static_cast<int32_t>(radioGroup->mRadioButtons->Length());
129 RefPtr<HTMLInputElement> radio;
130 do {
131 if (aPrevious) {
132 if (--index < 0) {
133 index = numRadios - 1;
135 } else if (++index >= numRadios) {
136 index = 0;
138 radio = radioGroup->mRadioButtons->ElementAt(index);
139 } while ((radio->Disabled() || !radio->GetPrimaryFrame() ||
140 !radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) &&
141 radio != currentRadio);
143 radio.forget(aRadioOut);
144 return NS_OK;
147 HTMLInputElement* RadioGroupContainer::GetFirstRadioButton(
148 const nsAString& aName) {
149 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
150 for (HTMLInputElement* radio : radioGroup->mRadioButtons.AsList()) {
151 if (!radio->Disabled() && radio->GetPrimaryFrame() &&
152 radio->GetPrimaryFrame()->IsVisibleConsideringAncestors()) {
153 return radio;
156 return nullptr;
159 void RadioGroupContainer::AddToRadioGroup(const nsAString& aName,
160 HTMLInputElement* aRadio,
161 nsIContent* aAncestor) {
162 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
163 radioGroup->mRadioButtons.Insert(*aRadio, aAncestor);
164 if (aRadio->IsRequired()) {
165 radioGroup->mRequiredRadioCount++;
169 void RadioGroupContainer::RemoveFromRadioGroup(const nsAString& aName,
170 HTMLInputElement* aRadio) {
171 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
172 MOZ_ASSERT(
173 radioGroup->mRadioButtons->Contains(aRadio),
174 "Attempting to remove radio button from group it is not a part of!");
176 radioGroup->mRadioButtons.RemoveElement(*aRadio);
178 if (aRadio->IsRequired()) {
179 MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
180 "mRequiredRadioCount about to wrap below 0!");
181 radioGroup->mRequiredRadioCount--;
185 uint32_t RadioGroupContainer::GetRequiredRadioCount(
186 const nsAString& aName) const {
187 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
188 return radioGroup ? radioGroup->mRequiredRadioCount : 0;
191 void RadioGroupContainer::RadioRequiredWillChange(const nsAString& aName,
192 bool aRequiredAdded) {
193 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
195 if (aRequiredAdded) {
196 radioGroup->mRequiredRadioCount++;
197 } else {
198 MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
199 "mRequiredRadioCount about to wrap below 0!");
200 radioGroup->mRequiredRadioCount--;
204 bool RadioGroupContainer::GetValueMissingState(const nsAString& aName) const {
205 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
206 return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
209 void RadioGroupContainer::SetValueMissingState(const nsAString& aName,
210 bool aValue) {
211 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
212 radioGroup->mGroupSuffersFromValueMissing = aValue;
215 nsRadioGroupStruct* RadioGroupContainer::GetRadioGroup(
216 const nsAString& aName) const {
217 nsRadioGroupStruct* radioGroup = nullptr;
218 mRadioGroups.Get(aName, &radioGroup);
219 return radioGroup;
222 nsRadioGroupStruct* RadioGroupContainer::GetOrCreateRadioGroup(
223 const nsAString& aName) {
224 return mRadioGroups.GetOrInsertNew(aName);
227 } // namespace mozilla::dom