Bug 1490406, radio groups should work in shadow DOM, r=ehsan

--HG--
extra : rebase_source : 952ffd47acea3d99d2209e7f05039767b73faa0d
This commit is contained in:
Olli Pettay 2018-09-21 03:39:47 +03:00
Родитель cee95ec8de
Коммит 697d41350d
9 изменённых файлов: 441 добавлений и 219 удалений

Просмотреть файл

@ -6,10 +6,13 @@
#include "DocumentOrShadowRoot.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/StyleSheetList.h"
#include "nsDocument.h"
#include "nsFocusManager.h"
#include "nsIRadioVisitor.h"
#include "nsIFormControl.h"
#include "nsLayoutUtils.h"
#include "nsSVGUtils.h"
#include "nsWindowSizes.h"
@ -398,5 +401,205 @@ DocumentOrShadowRoot::ReportEmptyGetElementByIdArg()
nsContentUtils::ReportEmptyGetElementByIdArg(AsNode().OwnerDoc());
}
/**
* 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;
nsCOMArray<nsIFormControl> mRadioButtons;
uint32_t mRequiredRadioCount;
bool mGroupSuffersFromValueMissing;
};
nsresult
DocumentOrShadowRoot::WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
return NS_OK;
}
}
return NS_OK;
}
void
DocumentOrShadowRoot::SetCurrentRadioButton(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mSelectedRadioButton = aRadio;
}
HTMLInputElement*
DocumentOrShadowRoot::GetCurrentRadioButton(const nsAString& aName)
{
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
}
nsresult
DocumentOrShadowRoot::GetNextRadioButton(const nsAString& aName,
const bool aPrevious,
HTMLInputElement* aFocusedRadio,
HTMLInputElement** aRadioOut)
{
// XXX Can we combine the HTML radio button method impls of
// nsDocument and nsHTMLFormControl?
// XXX Why is HTML radio button stuff in nsDocument, as
// opposed to nsHTMLDocument?
*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 = radioGroup->mRadioButtons.Count();
RefPtr<HTMLInputElement> radio;
do {
if (aPrevious) {
if (--index < 0) {
index = numRadios -1;
}
} else if (++index >= numRadios) {
index = 0;
}
NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
"mRadioButtons holding a non-radio button");
radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
} while (radio->Disabled() && radio != currentRadio);
radio.forget(aRadioOut);
return NS_OK;
}
void
DocumentOrShadowRoot::AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mRadioButtons.AppendObject(aRadio);
if (aRadio->IsRequired()) {
radioGroup->mRequiredRadioCount++;
}
}
void
DocumentOrShadowRoot::RemoveFromRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mRadioButtons.RemoveObject(aRadio);
if (aRadio->IsRequired()) {
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
"mRequiredRadioCount about to wrap below 0!");
radioGroup->mRequiredRadioCount--;
}
}
uint32_t
DocumentOrShadowRoot::GetRequiredRadioCount(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
}
void
DocumentOrShadowRoot::RadioRequiredWillChange(const nsAString& aName,
bool aRequiredAdded)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
if (aRequiredAdded) {
radioGroup->mRequiredRadioCount++;
} else {
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
"mRequiredRadioCount about to wrap below 0!");
radioGroup->mRequiredRadioCount--;
}
}
bool
DocumentOrShadowRoot::GetValueMissingState(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
}
void
DocumentOrShadowRoot::SetValueMissingState(const nsAString& aName, bool aValue)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mGroupSuffersFromValueMissing = aValue;
}
nsRadioGroupStruct*
DocumentOrShadowRoot::GetRadioGroup(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = nullptr;
mRadioGroups.Get(aName, &radioGroup);
return radioGroup;
}
nsRadioGroupStruct*
DocumentOrShadowRoot::GetOrCreateRadioGroup(const nsAString& aName)
{
return mRadioGroups.LookupForAdd(aName).OrInsert(
[] () { return new nsRadioGroupStruct(); });
}
void
DocumentOrShadowRoot::Traverse(DocumentOrShadowRoot* tmp,
nsCycleCollectionTraversalCallback &cb)
{
for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
nsRadioGroupStruct* radioGroup = iter.UserData();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mRadioGroups entry->mSelectedRadioButton");
cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
uint32_t i, count = radioGroup->mRadioButtons.Count();
for (i = 0; i < count; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mRadioGroups entry->mRadioButtons[i]");
cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
}
}
}
void
DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp)
{
tmp->mRadioGroups.Clear();
}
}
}

Просмотреть файл

@ -8,13 +8,16 @@
#define mozilla_dom_DocumentOrShadowRoot_h__
#include "mozilla/dom/NameSpaceConstants.h"
#include "nsClassHashtable.h"
#include "nsContentListDeclarations.h"
#include "nsTArray.h"
#include "nsIdentifierMapEntry.h"
class nsContentList;
class nsCycleCollectionTraversalCallback;
class nsIDocument;
class nsINode;
class nsIRadioVisitor;
class nsWindowSizes;
namespace mozilla {
@ -23,6 +26,9 @@ class StyleSheet;
namespace dom {
class Element;
class DocumentOrShadowRoot;
class HTMLInputElement;
struct nsRadioGroupStruct;
class StyleSheetList;
class ShadowRoot;
@ -45,6 +51,11 @@ public:
explicit DocumentOrShadowRoot(nsIDocument&);
explicit DocumentOrShadowRoot(mozilla::dom::ShadowRoot&);
// Unusual argument naming is because of cycle collection macros.
static void Traverse(DocumentOrShadowRoot* tmp,
nsCycleCollectionTraversalCallback &cb);
static void Unlink(DocumentOrShadowRoot* tmp);
nsINode& AsNode()
{
return mAsNode;
@ -186,6 +197,31 @@ public:
void ReportEmptyGetElementByIdArg();
// nsIRadioGroupContainer
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent);
void SetCurrentRadioButton(const nsAString& aName,
HTMLInputElement* aRadio);
HTMLInputElement* GetCurrentRadioButton(const nsAString& aName);
nsresult GetNextRadioButton(const nsAString& aName,
const bool aPrevious,
HTMLInputElement* aFocusedRadio,
HTMLInputElement** aRadioOut);
void AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio);
void RemoveFromRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio);
uint32_t GetRequiredRadioCount(const nsAString& aName) const;
void RadioRequiredWillChange(const nsAString& aName,
bool aRequiredAdded);
bool GetValueMissingState(const nsAString& aName) const;
void SetValueMissingState(const nsAString& aName, bool aValue);
// for radio group
nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
protected:
// Returns the reference to the sheet, if found in mStyleSheets.
already_AddRefed<StyleSheet> RemoveSheet(StyleSheet& aSheet);
@ -218,6 +254,8 @@ protected:
*/
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
nsINode& mAsNode;
const Kind mKind;
};

