summaryrefslogtreecommitdiffstats
path: root/dom/base/RadioGroupManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/RadioGroupManager.cpp')
-rw-r--r--dom/base/RadioGroupManager.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/dom/base/RadioGroupManager.cpp b/dom/base/RadioGroupManager.cpp
new file mode 100644
index 0000000000..e7c8a730fa
--- /dev/null
+++ b/dom/base/RadioGroupManager.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 "RadioGroupManager.h"
+#include "nsIRadioVisitor.h"
+#include "mozilla/dom/HTMLInputElement.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;
+ nsTArray<RefPtr<HTMLInputElement>> mRadioButtons;
+ uint32_t mRequiredRadioCount;
+ bool mGroupSuffersFromValueMissing;
+};
+
+RadioGroupManager::RadioGroupManager() = default;
+
+void RadioGroupManager::Traverse(RadioGroupManager* 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[i]));
+ }
+ }
+}
+
+void RadioGroupManager::Unlink(RadioGroupManager* tmp) {
+ tmp->mRadioGroups.Clear();
+}
+
+nsresult RadioGroupManager::WalkRadioGroup(const nsAString& aName,
+ nsIRadioVisitor* aVisitor) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+
+ for (size_t i = 0; i < radioGroup->mRadioButtons.Length(); i++) {
+ if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+void RadioGroupManager::SetCurrentRadioButton(const nsAString& aName,
+ HTMLInputElement* aRadio) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mSelectedRadioButton = aRadio;
+}
+
+HTMLInputElement* RadioGroupManager::GetCurrentRadioButton(
+ const nsAString& aName) {
+ return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
+}
+
+nsresult RadioGroupManager::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[index];
+ } while (radio->Disabled() && radio != currentRadio);
+
+ radio.forget(aRadioOut);
+ return NS_OK;
+}
+
+void RadioGroupManager::AddToRadioGroup(const nsAString& aName,
+ HTMLInputElement* aRadio) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mRadioButtons.AppendElement(aRadio);
+
+ if (aRadio->IsRequired()) {
+ radioGroup->mRequiredRadioCount++;
+ }
+}
+
+void RadioGroupManager::RemoveFromRadioGroup(const nsAString& aName,
+ HTMLInputElement* aRadio) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mRadioButtons.RemoveElement(aRadio);
+
+ if (aRadio->IsRequired()) {
+ MOZ_ASSERT(radioGroup->mRequiredRadioCount != 0,
+ "mRequiredRadioCount about to wrap below 0!");
+ radioGroup->mRequiredRadioCount--;
+ }
+}
+
+uint32_t RadioGroupManager::GetRequiredRadioCount(
+ const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
+ return radioGroup ? radioGroup->mRequiredRadioCount : 0;
+}
+
+void RadioGroupManager::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 RadioGroupManager::GetValueMissingState(const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
+ return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
+}
+
+void RadioGroupManager::SetValueMissingState(const nsAString& aName,
+ bool aValue) {
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ radioGroup->mGroupSuffersFromValueMissing = aValue;
+}
+
+nsRadioGroupStruct* RadioGroupManager::GetRadioGroup(
+ const nsAString& aName) const {
+ nsRadioGroupStruct* radioGroup = nullptr;
+ mRadioGroups.Get(aName, &radioGroup);
+ return radioGroup;
+}
+
+nsRadioGroupStruct* RadioGroupManager::GetOrCreateRadioGroup(
+ const nsAString& aName) {
+ return mRadioGroups.GetOrInsertNew(aName);
+}
+
+} // namespace mozilla::dom