Backed out 3 changesets (bug 1769586) for causing bug 1920082. a=backout

Backed out changeset d684010261d6 (bug 1769586)
Backed out changeset b3264bc533e2 (bug 1769586)
Backed out changeset 049fd286ce0f (bug 1769586)
This commit is contained in:
Ryan VanderMeulen 2024-10-17 16:39:13 -04:00
parent 2613e8be8e
commit 004d210bbf
27 changed files with 209 additions and 1101 deletions

View File

@ -1451,33 +1451,33 @@ static const AttrCharacteristics gWAIUnivAttrMap[] = {
{nsGkAtoms::aria_checked, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, /* exposes checkable obj attr */
{nsGkAtoms::aria_colcount, ATTR_VALINT },
{nsGkAtoms::aria_colindex, ATTR_VALINT },
{nsGkAtoms::aria_controls, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_controls, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_current, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_describedby, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_describedby, ATTR_BYPASSOBJ | ATTR_GLOBAL },
// XXX Ideally, aria-description shouldn't expose a description object
// attribute (i.e. it should have ATTR_BYPASSOBJ). However, until the
// description-from attribute is implemented (bug 1726087), clients such as
// NVDA depend on the description object attribute to work out whether the
// accDescription originated from aria-description.
{nsGkAtoms::aria_description, ATTR_GLOBAL },
{nsGkAtoms::aria_details, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_details, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_disabled, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_dropeffect, ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_errormessage, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_errormessage, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_expanded, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_flowto, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_flowto, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_grabbed, ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_haspopup, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_hidden, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */
{nsGkAtoms::aria_invalid, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_label, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_labelledby, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_labelledby, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_level, ATTR_BYPASSOBJ }, /* handled via groupPosition */
{nsGkAtoms::aria_live, ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_modal, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
{nsGkAtoms::aria_multiline, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_multiselectable, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_owns, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
{nsGkAtoms::aria_owns, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_orientation, ATTR_VALTOKEN },
{nsGkAtoms::aria_posinset, ATTR_BYPASSOBJ }, /* handled via groupPosition */
{nsGkAtoms::aria_pressed, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
@ -1737,22 +1737,3 @@ bool AttrIterator::ExposeAttr(AccAttributes* aTargetAttrs) const {
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// AttrWithCharacteristicsIterator class
bool AttrWithCharacteristicsIterator::Next() {
for (mIdx++; mIdx < static_cast<int32_t>(ArrayLength(gWAIUnivAttrMap));
mIdx++) {
if (gWAIUnivAttrMap[mIdx].characteristics & mCharacteristics) {
return true;
}
}
return false;
}
nsStaticAtom* AttrWithCharacteristicsIterator::AttrName() const {
return mIdx >= 0
? const_cast<nsStaticAtom*>(gWAIUnivAttrMap[mIdx].attributeName)
: nullptr;
}

View File

@ -124,11 +124,6 @@ const uint8_t ATTR_GLOBAL = 0x1 << 3;
*/
const uint8_t ATTR_VALINT = 0x1 << 4;
/**
* Indicates that the attribute can have reflected elements.
*/
const uint8_t ATTR_REFLECT_ELEMENTS = 0x1 << 5;
////////////////////////////////////////////////////////////////////////////////
// State map entry
@ -351,26 +346,6 @@ class AttrIterator {
uint8_t mAttrCharacteristics;
};
class AttrWithCharacteristicsIterator {
public:
explicit AttrWithCharacteristicsIterator(uint8_t aCharacteristics)
: mIdx(-1), mCharacteristics(aCharacteristics) {}
bool Next();
nsStaticAtom* AttrName() const;
private:
AttrWithCharacteristicsIterator() = delete;
AttrWithCharacteristicsIterator(const AttrWithCharacteristicsIterator&) =
delete;
AttrWithCharacteristicsIterator& operator=(
const AttrWithCharacteristicsIterator&) = delete;
int32_t mIdx;
uint8_t mCharacteristics;
};
} // namespace aria
} // namespace a11y
} // namespace mozilla

View File

@ -5,15 +5,12 @@
#include "AccIterator.h"
#include "AccGroupInfo.h"
#include "ARIAMap.h"
#include "DocAccessible-inl.h"
#include "LocalAccessible-inl.h"
#include "nsAccUtils.h"
#include "XULTreeAccessible.h"
#include "mozilla/a11y/DocAccessibleParent.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLLabelElement.h"
using namespace mozilla;
@ -243,24 +240,18 @@ LocalAccessible* XULDescriptionIterator::Next() {
}
////////////////////////////////////////////////////////////////////////////////
// AssociatedElementsIterator
// IDRefsIterator
////////////////////////////////////////////////////////////////////////////////
AssociatedElementsIterator::AssociatedElementsIterator(DocAccessible* aDoc,
nsIContent* aContent,
nsAtom* aIDRefsAttr)
: mContent(aContent), mDoc(aDoc), mCurrIdx(0), mElemIdx(0) {
IDRefsIterator::IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsAtom* aIDRefsAttr)
: mContent(aContent), mDoc(aDoc), mCurrIdx(0) {
if (mContent->IsElement()) {
mContent->AsElement()->GetAttr(aIDRefsAttr, mIDs);
if (mIDs.IsEmpty() &&
(aria::AttrCharacteristicsFor(aIDRefsAttr) & ATTR_REFLECT_ELEMENTS)) {
nsAccUtils::GetARIAElementsAttr(mContent->AsElement(), aIDRefsAttr,
mElements);
}
}
}
const nsDependentSubstring AssociatedElementsIterator::NextID() {
const nsDependentSubstring IDRefsIterator::NextID() {
for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx])) break;
}
@ -275,7 +266,7 @@ const nsDependentSubstring AssociatedElementsIterator::NextID() {
return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx);
}
nsIContent* AssociatedElementsIterator::NextElem() {
nsIContent* IDRefsIterator::NextElem() {
while (true) {
const nsDependentSubstring id = NextID();
if (id.IsEmpty()) break;
@ -284,18 +275,11 @@ nsIContent* AssociatedElementsIterator::NextElem() {
if (refContent) return refContent;
}
while (nsIContent* element = mElements.SafeElementAt(mElemIdx++)) {
if (nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor(element,
mContent)) {
return element;
}
}
return nullptr;
}
dom::Element* AssociatedElementsIterator::GetElem(nsIContent* aContent,
const nsAString& aID) {
dom::Element* IDRefsIterator::GetElem(nsIContent* aContent,
const nsAString& aID) {
// Get elements in DOM tree by ID attribute if this is an explicit content.
// In case of bound element check its anonymous subtree.
if (!aContent->IsInNativeAnonymousSubtree()) {
@ -311,12 +295,11 @@ dom::Element* AssociatedElementsIterator::GetElem(nsIContent* aContent,
return nullptr;
}
dom::Element* AssociatedElementsIterator::GetElem(
const nsDependentSubstring& aID) {
dom::Element* IDRefsIterator::GetElem(const nsDependentSubstring& aID) {
return GetElem(mContent, aID);
}
LocalAccessible* AssociatedElementsIterator::Next() {
LocalAccessible* IDRefsIterator::Next() {
nsIContent* nextEl = nullptr;
while ((nextEl = NextElem())) {
LocalAccessible* acc = mDoc->GetAccessible(nextEl);

View File

@ -16,10 +16,6 @@
class nsITreeView;
namespace mozilla {
namespace dom {
class Element;
}
namespace a11y {
class DocAccessibleParent;
@ -206,16 +202,15 @@ class XULDescriptionIterator : public AccIterable {
};
/**
* Used to iterate through elements referenced through explicitly set
* attr-elements or IDs listed in a content attribute. Note, any method used to
* iterate through IDs, elements, or accessibles moves iterator to next
* position.
* Used to iterate through IDs, elements or accessibles pointed by IDRefs
* attribute. Note, any method used to iterate through IDs, elements, or
* accessibles moves iterator to next position.
*/
class AssociatedElementsIterator : public AccIterable {
class IDRefsIterator : public AccIterable {
public:
AssociatedElementsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsAtom* aIDRefsAttr);
virtual ~AssociatedElementsIterator() {}
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsAtom* aIDRefsAttr);
virtual ~IDRefsIterator() {}
/**
* Return next ID.
@ -237,16 +232,14 @@ class AssociatedElementsIterator : public AccIterable {
virtual LocalAccessible* Next() override;
private:
AssociatedElementsIterator();
AssociatedElementsIterator(const AssociatedElementsIterator&);
AssociatedElementsIterator operator=(const AssociatedElementsIterator&);
IDRefsIterator();
IDRefsIterator(const IDRefsIterator&);
IDRefsIterator operator=(const IDRefsIterator&);
nsString mIDs;
nsIContent* mContent;
DocAccessible* mDoc;
nsAString::index_type mCurrIdx;
nsTArray<dom::Element*> mElements;
uint32_t mElemIdx;
};
/**

View File

@ -340,7 +340,7 @@ UniquePtr<AccIterable> CachedTableCellAccessible::GetExplicitHeadersIterator() {
}
}
} else if (LocalAccessible* localAcc = mAcc->AsLocal()) {
return MakeUnique<AssociatedElementsIterator>(
return MakeUnique<IDRefsIterator>(
localAcc->Document(), localAcc->GetContent(), nsGkAtoms::headers);
}
return nullptr;

View File

@ -568,22 +568,6 @@ const nsAttrValue* nsAccUtils::GetARIAAttr(dom::Element* aElement,
return defaults->GetAttr(aName, kNameSpaceID_None);
}
bool nsAccUtils::GetARIAElementsAttr(dom::Element* aElement, nsAtom* aName,
nsTArray<dom::Element*>& aElements) {
if (aElement->HasAttr(aName)) {
aElement->GetExplicitlySetAttrElements(aName, aElements);
return true;
}
if (auto* element = nsGenericHTMLElement::FromNode(aElement)) {
if (auto* internals = element->GetInternals()) {
return internals->GetAttrElements(aName, aElements);
}
}
return false;
}
bool nsAccUtils::ARIAAttrValueIs(dom::Element* aElement, const nsAtom* aName,
const nsAString& aValue,
nsCaseTreatment aCaseSensitive) {

View File

@ -288,8 +288,6 @@ class nsAccUtils {
nsAString& aResult);
static const nsAttrValue* GetARIAAttr(dom::Element* aElement,
const nsAtom* aName);
static bool GetARIAElementsAttr(dom::Element* aElement, nsAtom* aName,
nsTArray<dom::Element*>& aElements);
static bool ARIAAttrValueIs(dom::Element* aElement, const nsAtom* aName,
const nsAString& aValue,
nsCaseTreatment aCaseSensitive);

View File

@ -99,8 +99,7 @@ nsresult nsTextEquivUtils::GetTextEquivFromIDRefs(
if (!content) return NS_OK;
nsIContent* refContent = nullptr;
AssociatedElementsIterator iter(aAccessible->Document(), content,
aIDRefsAttr);
IDRefsIterator iter(aAccessible->Document(), content, aIDRefsAttr);
while ((refContent = iter.NextElem())) {
if (!aTextEquiv.IsEmpty()) aTextEquiv += ' ';

View File

@ -7,7 +7,6 @@
#include "LocalAccessible-inl.h"
#include "AccIterator.h"
#include "AccAttributes.h"
#include "ARIAMap.h"
#include "CachedTableAccessible.h"
#include "DocAccessible-inl.h"
#include "EventTree.h"
@ -804,10 +803,11 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
// Update dependent IDs cache. Take care of elements that are accessible
// because dependent IDs cache doesn't contain IDs from non accessible
// elements. We do this for attribute additions as well because there might
// be an ElementInternals default value.
RemoveDependentIDsFor(accessible, aAttribute);
RemoveDependentElementsFor(accessible, aAttribute);
// elements.
if (aModType != dom::MutationEvent_Binding::ADDITION) {
RemoveDependentIDsFor(accessible, aAttribute);
RemoveDependentElementsFor(accessible, aAttribute);
}
if (aAttribute == nsGkAtoms::id) {
if (accessible->IsActiveDescendantId()) {
@ -1240,7 +1240,7 @@ void DocAccessible::BindToDocument(LocalAccessible* aAccessible,
nsIContent* content = aAccessible->GetContent();
if (content->IsElement() &&
nsAccUtils::HasARIAAttr(content->AsElement(), nsGkAtoms::aria_owns)) {
content->AsElement()->HasAttr(nsGkAtoms::aria_owns)) {
mNotificationController->ScheduleRelocation(aAccessible);
}
}
@ -1802,7 +1802,7 @@ void DocAccessible::AddDependentIDsFor(LocalAccessible* aRelProvider,
}
}
AssociatedElementsIterator iter(this, relProviderEl, relAttr);
IDRefsIterator iter(this, relProviderEl, relAttr);
while (true) {
const nsDependentSubstring id = iter.NextID();
if (id.IsEmpty()) break;
@ -1845,7 +1845,7 @@ void DocAccessible::RemoveDependentIDsFor(LocalAccessible* aRelProvider,
nsStaticAtom* relAttr = kRelationAttrs[idx];
if (aRelAttr && aRelAttr != kRelationAttrs[idx]) continue;
AssociatedElementsIterator iter(this, relProviderElm, relAttr);
IDRefsIterator iter(this, relProviderElm, relAttr);
while (true) {
const nsDependentSubstring id = iter.NextID();
if (id.IsEmpty()) break;
@ -1891,28 +1891,6 @@ void DocAccessible::AddDependentElementsFor(LocalAccessible* aRelProvider,
break;
}
}
aria::AttrWithCharacteristicsIterator multipleElementsRelationIter(
ATTR_REFLECT_ELEMENTS);
while (multipleElementsRelationIter.Next()) {
nsStaticAtom* attr = multipleElementsRelationIter.AttrName();
if (aRelAttr && aRelAttr != attr) {
continue;
}
nsTArray<dom::Element*> elements;
nsAccUtils::GetARIAElementsAttr(providerEl, attr, elements);
for (dom::Element* targetEl : elements) {
AttrRelProviders& providers =
mDependentElementsMap.LookupOrInsert(targetEl);
AttrRelProvider* provider = new AttrRelProvider(attr, providerEl);
providers.AppendElement(provider);
}
// If the relation attribute was given, we've already handled it. We don't
// have anything else to check.
if (aRelAttr) {
break;
}
}
}
void DocAccessible::RemoveDependentElementsFor(LocalAccessible* aRelProvider,
@ -1943,34 +1921,6 @@ void DocAccessible::RemoveDependentElementsFor(LocalAccessible* aRelProvider,
break;
}
}
aria::AttrWithCharacteristicsIterator multipleElementsRelationIter(
ATTR_REFLECT_ELEMENTS);
while (multipleElementsRelationIter.Next()) {
nsStaticAtom* attr = multipleElementsRelationIter.AttrName();
if (aRelAttr && aRelAttr != attr) {
continue;
}
nsTArray<dom::Element*> elements;
nsAccUtils::GetARIAElementsAttr(providerEl, attr, elements);
for (dom::Element* targetEl : elements) {
if (auto providers = mDependentElementsMap.Lookup(targetEl)) {
providers.Data().RemoveElementsBy([attr,
providerEl](const auto& provider) {
return provider->mRelAttr == attr && provider->mContent == providerEl;
});
if (providers.Data().IsEmpty()) {
providers.Remove();
}
}
}
// If the relation attribute was given, we've already handled it. We don't
// have anything else to check.
if (aRelAttr) {
break;
}
}
}
bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
@ -2449,7 +2399,7 @@ void DocAccessible::DoARIAOwnsRelocation(LocalAccessible* aOwner) {
nsTArray<RefPtr<LocalAccessible>>* owned =
mARIAOwnsHash.GetOrInsertNew(aOwner);
AssociatedElementsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
uint32_t idx = 0;
while (nsIContent* childEl = iter.NextElem()) {
LocalAccessible* child = GetAccessible(childEl);

View File

@ -196,8 +196,7 @@ already_AddRefed<nsIURI> ImageAccessible::GetLongDescURI() const {
DocAccessible* document = Document();
if (document) {
AssociatedElementsIterator iter(document, mContent,
nsGkAtoms::aria_describedby);
IDRefsIterator iter(document, mContent, nsGkAtoms::aria_describedby);
while (nsIContent* target = iter.NextElem()) {
if ((target->IsHTMLElement(nsGkAtoms::a) ||
target->IsHTMLElement(nsGkAtoms::area)) &&

View File

@ -1416,7 +1416,7 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
if (aAttribute == nsGkAtoms::aria_label) {
// A valid aria-labelledby would take precedence so an aria-label change
// won't change the name.
AssociatedElementsIterator iter(mDoc, elm, nsGkAtoms::aria_labelledby);
IDRefsIterator iter(mDoc, elm, nsGkAtoms::aria_labelledby);
if (!iter.NextElem()) {
mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
}
@ -1426,7 +1426,7 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
if (aAttribute == nsGkAtoms::aria_description) {
// A valid aria-describedby would take precedence so an aria-description
// change won't change the description.
AssociatedElementsIterator iter(mDoc, elm, nsGkAtoms::aria_describedby);
IDRefsIterator iter(mDoc, elm, nsGkAtoms::aria_describedby);
if (!iter.NextElem()) {
mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE,
this);
@ -1442,7 +1442,7 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
// The subtrees of the new aria-describedby targets might be used to
// compute the description for this. Therefore, we need to set
// the eHasDescriptionDependent flag on all Accessibles in these subtrees.
AssociatedElementsIterator iter(mDoc, elm, nsGkAtoms::aria_describedby);
IDRefsIterator iter(mDoc, elm, nsGkAtoms::aria_describedby);
while (LocalAccessible* target = iter.Next()) {
target->ModifySubtreeContextFlags(eHasDescriptionDependent, true);
}
@ -1461,7 +1461,7 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
// The subtrees of the new aria-labelledby targets might be used to
// compute the name for this. Therefore, we need to set
// the eHasNameDependent flag on all Accessibles in these subtrees.
AssociatedElementsIterator iter(mDoc, elm, nsGkAtoms::aria_labelledby);
IDRefsIterator iter(mDoc, elm, nsGkAtoms::aria_labelledby);
while (LocalAccessible* target = iter.Next()) {
target->ModifySubtreeContextFlags(eHasNameDependent, true);
}
@ -2173,8 +2173,8 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
// defined on.
switch (aType) {
case RelationType::LABELLED_BY: {
Relation rel(new AssociatedElementsIterator(mDoc, mContent,
nsGkAtoms::aria_labelledby));
Relation rel(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_labelledby));
if (mContent->IsHTMLElement()) {
rel.AppendIter(new HTMLLabelIterator(Document(), this));
}
@ -2187,16 +2187,15 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
Relation rel(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_labelledby));
if (mContent->IsXULElement(nsGkAtoms::label)) {
rel.AppendIter(
new AssociatedElementsIterator(mDoc, mContent, nsGkAtoms::control));
rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
}
return rel;
}
case RelationType::DESCRIBED_BY: {
Relation rel(new AssociatedElementsIterator(mDoc, mContent,
nsGkAtoms::aria_describedby));
Relation rel(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_describedby));
if (mContent->IsXULElement()) {
rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
}
@ -2212,8 +2211,7 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
// which only affects accessibility, by allowing the description to be
// tied to a control.
if (mContent->IsXULElement(nsGkAtoms::description)) {
rel.AppendIter(
new AssociatedElementsIterator(mDoc, mContent, nsGkAtoms::control));
rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
}
return rel;
@ -2292,15 +2290,15 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
nsGkAtoms::aria_controls));
case RelationType::CONTROLLER_FOR: {
Relation rel(new AssociatedElementsIterator(mDoc, mContent,
nsGkAtoms::aria_controls));
Relation rel(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_controls));
rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
return rel;
}
case RelationType::FLOWS_TO:
return Relation(new AssociatedElementsIterator(mDoc, mContent,
nsGkAtoms::aria_flowto));
return Relation(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_flowto));
case RelationType::FLOWS_FROM:
return Relation(
@ -2450,10 +2448,9 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
case RelationType::DETAILS: {
if (mContent->IsElement() &&
nsAccUtils::HasARIAAttr(mContent->AsElement(),
nsGkAtoms::aria_details)) {
return Relation(new AssociatedElementsIterator(
mDoc, mContent, nsGkAtoms::aria_details));
mContent->AsElement()->HasAttr(nsGkAtoms::aria_details)) {
return Relation(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_details));
}
if (LocalAccessible* target = GetPopoverTargetDetailsRelation()) {
return Relation(target);
@ -2480,8 +2477,8 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
}
case RelationType::ERRORMSG:
return Relation(new AssociatedElementsIterator(
mDoc, mContent, nsGkAtoms::aria_errormessage));
return Relation(
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_errormessage));
case RelationType::ERRORMSG_FOR:
return Relation(
@ -3995,7 +3992,7 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
}
if (mContent->AsElement()->HasAttr(nsGkAtoms::headers)) {
nsTArray<uint64_t> headers;
AssociatedElementsIterator iter(mDoc, mContent, nsGkAtoms::headers);
IDRefsIterator iter(mDoc, mContent, nsGkAtoms::headers);
while (LocalAccessible* cell = iter.Next()) {
if (cell->IsTableCell()) {
headers.AppendElement(cell->ID());
@ -4066,12 +4063,11 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
// relation, so using RelationByType here is fine.
rel = RelationByType(RelationType::DETAILS);
} else {
// We use an AssociatedElementsIterator here instead of calling
// RelationByType directly because we only want to cache explicit
// relations. Implicit relations (e.g. LABEL_FOR exposed on the target
// of aria-labelledby) will be computed and stored separately in the
// parent process.
rel.AppendIter(new AssociatedElementsIterator(mDoc, mContent, relAtom));
// We use an IDRefsIterator here instead of calling RelationByType
// directly because we only want to cache explicit relations. Implicit
// relations (e.g. LABEL_FOR exposed on the target of aria-labelledby)
// will be computed and stored separately in the parent process.
rel.AppendIter(new IDRefsIterator(mDoc, mContent, relAtom));
}
while (LocalAccessible* acc = rel.LocalNext()) {

View File

@ -88,8 +88,7 @@ void HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
Relation HTMLOutputAccessible::RelationByType(RelationType aType) const {
Relation rel = AccessibleWrap::RelationByType(aType);
if (aType == RelationType::CONTROLLED_BY) {
rel.AppendIter(
new AssociatedElementsIterator(mDoc, mContent, nsGkAtoms::_for));
rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
}
return rel;

View File

@ -467,247 +467,3 @@ between
},
{ chrome: true, topLevel: true }
);
/**
* Test relation defaults via element internals
*/
addAccessibleTask(
`
<div id="dependant1">label</div>
<custom-checkbox id="host"></custom-checkbox>
<div id="dependant2">label2</div>
<script>
customElements.define("custom-checkbox",
class extends HTMLElement {
constructor() {
super();
this.tabIndex = "0";
this._internals = this.attachInternals();
this._internals.role = "checkbox";
this._internals.ariaChecked = "true";
}
get internals() {
return this._internals;
}
}
);
</script>`,
async function (browser, accDoc) {
let host = findAccessibleChildByID(accDoc, "host");
let dependant1 = findAccessibleChildByID(accDoc, "dependant1");
let dependant2 = findAccessibleChildByID(accDoc, "dependant2");
function invokeSetInternals(reflectionAttrName, targetIds) {
if (targetIds) {
Logger.log(
`Setting internals reflected ${reflectionAttrName} attribute to ${targetIds} for host`
);
} else {
Logger.log(
`Removing internals reflected ${reflectionAttrName} attribute from node with host`
);
}
return invokeContentTask(
browser,
[reflectionAttrName, targetIds],
(contentAttr, contentTargetIds) => {
let internals = content.document.getElementById("host").internals;
if (contentTargetIds) {
internals[contentAttr] = contentTargetIds.map(targetId =>
content.document.getElementById(targetId)
);
} else {
internals[contentAttr] = null;
}
}
);
}
async function testInternalsRelation(
attrName,
reflectionAttrName,
hostRelation,
dependantRelation
) {
info(`setting default ${reflectionAttrName}`);
await invokeSetInternals(reflectionAttrName, ["dependant1"]);
await testCachedRelation(host, hostRelation, [dependant1]);
await testCachedRelation(dependant1, dependantRelation, [host]);
await testCachedRelation(dependant2, dependantRelation, null);
info(`setting override ${attrName}`);
await invokeSetAttribute(browser, "host", attrName, "dependant2");
await testCachedRelation(host, hostRelation, [dependant2]);
await testCachedRelation(dependant2, dependantRelation, [host]);
await testCachedRelation(dependant1, dependantRelation, null);
info(`unsetting default ${reflectionAttrName} and ${attrName} override`);
await invokeSetInternals(reflectionAttrName, null);
await invokeSetAttribute(browser, "host", attrName, null);
await testCachedRelation(host, hostRelation, null);
await testCachedRelation(dependant2, dependantRelation, null);
await testCachedRelation(dependant1, dependantRelation, null);
}
await testInternalsRelation(
"aria-labelledby",
"ariaLabelledByElements",
RELATION_LABELLED_BY,
RELATION_LABEL_FOR
);
await testInternalsRelation(
"aria-describedby",
"ariaDescribedByElements",
RELATION_DESCRIBED_BY,
RELATION_DESCRIPTION_FOR
);
await testInternalsRelation(
"aria-controls",
"ariaControlsElements",
RELATION_CONTROLLER_FOR,
RELATION_CONTROLLED_BY
);
await testInternalsRelation(
"aria-flowto",
"ariaFlowToElements",
RELATION_FLOWS_TO,
RELATION_FLOWS_FROM
);
await testInternalsRelation(
"aria-details",
"ariaDetailsElements",
RELATION_DETAILS,
RELATION_DETAILS_FOR
);
await testInternalsRelation(
"aria-errormessage",
"ariaErrorMessageElements",
RELATION_ERRORMSG,
RELATION_ERRORMSG_FOR
);
}
);
/**
* Moving explicitly set elements across shadow DOM boundaries.
*/
addAccessibleTask(
`
<div id="describedButtonContainer">
<div id="buttonDescription1">Delicious</div>
<div id="buttonDescription2">Nutritious</div>
<div id="outerShadowHost"></div>
<button id="describedElement">Button</button>
</div>
<script>
const buttonDescription1 = document.getElementById("buttonDescription1");
const buttonDescription2 = document.getElementById("buttonDescription2");
const outerShadowRoot = outerShadowHost.attachShadow({mode: "open"});
const innerShadowHost = document.createElement("div");
outerShadowRoot.appendChild(innerShadowHost);
const innerShadowRoot = innerShadowHost.attachShadow({mode: "open"});
const describedElement = document.getElementById("describedElement");
// Add some attr associated light DOM elements.
describedElement.ariaDescribedByElements = [buttonDescription1, buttonDescription2];
</script>`,
async function (browser, accDoc) {
const waitAndReturnRecreated = acc => {
const id = getAccessibleDOMNodeID(acc);
return waitForEvents([
[EVENT_HIDE, acc],
[EVENT_SHOW, id],
]).then(evts => evts[1].accessible);
};
let describedAcc = findAccessibleChildByID(accDoc, "describedElement");
let accDescription1 = findAccessibleChildByID(accDoc, "buttonDescription1");
let accDescription2 = findAccessibleChildByID(accDoc, "buttonDescription2");
// All elements were in the same scope, so relations are intact.
await testCachedRelation(describedAcc, RELATION_DESCRIBED_BY, [
accDescription1,
accDescription2,
]);
await testCachedRelation(accDescription1, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
await testCachedRelation(accDescription2, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
let onRecreated = waitAndReturnRecreated(describedAcc);
await invokeContentTask(browser, [], () => {
const outerShadowRoot =
content.document.getElementById("outerShadowHost").shadowRoot;
const describedElement =
content.document.getElementById("describedElement");
outerShadowRoot.appendChild(describedElement);
});
info("Waiting for described accessible to be recreated");
describedAcc = await onRecreated;
// Relations should still be intact, we are referencing elements in a lighter scope.
await testCachedRelation(describedAcc, RELATION_DESCRIBED_BY, [
accDescription1,
accDescription2,
]);
await testCachedRelation(accDescription1, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
await testCachedRelation(accDescription2, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
// Move the explicitly set elements into a deeper shadow DOM.
onRecreated = Promise.all([
waitAndReturnRecreated(accDescription1),
waitAndReturnRecreated(accDescription2),
]);
await invokeContentTask(browser, [], () => {
const buttonDescription1 =
content.document.getElementById("buttonDescription1");
const buttonDescription2 =
content.document.getElementById("buttonDescription2");
const innerShadowRoot =
content.document.getElementById("outerShadowHost").shadowRoot
.firstElementChild.shadowRoot;
innerShadowRoot.appendChild(buttonDescription1);
innerShadowRoot.appendChild(buttonDescription2);
});
[accDescription1, accDescription2] = await onRecreated;
// Relation is severed, because relation dependants are no longer in a valid scope.
await testCachedRelation(describedAcc, RELATION_DESCRIBED_BY, []);
await testCachedRelation(accDescription1, RELATION_DESCRIPTION_FOR, []);
await testCachedRelation(accDescription2, RELATION_DESCRIPTION_FOR, []);
// Move into the same shadow scope as the explicitly set elements.
onRecreated = waitAndReturnRecreated(describedAcc);
await invokeContentTask(browser, [], () => {
const outerShadowRoot =
content.document.getElementById("outerShadowHost").shadowRoot;
const describedElement =
outerShadowRoot.getElementById("describedElement");
const innerShadowRoot = outerShadowRoot.firstElementChild.shadowRoot;
innerShadowRoot.appendChild(describedElement);
});
describedAcc = await onRecreated;
// Relation is restored, because target is now in same shadow scope.
await testCachedRelation(describedAcc, RELATION_DESCRIBED_BY, [
accDescription1,
accDescription2,
]);
await testCachedRelation(accDescription1, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
await testCachedRelation(accDescription2, RELATION_DESCRIPTION_FOR, [
describedAcc,
]);
}
);

View File

@ -7,35 +7,7 @@
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
requestLongerTimeout(2);
function invokeSetAriaOwns(
browser,
id,
children = null,
elementReflection = false
) {
if (!elementReflection) {
return invokeSetAttribute(browser, id, "aria-owns", children);
}
return invokeContentTask(
browser,
[id, children],
(contentId, contentChildrenIds) => {
let elm = content.document.getElementById(contentId);
if (contentChildrenIds) {
elm.ariaOwnsElements = contentChildrenIds
.split(" ")
.map(childId => content.document.getElementById(childId));
} else {
elm.ariaOwnsElements = null;
}
}
);
}
async function testContainer1(browser, accDoc, elementReflection = false) {
async function testContainer1(browser, accDoc) {
const id = "t1_container";
const docID = getAccessibleDOMNodeID(accDoc);
const acc = findAccessibleChildByID(accDoc, id);
@ -49,12 +21,7 @@ async function testContainer1(browser, accDoc, elementReflection = false) {
/* ================ Change ARIA owns ====================================== */
let onReorder = waitForEvent(EVENT_REORDER, id);
await invokeSetAriaOwns(
browser,
id,
"t1_button t1_subdiv",
elementReflection
);
await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv");
await onReorder;
// children are swapped again, button and subdiv are appended to
@ -70,7 +37,7 @@ async function testContainer1(browser, accDoc, elementReflection = false) {
/* ================ Remove ARIA owns ====================================== */
onReorder = waitForEvent(EVENT_REORDER, id);
await invokeSetAriaOwns(browser, id, null, elementReflection);
await invokeSetAttribute(browser, id, "aria-owns");
await onReorder;
// children follow the DOM order
@ -81,12 +48,7 @@ async function testContainer1(browser, accDoc, elementReflection = false) {
/* ================ Set ARIA owns ========================================= */
onReorder = waitForEvent(EVENT_REORDER, id);
await invokeSetAriaOwns(
browser,
id,
"t1_button t1_subdiv",
elementReflection
);
await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv");
await onReorder;
// children are swapped again, button and subdiv are appended to
@ -218,7 +180,7 @@ async function removeContainer(browser, accDoc) {
testAccessibleTree(acc, tree);
}
async function stealAndRecacheChildren(browser, accDoc, elementReflection) {
async function stealAndRecacheChildren(browser, accDoc) {
const id1 = "t3_container1";
const id2 = "t3_container2";
const acc1 = findAccessibleChildByID(accDoc, id1);
@ -226,7 +188,7 @@ async function stealAndRecacheChildren(browser, accDoc, elementReflection) {
/* ================ Attempt to steal from other ARIA owns ================= */
let onReorder = waitForEvent(EVENT_REORDER, id2);
await invokeSetAriaOwns(browser, id2, "t3_child", elementReflection);
await invokeSetAttribute(browser, id2, "aria-owns", "t3_child");
await invokeContentTask(browser, [id2], id => {
let div = content.document.createElement("div");
div.setAttribute("role", "radio");
@ -266,7 +228,7 @@ async function showHiddenElement(browser, accDoc) {
testAccessibleTree(acc, tree);
}
async function rearrangeARIAOwns(browser, accDoc, elementReflection) {
async function rearrangeARIAOwns(browser, accDoc) {
const id = "t5_container";
const acc = findAccessibleChildByID(accDoc, id);
const tests = [
@ -282,7 +244,7 @@ async function rearrangeARIAOwns(browser, accDoc, elementReflection) {
for (let { val, roleList } of tests) {
let onReorder = waitForEvent(EVENT_REORDER, id);
await invokeSetAriaOwns(browser, id, val, elementReflection);
await invokeSetAttribute(browser, id, "aria-owns", val);
await onReorder;
let tree = { SECTION: [] };
@ -331,19 +293,6 @@ addAccessibleTask(
{ iframe: true, remoteIframe: true }
);
addAccessibleTask(
"e10s/doc_treeupdate_ariaowns.html",
async function (browser, accDoc) {
await testContainer1(browser, accDoc, true);
await removeContainer(browser, accDoc);
await stealAndRecacheChildren(browser, accDoc, true);
await showHiddenElement(browser, accDoc);
await rearrangeARIAOwns(browser, accDoc, true);
await removeNotARIAOwnedEl(browser, accDoc);
},
{ iframe: true, remoteIframe: true }
);
// Test owning an ancestor which isn't created yet with an iframe in the
// subtree.
addAccessibleTask(
@ -507,44 +456,6 @@ addAccessibleTask(
{ chrome: false, iframe: true, remoteIframe: true }
);
/**
* Test relation defaults via element internals
*/
addAccessibleTask(
`
<div role="listbox">
<div role="listitem" id="l1"></div>
<div role="listitem" id="l2"></div>
<div role="listitem" id="l3"></div>
</div>
<custom-listbox id="listbox"></custom-listbox>
<div role="listbox">
<div role="listitem" id="l4"></div>
</div>
<script>
customElements.define("custom-listbox",
class extends HTMLElement {
constructor() {
super();
this.tabIndex = "0"
this._internals = this.attachInternals();
this._internals.role = "listbox";
this._internals.ariaOwnsElements = Array.from(this.previousElementSibling.children)
}
}
);
</script>`,
async function (browser, accDoc) {
let listbox = findAccessibleChildByID(accDoc, "listbox");
is(listbox.children.length, 3, "got children");
let onReorder = waitForEvent(EVENT_REORDER, "listbox");
invokeSetAriaOwns(browser, "listbox", "l4");
await onReorder;
}
);
/**
* Test insertion of relocated by ID child after initial load
*/

View File

@ -134,50 +134,6 @@ async function testCachedRelation(identifier, relType, relatedIdentifiers) {
}, "No unexpected targets found.");
}
/**
* Asynchronously set or remove content element's reflected elements attribute
* (in content process if e10s is enabled).
* @param {Object} browser current "tabbrowser" element
* @param {String} id content element id
* @param {String} attr attribute name
* @param {String?} value optional attribute value, if not present, remove
* attribute
* @return {Promise} promise indicating that attribute is set/removed
*/
function invokeSetReflectedElementsAttribute(browser, id, attr, targetIds) {
if (targetIds) {
Logger.log(
`Setting reflected ${attr} attribute to ${targetIds} for node with id: ${id}`
);
} else {
Logger.log(`Removing reflected ${attr} attribute from node with id: ${id}`);
}
return invokeContentTask(
browser,
[id, attr, targetIds],
(contentId, contentAttr, contentTargetIds) => {
let elm = content.document.getElementById(contentId);
if (contentTargetIds) {
elm[contentAttr] = contentTargetIds.map(targetId =>
content.document.getElementById(targetId)
);
} else {
elm[contentAttr] = null;
}
}
);
}
const REFLECTEDATTR_NAME_MAP = {
"aria-controls": "ariaControlsElements",
"aria-describedby": "ariaDescribedByElements",
"aria-details": "ariaDetailsElements",
"aria-errormessage": "ariaErrorMessageElements",
"aria-flowto": "ariaFlowToElements",
"aria-labelledby": "ariaLabelledByElements",
};
async function testRelated(
browser,
accDoc,
@ -192,14 +148,13 @@ async function testRelated(
/**
* Test data has the format of:
* {
* desc {String} description for better logging
* attrs {?Array} an optional list of attributes to update
* reflectedattr {?Array} an optional list of reflected attributes to update
* expected {Array} expected relation values for dependant1, dependant2
* desc {String} description for better logging
* attrs {?Array} an optional list of attributes to update
* expected {Array} expected relation values for dependant1, dependant2
* and host respectively.
* }
*/
let tests = [
const tests = [
{
desc: "No attribute",
expected: [null, null, null],
@ -226,45 +181,13 @@ async function testRelated(
},
];
let reflectedAttrName = REFLECTEDATTR_NAME_MAP[attr];
if (reflectedAttrName) {
tests = tests.concat([
{
desc: "Set reflected attribute",
reflectedattr: [{ key: reflectedAttrName, value: ["dependant1"] }],
expected: [host, null, dependant1],
},
{
desc: "Change reflected attribute",
reflectedattr: [{ key: reflectedAttrName, value: ["dependant2"] }],
expected: [null, host, dependant2],
},
{
desc: "Change reflected attribute to multiple targets",
reflectedattr: [
{ key: reflectedAttrName, value: ["dependant2", "dependant1"] },
],
expected: [host, host, [dependant1, dependant2]],
},
{
desc: "Remove reflected attribute",
reflectedattr: [{ key: reflectedAttrName, value: null }],
expected: [null, null, null],
},
]);
}
for (let { desc, attrs, reflectedattr, expected } of tests) {
for (let { desc, attrs, expected } of tests) {
info(desc);
if (attrs) {
for (let { key, value } of attrs) {
await invokeSetAttribute(browser, "host", key, value);
}
} else if (reflectedattr) {
for (let { key, value } of reflectedattr) {
await invokeSetReflectedElementsAttribute(browser, "host", key, value);
}
}
await testCachedRelation(dependant1, dependantRelation, expected[0]);

View File

@ -1755,40 +1755,25 @@ already_AddRefed<nsIHTMLCollection> Element::GetElementsByClassName(
return nsContentUtils::GetElementsByClassName(this, aClassNames);
}
bool Element::HasSharedRoot(const Element* aElement) const {
nsINode* root = SubtreeRoot();
nsINode* attrSubtreeRoot = aElement->SubtreeRoot();
do {
if (root == attrSubtreeRoot) {
return true;
}
auto* shadow = ShadowRoot::FromNode(root);
if (!shadow || !shadow->GetHost()) {
break;
}
root = shadow->GetHost()->SubtreeRoot();
} while (true);
return false;
}
Element* Element::GetElementByIdInDocOrSubtree(nsAtom* aID) const {
if (auto* docOrShadowRoot = GetContainingDocumentOrShadowRoot()) {
return docOrShadowRoot->GetElementById(aID);
}
return nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), aID);
}
Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const {
if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr);
nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElements.Get(aAttr);
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) {
// If reflectedTarget's explicitly set attr-element |attrEl| is
// a descendant of any of element's shadow-including ancestors, then
// return |atrEl|.
if (HasSharedRoot(attrEl)) {
return attrEl;
}
nsINode* root = SubtreeRoot();
nsINode* attrSubtreeRoot = attrEl->SubtreeRoot();
do {
if (root == attrSubtreeRoot) {
return attrEl;
}
auto* shadow = ShadowRoot::FromNode(root);
if (!shadow || !shadow->GetHost()) {
break;
}
root = shadow->GetHost()->SubtreeRoot();
} while (true);
return nullptr;
}
}
@ -1801,102 +1786,23 @@ Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const {
MOZ_ASSERT(value->Type() == nsAttrValue::eAtom,
"Attribute used for attr associated element must be parsed");
return GetElementByIdInDocOrSubtree(value->GetAtomValue());
}
nsAtom* valueAtom = value->GetAtomValue();
if (auto* docOrShadowRoot = GetContainingDocumentOrShadowRoot()) {
return docOrShadowRoot->GetElementById(valueAtom);
}
void Element::GetAttrAssociatedElements(
nsAtom* aAttr, bool* aUseCachedValue,
Nullable<nsTArray<RefPtr<Element>>>& aElements) {
MOZ_ASSERT(aElements.IsNull());
auto& [explicitlySetAttrElements, cachedAttrElements] =
ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr);
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements
auto getAttrAssociatedElements =
[&, &explicitlySetAttrElements =
explicitlySetAttrElements]() -> Maybe<nsTArray<RefPtr<Element>>> {
nsTArray<RefPtr<Element>> elements;
if (explicitlySetAttrElements) {
// 3. If reflectedTarget's explicitly set attr-elements is not null
for (const nsWeakPtr& weakEl : *explicitlySetAttrElements) {
// For each attrElement in reflectedTarget's explicitly set
// attr-elements:
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
// If attrElement is not a descendant of any of element's
// shadow-including ancestors, then continue.
if (!HasSharedRoot(attrEl)) {
continue;
}
// Append attrElement to elements.
elements.AppendElement(attrEl);
}
}
} else {
// 4. Otherwise
// 1. Let contentAttributeValue be the result of running
// reflectedTarget's get the content attribute.
const nsAttrValue* value = GetParsedAttr(aAttr);
// 2. If contentAttributeValue is null, then return null.
if (!value) {
return Nothing();
}
// 3. Let tokens be contentAttributeValue, split on ASCII whitespace.
MOZ_ASSERT(value->Type() == nsAttrValue::eAtomArray ||
value->Type() == nsAttrValue::eAtom,
"Attribute used for attr associated elements must be parsed");
for (uint32_t i = 0; i < value->GetAtomCount(); i++) {
// For each id of tokens:
if (auto* candidate = GetElementByIdInDocOrSubtree(
value->AtomAt(static_cast<int32_t>(i)))) {
// Append candidate to elements.
elements.AppendElement(candidate);
}
}
nsINode* root = SubtreeRoot();
for (auto* node = root; node; node = node->GetNextNode(root)) {
if (node->HasID() && node->AsContent()->GetID() == valueAtom) {
return node->AsElement();
}
return Some(std::move(elements));
};
// getter steps:
// 1. Let elements be the result of running this's get the attr-associated
// elements.
auto elements = getAttrAssociatedElements();
if (elements && elements == cachedAttrElements) {
// 2. If the contents of elements is equal to the contents of this's cached
// attr-associated elements, then return this's cached attr-associated
// elements object.
MOZ_ASSERT(!*aUseCachedValue);
*aUseCachedValue = true;
return;
}
// 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?.
// (the binding code takes aElements and returns it as a FrozenArray)
// 5. Set this's cached attr-associated elements object to
// elementsAsFrozenArray.
// (the binding code stores the attr-associated elements object in a slot)
// 6. Return elementsAsFrozenArray.
if (elements) {
aElements.SetValue(elements->Clone());
}
// 4. Set this's cached attr-associated elements to elements.
cachedAttrElements = std::move(elements);
return nullptr;
}
void Element::ClearExplicitlySetAttrElement(nsAtom* aAttr) {
if (auto* slots = GetExistingExtendedDOMSlots()) {
slots->mExplicitlySetAttrElementMap.Remove(aAttr);
}
}
void Element::ClearExplicitlySetAttrElements(nsAtom* aAttr) {
if (auto* slots = GetExistingExtendedDOMSlots()) {
slots->mAttrElementsMap.Remove(aAttr);
slots->mExplicitlySetAttrElements.Remove(aAttr);
}
}
@ -1919,7 +1825,7 @@ void Element::ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement) {
#endif
SetAttr(aAttr, EmptyString(), IgnoreErrors());
nsExtendedDOMSlots* slots = ExtendedDOMSlots();
slots->mExplicitlySetAttrElementMap.InsertOrUpdate(
slots->mExplicitlySetAttrElements.InsertOrUpdate(
aAttr, do_GetWeakReference(aElement));
#ifdef ACCESSIBILITY
if (accService) {
@ -1943,48 +1849,9 @@ void Element::ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement) {
#endif
}
void Element::ExplicitlySetAttrElements(
nsAtom* aAttr,
const Nullable<Sequence<OwningNonNull<Element>>>& aElements) {
#ifdef ACCESSIBILITY
nsAccessibilityService* accService = GetAccService();
#endif
// Accessibility requires that no other attribute changes occur between
// AttrElementWillChange and AttrElementChanged. Scripts could cause
// this, so don't let them run here. We do this even if accessibility isn't
// running so that the JS behavior is consistent regardless of accessibility.
// Otherwise, JS might be able to use this difference to determine whether
// accessibility is running, which would be a privacy concern.
nsAutoScriptBlocker scriptBlocker;
#ifdef ACCESSIBILITY
if (accService) {
accService->NotifyAttrElementWillChange(this, aAttr);
}
#endif
if (aElements.IsNull()) {
ClearExplicitlySetAttrElements(aAttr);
UnsetAttr(aAttr, IgnoreErrors());
} else {
SetAttr(aAttr, EmptyString(), IgnoreErrors());
auto& entry = ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr);
entry.first.emplace(nsTArray<nsWeakPtr>());
for (Element* el : aElements.Value()) {
entry.first->AppendElement(do_GetWeakReference(el));
}
}
#ifdef ACCESSIBILITY
if (accService) {
accService->NotifyAttrElementChanged(this, aAttr);
}
#endif
}
Element* Element::GetExplicitlySetAttrElement(nsAtom* aAttr) const {
if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr);
nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElements.Get(aAttr);
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) {
return attrEl;
}
@ -1992,22 +1859,6 @@ Element* Element::GetExplicitlySetAttrElement(nsAtom* aAttr) const {
return nullptr;
}
void Element::GetExplicitlySetAttrElements(
nsAtom* aAttr, nsTArray<Element*>& aElements) const {
if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
if (auto attrElementsMaybeEntry = slots->mAttrElementsMap.Lookup(aAttr)) {
auto& [attrElements, cachedAttrElements] = attrElementsMaybeEntry.Data();
if (attrElements) {
for (const nsWeakPtr& weakEl : *attrElements) {
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
aElements.AppendElement(attrEl);
}
}
}
}
}
}
void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) {
nsINode* cur = this;
while (cur) {
@ -2966,14 +2817,7 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
}
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part ||
aAttribute == nsGkAtoms::aria_controls ||
aAttribute == nsGkAtoms::aria_describedby ||
aAttribute == nsGkAtoms::aria_details ||
aAttribute == nsGkAtoms::aria_errormessage ||
aAttribute == nsGkAtoms::aria_flowto ||
aAttribute == nsGkAtoms::aria_labelledby ||
aAttribute == nsGkAtoms::aria_owns) {
if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part) {
aResult.ParseAtomArray(aValue);
return true;
}
@ -3045,14 +2889,6 @@ void Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
}
} else if (aName == nsGkAtoms::aria_activedescendant) {
ClearExplicitlySetAttrElement(aName);
} else if (aName == nsGkAtoms::aria_controls ||
aName == nsGkAtoms::aria_describedby ||
aName == nsGkAtoms::aria_details ||
aName == nsGkAtoms::aria_errormessage ||
aName == nsGkAtoms::aria_flowto ||
aName == nsGkAtoms::aria_labelledby ||
aName == nsGkAtoms::aria_owns) {
ClearExplicitlySetAttrElements(aName);
}
}
}
@ -3109,16 +2945,6 @@ void Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
aName == nsGkAtoms::aria_activedescendant) {
ClearExplicitlySetAttrElement(aName);
}
if (aNamespaceID == kNameSpaceID_None &&
(aName == nsGkAtoms::aria_controls ||
aName == nsGkAtoms::aria_describedby ||
aName == nsGkAtoms::aria_details ||
aName == nsGkAtoms::aria_errormessage ||
aName == nsGkAtoms::aria_flowto || aName == nsGkAtoms::aria_labelledby ||
aName == nsGkAtoms::aria_owns)) {
ClearExplicitlySetAttrElements(aName);
}
}
EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,

View File

@ -263,17 +263,6 @@ class Grid;
ExplicitlySetAttrElement(nsGkAtoms::attr, aElement); \
}
#define REFLECT_NULLABLE_ELEMENTS_ATTR(method, attr) \
void Get##method(bool* aUseCachedValue, \
Nullable<nsTArray<RefPtr<Element>>>& aElements) { \
GetAttrAssociatedElements(nsGkAtoms::attr, aUseCachedValue, aElements); \
} \
\
void Set##method( \
const Nullable<Sequence<OwningNonNull<Element>>>& aElements) { \
ExplicitlySetAttrElements(nsGkAtoms::attr, aElements); \
}
// TODO(keithamus): Reference the spec link once merged.
// https://github.com/whatwg/html/pull/9841/files#diff-41cf6794ba4200b839c53531555f0f3998df4cbb01a4d5cb0b94e3ca5e23947dR86024
enum class InvokeAction : uint8_t {
@ -713,28 +702,21 @@ class Element : public FragmentOrElement {
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaColIndex, aria_colindex)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaColIndexText, aria_colindextext)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaColSpan, aria_colspan)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaControlsElements, aria_controls)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaCurrent, aria_current)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaDescribedByElements, aria_describedby)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaDescription, aria_description)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaDetailsElements, aria_details)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaDisabled, aria_disabled)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaErrorMessageElements, aria_errormessage)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaExpanded, aria_expanded)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaFlowToElements, aria_flowto)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaHasPopup, aria_haspopup)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaHidden, aria_hidden)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaInvalid, aria_invalid)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaKeyShortcuts, aria_keyshortcuts)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaLabel, aria_label)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaLabelledByElements, aria_labelledby)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaLevel, aria_level)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaLive, aria_live)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaModal, aria_modal)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaMultiLine, aria_multiline)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaMultiSelectable, aria_multiselectable)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaOrientation, aria_orientation)
REFLECT_NULLABLE_ELEMENTS_ATTR(AriaOwnsElements, aria_owns)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaPlaceholder, aria_placeholder)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaPosInSet, aria_posinset)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaPressed, aria_pressed)
@ -1186,10 +1168,6 @@ class Element : public FragmentOrElement {
const MappedAttributeEntry* const aMaps[],
uint32_t aMapCount);
bool HasSharedRoot(const Element* aElement) const;
Element* GetElementByIdInDocOrSubtree(nsAtom* aID) const;
protected:
inline bool GetAttr(const nsAtom* aName, DOMString& aResult) const {
MOZ_ASSERT(aResult.IsEmpty(), "Should have empty string coming in");
@ -1307,21 +1285,14 @@ class Element : public FragmentOrElement {
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-element
*/
Element* GetAttrAssociatedElement(nsAtom* aAttr) const;
void GetAttrAssociatedElements(
nsAtom* aAttr, bool* aUseCachedValue,
Nullable<nsTArray<RefPtr<Element>>>& aElements);
/**
* Sets an attribute element for the given attribute.
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element
*/
void ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement);
void ExplicitlySetAttrElements(
nsAtom* aAttr,
const Nullable<Sequence<OwningNonNull<Element>>>& aElements);
void ClearExplicitlySetAttrElement(nsAtom*);
void ClearExplicitlySetAttrElements(nsAtom*);
/**
* Gets the attribute element for the given attribute.
@ -1333,16 +1304,6 @@ class Element : public FragmentOrElement {
*/
Element* GetExplicitlySetAttrElement(nsAtom* aAttr) const;
/**
* Gets the attribute elements for the given attribute. Unlike
* GetAttrAssociatedElements, this returns an uncached array of explicitly set
* elements without checking if they are a descendant of any of this element's
* shadow-including ancestors. It also does not attempt to retrieve elements
* using the ids set in the content attribute.
*/
void GetExplicitlySetAttrElements(nsAtom* aAttr,
nsTArray<Element*>& aElements) const;
PseudoStyleType GetPseudoElementType() const {
nsresult rv = NS_OK;
auto raw = GetProperty(nsGkAtoms::pseudoProperty, &rv);

View File

@ -637,8 +637,7 @@ void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
mAnimations = nullptr;
aContent.ClearMayHaveAnimations();
}
mExplicitlySetAttrElementMap.Clear();
mAttrElementsMap.Clear();
mExplicitlySetAttrElements.Clear();
mRadioGroupContainer = nullptr;
mPart = nullptr;
}
@ -662,15 +661,6 @@ void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart");
aCb.NoteXPCOMChild(mPart.get());
for (auto& tableEntry : mAttrElementsMap) {
auto& [explicitlySetElements, cachedAttrElements] =
*tableEntry.GetModifiableData();
if (cachedAttrElements) {
ImplCycleCollectionTraverse(aCb, *cachedAttrElements,
"cached attribute elements entry", 0);
}
}
if (mCustomElementData) {
mCustomElementData->Traverse(aCb);
}