Просмотреть файл

@ -41,6 +41,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
iter.Next()) {
iter.Get()->Traverse(&cb);
}
DocumentOrShadowRoot::Traverse(tmp, cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
@ -49,11 +50,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
tmp->mIdentifierMap.Clear();
DocumentOrShadowRoot::Unlink(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)

Просмотреть файл

@ -16,6 +16,7 @@
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIdentifierMapEntry.h"
#include "nsIRadioGroupContainer.h"
#include "nsStubMutationObserver.h"
#include "nsTHashtable.h"
@ -35,10 +36,12 @@ class Rule;
namespace dom {
class Element;
class HTMLInputElement;
class ShadowRoot final : public DocumentFragment,
public DocumentOrShadowRoot,
public nsStubMutationObserver
public nsStubMutationObserver,
public nsIRadioGroupContainer
{
public:
NS_IMPL_FROMNODE_HELPER(ShadowRoot, IsShadowRoot());
@ -207,6 +210,61 @@ public:
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
// nsIRadioGroupContainer
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent) override
{
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent);
}
virtual void
SetCurrentRadioButton(const nsAString& aName,
HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
}
virtual 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);
}
virtual void AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
}
virtual void RemoveFromRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
}
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override
{
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
}
virtual void RadioRequiredWillChange(const nsAString& aName,
bool aRequiredAdded) override
{
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
}
virtual bool GetValueMissingState(const nsAString& aName) const override
{
return DocumentOrShadowRoot::GetValueMissingState(aName);
}
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override
{
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
}
protected:
// FIXME(emilio): This will need to become more fine-grained.
void ApplicableRulesChanged();

