Bug 1883996 - P2: Add ariaActiveDescendantElement to ARIAMixin and implement. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D203850
This commit is contained in:
Eitan Isaacson 2024-03-12 20:32:16 +00:00
parent 2da285001d
commit 41c0dbb5ad
11 changed files with 101 additions and 51 deletions

View File

@ -2787,6 +2787,12 @@ bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
return true;
}
if (aAttribute == nsGkAtoms::aria_activedescendant) {
// String in aria-activedescendant is an id, so store as an atom.
aResult.ParseAtom(aValue);
return true;
}
if (aAttribute == nsGkAtoms::id) {
// Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id.
@ -2841,6 +2847,8 @@ void Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) {
shadow->MaybeReassignContent(*this);
}
} else if (aName == nsGkAtoms::aria_activedescendant) {
ClearExplicitlySetAttrElement(aName);
}
}
}
@ -2892,6 +2900,11 @@ void Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
ElementCallbackType::eAttributeChanged, this, args, definition);
}
}
if (aNamespaceID == kNameSpaceID_None &&
aName == nsGkAtoms::aria_activedescendant) {
ClearExplicitlySetAttrElement(aName);
}
}
EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,

View File

@ -243,6 +243,15 @@ class Grid;
SetOrRemoveNullableStringAttr(nsGkAtoms::attr, aValue, aRv); \
}
#define REFLECT_NULLABLE_ELEMENT_ATTR(method, attr) \
Element* Get##method() const { \
return GetAttrAssociatedElement(nsGkAtoms::attr); \
} \
\
void Set##method(Element* aElement) { \
ExplicitlySetAttrElement(nsGkAtoms::attr, aElement); \
}
class Element : public FragmentOrElement {
public:
#ifdef MOZILLA_INTERNAL_API
@ -648,6 +657,8 @@ class Element : public FragmentOrElement {
REFLECT_NULLABLE_DOMSTRING_ATTR(Role, role)
// AriaAttributes
REFLECT_NULLABLE_ELEMENT_ATTR(AriaActiveDescendantElement,
aria_activedescendant)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaAtomic, aria_atomic)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaAutoComplete, aria_autocomplete)
REFLECT_NULLABLE_DOMSTRING_ATTR(AriaBrailleLabel, aria_braillelabel)

View File

@ -22,6 +22,10 @@
#include "nsDebug.h"
#include "nsGenericHTMLElement.h"
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
#endif
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ElementInternals)
@ -482,4 +486,44 @@ void ElementInternals::InitializeControlNumber() {
mControlNumber = mTarget->OwnerDoc()->GetNextControlNumber();
}
void ElementInternals::SetAttrElement(nsAtom* aAttr, Element* aElement) {
// 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 the target has this attribute defined then it overrides the defaults
// defined here in the Internals instance. In that case we don't need to
// notify the change to a11y since the attribute hasn't changed, just the
// underlying default. We can set accService to null and not notify.
nsAccessibilityService* accService =
!mTarget->HasAttr(aAttr) ? GetAccService() : nullptr;
if (accService) {
accService->NotifyAttrElementWillChange(mTarget, aAttr);
}
#endif
if (aElement) {
mAttrElements.InsertOrUpdate(aAttr, do_GetWeakReference(aElement));
} else {
mAttrElements.Remove(aAttr);
}
#ifdef ACCESSIBILITY
if (accService) {
accService->NotifyAttrElementChanged(mTarget, aAttr);
}
#endif
}
Element* ElementInternals::GetAttrElement(nsAtom* aAttr) const {
nsWeakPtr weakAttrEl = mAttrElements.Get(aAttr);
nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl);
return attrEl;
}
} // namespace mozilla::dom

View File