View File

@ -268,21 +268,10 @@ class FragmentOrElement : public nsIContent {
RefPtr<nsDOMTokenList> mPart;
/**
* Explicitly set attr-element, see
* Explicitly set attr-elements, see
* https://html.spec.whatwg.org/#explicitly-set-attr-element
*/
nsTHashMap<RefPtr<nsAtom>, nsWeakPtr> mExplicitlySetAttrElementMap;
/**
* Explicitly set attr-elements, see
* https://html.spec.whatwg.org/#explicitly-set-attr-elements
*
* The first member of the pair are the explicitly set attr-elements. The
* second member is the cached attr-associated elements.
*/
nsTHashMap<RefPtr<nsAtom>, std::pair<Maybe<nsTArray<nsWeakPtr>>,
Maybe<nsTArray<RefPtr<Element>>>>>
mAttrElementsMap;
nsTHashMap<RefPtr<nsAtom>, nsWeakPtr> mExplicitlySetAttrElements;
};
class nsDOMSlots : public nsIContent::nsContentSlots {

View File

@ -41,13 +41,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ElementInternals)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mSubmissionValue, mState,
mValidity, mValidationAnchor,
mCustomStateSet);
for (auto& tableEntry : tmp->mAttrElementsMap) {
auto& [explicitlySetElements, cachedAttrElements] =
*tableEntry.GetModifiableData();
ImplCycleCollectionTraverse(cb, cachedAttrElements,
"cached attribute elements entry", 0);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ElementInternals)
@ -435,7 +428,6 @@ void ElementInternals::Unlink() {
mFieldSet->RemoveElement(mTarget);
mFieldSet = nullptr;
}
mAttrElementsMap.Clear();
}
void ElementInternals::GetAttr(const nsAtom* aName, nsAString& aResult) const {
@ -477,23 +469,6 @@ nsresult ElementInternals::SetAttr(nsAtom* aName, const nsAString& aValue) {
return rs;
}
nsresult ElementInternals::SetAttrInternal(nsAtom* aName,
const nsAString& aValue) {
bool attrHadValue;
nsAttrValue attrValue(aValue);
return mAttrs.SetAndSwapAttr(aName, attrValue, &attrHadValue);
}
nsresult ElementInternals::UnsetAttrInternal(nsAtom* aName) {
nsAttrValue attrValue;
auto attrPos = mAttrs.IndexOfAttr(aName);
if (attrPos >= 0) {
return mAttrs.RemoveAttrAt(attrPos, attrValue);
}
return NS_OK;
}
DocGroup* ElementInternals::GetDocGroup() {
return mTarget->OwnerDoc()->GetDocGroup();
}
@ -541,11 +516,9 @@ void ElementInternals::SetAttrElement(nsAtom* aAttr, Element* aElement) {
#endif
if (aElement) {
mAttrElementMap.InsertOrUpdate(aAttr, do_GetWeakReference(aElement));
SetAttrInternal(aAttr, EmptyString());
mAttrElements.InsertOrUpdate(aAttr, do_GetWeakReference(aElement));
} else {
mAttrElementMap.Remove(aAttr);
UnsetAttrInternal(aAttr);
mAttrElements.Remove(aAttr);
}
#ifdef ACCESSIBILITY
@ -556,121 +529,9 @@ void ElementInternals::SetAttrElement(nsAtom* aAttr, Element* aElement) {
}
Element* ElementInternals::GetAttrElement(nsAtom* aAttr) const {
nsWeakPtr weakAttrEl = mAttrElementMap.Get(aAttr);
nsWeakPtr weakAttrEl = mAttrElements.Get(aAttr);
nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl);
return attrEl;
}
void ElementInternals::SetAttrElements(
nsAtom* aAttr,
const Nullable<Sequence<OwningNonNull<Element>>>& aElements) {
#ifdef ACCESSIBILITY
nsAccessibilityService* accService = GetAccService();
#endif
// Accessibility requires that no other attribute changes occur between
// AttrElementWillChange and AttrElementChanged. Scripts could cause
// this, so don't let them run here. We do this even if accessibility isn't
// running so that the JS behavior is consistent regardless of accessibility.
// Otherwise, JS might be able to use this difference to determine whether
// accessibility is running, which would be a privacy concern.
nsAutoScriptBlocker scriptBlocker;
#ifdef ACCESSIBILITY
if (accService) {
accService->NotifyAttrElementWillChange(mTarget, aAttr);
}
#endif
nsAttrValue emptyAttr;
if (aElements.IsNull()) {
mAttrElementsMap.Remove(aAttr);
UnsetAttrInternal(aAttr);
} else {
auto& [attrElements, cachedAttrElements] =
mAttrElementsMap.LookupOrInsert(aAttr);
attrElements.Clear();
for (Element* el : aElements.Value()) {
attrElements.AppendElement(do_GetWeakReference(el));
}
SetAttrInternal(aAttr, EmptyString());
}
#ifdef ACCESSIBILITY
if (accService) {
accService->NotifyAttrElementChanged(mTarget, aAttr);
}
#endif
}
void ElementInternals::GetAttrElements(
nsAtom* aAttr, bool* aUseCachedValue,
Nullable<nsTArray<RefPtr<Element>>>& aElements) {
MOZ_ASSERT(aElements.IsNull());
auto attrElementsMaybeEntry = mAttrElementsMap.Lookup(aAttr);
if (!attrElementsMaybeEntry) {
return;
}
aElements.SetValue(nsTArray<RefPtr<Element>>());
auto& [attrElements, cachedAttrElements] = attrElementsMaybeEntry.Data();
auto getAttrAssociatedElements = [&, &attrElements = attrElements]() {
CopyableTArray<RefPtr<Element>> elements;
for (const nsWeakPtr& weakEl : attrElements) {
// For each attrElement in reflectedTarget's explicitly set attr-elements:
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
// Append attrElement to elements.
elements.AppendElement(attrEl);
}
}
return elements;
};
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements
// Getter steps:
// 1. Let elements be the result of running this's get the attr-associated
// elements.
auto elements = getAttrAssociatedElements();
if (elements == cachedAttrElements) {
// 2. If the contents of elements is equal to the contents of this's cached
// attr-associated elements, then return this's cached attr-associated
// elements object.
MOZ_ASSERT(!*aUseCachedValue);
*aUseCachedValue = true;
return;
}
// 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?.
// (the binding code takes aElements and returns it as a FrozenArray)
// 5. Set this's cached attr-associated elements object to
// elementsAsFrozenArray.
// (the binding code stores the attr-associated elements object in a slot)
// 6. Return elementsAsFrozenArray.
aElements.SetValue(elements.Clone());
// 4. Set this's cached attr-associated elements to elements.
cachedAttrElements = std::move(elements);
}
bool ElementInternals::GetAttrElements(nsAtom* aAttr,
nsTArray<Element*>& aElements) {
aElements.Clear();
auto attrElementsMaybeEntry = mAttrElementsMap.Lookup(aAttr);
if (!attrElementsMaybeEntry) {
return false;
}
auto& [attrElements, cachedAttrElements] = attrElementsMaybeEntry.Data();
for (const nsWeakPtr& weakEl : attrElements) {
if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
aElements.AppendElement(attrEl);
}
}
return true;
}
} // namespace mozilla::dom

