forked from mirrors/gecko-dev
Bug 1685926 - Group disconnected radio buttons together. r=saschanaz,smaug
Differential Revision: https://phabricator.services.mozilla.com/D162349
This commit is contained in:
parent
a861bb857d
commit
3198d33ca2
21 changed files with 390 additions and 423 deletions
|
|
@ -2429,7 +2429,6 @@ NS_INTERFACE_TABLE_HEAD(Document)
|
||||||
NS_INTERFACE_TABLE_ENTRY(Document, nsIScriptObjectPrincipal)
|
NS_INTERFACE_TABLE_ENTRY(Document, nsIScriptObjectPrincipal)
|
||||||
NS_INTERFACE_TABLE_ENTRY(Document, EventTarget)
|
NS_INTERFACE_TABLE_ENTRY(Document, EventTarget)
|
||||||
NS_INTERFACE_TABLE_ENTRY(Document, nsISupportsWeakReference)
|
NS_INTERFACE_TABLE_ENTRY(Document, nsISupportsWeakReference)
|
||||||
NS_INTERFACE_TABLE_ENTRY(Document, nsIRadioGroupContainer)
|
|
||||||
NS_INTERFACE_TABLE_END
|
NS_INTERFACE_TABLE_END
|
||||||
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document)
|
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document)
|
||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
|
|
@ -2504,6 +2503,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
|
||||||
|
|
||||||
DocumentOrShadowRoot::Traverse(tmp, cb);
|
DocumentOrShadowRoot::Traverse(tmp, cb);
|
||||||
|
|
||||||
|
if (tmp->mRadioGroupContainer) {
|
||||||
|
RadioGroupContainer::Traverse(tmp->mRadioGroupContainer.get(), cb);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& sheets : tmp->mAdditionalSheets) {
|
for (auto& sheets : tmp->mAdditionalSheets) {
|
||||||
tmp->TraverseStyleSheets(sheets, "mAdditionalSheets[<origin>][i]", cb);
|
tmp->TraverseStyleSheets(sheets, "mAdditionalSheets[<origin>][i]", cb);
|
||||||
}
|
}
|
||||||
|
|
@ -2703,6 +2706,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
|
||||||
|
|
||||||
DocumentOrShadowRoot::Unlink(tmp);
|
DocumentOrShadowRoot::Unlink(tmp);
|
||||||
|
|
||||||
|
tmp->mRadioGroupContainer = nullptr;
|
||||||
|
|
||||||
// Document has a pretty complex destructor, so we're going to
|
// Document has a pretty complex destructor, so we're going to
|
||||||
// assume that *most* cycles you actually want to break somewhere
|
// assume that *most* cycles you actually want to break somewhere
|
||||||
// else, and not unlink an awful lot here.
|
// else, and not unlink an awful lot here.
|
||||||
|
|
@ -15819,6 +15824,12 @@ void Document::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const {
|
||||||
aWindowSizes.mState.mMallocSizeOf);
|
aWindowSizes.mState.mMallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mRadioGroupContainer) {
|
||||||
|
aWindowSizes.mDOMSizes.mDOMOtherSize +=
|
||||||
|
mRadioGroupContainer->SizeOfIncludingThis(
|
||||||
|
aWindowSizes.mState.mMallocSizeOf);
|
||||||
|
}
|
||||||
|
|
||||||
aWindowSizes.mDOMSizes.mDOMOtherSize +=
|
aWindowSizes.mDOMSizes.mDOMOtherSize +=
|
||||||
mStyledLinks.ShallowSizeOfExcludingThis(
|
mStyledLinks.ShallowSizeOfExcludingThis(
|
||||||
aWindowSizes.mState.mMallocSizeOf);
|
aWindowSizes.mState.mMallocSizeOf);
|
||||||
|
|
@ -18761,4 +18772,11 @@ HighlightRegistry& Document::HighlightRegistry() {
|
||||||
return *mHighlightRegistry;
|
return *mHighlightRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RadioGroupContainer& Document::OwnedRadioGroupContainer() {
|
||||||
|
if (!mRadioGroupContainer) {
|
||||||
|
mRadioGroupContainer = MakeUnique<RadioGroupContainer>();
|
||||||
|
}
|
||||||
|
return *mRadioGroupContainer;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
#include "mozilla/dom/EventTarget.h"
|
#include "mozilla/dom/EventTarget.h"
|
||||||
#include "mozilla/dom/Nullable.h"
|
#include "mozilla/dom/Nullable.h"
|
||||||
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "mozilla/dom/TreeOrderedArray.h"
|
#include "mozilla/dom/TreeOrderedArray.h"
|
||||||
#include "mozilla/dom/ViewportMetaData.h"
|
#include "mozilla/dom/ViewportMetaData.h"
|
||||||
#include "mozilla/glean/GleanMetrics.h"
|
#include "mozilla/glean/GleanMetrics.h"
|
||||||
|
|
@ -79,7 +80,6 @@
|
||||||
#include "nsIParser.h"
|
#include "nsIParser.h"
|
||||||
#include "nsIPrincipal.h"
|
#include "nsIPrincipal.h"
|
||||||
#include "nsIProgressEventSink.h"
|
#include "nsIProgressEventSink.h"
|
||||||
#include "nsIRadioGroupContainer.h"
|
|
||||||
#include "nsIReferrerInfo.h"
|
#include "nsIReferrerInfo.h"
|
||||||
#include "nsIRequestObserver.h"
|
#include "nsIRequestObserver.h"
|
||||||
#include "nsIScriptObjectPrincipal.h"
|
#include "nsIScriptObjectPrincipal.h"
|
||||||
|
|
@ -532,7 +532,6 @@ enum class SheetPreloadStatus : uint8_t {
|
||||||
class Document : public nsINode,
|
class Document : public nsINode,
|
||||||
public DocumentOrShadowRoot,
|
public DocumentOrShadowRoot,
|
||||||
public nsSupportsWeakReference,
|
public nsSupportsWeakReference,
|
||||||
public nsIRadioGroupContainer,
|
|
||||||
public nsIScriptObjectPrincipal,
|
public nsIScriptObjectPrincipal,
|
||||||
public DispatcherTrait,
|
public DispatcherTrait,
|
||||||
public SupportsWeakPtr {
|
public SupportsWeakPtr {
|
||||||
|
|
@ -584,49 +583,6 @@ class Document : public nsINode,
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// nsIRadioGroupContainer
|
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
|
||||||
nsIRadioVisitor* aVisitor) final {
|
|
||||||
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetCurrentRadioButton(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) final {
|
|
||||||
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
|
|
||||||
}
|
|
||||||
|
|
||||||
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) final {
|
|
||||||
return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD
|
|
||||||
GetNextRadioButton(const nsAString& aName, const bool aPrevious,
|
|
||||||
HTMLInputElement* aFocusedRadio,
|
|
||||||
HTMLInputElement** aRadioOut) final {
|
|
||||||
return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
|
|
||||||
aFocusedRadio, aRadioOut);
|
|
||||||
}
|
|
||||||
void AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio) final {
|
|
||||||
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio, nullptr);
|
|
||||||
}
|
|
||||||
void RemoveFromRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) final {
|
|
||||||
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
|
|
||||||
}
|
|
||||||
uint32_t GetRequiredRadioCount(const nsAString& aName) const final {
|
|
||||||
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
|
|
||||||
}
|
|
||||||
void RadioRequiredWillChange(const nsAString& aName,
|
|
||||||
bool aRequiredAdded) final {
|
|
||||||
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
|
|
||||||
}
|
|
||||||
bool GetValueMissingState(const nsAString& aName) const final {
|
|
||||||
return DocumentOrShadowRoot::GetValueMissingState(aName);
|
|
||||||
}
|
|
||||||
void SetValueMissingState(const nsAString& aName, bool aValue) final {
|
|
||||||
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIPrincipal* EffectiveCookiePrincipal() const;
|
nsIPrincipal* EffectiveCookiePrincipal() const;
|
||||||
|
|
||||||
nsIPrincipal* EffectiveStoragePrincipal() const;
|
nsIPrincipal* EffectiveStoragePrincipal() const;
|
||||||
|
|
@ -5360,6 +5316,8 @@ class Document : public nsINode,
|
||||||
// Registry of custom highlight definitions associated with this document.
|
// Registry of custom highlight definitions associated with this document.
|
||||||
RefPtr<class HighlightRegistry> mHighlightRegistry;
|
RefPtr<class HighlightRegistry> mHighlightRegistry;
|
||||||
|
|
||||||
|
UniquePtr<RadioGroupContainer> mRadioGroupContainer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Needs to be public because the bindings code pokes at it.
|
// Needs to be public because the bindings code pokes at it.
|
||||||
JS::ExpandoAndGeneration mExpandoAndGeneration;
|
JS::ExpandoAndGeneration mExpandoAndGeneration;
|
||||||
|
|
@ -5375,6 +5333,8 @@ class Document : public nsINode,
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadEventFired();
|
void LoadEventFired();
|
||||||
|
|
||||||
|
RadioGroupContainer& OwnedRadioGroupContainer();
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(Document, NS_IDOCUMENT_IID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(Document, NS_IDOCUMENT_IID)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
#include "nsTHashtable.h"
|
#include "nsTHashtable.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsFocusManager.h"
|
#include "nsFocusManager.h"
|
||||||
#include "nsIRadioVisitor.h"
|
|
||||||
#include "nsIFormControl.h"
|
#include "nsIFormControl.h"
|
||||||
#include "nsLayoutUtils.h"
|
#include "nsLayoutUtils.h"
|
||||||
#include "nsNameSpaceManager.h"
|
#include "nsNameSpaceManager.h"
|
||||||
|
|
@ -668,8 +667,6 @@ void DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
|
||||||
for (auto iter = tmp->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
|
for (auto iter = tmp->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
|
||||||
iter.Get()->Traverse(&cb);
|
iter.Get()->Traverse(&cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioGroupManager::Traverse(tmp, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocumentOrShadowRoot::UnlinkStyleSheets(
|
void DocumentOrShadowRoot::UnlinkStyleSheets(
|
||||||
|
|
@ -691,7 +688,6 @@ void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
|
||||||
});
|
});
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdoptedStyleSheets);
|
||||||
tmp->mIdentifierMap.Clear();
|
tmp->mIdentifierMap.Clear();
|
||||||
RadioGroupManager::Unlink(tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,11 @@
|
||||||
#include "nsContentListDeclarations.h"
|
#include "nsContentListDeclarations.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsTHashSet.h"
|
#include "nsTHashSet.h"
|
||||||
#include "RadioGroupManager.h"
|
|
||||||
|
|
||||||
class nsContentList;
|
class nsContentList;
|
||||||
class nsCycleCollectionTraversalCallback;
|
class nsCycleCollectionTraversalCallback;
|
||||||
class nsINode;
|
class nsINode;
|
||||||
class nsINodeList;
|
class nsINodeList;
|
||||||
class nsIRadioVisitor;
|
|
||||||
class nsWindowSizes;
|
class nsWindowSizes;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
@ -48,7 +46,7 @@ class Sequence;
|
||||||
* TODO(emilio, bug 1418159): In the future this should hold most of the
|
* TODO(emilio, bug 1418159): In the future this should hold most of the
|
||||||
* relevant style state, this should allow us to fix bug 548397.
|
* relevant style state, this should allow us to fix bug 548397.
|
||||||
*/
|
*/
|
||||||
class DocumentOrShadowRoot : public RadioGroupManager {
|
class DocumentOrShadowRoot {
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
Document,
|
Document,
|
||||||
ShadowRoot,
|
ShadowRoot,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "mozilla/TouchEvents.h"
|
#include "mozilla/TouchEvents.h"
|
||||||
#include "mozilla/URLExtraData.h"
|
#include "mozilla/URLExtraData.h"
|
||||||
#include "mozilla/dom/Attr.h"
|
#include "mozilla/dom/Attr.h"
|
||||||
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "nsDOMAttributeMap.h"
|
#include "nsDOMAttributeMap.h"
|
||||||
#include "nsAtom.h"
|
#include "nsAtom.h"
|
||||||
#include "mozilla/dom/NodeInfo.h"
|
#include "mozilla/dom/NodeInfo.h"
|
||||||
|
|
@ -659,6 +660,7 @@ void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
|
||||||
aContent.ClearMayHaveAnimations();
|
aContent.ClearMayHaveAnimations();
|
||||||
}
|
}
|
||||||
mExplicitlySetAttrElements.Clear();
|
mExplicitlySetAttrElements.Clear();
|
||||||
|
mRadioGroupContainer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
|
void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
|
||||||
|
|
@ -683,6 +685,9 @@ void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
|
||||||
if (mAnimations) {
|
if (mAnimations) {
|
||||||
mAnimations->Traverse(aCb);
|
mAnimations->Traverse(aCb);
|
||||||
}
|
}
|
||||||
|
if (mRadioGroupContainer) {
|
||||||
|
RadioGroupContainer::Traverse(mRadioGroupContainer.get(), aCb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
|
size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
|
||||||
|
|
@ -717,6 +722,10 @@ size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
|
||||||
n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
|
n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mRadioGroupContainer) {
|
||||||
|
n += mRadioGroupContainer->SizeOfIncludingThis(aMallocSizeOf);
|
||||||
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "mozilla/EnumSet.h"
|
#include "mozilla/EnumSet.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
|
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
|
||||||
#include "nsIContent.h" // base class
|
#include "nsIContent.h" // base class
|
||||||
#include "nsAtomHashKeys.h"
|
#include "nsAtomHashKeys.h"
|
||||||
|
|
@ -114,6 +115,14 @@ class FragmentOrElement : public nsIContent {
|
||||||
return Children()->Length();
|
return Children()->Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RadioGroupContainer& OwnedRadioGroupContainer() {
|
||||||
|
auto* slots = ExtendedDOMSlots();
|
||||||
|
if (!slots->mRadioGroupContainer) {
|
||||||
|
slots->mRadioGroupContainer = MakeUnique<RadioGroupContainer>();
|
||||||
|
}
|
||||||
|
return *slots->mRadioGroupContainer;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* If there are listeners for DOMNodeInserted event, fires the event on all
|
* If there are listeners for DOMNodeInserted event, fires the event on all
|
||||||
|
|
@ -208,6 +217,12 @@ class FragmentOrElement : public nsIContent {
|
||||||
*/
|
*/
|
||||||
UniquePtr<PopoverData> mPopoverData;
|
UniquePtr<PopoverData> mPopoverData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RadioGroupContainer for radio buttons grouped under this disconnected
|
||||||
|
* element.
|
||||||
|
*/
|
||||||
|
UniquePtr<RadioGroupContainer> mRadioGroupContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last remembered size (in CSS pixels) for the element.
|
* Last remembered size (in CSS pixels) for the element.
|
||||||
* @see {@link https://drafts.csswg.org/css-sizing-4/#last-remembered}
|
* @see {@link https://drafts.csswg.org/css-sizing-4/#last-remembered}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "RadioGroupManager.h"
|
|
||||||
#include "nsIRadioVisitor.h"
|
|
||||||
#include "mozilla/dom/HTMLInputElement.h"
|
#include "mozilla/dom/HTMLInputElement.h"
|
||||||
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "nsIRadioVisitor.h"
|
||||||
|
#include "nsRadioVisitor.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
|
|
@ -26,10 +28,24 @@ struct nsRadioGroupStruct {
|
||||||
bool mGroupSuffersFromValueMissing;
|
bool mGroupSuffersFromValueMissing;
|
||||||
};
|
};
|
||||||
|
|
||||||
RadioGroupManager::RadioGroupManager() = default;
|
RadioGroupContainer::RadioGroupContainer() = default;
|
||||||
|
|
||||||
void RadioGroupManager::Traverse(RadioGroupManager* tmp,
|
RadioGroupContainer::~RadioGroupContainer() {
|
||||||
nsCycleCollectionTraversalCallback& cb) {
|
for (const auto& group : mRadioGroups) {
|
||||||
|
for (const auto& button : group.GetData()->mRadioButtons) {
|
||||||
|
// 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) {
|
for (const auto& entry : tmp->mRadioGroups) {
|
||||||
nsRadioGroupStruct* radioGroup = entry.GetWeak();
|
nsRadioGroupStruct* radioGroup = entry.GetWeak();
|
||||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
||||||
|
|
@ -45,12 +61,13 @@ void RadioGroupManager::Traverse(RadioGroupManager* tmp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::Unlink(RadioGroupManager* tmp) {
|
size_t RadioGroupContainer::SizeOfIncludingThis(
|
||||||
tmp->mRadioGroups.Clear();
|
MallocSizeOf aMallocSizeOf) const {
|
||||||
|
return mRadioGroups.SizeOfIncludingThis(aMallocSizeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult RadioGroupManager::WalkRadioGroup(const nsAString& aName,
|
nsresult RadioGroupContainer::WalkRadioGroup(const nsAString& aName,
|
||||||
nsIRadioVisitor* aVisitor) {
|
nsIRadioVisitor* aVisitor) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
|
|
||||||
for (size_t i = 0; i < radioGroup->mRadioButtons.Length(); i++) {
|
for (size_t i = 0; i < radioGroup->mRadioButtons.Length(); i++) {
|
||||||
|
|
@ -62,21 +79,20 @@ nsresult RadioGroupManager::WalkRadioGroup(const nsAString& aName,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::SetCurrentRadioButton(const nsAString& aName,
|
void RadioGroupContainer::SetCurrentRadioButton(const nsAString& aName,
|
||||||
HTMLInputElement* aRadio) {
|
HTMLInputElement* aRadio) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
radioGroup->mSelectedRadioButton = aRadio;
|
radioGroup->mSelectedRadioButton = aRadio;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTMLInputElement* RadioGroupManager::GetCurrentRadioButton(
|
HTMLInputElement* RadioGroupContainer::GetCurrentRadioButton(
|
||||||
const nsAString& aName) {
|
const nsAString& aName) {
|
||||||
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
|
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult RadioGroupManager::GetNextRadioButton(const nsAString& aName,
|
nsresult RadioGroupContainer::GetNextRadioButton(
|
||||||
const bool aPrevious,
|
const nsAString& aName, const bool aPrevious,
|
||||||
HTMLInputElement* aFocusedRadio,
|
HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut) {
|
||||||
HTMLInputElement** aRadioOut) {
|
|
||||||
*aRadioOut = nullptr;
|
*aRadioOut = nullptr;
|
||||||
|
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
|
|
@ -114,9 +130,9 @@ nsresult RadioGroupManager::GetNextRadioButton(const nsAString& aName,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::AddToRadioGroup(const nsAString& aName,
|
void RadioGroupContainer::AddToRadioGroup(const nsAString& aName,
|
||||||
HTMLInputElement* aRadio,
|
HTMLInputElement* aRadio,
|
||||||
nsIContent* aAncestor) {
|
nsIContent* aAncestor) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
nsContentUtils::AddElementToListByTreeOrder(radioGroup->mRadioButtons, aRadio,
|
nsContentUtils::AddElementToListByTreeOrder(radioGroup->mRadioButtons, aRadio,
|
||||||
aAncestor);
|
aAncestor);
|
||||||
|
|
@ -126,9 +142,13 @@ void RadioGroupManager::AddToRadioGroup(const nsAString& aName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::RemoveFromRadioGroup(const nsAString& aName,
|
void RadioGroupContainer::RemoveFromRadioGroup(const nsAString& aName,
|
||||||
HTMLInputElement* aRadio) {
|
HTMLInputElement* aRadio) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
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);
|
radioGroup->mRadioButtons.RemoveElement(aRadio);
|
||||||
|
|
||||||
if (aRadio->IsRequired()) {
|
if (aRadio->IsRequired()) {
|
||||||
|
|
@ -138,14 +158,14 @@ void RadioGroupManager::RemoveFromRadioGroup(const nsAString& aName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RadioGroupManager::GetRequiredRadioCount(
|
uint32_t RadioGroupContainer::GetRequiredRadioCount(
|
||||||
const nsAString& aName) const {
|
const nsAString& aName) const {
|
||||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||||
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
|
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::RadioRequiredWillChange(const nsAString& aName,
|
void RadioGroupContainer::RadioRequiredWillChange(const nsAString& aName,
|
||||||
bool aRequiredAdded) {
|
bool aRequiredAdded) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
|
|
||||||
if (aRequiredAdded) {
|
if (aRequiredAdded) {
|
||||||
|
|
@ -157,25 +177,25 @@ void RadioGroupManager::RadioRequiredWillChange(const nsAString& aName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RadioGroupManager::GetValueMissingState(const nsAString& aName) const {
|
bool RadioGroupContainer::GetValueMissingState(const nsAString& aName) const {
|
||||||
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
|
||||||
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
|
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioGroupManager::SetValueMissingState(const nsAString& aName,
|
void RadioGroupContainer::SetValueMissingState(const nsAString& aName,
|
||||||
bool aValue) {
|
bool aValue) {
|
||||||
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
|
||||||
radioGroup->mGroupSuffersFromValueMissing = aValue;
|
radioGroup->mGroupSuffersFromValueMissing = aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRadioGroupStruct* RadioGroupManager::GetRadioGroup(
|
nsRadioGroupStruct* RadioGroupContainer::GetRadioGroup(
|
||||||
const nsAString& aName) const {
|
const nsAString& aName) const {
|
||||||
nsRadioGroupStruct* radioGroup = nullptr;
|
nsRadioGroupStruct* radioGroup = nullptr;
|
||||||
mRadioGroups.Get(aName, &radioGroup);
|
mRadioGroups.Get(aName, &radioGroup);
|
||||||
return radioGroup;
|
return radioGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRadioGroupStruct* RadioGroupManager::GetOrCreateRadioGroup(
|
nsRadioGroupStruct* RadioGroupContainer::GetOrCreateRadioGroup(
|
||||||
const nsAString& aName) {
|
const nsAString& aName) {
|
||||||
return mRadioGroups.GetOrInsertNew(aName);
|
return mRadioGroups.GetOrInsertNew(aName);
|
||||||
}
|
}
|
||||||
|
|
@ -7,32 +7,25 @@
|
||||||
#ifndef mozilla_dom_nsRadioGroupStruct_h
|
#ifndef mozilla_dom_nsRadioGroupStruct_h
|
||||||
#define mozilla_dom_nsRadioGroupStruct_h
|
#define mozilla_dom_nsRadioGroupStruct_h
|
||||||
|
|
||||||
#include "nsCOMArray.h"
|
|
||||||
#include "nsIFormControl.h"
|
|
||||||
#include "nsIRadioGroupContainer.h"
|
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
|
|
||||||
class nsIContent;
|
class nsIContent;
|
||||||
|
|
||||||
namespace mozilla {
|
|
||||||
|
|
||||||
namespace html {
|
|
||||||
class nsIRadioVisitor;
|
class nsIRadioVisitor;
|
||||||
}
|
|
||||||
|
|
||||||
namespace dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
class HTMLInputElement;
|
class HTMLInputElement;
|
||||||
struct nsRadioGroupStruct;
|
struct nsRadioGroupStruct;
|
||||||
|
|
||||||
class RadioGroupManager {
|
class RadioGroupContainer final {
|
||||||
public:
|
public:
|
||||||
RadioGroupManager();
|
RadioGroupContainer();
|
||||||
|
~RadioGroupContainer();
|
||||||
|
|
||||||
static void Traverse(RadioGroupManager* tmp,
|
static void Traverse(RadioGroupContainer* tmp,
|
||||||
nsCycleCollectionTraversalCallback& cb);
|
nsCycleCollectionTraversalCallback& cb);
|
||||||
static void Unlink(RadioGroupManager* tmp);
|
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||||
|
|
||||||
// nsIRadioGroupContainer
|
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor);
|
NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor);
|
||||||
void SetCurrentRadioButton(const nsAString& aName, HTMLInputElement* aRadio);
|
void SetCurrentRadioButton(const nsAString& aName, HTMLInputElement* aRadio);
|
||||||
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName);
|
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName);
|
||||||
|
|
@ -55,7 +48,6 @@ class RadioGroupManager {
|
||||||
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
|
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace mozilla::dom
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // mozilla_dom_nsRadioGroupStruct_h
|
#endif // mozilla_dom_nsRadioGroupStruct_h
|
||||||
|
|
@ -44,7 +44,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
|
||||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
|
|
||||||
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
|
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
|
||||||
|
|
||||||
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
|
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
#include "mozilla/ServoBindings.h"
|
#include "mozilla/ServoBindings.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsIRadioGroupContainer.h"
|
|
||||||
#include "nsStubMutationObserver.h"
|
#include "nsStubMutationObserver.h"
|
||||||
#include "nsTHashtable.h"
|
#include "nsTHashtable.h"
|
||||||
|
|
||||||
|
|
@ -40,9 +39,7 @@ class CSSImportRule;
|
||||||
class Element;
|
class Element;
|
||||||
class HTMLInputElement;
|
class HTMLInputElement;
|
||||||
|
|
||||||
class ShadowRoot final : public DocumentFragment,
|
class ShadowRoot final : public DocumentFragment, public DocumentOrShadowRoot {
|
||||||
public DocumentOrShadowRoot,
|
|
||||||
public nsIRadioGroupContainer {
|
|
||||||
friend class DocumentOrShadowRoot;
|
friend class DocumentOrShadowRoot;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -234,58 +231,16 @@ class ShadowRoot final : public DocumentFragment,
|
||||||
|
|
||||||
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
|
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
|
||||||
|
|
||||||
// nsIRadioGroupContainer
|
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
|
||||||
nsIRadioVisitor* aVisitor) override {
|
|
||||||
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor);
|
|
||||||
}
|
|
||||||
void SetCurrentRadioButton(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override {
|
|
||||||
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
|
|
||||||
}
|
|
||||||
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) override {
|
|
||||||
return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
|
|
||||||
}
|
|
||||||
NS_IMETHOD
|
|
||||||
GetNextRadioButton(const nsAString& aName, const bool aPrevious,
|
|
||||||
HTMLInputElement* aFocusedRadio,
|
|
||||||
HTMLInputElement** aRadioOut) override {
|
|
||||||
return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
|
|
||||||
aFocusedRadio, aRadioOut);
|
|
||||||
}
|
|
||||||
void AddToRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override {
|
|
||||||
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio, this);
|
|
||||||
}
|
|
||||||
void RemoveFromRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override {
|
|
||||||
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
|
|
||||||
}
|
|
||||||
uint32_t GetRequiredRadioCount(const nsAString& aName) const override {
|
|
||||||
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
|
|
||||||
}
|
|
||||||
void RadioRequiredWillChange(const nsAString& aName,
|
|
||||||
bool aRequiredAdded) override {
|
|
||||||
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
|
|
||||||
}
|
|
||||||
bool GetValueMissingState(const nsAString& aName) const override {
|
|
||||||
return DocumentOrShadowRoot::GetValueMissingState(aName);
|
|
||||||
}
|
|
||||||
void SetValueMissingState(const nsAString& aName, bool aValue) override {
|
|
||||||
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// FIXME(emilio): This will need to become more fine-grained.
|
// FIXME(emilio): This will need to become more fine-grained.
|
||||||
void ApplicableRulesChanged();
|
void ApplicableRulesChanged();
|
||||||
|
|
||||||
virtual ~ShadowRoot();
|
virtual ~ShadowRoot();
|
||||||
|
|
||||||
const ShadowRootMode mMode;
|
// Make sure that the first field is pointer-aligned so it doesn't get packed
|
||||||
|
// in the base class' padding, since otherwise rust-bindgen can't generate
|
||||||
Element::DelegatesFocus mDelegatesFocus;
|
// correct bindings for it, see
|
||||||
|
// https://github.com/rust-lang/rust-bindgen/issues/380
|
||||||
const SlotAssignmentMode mSlotAssignment;
|
|
||||||
|
|
||||||
// The computed data from the style sheets.
|
// The computed data from the style sheets.
|
||||||
UniquePtr<StyleAuthorStyles> mServoStyles;
|
UniquePtr<StyleAuthorStyles> mServoStyles;
|
||||||
|
|
@ -301,6 +256,12 @@ class ShadowRoot final : public DocumentFragment,
|
||||||
// tree.
|
// tree.
|
||||||
nsTArray<const Element*> mParts;
|
nsTArray<const Element*> mParts;
|
||||||
|
|
||||||
|
const ShadowRootMode mMode;
|
||||||
|
|
||||||
|
Element::DelegatesFocus mDelegatesFocus;
|
||||||
|
|
||||||
|
const SlotAssignmentMode mSlotAssignment;
|
||||||
|
|
||||||
// Whether this is the <details> internal shadow tree.
|
// Whether this is the <details> internal shadow tree.
|
||||||
bool mIsDetailsShadowTree : 1;
|
bool mIsDetailsShadowTree : 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ EXPORTS.mozilla.dom += [
|
||||||
"Pose.h",
|
"Pose.h",
|
||||||
"PostMessageEvent.h",
|
"PostMessageEvent.h",
|
||||||
"ProcessMessageManager.h",
|
"ProcessMessageManager.h",
|
||||||
"RadioGroupManager.h",
|
"RadioGroupContainer.h",
|
||||||
"ResizeObserver.h",
|
"ResizeObserver.h",
|
||||||
"ResizeObserverController.h",
|
"ResizeObserverController.h",
|
||||||
"ResponsiveImageSelector.h",
|
"ResponsiveImageSelector.h",
|
||||||
|
|
@ -438,7 +438,7 @@ UNIFIED_SOURCES += [
|
||||||
"Pose.cpp",
|
"Pose.cpp",
|
||||||
"PostMessageEvent.cpp",
|
"PostMessageEvent.cpp",
|
||||||
"ProcessMessageManager.cpp",
|
"ProcessMessageManager.cpp",
|
||||||
"RadioGroupManager.cpp",
|
"RadioGroupContainer.cpp",
|
||||||
"RangeUtils.cpp",
|
"RangeUtils.cpp",
|
||||||
"RemoteOuterWindowProxy.cpp",
|
"RemoteOuterWindowProxy.cpp",
|
||||||
"ResizeObserver.cpp",
|
"ResizeObserver.cpp",
|
||||||
|
|
|
||||||
|
|
@ -145,21 +145,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFormElement,
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelList)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTargetContext)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTargetContext)
|
||||||
RadioGroupManager::Traverse(tmp, cb);
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFormElement,
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFormElement,
|
||||||
nsGenericHTMLElement)
|
nsGenericHTMLElement)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRelList)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTargetContext)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTargetContext)
|
||||||
RadioGroupManager::Unlink(tmp);
|
|
||||||
tmp->Clear();
|
tmp->Clear();
|
||||||
tmp->mExpandoAndGeneration.OwnerUnlinked();
|
tmp->mExpandoAndGeneration.OwnerUnlinked();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLFormElement,
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLFormElement,
|
||||||
nsGenericHTMLElement,
|
nsGenericHTMLElement)
|
||||||
nsIRadioGroupContainer)
|
|
||||||
|
|
||||||
// EventTarget
|
// EventTarget
|
||||||
void HTMLFormElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
|
void HTMLFormElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
|
||||||
|
|
@ -1251,7 +1248,7 @@ nsresult HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
|
||||||
// being count twice.
|
// being count twice.
|
||||||
if (type == FormControlType::InputRadio) {
|
if (type == FormControlType::InputRadio) {
|
||||||
RefPtr<HTMLInputElement> radio = static_cast<HTMLInputElement*>(aChild);
|
RefPtr<HTMLInputElement> radio = static_cast<HTMLInputElement*>(aChild);
|
||||||
radio->AddedToRadioGroup();
|
radio->AddToRadioGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
@ -1287,7 +1284,7 @@ nsresult HTMLFormElement::RemoveElement(nsGenericHTMLFormElement* aChild,
|
||||||
MOZ_ASSERT(fc);
|
MOZ_ASSERT(fc);
|
||||||
if (fc->ControlType() == FormControlType::InputRadio) {
|
if (fc->ControlType() == FormControlType::InputRadio) {
|
||||||
RefPtr<HTMLInputElement> radio = static_cast<HTMLInputElement*>(aChild);
|
RefPtr<HTMLInputElement> radio = static_cast<HTMLInputElement*>(aChild);
|
||||||
radio->WillRemoveFromRadioGroup();
|
radio->RemoveFromRadioGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether to remove the child from the elements list
|
// Determine whether to remove the child from the elements list
|
||||||
|
|
@ -1860,59 +1857,6 @@ int32_t HTMLFormElement::IndexOfContent(nsIContent* aContent) {
|
||||||
return mControls->IndexOfContent(aContent, &index) == NS_OK ? index : 0;
|
return mControls->IndexOfContent(aContent, &index) == NS_OK ? index : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLFormElement::SetCurrentRadioButton(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) {
|
|
||||||
RadioGroupManager::SetCurrentRadioButton(aName, aRadio);
|
|
||||||
}
|
|
||||||
|
|
||||||
HTMLInputElement* HTMLFormElement::GetCurrentRadioButton(
|
|
||||||
const nsAString& aName) {
|
|
||||||
return RadioGroupManager::GetCurrentRadioButton(aName);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
HTMLFormElement::GetNextRadioButton(const nsAString& aName,
|
|
||||||
const bool aPrevious,
|
|
||||||
HTMLInputElement* aFocusedRadio,
|
|
||||||
HTMLInputElement** aRadioOut) {
|
|
||||||
return RadioGroupManager::GetNextRadioButton(aName, aPrevious, aFocusedRadio,
|
|
||||||
aRadioOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
HTMLFormElement::WalkRadioGroup(const nsAString& aName,
|
|
||||||
nsIRadioVisitor* aVisitor) {
|
|
||||||
return RadioGroupManager::WalkRadioGroup(aName, aVisitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLFormElement::AddToRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) {
|
|
||||||
RadioGroupManager::AddToRadioGroup(aName, aRadio, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) {
|
|
||||||
RadioGroupManager::RemoveFromRadioGroup(aName, aRadio);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t HTMLFormElement::GetRequiredRadioCount(const nsAString& aName) const {
|
|
||||||
return RadioGroupManager::GetRequiredRadioCount(aName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLFormElement::RadioRequiredWillChange(const nsAString& aName,
|
|
||||||
bool aRequiredAdded) {
|
|
||||||
RadioGroupManager::RadioRequiredWillChange(aName, aRequiredAdded);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTMLFormElement::GetValueMissingState(const nsAString& aName) const {
|
|
||||||
return RadioGroupManager::GetValueMissingState(aName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLFormElement::SetValueMissingState(const nsAString& aName,
|
|
||||||
bool aValue) {
|
|
||||||
RadioGroupManager::SetValueMissingState(aName, aValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTMLFormElement::Clear() {
|
void HTMLFormElement::Clear() {
|
||||||
for (int32_t i = mImageElements.Length() - 1; i >= 0; i--) {
|
for (int32_t i = mImageElements.Length() - 1; i >= 0; i--) {
|
||||||
mImageElements[i]->ClearForm(false);
|
mImageElements[i]->ClearForm(false);
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,10 @@
|
||||||
#include "mozilla/dom/AnchorAreaFormRelValues.h"
|
#include "mozilla/dom/AnchorAreaFormRelValues.h"
|
||||||
#include "mozilla/dom/BrowsingContext.h"
|
#include "mozilla/dom/BrowsingContext.h"
|
||||||
#include "mozilla/dom/PopupBlocker.h"
|
#include "mozilla/dom/PopupBlocker.h"
|
||||||
#include "mozilla/dom/RadioGroupManager.h"
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsIFormControl.h"
|
#include "nsIFormControl.h"
|
||||||
#include "nsGenericHTMLElement.h"
|
#include "nsGenericHTMLElement.h"
|
||||||
#include "nsIRadioGroupContainer.h"
|
|
||||||
#include "nsIWeakReferenceUtils.h"
|
#include "nsIWeakReferenceUtils.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsInterfaceHashtable.h"
|
#include "nsInterfaceHashtable.h"
|
||||||
|
|
@ -39,9 +38,7 @@ class HTMLImageElement;
|
||||||
class FormData;
|
class FormData;
|
||||||
|
|
||||||
class HTMLFormElement final : public nsGenericHTMLElement,
|
class HTMLFormElement final : public nsGenericHTMLElement,
|
||||||
public nsIRadioGroupContainer,
|
public AnchorAreaFormRelValues {
|
||||||
public AnchorAreaFormRelValues,
|
|
||||||
RadioGroupManager {
|
|
||||||
friend class HTMLFormControlsCollection;
|
friend class HTMLFormControlsCollection;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -61,25 +58,6 @@ class HTMLFormElement final : public nsGenericHTMLElement,
|
||||||
return aElement == mDefaultSubmitElement;
|
return aElement == mDefaultSubmitElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
// nsIRadioGroupContainer
|
|
||||||
void SetCurrentRadioButton(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override;
|
|
||||||
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) override;
|
|
||||||
NS_IMETHOD GetNextRadioButton(const nsAString& aName, const bool aPrevious,
|
|
||||||
HTMLInputElement* aFocusedRadio,
|
|
||||||
HTMLInputElement** aRadioOut) override;
|
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
|
||||||
nsIRadioVisitor* aVisitor) override;
|
|
||||||
void AddToRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override;
|
|
||||||
void RemoveFromRadioGroup(const nsAString& aName,
|
|
||||||
HTMLInputElement* aRadio) override;
|
|
||||||
uint32_t GetRequiredRadioCount(const nsAString& aName) const override;
|
|
||||||
void RadioRequiredWillChange(const nsAString& aName,
|
|
||||||
bool aRequiredAdded) override;
|
|
||||||
bool GetValueMissingState(const nsAString& aName) const override;
|
|
||||||
void SetValueMissingState(const nsAString& aName, bool aValue) override;
|
|
||||||
|
|
||||||
// EventTarget
|
// EventTarget
|
||||||
void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
|
void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
|
|
||||||
#include "nsIRadioVisitor.h"
|
|
||||||
|
|
||||||
#include "HTMLDataListElement.h"
|
#include "HTMLDataListElement.h"
|
||||||
#include "HTMLFormSubmissionConstants.h"
|
#include "HTMLFormSubmissionConstants.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
@ -85,7 +83,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
// input type=radio
|
// input type=radio
|
||||||
#include "nsIRadioGroupContainer.h"
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
|
#include "nsIRadioVisitor.h"
|
||||||
|
#include "nsRadioVisitor.h"
|
||||||
|
|
||||||
// input type=file
|
// input type=file
|
||||||
#include "mozilla/dom/FileSystemEntry.h"
|
#include "mozilla/dom/FileSystemEntry.h"
|
||||||
|
|
@ -106,7 +106,6 @@
|
||||||
#include "nsContentCreatorFunctions.h"
|
#include "nsContentCreatorFunctions.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "mozilla/dom/DirectionalityUtils.h"
|
#include "mozilla/dom/DirectionalityUtils.h"
|
||||||
#include "nsRadioVisitor.h"
|
|
||||||
|
|
||||||
#include "mozilla/LookAndFeel.h"
|
#include "mozilla/LookAndFeel.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
|
|
@ -1014,7 +1013,8 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<dom::NodeInfo>&& aNodeInfo,
|
||||||
mPickerRunning(false),
|
mPickerRunning(false),
|
||||||
mIsPreviewEnabled(false),
|
mIsPreviewEnabled(false),
|
||||||
mHasBeenTypePassword(false),
|
mHasBeenTypePassword(false),
|
||||||
mHasPatternAttribute(false) {
|
mHasPatternAttribute(false),
|
||||||
|
mRadioGroupContainer(nullptr) {
|
||||||
// If size is above 512, mozjemalloc allocates 1kB, see
|
// If size is above 512, mozjemalloc allocates 1kB, see
|
||||||
// memory/build/mozjemalloc.cpp
|
// memory/build/mozjemalloc.cpp
|
||||||
static_assert(sizeof(HTMLInputElement) <= 512,
|
static_assert(sizeof(HTMLInputElement) <= 512,
|
||||||
|
|
@ -1186,9 +1186,9 @@ void HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||||
if (mType == FormControlType::InputRadio) {
|
if (mType == FormControlType::InputRadio) {
|
||||||
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
|
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
|
||||||
(mForm || mDoneCreating)) {
|
(mForm || mDoneCreating)) {
|
||||||
WillRemoveFromRadioGroup();
|
RemoveFromRadioGroup();
|
||||||
} else if (aName == nsGkAtoms::required) {
|
} else if (aName == nsGkAtoms::required) {
|
||||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
auto* container = GetCurrentRadioGroupContainer();
|
||||||
|
|
||||||
if (container && ((aValue && !HasAttr(aNameSpaceID, aName)) ||
|
if (container && ((aValue && !HasAttr(aNameSpaceID, aName)) ||
|
||||||
(!aValue && HasAttr(aNameSpaceID, aName)))) {
|
(!aValue && HasAttr(aNameSpaceID, aName)))) {
|
||||||
|
|
@ -1284,7 +1284,7 @@ void HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||||
// If we are not done creating the radio, we also should not do it.
|
// If we are not done creating the radio, we also should not do it.
|
||||||
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
|
if ((aName == nsGkAtoms::name || (aName == nsGkAtoms::type && !mForm)) &&
|
||||||
mType == FormControlType::InputRadio && (mForm || mDoneCreating)) {
|
mType == FormControlType::InputRadio && (mForm || mDoneCreating)) {
|
||||||
AddedToRadioGroup();
|
AddToRadioGroup();
|
||||||
UpdateValueMissingValidityStateForRadio(false);
|
UpdateValueMissingValidityStateForRadio(false);
|
||||||
needValidityUpdate = true;
|
needValidityUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1416,7 +1416,7 @@ void HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||||
void HTMLInputElement::BeforeSetForm(bool aBindToTree) {
|
void HTMLInputElement::BeforeSetForm(bool aBindToTree) {
|
||||||
// No need to remove from radio group if we are just binding to tree.
|
// No need to remove from radio group if we are just binding to tree.
|
||||||
if (mType == FormControlType::InputRadio && !aBindToTree) {
|
if (mType == FormControlType::InputRadio && !aBindToTree) {
|
||||||
WillRemoveFromRadioGroup();
|
RemoveFromRadioGroup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1424,8 +1424,9 @@ void HTMLInputElement::AfterClearForm(bool aUnbindOrDelete) {
|
||||||
MOZ_ASSERT(!mForm);
|
MOZ_ASSERT(!mForm);
|
||||||
|
|
||||||
// Do not add back to radio group if we are releasing or unbinding from tree.
|
// Do not add back to radio group if we are releasing or unbinding from tree.
|
||||||
if (mType == FormControlType::InputRadio && !aUnbindOrDelete) {
|
if (mType == FormControlType::InputRadio && !aUnbindOrDelete &&
|
||||||
AddedToRadioGroup();
|
!GetCurrentRadioGroupContainer()) {
|
||||||
|
AddToRadioGroup();
|
||||||
UpdateValueMissingValidityStateForRadio(false);
|
UpdateValueMissingValidityStateForRadio(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2861,8 +2862,7 @@ void HTMLInputElement::DoSetChecked(bool aChecked, bool aNotify,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
if (auto* container = GetCurrentRadioGroupContainer()) {
|
||||||
if (container) {
|
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
container->SetCurrentRadioButton(name, nullptr);
|
container->SetCurrentRadioButton(name, nullptr);
|
||||||
|
|
@ -2885,8 +2885,7 @@ void HTMLInputElement::RadioSetChecked(bool aNotify) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let the group know that we are now the One True Radio Button
|
// Let the group know that we are now the One True Radio Button
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
if (auto* container = GetCurrentRadioGroupContainer()) {
|
||||||
if (container) {
|
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
container->SetCurrentRadioButton(name, this);
|
container->SetCurrentRadioButton(name, this);
|
||||||
|
|
@ -2897,38 +2896,39 @@ void HTMLInputElement::RadioSetChecked(bool aNotify) {
|
||||||
SetCheckedInternal(true, aNotify);
|
SetCheckedInternal(true, aNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIRadioGroupContainer* HTMLInputElement::GetRadioGroupContainer() const {
|
RadioGroupContainer* HTMLInputElement::GetCurrentRadioGroupContainer() const {
|
||||||
NS_ASSERTION(
|
NS_ASSERTION(
|
||||||
mType == FormControlType::InputRadio,
|
mType == FormControlType::InputRadio,
|
||||||
"GetRadioGroupContainer should only be called when type='radio'");
|
"GetRadioGroupContainer should only be called when type='radio'");
|
||||||
|
return mRadioGroupContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioGroupContainer* HTMLInputElement::FindTreeRadioGroupContainer() const {
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
|
|
||||||
if (name.IsEmpty()) {
|
if (name.IsEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mForm) {
|
if (mForm) {
|
||||||
return mForm;
|
return &mForm->OwnedRadioGroupContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsInNativeAnonymousSubtree()) {
|
if (IsInNativeAnonymousSubtree()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (Document* doc = GetUncomposedDoc()) {
|
||||||
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
|
return &doc->OwnedRadioGroupContainer();
|
||||||
if (!docOrShadow) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
return &static_cast<FragmentOrElement*>(SubtreeRoot())
|
||||||
|
->OwnedRadioGroupContainer();
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIRadioGroupContainer> container =
|
void HTMLInputElement::DisconnectRadioGroupContainer() {
|
||||||
do_QueryInterface(&(docOrShadow->AsNode()));
|
mRadioGroupContainer = nullptr;
|
||||||
return container;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HTMLInputElement* HTMLInputElement::GetSelectedRadioButton() const {
|
HTMLInputElement* HTMLInputElement::GetSelectedRadioButton() const {
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
auto* container = GetCurrentRadioGroupContainer();
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -4140,7 +4140,7 @@ nsresult HTMLInputElement::MaybeHandleRadioButtonNavigation(
|
||||||
}
|
}
|
||||||
// Arrow key pressed, focus+select prev/next radio button
|
// Arrow key pressed, focus+select prev/next radio button
|
||||||
RefPtr<HTMLInputElement> selectedRadioButton;
|
RefPtr<HTMLInputElement> selectedRadioButton;
|
||||||
if (nsIRadioGroupContainer* container = GetRadioGroupContainer()) {
|
if (auto* container = GetCurrentRadioGroupContainer()) {
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
container->GetNextRadioButton(name, move == RadioButtonMove::Back, this,
|
container->GetNextRadioButton(name, move == RadioButtonMove::Back, this,
|
||||||
|
|
@ -4270,6 +4270,12 @@ void HTMLInputElement::MaybeLoadImage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult HTMLInputElement::BindToTree(BindContext& aContext, nsINode& aParent) {
|
nsresult HTMLInputElement::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||||
|
// If we are currently bound to a disconnected subtree root, remove
|
||||||
|
// ourselves from it first.
|
||||||
|
if (!mForm && mType == FormControlType::InputRadio) {
|
||||||
|
RemoveFromRadioGroup();
|
||||||
|
}
|
||||||
|
|
||||||
nsresult rv =
|
nsresult rv =
|
||||||
nsGenericHTMLFormControlElementWithState::BindToTree(aContext, aParent);
|
nsGenericHTMLFormControlElementWithState::BindToTree(aContext, aParent);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
@ -4292,9 +4298,8 @@ nsresult HTMLInputElement::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||||
|
|
||||||
// Add radio to document if we don't have a form already (if we do it's
|
// Add radio to document if we don't have a form already (if we do it's
|
||||||
// already been added into that group)
|
// already been added into that group)
|
||||||
if (!mForm && mType == FormControlType::InputRadio &&
|
if (!mForm && mType == FormControlType::InputRadio) {
|
||||||
GetUncomposedDocOrConnectedShadowRoot()) {
|
AddToRadioGroup();
|
||||||
AddedToRadioGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set direction based on value if dir=auto
|
// Set direction based on value if dir=auto
|
||||||
|
|
@ -4346,7 +4351,7 @@ void HTMLInputElement::UnbindFromTree(bool aNullParent) {
|
||||||
// of the case where we're removing from the document and we don't
|
// of the case where we're removing from the document and we don't
|
||||||
// have a form
|
// have a form
|
||||||
if (!mForm && mType == FormControlType::InputRadio) {
|
if (!mForm && mType == FormControlType::InputRadio) {
|
||||||
WillRemoveFromRadioGroup();
|
RemoveFromRadioGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CreatesDateTimeWidget() && IsInComposedDoc()) {
|
if (CreatesDateTimeWidget() && IsInComposedDoc()) {
|
||||||
|
|
@ -4356,6 +4361,12 @@ void HTMLInputElement::UnbindFromTree(bool aNullParent) {
|
||||||
nsImageLoadingContent::UnbindFromTree(aNullParent);
|
nsImageLoadingContent::UnbindFromTree(aNullParent);
|
||||||
nsGenericHTMLFormControlElementWithState::UnbindFromTree(aNullParent);
|
nsGenericHTMLFormControlElementWithState::UnbindFromTree(aNullParent);
|
||||||
|
|
||||||
|
// If we are contained within a disconnected subtree, attempt to add
|
||||||
|
// ourselves to the subtree root's radio group.
|
||||||
|
if (!mForm && mType == FormControlType::InputRadio) {
|
||||||
|
AddToRadioGroup();
|
||||||
|
}
|
||||||
|
|
||||||
// GetCurrentDoc is returning nullptr so we can update the value
|
// GetCurrentDoc is returning nullptr so we can update the value
|
||||||
// missing validity state to reflect we are no longer into a doc.
|
// missing validity state to reflect we are no longer into a doc.
|
||||||
UpdateValueMissingValidityState();
|
UpdateValueMissingValidityState();
|
||||||
|
|
@ -6220,16 +6231,27 @@ bool HTMLInputElement::RestoreState(PresState* aState) {
|
||||||
* Radio group stuff
|
* Radio group stuff
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void HTMLInputElement::AddedToRadioGroup() {
|
void HTMLInputElement::AddToRadioGroup() {
|
||||||
// If the element is neither in a form nor a document, there is no group so we
|
MOZ_ASSERT(!mRadioGroupContainer,
|
||||||
// should just stop here.
|
"Radio button must be removed from previous radio group container "
|
||||||
if (!mForm && (!GetUncomposedDocOrConnectedShadowRoot() ||
|
"before being added to another!");
|
||||||
IsInNativeAnonymousSubtree())) {
|
|
||||||
|
// If the element has no radio group container we can stop here.
|
||||||
|
auto* container = FindTreeRadioGroupContainer();
|
||||||
|
if (!container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure not to notify if we're still being created
|
nsAutoString name;
|
||||||
bool notify = mDoneCreating;
|
GetAttr(nsGkAtoms::name, name);
|
||||||
|
// If we are part of a radio group, the element must have a name.
|
||||||
|
MOZ_ASSERT(!name.IsEmpty());
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add the radio to the radio group container.
|
||||||
|
//
|
||||||
|
container->AddToRadioGroup(name, this, mForm);
|
||||||
|
mRadioGroupContainer = container;
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the input element is checked, and we add it to the group, it will
|
// If the input element is checked, and we add it to the group, it will
|
||||||
|
|
@ -6242,8 +6264,9 @@ void HTMLInputElement::AddedToRadioGroup() {
|
||||||
// radio button, but as adding a checked radio button into the group
|
// radio button, but as adding a checked radio button into the group
|
||||||
// should not be that common an occurrence, I think we can live with
|
// should not be that common an occurrence, I think we can live with
|
||||||
// that.
|
// that.
|
||||||
|
// Make sure not to notify if we're still being created.
|
||||||
//
|
//
|
||||||
RadioSetChecked(notify);
|
RadioSetChecked(mDoneCreating);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -6258,24 +6281,14 @@ void HTMLInputElement::AddedToRadioGroup() {
|
||||||
|
|
||||||
SetCheckedChangedInternal(checkedChanged);
|
SetCheckedChangedInternal(checkedChanged);
|
||||||
|
|
||||||
//
|
// We initialize the validity of the element to the validity of the group
|
||||||
// Add the radio to the radio group container.
|
// because we assume UpdateValueMissingState() will be called after.
|
||||||
//
|
SetValidityState(VALIDITY_STATE_VALUE_MISSING,
|
||||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
container->GetValueMissingState(name));
|
||||||
if (container) {
|
|
||||||
nsAutoString name;
|
|
||||||
GetAttr(nsGkAtoms::name, name);
|
|
||||||
container->AddToRadioGroup(name, this);
|
|
||||||
|
|
||||||
// We initialize the validity of the element to the validity of the group
|
|
||||||
// because we assume UpdateValueMissingState() will be called after.
|
|
||||||
SetValidityState(VALIDITY_STATE_VALUE_MISSING,
|
|
||||||
container->GetValueMissingState(name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLInputElement::WillRemoveFromRadioGroup() {
|
void HTMLInputElement::RemoveFromRadioGroup() {
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
auto* container = GetCurrentRadioGroupContainer();
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -6296,6 +6309,7 @@ void HTMLInputElement::WillRemoveFromRadioGroup() {
|
||||||
// the group validity is updated (with this element being ignored).
|
// the group validity is updated (with this element being ignored).
|
||||||
UpdateValueMissingValidityStateForRadio(true);
|
UpdateValueMissingValidityStateForRadio(true);
|
||||||
container->RemoveFromRadioGroup(name, this);
|
container->RemoveFromRadioGroup(name, this);
|
||||||
|
mRadioGroupContainer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
||||||
|
|
@ -6352,7 +6366,7 @@ bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
||||||
|
|
||||||
// Current radio button is not selected.
|
// Current radio button is not selected.
|
||||||
// But make it tabbable if nothing in group is selected.
|
// But make it tabbable if nothing in group is selected.
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
auto* container = GetCurrentRadioGroupContainer();
|
||||||
if (!container) {
|
if (!container) {
|
||||||
*aIsFocusable = defaultFocusable;
|
*aIsFocusable = defaultFocusable;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -6369,8 +6383,7 @@ bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult HTMLInputElement::VisitGroup(nsIRadioVisitor* aVisitor) {
|
nsresult HTMLInputElement::VisitGroup(nsIRadioVisitor* aVisitor) {
|
||||||
nsIRadioGroupContainer* container = GetRadioGroupContainer();
|
if (auto* container = GetCurrentRadioGroupContainer()) {
|
||||||
if (container) {
|
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
return container->WalkRadioGroup(name, aVisitor);
|
return container->WalkRadioGroup(name, aVisitor);
|
||||||
|
|
@ -6661,21 +6674,15 @@ void HTMLInputElement::UpdateValueMissingValidityStateForRadio(
|
||||||
bool selected = selection || (!aIgnoreSelf && mChecked);
|
bool selected = selection || (!aIgnoreSelf && mChecked);
|
||||||
bool required = !aIgnoreSelf && IsRequired();
|
bool required = !aIgnoreSelf && IsRequired();
|
||||||
|
|
||||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
auto* container = GetCurrentRadioGroupContainer();
|
||||||
|
if (!container) {
|
||||||
|
SetValidityState(VALIDITY_STATE_VALUE_MISSING, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsAutoString name;
|
nsAutoString name;
|
||||||
GetAttr(nsGkAtoms::name, name);
|
GetAttr(nsGkAtoms::name, name);
|
||||||
|
|
||||||
if (!container) {
|
|
||||||
// As per the spec, a radio button not within a radio button group cannot
|
|
||||||
// suffer from being missing; however, we currently are failing to get a
|
|
||||||
// radio group in the case of a single, named radio button that has no
|
|
||||||
// form owner, forcing us to check for validity in that case here.
|
|
||||||
SetValidityState(VALIDITY_STATE_VALUE_MISSING,
|
|
||||||
required && !selected && !name.IsEmpty());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current radio is required and not ignored, we can assume the entire
|
// If the current radio is required and not ignored, we can assume the entire
|
||||||
// group is required.
|
// group is required.
|
||||||
if (!required) {
|
if (!required) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "mozilla/dom/ConstraintValidation.h"
|
#include "mozilla/dom/ConstraintValidation.h"
|
||||||
#include "mozilla/dom/FileInputType.h"
|
#include "mozilla/dom/FileInputType.h"
|
||||||
#include "mozilla/dom/HiddenInputType.h"
|
#include "mozilla/dom/HiddenInputType.h"
|
||||||
|
#include "mozilla/dom/RadioGroupContainer.h"
|
||||||
#include "nsGenericHTMLElement.h"
|
#include "nsGenericHTMLElement.h"
|
||||||
#include "nsImageLoadingContent.h"
|
#include "nsImageLoadingContent.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
|
@ -35,7 +36,6 @@
|
||||||
#include "nsIContentPrefService2.h"
|
#include "nsIContentPrefService2.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
|
||||||
class nsIRadioGroupContainer;
|
|
||||||
class nsIRadioVisitor;
|
class nsIRadioVisitor;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
@ -277,8 +277,9 @@ class HTMLInputElement final : public TextControlElement,
|
||||||
|
|
||||||
void SetCheckedChangedInternal(bool aCheckedChanged);
|
void SetCheckedChangedInternal(bool aCheckedChanged);
|
||||||
bool GetCheckedChanged() const { return mCheckedChanged; }
|
bool GetCheckedChanged() const { return mCheckedChanged; }
|
||||||
void AddedToRadioGroup();
|
void AddToRadioGroup();
|
||||||
void WillRemoveFromRadioGroup();
|
void RemoveFromRadioGroup();
|
||||||
|
void DisconnectRadioGroupContainer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function returning the currently selected button in the radio group.
|
* Helper function returning the currently selected button in the radio group.
|
||||||
|
|
@ -1144,12 +1145,15 @@ class HTMLInputElement final : public TextControlElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the radio group container if the element has one, null otherwise.
|
* Returns the radio group container within the DOM tree that the element
|
||||||
* The radio group container will be the form owner if there is one.
|
* is currently a member of, if one exists.
|
||||||
* The current document otherwise.
|
|
||||||
* @return the radio group container if the element has one, null otherwise.
|
|
||||||
*/
|
*/
|
||||||
nsIRadioGroupContainer* GetRadioGroupContainer() const;
|
RadioGroupContainer* GetCurrentRadioGroupContainer() const;
|
||||||
|
/**
|
||||||
|
* Returns the radio group container within the DOM tree that the element
|
||||||
|
* should be added into, if one exists.
|
||||||
|
*/
|
||||||
|
RadioGroupContainer* FindTreeRadioGroupContainer() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a color string of the form #XXXXXX where X should be hexa characters
|
* Parse a color string of the form #XXXXXX where X should be hexa characters
|
||||||
|
|
@ -1633,6 +1637,12 @@ class HTMLInputElement final : public TextControlElement,
|
||||||
*/
|
*/
|
||||||
static bool IsDateTimeTypeSupported(FormControlType);
|
static bool IsDateTimeTypeSupported(FormControlType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The radio group container containing the group the element is a part of.
|
||||||
|
* This allows the element to only access a container it has been added to.
|
||||||
|
*/
|
||||||
|
RadioGroupContainer* mRadioGroupContainer;
|
||||||
|
|
||||||
struct nsFilePickerFilter {
|
struct nsFilePickerFilter {
|
||||||
nsFilePickerFilter() : mFilterMask(0) {}
|
nsFilePickerFilter() : mFilterMask(0) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ EXPORTS += [
|
||||||
"nsIConstraintValidation.h",
|
"nsIConstraintValidation.h",
|
||||||
"nsIFormControl.h",
|
"nsIFormControl.h",
|
||||||
"nsIHTMLCollection.h",
|
"nsIHTMLCollection.h",
|
||||||
"nsIRadioGroupContainer.h",
|
|
||||||
"nsIRadioVisitor.h",
|
"nsIRadioVisitor.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
/* -*- 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/. */
|
|
||||||
#ifndef nsIRadioGroupContainer_h___
|
|
||||||
#define nsIRadioGroupContainer_h___
|
|
||||||
|
|
||||||
#include "nsISupports.h"
|
|
||||||
|
|
||||||
class nsIRadioVisitor;
|
|
||||||
class nsIFormControl;
|
|
||||||
|
|
||||||
namespace mozilla::dom {
|
|
||||||
class HTMLInputElement;
|
|
||||||
} // namespace mozilla::dom
|
|
||||||
|
|
||||||
#define NS_IRADIOGROUPCONTAINER_IID \
|
|
||||||
{ \
|
|
||||||
0x800320a0, 0x733f, 0x11e4, { \
|
|
||||||
0x82, 0xf8, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container that has multiple radio groups in it, defined by name.
|
|
||||||
*/
|
|
||||||
class nsIRadioGroupContainer : public nsISupports {
|
|
||||||
public:
|
|
||||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRADIOGROUPCONTAINER_IID)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walk through the radio group, visiting each note with avisitor->Visit()
|
|
||||||
* @param aName the group name
|
|
||||||
* @param aVisitor the visitor to visit with
|
|
||||||
*/
|
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
|
||||||
nsIRadioVisitor* aVisitor) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current radio button in a group
|
|
||||||
* @param aName the group name
|
|
||||||
* @param aRadio the currently selected radio button
|
|
||||||
*/
|
|
||||||
virtual void SetCurrentRadioButton(
|
|
||||||
const nsAString& aName, mozilla::dom::HTMLInputElement* aRadio) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current radio button in a group
|
|
||||||
* @param aName the group name
|
|
||||||
* @return the currently selected radio button
|
|
||||||
*/
|
|
||||||
virtual mozilla::dom::HTMLInputElement* GetCurrentRadioButton(
|
|
||||||
const nsAString& aName) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next/prev radio button in a group
|
|
||||||
* @param aName the group name
|
|
||||||
* @param aPrevious, true gets previous radio button, false gets next
|
|
||||||
* @param aFocusedRadio the currently focused radio button
|
|
||||||
* @param aRadio the currently selected radio button [OUT]
|
|
||||||
*/
|
|
||||||
NS_IMETHOD GetNextRadioButton(const nsAString& aName, const bool aPrevious,
|
|
||||||
mozilla::dom::HTMLInputElement* aFocusedRadio,
|
|
||||||
mozilla::dom::HTMLInputElement** aRadio) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add radio button to radio group
|
|
||||||
*
|
|
||||||
* Note that forms do not do anything for this method since they already
|
|
||||||
* store radio groups on their own.
|
|
||||||
*
|
|
||||||
* @param aName radio group's name
|
|
||||||
* @param aRadio radio button's pointer
|
|
||||||
*/
|
|
||||||
virtual void AddToRadioGroup(const nsAString& aName,
|
|
||||||
mozilla::dom::HTMLInputElement* aRadio) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove radio button from radio group
|
|
||||||
*
|
|
||||||
* Note that forms do not do anything for this method since they already
|
|
||||||
* store radio groups on their own.
|
|
||||||
*
|
|
||||||
* @param aName radio group's name
|
|
||||||
* @param aRadio radio button's pointer
|
|
||||||
*/
|
|
||||||
virtual void RemoveFromRadioGroup(const nsAString& aName,
|
|
||||||
mozilla::dom::HTMLInputElement* aRadio) = 0;
|
|
||||||
|
|
||||||
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const = 0;
|
|
||||||
virtual void RadioRequiredWillChange(const nsAString& aName,
|
|
||||||
bool aRequiredAdded) = 0;
|
|
||||||
virtual bool GetValueMissingState(const nsAString& aName) const = 0;
|
|
||||||
virtual void SetValueMissingState(const nsAString& aName, bool aValue) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer,
|
|
||||||
NS_IRADIOGROUPCONTAINER_IID)
|
|
||||||
|
|
||||||
#endif /* nsIRadioGroupContainer_h__ */
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "PerformanceEventTiming.h"
|
#include "PerformanceEventTiming.h"
|
||||||
#include "PerformanceMainThread.h"
|
#include "PerformanceMainThread.h"
|
||||||
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
#include "mozilla/dom/PerformanceEventTimingBinding.h"
|
#include "mozilla/dom/PerformanceEventTimingBinding.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/Performance.h"
|
#include "mozilla/dom/Performance.h"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "DOMSVGPathSegList.h"
|
#include "DOMSVGPathSegList.h"
|
||||||
#include "SVGPathSegListSMILType.h"
|
#include "SVGPathSegListSMILType.h"
|
||||||
#include "mozilla/SMILValue.h"
|
#include "mozilla/SMILValue.h"
|
||||||
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
#include "mozilla/dom/SVGElement.h"
|
#include "mozilla/dom/SVGElement.h"
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
[radio.html]
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
||||||
[Radio buttons in an orphan tree should make a group]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for validity of disconnected radio buttons</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<input type="radio" name="group" id="radio1" required />
|
||||||
|
<input type="radio" name="group" id="radio2" checked />
|
||||||
|
<form>
|
||||||
|
<input type="radio" name="group" id="radio3" required />
|
||||||
|
<input type="radio" name="group" id="radio4" checked />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const radio1 = document.getElementById("radio1");
|
||||||
|
const radio2 = document.getElementById("radio2");
|
||||||
|
assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
radio2.remove();
|
||||||
|
assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
|
||||||
|
radio1.checked = true;
|
||||||
|
radio2.required = true;
|
||||||
|
assert_false(radio2.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
const radio3 = document.getElementById("radio3");
|
||||||
|
const radio4 = document.getElementById("radio4");
|
||||||
|
assert_false(radio3.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
radio4.remove();
|
||||||
|
assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
|
||||||
|
radio3.checked = true;
|
||||||
|
assert_true(radio4.checked, "Element should remain checked");
|
||||||
|
assert_false(radio3.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
document.querySelector("form").appendChild(radio2);
|
||||||
|
assert_false(radio3.checked, "Element should be unchecked");
|
||||||
|
assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
}, "Removed elements are moved into separate radio groups.");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
const radio = document.createElement("input");
|
||||||
|
radio.type = "radio";
|
||||||
|
radio.name = "group";
|
||||||
|
radio.id = "radio5";
|
||||||
|
radio.required = true;
|
||||||
|
container.appendChild(radio);
|
||||||
|
assert_true(radio.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
|
||||||
|
const outerContainer = document.createElement("div");
|
||||||
|
const outerRadio = document.createElement("input");
|
||||||
|
outerRadio.type = "radio";
|
||||||
|
outerRadio.name = "group";
|
||||||
|
outerRadio.id = "radio6";
|
||||||
|
outerRadio.checked = true;
|
||||||
|
outerContainer.appendChild(outerRadio);
|
||||||
|
outerContainer.appendChild(container);
|
||||||
|
assert_false(radio.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
container.remove();
|
||||||
|
assert_true(radio.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
}, "Disconnected radio buttons should be contained by their tree root.");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const radioParent = document.createElement("input");
|
||||||
|
radioParent.type = "radio";
|
||||||
|
radioParent.name = "group";
|
||||||
|
radioParent.id = "radio-parent";
|
||||||
|
radioParent.required = true;
|
||||||
|
assert_true(radioParent.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
|
||||||
|
const radioChild = document.createElement("input");
|
||||||
|
radioChild.type = "radio";
|
||||||
|
radioChild.name = "group";
|
||||||
|
radioChild.id = "radio-child";
|
||||||
|
radioChild.checked = true;
|
||||||
|
assert_false(radioChild.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
radioParent.appendChild(radioChild);
|
||||||
|
assert_false(radioChild.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
assert_false(radioParent.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
|
||||||
|
radioParent.checked = true;
|
||||||
|
assert_false(radioChild.checked, "Element should no longer be checked");
|
||||||
|
}, "Disconnected radio buttons can serve as radio group containers.");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const shadowHost = document.createElement("div");
|
||||||
|
const root = shadowHost.attachShadow({mode: "open"});
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.appendChild(shadowHost);
|
||||||
|
|
||||||
|
const radio1 = document.createElement("input");
|
||||||
|
radio1.type = "radio";
|
||||||
|
radio1.name = "group";
|
||||||
|
radio1.required = true;
|
||||||
|
const radio2 = document.createElement("input");
|
||||||
|
radio2.type = "radio";
|
||||||
|
radio2.name = "group";
|
||||||
|
radio2.checked = true;
|
||||||
|
const radio3 = document.createElement("input");
|
||||||
|
radio3.type = "radio";
|
||||||
|
radio3.name = "group";
|
||||||
|
radio3.required = true;
|
||||||
|
|
||||||
|
root.appendChild(radio1);
|
||||||
|
container.appendChild(radio3);
|
||||||
|
assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
|
||||||
|
root.appendChild(radio2);
|
||||||
|
assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
assert_true(radio3.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
}, "Shadow roots in disconnected trees can serve as radio group containers.");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const svgRoot = document.createElementNS("http://www.w3.org/2000/svg", "g")
|
||||||
|
const htmlContainer = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
||||||
|
svgRoot.appendChild(htmlContainer);
|
||||||
|
|
||||||
|
const radio1 = document.createElement("input");
|
||||||
|
radio1.type = "radio";
|
||||||
|
radio1.name = "group";
|
||||||
|
radio1.required = true;
|
||||||
|
const radio2 = document.createElement("input");
|
||||||
|
radio2.type = "radio";
|
||||||
|
radio2.name = "group";
|
||||||
|
radio2.checked = true;
|
||||||
|
|
||||||
|
htmlContainer.appendChild(radio1);
|
||||||
|
assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
htmlContainer.appendChild(radio2);
|
||||||
|
assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
radio1.checked = true;
|
||||||
|
assert_false(radio2.checked, "Element should no longer be checked");
|
||||||
|
}, "Non-HTML elements in disconnected trees can serve as radio group containers.");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
const radio1 = document.createElement("input");
|
||||||
|
radio1.type = "radio";
|
||||||
|
radio1.name = "group";
|
||||||
|
radio1.required = true;
|
||||||
|
const radio2 = document.createElement("input");
|
||||||
|
radio2.type = "radio";
|
||||||
|
radio2.name = "group";
|
||||||
|
radio2.checked = true;
|
||||||
|
|
||||||
|
fragment.appendChild(radio1);
|
||||||
|
assert_true(radio1.validity.valueMissing, "Element should suffer from value missing");
|
||||||
|
fragment.appendChild(radio2);
|
||||||
|
assert_false(radio1.validity.valueMissing, "Element should not suffer from value missing");
|
||||||
|
radio1.checked = true;
|
||||||
|
assert_false(radio2.checked, "Element should no longer be checked");
|
||||||
|
}, "Disconnected document fragments can serve as radio group containers.");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue