summaryrefslogtreecommitdiffstats
path: root/dom/base/RadioGroupContainer.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/base/RadioGroupContainer.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base/RadioGroupContainer.cpp')
-rw-r--r--dom/base/RadioGroupContainer.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/dom/base/RadioGroupContainer.cpp b/dom/base/RadioGroupContainer.cpp
new file mode 100644
index 0000000000..1d5abb78c6
--- /dev/null
+++ b/dom/base/RadioGroupContainer.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/RadioGroupContainer.h"
+#include "mozilla/dom/TreeOrderedArrayInlines.h"
+#include "mozilla/Assertions.h"
+#include "nsIRadioVisitor.h"
+#include "nsRadioVisitor.h"
+
+namespace mozilla::dom {
+
+/**
+ * A struct that holds all the information about a radio group.
+ */
+struct nsRadioGroupStruct {
+ nsRadioGroupStruct()
+ : mRequiredRadioCount(0), mGroupSuffersFromValueMissing(false) {}
+
+ /**
+ * A strong pointer to the currently selected radio button.
+ */
+ RefPtr<HTMLInputElement> mSelectedRadioButton;
+ TreeOrderedArray<RefPtr<HTMLInputElement>> mRadioButtons;
+ uint32_t mRequiredRadioCount;
+ bool mGroupSuffersFromValueMissing;
+};
+
+RadioGroupContainer::RadioGroupContainer() = default;
+
+RadioGroupContainer::~RadioGroupContainer() {
+ for (const auto& group : mRadioGroups) {
+ for (const auto& button : group.GetData()->mRadioButtons.AsList()) {
+ // When the radio group container is being cycle-collected, any remaining
+ // connected buttons will also be in the process of being cycle-collected.
+ // Here, we unset the button's reference to the container so that when it
+ // is collected it does not attempt to remove itself from a potentially
+ // already deleted radio group.
+ button->DisconnectRadioGroupContainer();
+ }
+ }
+}
+
+/* static */
+void RadioGroupContainer::Traverse(RadioGroupContainer* tmp,
+ nsCycleCollectionTraversalCallback& cb) {
+ for (const auto& entry : tmp->mRadioGroups) {
+ nsRadioGroupStruct* radioGroup = entry.GetWeak();
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
+ cb, "mRadioGroups entry->mSelectedRadioButton");
+ cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
+
+ uint32_t i, count = radioGroup->mRadioButtons->Length();
+ for (i = 0; i < count; ++i) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
+ cb, "mRadioGroups entry->mRadioButtons[i]");
+ cb.NoteXPCOMChild(ToSupports(radioGroup->mRadioButtons->ElementAt(i)));
+ }
+ }
+}
+
+size_t RadioGroupContainer::SizeOfIncludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ return mRadioGroups.SizeOfIncludingThis(aMallocSizeOf);
+}
+
+nsresult RadioGroupContainer::WalkRadioGroup(const nsAString& aName,
+ nsIRadioVisitor* aVisitor) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+
+ for (HTMLInputElement* button : radioGroup->mRadioButtons.AsList()) {
+ if (!aVisitor->Visit(button)) {
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+void RadioGroupContainer::SetCurrentRadioButton(const nsAString& aName,
+ HTMLInputElement* aRadio) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mSelectedRadioButton = aRadio;
+}
+
+HTMLInputElement* RadioGroupContainer::GetCurrentRadioButton(
+ const nsAString& aName) {
+ return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
+}
+
+nsresult RadioGroupContainer::GetNextRadioButton(
+ const nsAString& aName, const bool aPrevious,
+ HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut) {
+ *aRadioOut = nullptr;
+
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+
+ // Return the radio button relative to the focused radio button.
+ // If no radio is focused, get the radio relative to the selected one.
+ RefPtr<HTMLInputElement> currentRadio;
+ if (aFocusedRadio) {
+ currentRadio = aFocusedRadio;
+ } else {
+ currentRadio = radioGroup->mSelectedRadioButton;
+ if (!currentRadio) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ int32_t index = radioGroup->mRadioButtons->IndexOf(currentRadio);
+ if (index < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t numRadios = static_cast<int32_t>(radioGroup->mRadioButtons->Length());
+ RefPtr<HTMLInputElement> radio;
+ do {
+ if (aPrevious) {
+ if (--index < 0) {
+ index = numRadios - 1;
+ }
+ } else if (++index >= numRadios) {
+ index = 0;
+ }
+ radio = radioGroup->mRadioButtons->ElementAt(index);
+ } while (radio->Disabled() && radio != currentRadio);
+
+ radio.forget(aRadioOut);
+ return NS_OK;
+}
+
+void RadioGroupContainer::AddToRadioGroup(const nsAString& aName,
+ HTMLInputElement* aRadio,
+ nsIContent* aAncestor) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mRadioButtons.Insert(*aRadio, aAncestor);
+ if (aRadio->IsRequired()) {
+ radioGroup->mRequiredRadioCount++;
+ }
+}
+
+void RadioGroupContainer::RemoveFromRadioGroup(const nsAString& aName,
+ HTMLInputElement* aRadio) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ MOZ_ASSERT(
+ radioGroup->mRadioButtons->Contains(aRadio),
+ "Attempting to remove radio button from group it is not a part of!");
+
+ radioGroup->mRadioButtons.RemoveElement(*aRadio);
+
+ if (aRadio->IsRequired()) {
+ MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
+ "mRequiredRadioCount about to wrap below 0!");
+ radioGroup->mRequiredRadioCount--;
+ }
+}
+
+uint32_t RadioGroupContainer::GetRequiredRadioCount(
+ const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
+ return radioGroup ? radioGroup->mRequiredRadioCount : 0;
+}
+
+void RadioGroupContainer::RadioRequiredWillChange(const nsAString& aName,
+ bool aRequiredAdded) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+
+ if (aRequiredAdded) {
+ radioGroup->mRequiredRadioCount++;
+ } else {
+ MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
+ "mRequiredRadioCount about to wrap below 0!");
+ radioGroup->mRequiredRadioCount--;
+ }
+}
+
+bool RadioGroupContainer::GetValueMissingState(const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
+ return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
+}
+
+void RadioGroupContainer::SetValueMissingState(const nsAString& aName,
+ bool aValue) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mGroupSuffersFromValueMissing = aValue;
+}
+
+nsRadioGroupStruct* RadioGroupContainer::GetRadioGroup(
+ const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = nullptr;
+ mRadioGroups.Get(aName, &radioGroup);
+ return radioGroup;
+}
+
+nsRadioGroupStruct* RadioGroupContainer::GetOrCreateRadioGroup(
+ const nsAString& aName) {
+ return mRadioGroups.GetOrInsertNew(aName);
+}
+
+} // namespace mozilla::dom