View File

@ -34,17 +34,6 @@
SetAttrElement(nsGkAtoms::attr, aElement); \
}
#define ARIA_REFLECT_ATTR_ELEMENTS(method, attr) \
void Get##method(bool* aUseCachedValue, \
Nullable<nsTArray<RefPtr<Element>>>& aElements) { \
GetAttrElements(nsGkAtoms::attr, aUseCachedValue, aElements); \
} \
\
void Set##method( \
const Nullable<Sequence<OwningNonNull<Element>>>& aElements) { \
SetAttrElements(nsGkAtoms::attr, aElements); \
}
class nsINodeList;
class nsGenericHTMLElement;
@ -148,28 +137,21 @@ class ElementInternals final : public nsIFormControl,
ARIA_REFLECT_ATTR(AriaColIndex, aria_colindex)
ARIA_REFLECT_ATTR(AriaColIndexText, aria_colindextext)
ARIA_REFLECT_ATTR(AriaColSpan, aria_colspan)
ARIA_REFLECT_ATTR_ELEMENTS(AriaControlsElements, aria_controls)
ARIA_REFLECT_ATTR(AriaCurrent, aria_current)
ARIA_REFLECT_ATTR_ELEMENTS(AriaDescribedByElements, aria_describedby)
ARIA_REFLECT_ATTR(AriaDescription, aria_description)
ARIA_REFLECT_ATTR_ELEMENTS(AriaDetailsElements, aria_details)
ARIA_REFLECT_ATTR(AriaDisabled, aria_disabled)
ARIA_REFLECT_ATTR_ELEMENTS(AriaErrorMessageElements, aria_errormessage)
ARIA_REFLECT_ATTR(AriaExpanded, aria_expanded)
ARIA_REFLECT_ATTR_ELEMENTS(AriaFlowToElements, aria_flowto)
ARIA_REFLECT_ATTR(AriaHasPopup, aria_haspopup)
ARIA_REFLECT_ATTR(AriaHidden, aria_hidden)
ARIA_REFLECT_ATTR(AriaInvalid, aria_invalid)
ARIA_REFLECT_ATTR(AriaKeyShortcuts, aria_keyshortcuts)
ARIA_REFLECT_ATTR(AriaLabel, aria_label)
ARIA_REFLECT_ATTR_ELEMENTS(AriaLabelledByElements, aria_labelledby)
ARIA_REFLECT_ATTR(AriaLevel, aria_level)
ARIA_REFLECT_ATTR(AriaLive, aria_live)
ARIA_REFLECT_ATTR(AriaModal, aria_modal)
ARIA_REFLECT_ATTR(AriaMultiLine, aria_multiline)
ARIA_REFLECT_ATTR(AriaMultiSelectable, aria_multiselectable)
ARIA_REFLECT_ATTR(AriaOrientation, aria_orientation)
ARIA_REFLECT_ATTR_ELEMENTS(AriaOwnsElements, aria_owns)
ARIA_REFLECT_ATTR(AriaPlaceholder, aria_placeholder)
ARIA_REFLECT_ATTR(AriaPosInSet, aria_posinset)
ARIA_REFLECT_ATTR(AriaPressed, aria_pressed)
@ -193,8 +175,6 @@ class ElementInternals final : public nsIFormControl,
nsresult SetAttr(nsAtom* aName, const nsAString& aValue);
bool GetAttrElements(nsAtom* aAttr, nsTArray<Element*>& aElements);
const AttrArray& GetAttrs() const { return mAttrs; }
DocGroup* GetDocGroup();
@ -214,17 +194,6 @@ class ElementInternals final : public nsIFormControl,
*/
void SetAttrElement(nsAtom* aAttr, Element* aElement);
void SetAttrElements(
nsAtom* aAttr,
const Nullable<Sequence<OwningNonNull<Element>>>& aElements);
void GetAttrElements(nsAtom* aAttr, bool* aUseCachedValue,
Nullable<nsTArray<RefPtr<Element>>>& aElements);
nsresult SetAttrInternal(nsAtom* aName, const nsAString& aValue);
nsresult UnsetAttrInternal(nsAtom* aName);
// It's a target element which is a custom element.
RefPtr<HTMLElement> mTarget;
@ -269,11 +238,7 @@ class ElementInternals final : public nsIFormControl,
* Explicitly set attr-elements, see
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element
*/
nsTHashMap<RefPtr<nsAtom>, nsWeakPtr> mAttrElementMap;
nsTHashMap<RefPtr<nsAtom>,
std::pair<nsTArray<nsWeakPtr>, nsTArray<RefPtr<Element>>>>
mAttrElementsMap;
nsTHashMap<RefPtr<nsAtom>, nsWeakPtr> mAttrElements;
};
} // namespace mozilla::dom

