Backed out 4 changesets (bug 1769586) for causing multiple failures.

Backed out changeset 1125ec697fdd (bug 1769586)
Backed out changeset ced88b84eef3 (bug 1769586)
Backed out changeset 486c9854c11d (bug 1769586)
Backed out changeset f543f54d90a0 (bug 1769586)
This commit is contained in:
Alexandru Marc 2024-11-19 19:10:30 +02:00
parent 7c12aeeafd
commit 791097f054
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>(std::size(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()) {
@ -1247,7 +1247,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);
}
}
@ -1808,7 +1808,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;
@ -1851,7 +1851,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;
@ -1897,28 +1897,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,
@ -1949,34 +1927,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,
@ -2465,7 +2415,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

@ -1417,7 +1417,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);
}
@ -1427,7 +1427,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);
@ -1443,7 +1443,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);
}
@ -1462,7 +1462,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);
}
@ -2168,8 +2168,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));
}
@ -2182,16 +2182,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));
}
@ -2207,8 +2206,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;
@ -2287,15 +2285,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(
@ -2445,10 +2443,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);
@ -2475,8 +2472,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(
@ -3994,7 +3991,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());
@ -4065,12 +4062,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(
@ -506,41 +455,3 @@ 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;
}
);

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

@ -1760,40 +1760,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;
}
}
@ -1806,102 +1791,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);
}
}
@ -1924,7 +1830,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) {
@ -1948,48 +1854,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;
}
@ -1997,22 +1864,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) {
@ -2971,14 +2822,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;
}
@ -3050,14 +2894,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);
}
}
}
@ -3114,16 +2950,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

@ -266,17 +266,6 @@ class TrustedHTMLOrNullIsEmptyString;
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 {
@ -716,28 +705,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)
@ -1189,10 +1171,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");
@ -1313,21 +1291,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.
@ -1339,16 +1310,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,5 +1,6 @@
[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
[Setting ariaLabelledByElements should change the accessible name of the custom element]
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