Просмотреть файл

@ -119,10 +119,6 @@
#include "nsFocusManager.h"
#include "nsICookieService.h"
// for radio group stuff
#include "nsIRadioVisitor.h"
#include "nsIFormControl.h"
#include "nsBidiUtils.h"
#include "nsContentCreatorFunctions.h"
@ -724,26 +720,6 @@ public:
nsIDocument *mSubDocument;
};
/**
* 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;
nsCOMArray<nsIFormControl> mRadioButtons;
uint32_t mRequiredRadioCount;
bool mGroupSuffersFromValueMissing;
};
// nsOnloadBlocker implementation
NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
@ -1924,19 +1900,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
nsRadioGroupStruct* radioGroup = iter.UserData();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mRadioGroups entry->mSelectedRadioButton");
cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
uint32_t i, count = radioGroup->mRadioButtons.Count();
for (i = 0; i < count; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
cb, "mRadioGroups entry->mRadioButtons[i]");
cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
}
}
DocumentOrShadowRoot::Traverse(tmp, cb);
// The boxobject for an element will only exist as long as it's in the
// document, so we'll traverse the table here instead of from the element.
@ -2092,7 +2056,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
"How did we get here without our presshell going away "
"first?");
tmp->mRadioGroups.Clear();
DocumentOrShadowRoot::Unlink(tmp);
// nsDocument has a pretty complex destructor, so we're going to
// assume that *most* cycles you actually want to break somewhere
@ -7713,163 +7677,6 @@ nsIDocument::IsScriptEnabled()
return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
}
nsRadioGroupStruct*
nsDocument::GetRadioGroup(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = nullptr;
mRadioGroups.Get(aName, &radioGroup);
return radioGroup;
}
nsRadioGroupStruct*
nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
{
return mRadioGroups.LookupForAdd(aName).OrInsert(
[] () { return new nsRadioGroupStruct(); });
}
void
nsDocument::SetCurrentRadioButton(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mSelectedRadioButton = aRadio;
}
HTMLInputElement*
nsDocument::GetCurrentRadioButton(const nsAString& aName)
{
return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
}
NS_IMETHODIMP
nsDocument::GetNextRadioButton(const nsAString& aName,
const bool aPrevious,
HTMLInputElement* aFocusedRadio,
HTMLInputElement** aRadioOut)
{
// XXX Can we combine the HTML radio button method impls of
// nsDocument and nsHTMLFormControl?
// XXX Why is HTML radio button stuff in nsDocument, as
// opposed to nsHTMLDocument?
*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 = radioGroup->mRadioButtons.Count();
RefPtr<HTMLInputElement> radio;
do {
if (aPrevious) {
if (--index < 0) {
index = numRadios -1;
}
}
else if (++index >= numRadios) {
index = 0;
}
NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
"mRadioButtons holding a non-radio button");
radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
} while (radio->Disabled() && radio != currentRadio);
radio.forget(aRadioOut);
return NS_OK;
}
void
nsDocument::AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mRadioButtons.AppendObject(aRadio);
if (aRadio->IsRequired()) {
radioGroup->mRequiredRadioCount++;
}
}
void
nsDocument::RemoveFromRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mRadioButtons.RemoveObject(aRadio);
if (aRadio->IsRequired()) {
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
"mRequiredRadioCount about to wrap below 0!");
radioGroup->mRequiredRadioCount--;
}
}
NS_IMETHODIMP
nsDocument::WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
return NS_OK;
}
}
return NS_OK;
}
uint32_t
nsDocument::GetRequiredRadioCount(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
return radioGroup ? radioGroup->mRequiredRadioCount : 0;
}
void
nsDocument::RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
if (aRequiredAdded) {
radioGroup->mRequiredRadioCount++;
} else {
NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
"mRequiredRadioCount about to wrap below 0!");
radioGroup->mRequiredRadioCount--;
}
}
bool
nsDocument::GetValueMissingState(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
}
void
nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
{
nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
radioGroup->mGroupSuffersFromValueMissing = aValue;
}
void
nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
{

Просмотреть файл

@ -66,9 +66,7 @@
class nsDOMStyleSheetSetList;
class nsDocument;
class nsIRadioVisitor;
class nsIFormControl;
struct nsRadioGroupStruct;
class nsOnloadBlocker;
class nsDOMNavigationTiming;
class nsWindowSizes;
@ -147,30 +145,57 @@ public:
// nsIRadioGroupContainer
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
nsIRadioVisitor* aVisitor,
bool aFlushContent) override;
bool aFlushContent) override
{
return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent);
}
virtual void
SetCurrentRadioButton(const nsAString& aName,
mozilla::dom::HTMLInputElement* aRadio) override;
mozilla::dom::HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
}
virtual mozilla::dom::HTMLInputElement*
GetCurrentRadioButton(const nsAString& aName) override;
GetCurrentRadioButton(const nsAString& aName) override
{
return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
}
NS_IMETHOD
GetNextRadioButton(const nsAString& aName,
const bool aPrevious,
mozilla::dom::HTMLInputElement* aFocusedRadio,
mozilla::dom::HTMLInputElement** aRadioOut) override;
mozilla::dom::HTMLInputElement** aRadioOut) override
{
return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
aFocusedRadio, aRadioOut);
}
virtual void AddToRadioGroup(const nsAString& aName,
mozilla::dom::HTMLInputElement* aRadio) override;
mozilla::dom::HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
}
virtual void RemoveFromRadioGroup(const nsAString& aName,
mozilla::dom::HTMLInputElement* aRadio) override;
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override;
mozilla::dom::HTMLInputElement* aRadio) override
{
DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
}
virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override
{
return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
}
virtual void RadioRequiredWillChange(const nsAString& aName,
bool aRequiredAdded) override;
virtual bool GetValueMissingState(const nsAString& aName) const override;
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override;
// for radio group
nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
bool aRequiredAdded) override
{
DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
}
virtual bool GetValueMissingState(const nsAString& aName) const override
{
return DocumentOrShadowRoot::GetValueMissingState(aName);
}
virtual void SetValueMissingState(const nsAString& aName, bool aValue) override
{
return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
}
// Check whether shadow DOM is enabled for aGlobal.
static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal);
@ -262,8 +287,6 @@ public:
// include https://github.com/rust-lang-nursery/rust-bindgen/pull/1271.
js::ExpandoAndGeneration mExpandoAndGeneration;
nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
friend class nsCallRequestFullscreen;
// The application cache that this document is associated with, if

Просмотреть файл

@ -3052,8 +3052,14 @@ HTMLInputElement::GetRadioGroupContainer() const
return nullptr;
}
//XXXsmaug It isn't clear how this should work in Shadow DOM.
return static_cast<nsDocument*>(GetUncomposedDoc());
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
if (!docOrShadow) {
return nullptr;
}
nsCOMPtr<nsIRadioGroupContainer> container =
do_QueryInterface(&(docOrShadow->AsNode()));
return container;
}
HTMLInputElement*
@ -4632,7 +4638,8 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// Add radio to document if we don't have a form already (if we do it's
// already been added into that group)
if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
if (!mForm && mType == NS_FORM_INPUT_RADIO &&
GetUncomposedDocOrConnectedShadowRoot()) {
AddedToRadioGroup();
}
@ -6567,7 +6574,7 @@ HTMLInputElement::AddedToRadioGroup()
{
// If the element is neither in a form nor a document, there is no group so we
// should just stop here.
if (!mForm && (!IsInUncomposedDoc() || IsInAnonymousSubtree())) {
if (!mForm && (!GetUncomposedDocOrConnectedShadowRoot() || IsInAnonymousSubtree())) {
return;
}

Просмотреть файл

@ -397866,6 +397866,12 @@
{}
]
],
"shadow-dom/input-type-radio.html": [
[
"/shadow-dom/input-type-radio.html",
{}
]
],
"shadow-dom/leaktests/get-elements.html": [
[
"/shadow-dom/leaktests/get-elements.html",
@ -646387,6 +646393,10 @@
"b571534eb0d6f3f57cfbec3e706648b19848b6d6",
"testharness"
],
"shadow-dom/input-type-radio.html": [
"bd5d8e43b0fd9d0c9f1e078ed97a1bbd18b7b0be",
"testharness"
],
"shadow-dom/layout-slot-no-longer-assigned.html": [
"dfcac99da023ec2bbd94835f71efaef952a62341",
"reftest"

Просмотреть файл

@ -0,0 +1,73 @@
<!doctype html>
<meta charset=utf-8>
<title></title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<input type="radio" name="group" id="lightRadio1">
<input type="radio" name="group" id="lightRadio2">
<div id="host"></div>
<script>
test(() => {
var lightRadio1 = document.getElementById("lightRadio1");
var lightRadio2 = document.getElementById("lightRadio2");
var host = document.getElementById("host");
var sr = host.attachShadow({mode: "closed"});
var shadowRadio1 = document.createElement("input");
shadowRadio1.name = "group";
shadowRadio1.id = "shadowRadio1";
shadowRadio1.type = "radio";
sr.appendChild(shadowRadio1);
var shadowRadio2 = document.createElement("input");
shadowRadio2.name = "group";
shadowRadio2.id = "shadowRadio2";
shadowRadio2.type = "radio";
sr.appendChild(shadowRadio2);
assert_false(lightRadio1.checked);
assert_false(lightRadio2.checked);
assert_false(shadowRadio1.checked);
assert_false(shadowRadio2.checked);
lightRadio1.click();
assert_true(lightRadio1.checked);
assert_false(lightRadio2.checked);
assert_false(shadowRadio1.checked);
assert_false(shadowRadio2.checked);
lightRadio2.click();
assert_false(lightRadio1.checked);
assert_true(lightRadio2.checked);
assert_false(shadowRadio1.checked);
assert_false(shadowRadio2.checked);
shadowRadio1.click();
assert_false(lightRadio1.checked);
assert_true(lightRadio2.checked);
assert_true(shadowRadio1.checked);
assert_false(shadowRadio2.checked);
shadowRadio2.click();
assert_false(lightRadio1.checked);
assert_true(lightRadio2.checked);
assert_false(shadowRadio1.checked);
assert_true(shadowRadio2.checked);
// Ensure radio groups work even when modifying shadow DOM.
shadowRadio2.remove();
sr.appendChild(shadowRadio2);
shadowRadio2.click();
assert_false(lightRadio1.checked);
assert_true(lightRadio2.checked);
assert_false(shadowRadio1.checked);
assert_true(shadowRadio2.checked);
shadowRadio1.click();
assert_false(lightRadio1.checked);
assert_true(lightRadio2.checked);
assert_true(shadowRadio1.checked);
assert_false(shadowRadio2.checked);
}, "input type=radio elements should form a group inside shadow DOM.");
</script>