@ -27,6 +27,13 @@
aResult = ErrorResult(SetAttr(nsGkAtoms::attr, aValue)); \
}
#define ARIA_REFLECT_ATTR_ELEMENT(method, attr) \
Element* Get##method() const { return GetAttrElement(nsGkAtoms::attr); } \
\
void Set##method(Element* aElement) { \
SetAttrElement(nsGkAtoms::attr, aElement); \
}
class nsINodeList;
class nsGenericHTMLElement;
@ -119,6 +126,7 @@ class ElementInternals final : public nsIFormControl,
ARIA_REFLECT_ATTR(Role, role)
// AriaAttributes
ARIA_REFLECT_ATTR_ELEMENT(AriaActiveDescendantElement, aria_activedescendant)
ARIA_REFLECT_ATTR(AriaAtomic, aria_atomic)
ARIA_REFLECT_ATTR(AriaAutoComplete, aria_autocomplete)
ARIA_REFLECT_ATTR(AriaBusy, aria_busy)
@ -174,6 +182,18 @@ class ElementInternals final : public nsIFormControl,
private:
~ElementInternals() = default;
/**
* Gets the attribute element for the given attribute.
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element
*/
Element* GetAttrElement(nsAtom* aAttr) const;
/**
* Sets an attribute element for the given attribute.
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element
*/
void SetAttrElement(nsAtom* aAttr, Element* aElement);
// It's a target element which is a custom element.
RefPtr<HTMLElement> mTarget;
@ -213,6 +233,12 @@ class ElementInternals final : public nsIFormControl,
// owner document. This is only set to a number for elements inserted into the
// document by the parser from the network. Otherwise, it is -1.
int32_t mControlNumber;
/**
* Explicitly set attr-elements, see
* https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#explicitly-set-attr-element
*/
nsTHashMap<RefPtr<nsAtom>, nsWeakPtr> mAttrElements;
};
} // namespace mozilla::dom

View File

@ -11,6 +11,9 @@
*/
interface mixin ARIAMixin {
[Pref="accessibility.ARIAReflection.enabled", CEReactions]
attribute Element? ariaActiveDescendantElement;
[Pref="accessibility.ARIAReflection.enabled", CEReactions, SetterThrows]
attribute DOMString? role;

View File

@ -1425,6 +1425,10 @@ nsresult nsXULPrototypeElement::SetAttrAt(uint32_t aPos,
// emptystring as id.
mAttributes[aPos].mValue.ParseAtom(aValue);
return NS_OK;
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::aria_activedescendant)) {
mAttributes[aPos].mValue.ParseAtom(aValue);
return NS_OK;
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
// Store is as atom.

View File

@ -1,7 +1,4 @@
[ElementInternals-accessibility.html]
[ariaActiveDescendantElement is defined in ElementInternals]
expected: FAIL
[ariaControlsElements is defined in ElementInternals]
expected: FAIL

View File

@ -1,10 +1,4 @@
[AriaMixin-element-attributes.html]
[ariaActiveDescendantElement in Element must enqueue an attributeChanged reaction when adding aria-activedescendant content attribute]
expected: FAIL
[ariaActiveDescendantElement in Element must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL
[ariaControlsElements in Element must enqueue an attributeChanged reaction when adding aria-controls content attribute]
expected: FAIL

View File

@ -1,6 +1,3 @@
[aria-element-reflection-disconnected.html]
[Element references should stay valid when content is disconnected (single element)]
expected: FAIL
[Element references should stay valid when content is disconnected (element array)]
expected: FAIL

View File

@ -1,31 +1,10 @@
[aria-element-reflection.html]
[aria-activedescendant element reflection]
expected: FAIL
[If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.]
expected: FAIL
[Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string]
expected: FAIL
[Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.]
expected: FAIL
[aria-errormessage]
expected: FAIL
[aria-details]
expected: FAIL
[Reparenting an element into a descendant shadow scope hides the element reference.]
expected: FAIL
[Reparenting referenced element cannot cause retargeting of reference.]
expected: FAIL
[Element reference set in invalid scope remains intact throughout move to valid scope.]
expected: FAIL
[aria-labelledby.]
expected: FAIL
@ -50,17 +29,5 @@
[Moving explicitly set elements around within the same scope, and removing from the DOM.]
expected: FAIL
[Attaching element reference before it's inserted into the DOM.]
expected: FAIL
[Cross-document references and moves.]
expected: FAIL
[Deleting a reflected element should return null for the IDL attribute and the content attribute will be empty.]
expected: FAIL
[Changing the ID of an element doesn't lose the reference.]
expected: FAIL
[Passing values of the wrong type should throw a TypeError]
expected: FAIL

View File

@ -1,7 +1,4 @@
[idlharness.window.html]
[Element interface: attribute ariaActiveDescendantElement]
expected: FAIL
[Element interface: attribute ariaControlsElements]
expected: FAIL
@ -20,9 +17,6 @@
[Element interface: attribute ariaOwnsElements]
expected: FAIL
[Element interface: element must inherit property "ariaActiveDescendantElement" with the proper type]
expected: FAIL
[Element interface: element must inherit property "ariaControlsElements" with the proper type]
expected: FAIL