View File

@ -47,38 +47,18 @@ interface mixin ARIAMixin {
[CEReactions, SetterThrows]
attribute DOMString? ariaColSpan;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaControlsElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaCurrent;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaDescribedByElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaDescription;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaDetailsElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaDisabled;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaErrorMessageElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaExpanded;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaFlowToElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaHasPopup;
@ -94,10 +74,6 @@ interface mixin ARIAMixin {
[CEReactions, SetterThrows]
attribute DOMString? ariaLabel;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaLabelledByElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaLevel;
@ -116,10 +92,6 @@ interface mixin ARIAMixin {
[CEReactions, SetterThrows]
attribute DOMString? ariaOrientation;
// TODO: Use FrozenArray once available. (Bug 1236777)
[Pref="accessibility.ARIAElementReflection.enabled", Frozen, ReflectedHTMLAttributeReturningFrozenArray]
attribute sequence<Element>? ariaOwnsElements;
[CEReactions, SetterThrows]
attribute DOMString? ariaPlaceholder;

View File

@ -1,2 +1,23 @@
[ElementInternals-accessibility.html]
prefs: [accessibility.ARIAElementReflection.enabled:true]
[ariaControlsElements is defined in ElementInternals]
expected: FAIL
[ariaDescribedByElements is defined in ElementInternals]
expected: FAIL
[ariaDetailsElements is defined in ElementInternals]
expected: FAIL
[ariaFlowToElements is defined in ElementInternals]
expected: FAIL
[ariaLabelledByElements is defined in ElementInternals]
expected: FAIL
[ariaOwnsElements is defined in ElementInternals]
expected: FAIL
[ariaErrorMessageElements is defined in ElementInternals]
expected: FAIL

View File

@ -1,2 +1,3 @@
[element-internals-aria-element-reflection.html]
prefs: [accessibility.ARIAElementReflection.enabled:true]
[Getting previously-unset ARIA element reflection properties on ElementInternals should return null.]
expected: FAIL

View File

@ -1,2 +1,4 @@
[aria-element-reflection-disconnected.html]
prefs: [accessibility.ARIAElementReflection.enabled:true]
[Element references should stay valid when content is disconnected (element array)]
expected: FAIL

View File

@ -1,2 +1,34 @@
[aria-element-reflection.html]
prefs: [accessibility.ARIAElementReflection.enabled:true]
[aria-errormessage]
expected: FAIL
[aria-details]
expected: FAIL
[aria-labelledby.]
expected: FAIL
[aria-controls.]
expected: FAIL
[aria-describedby.]
expected: FAIL
[aria-flowto.]
expected: FAIL
[aria-owns.]
expected: FAIL
[shadow DOM behaviour for FrozenArray element reflection.]
expected: FAIL
[Moving explicitly set elements across shadow DOM boundaries.]
expected: FAIL
[Moving explicitly set elements around within the same scope, and removing from the DOM.]
expected: FAIL
[Passing values of the wrong type should throw a TypeError]
expected: FAIL

View File

@ -1,2 +1,43 @@
[idlharness.window.html]
prefs: [accessibility.ARIAElementReflection.enabled:true]
[Element interface: attribute ariaControlsElements]
expected: FAIL
[Element interface: attribute ariaDescribedByElements]
expected: FAIL
[Element interface: attribute ariaDetailsElements]
expected: FAIL
[Element interface: attribute ariaFlowToElements]
expected: FAIL
[Element interface: attribute ariaLabelledByElements]
expected: FAIL
[Element interface: attribute ariaOwnsElements]
expected: FAIL
[Element interface: element must inherit property "ariaControlsElements" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaDescribedByElements" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaDetailsElements" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaFlowToElements" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaLabelledByElements" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaOwnsElements" with the proper type]
expected: FAIL
[Element interface: attribute ariaErrorMessageElements]
expected: FAIL
[Element interface: element must inherit property "ariaErrorMessageElements" with the proper type]
expected: FAIL