Bug 1625864 - Fire state change event on LINKED change. r=Jamie,morgan

Also stop recreating any accessible that has href modified.

Differential Revision: https://phabricator.services.mozilla.com/D71258
This commit is contained in:
Eitan Isaacson 2020-04-20 17:59:09 +00:00
Родитель 3d7faad085
Коммит 7fac737410
9 изменённых файлов: 80 добавлений и 29 удалений

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

@ -50,6 +50,7 @@ enum AccType {
* Other accessible types.
*/
eApplicationType,
eHTMLLinkType,
eHTMLOptGroupType,
eImageMapType,
eMenuPopupType,

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

@ -285,16 +285,23 @@ nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges) {
change->GetCountOfEventListenerChangesAffectingAccessibility(&changeCount);
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < changeCount; i++) {
if (changeCount) {
Document* ownerDoc = node->OwnerDoc();
DocAccessible* document = GetExistingDocAccessible(ownerDoc);
// Create an accessible for a inaccessible element having click event
// handler.
if (document && !document->HasAccessible(node) &&
nsCoreUtils::HasClickListener(node)) {
document->ContentInserted(node, node->GetNextSibling());
break;
if (document) {
Accessible* acc = document->GetAccessible(node);
if (!acc && nsCoreUtils::HasClickListener(node)) {
// Create an accessible for a inaccessible element having click event
// handler.
document->ContentInserted(node, node->GetNextSibling());
} else if (acc && acc->IsHTMLLink() && !acc->AsHTMLLink()->IsLinked()) {
// Notify of a LINKED state change if an HTML link gets a click
// listener but does not have an href attribute.
RefPtr<AccEvent> linkedChangeEvent =
new AccStateChangeEvent(acc, states::LINKED);
document->FireDelayedEvent(linkedChangeEvent);
}
}
}
}

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

@ -39,6 +39,7 @@ class EmbeddedObjCollector;
class EventTree;
class HTMLImageMapAccessible;
class HTMLLIAccessible;
class HTMLLinkAccessible;
class HyperTextAccessible;
class ImageAccessible;
class KeyBinding;
@ -576,6 +577,9 @@ class Accessible : public nsISupports {
bool IsHTMLListItem() const { return mType == eHTMLLiType; }
HTMLLIAccessible* AsHTMLListItem();
bool IsHTMLLink() const { return mType == eHTMLLinkType; }
HTMLLinkAccessible* AsHTMLLink();
bool IsHTMLOptGroup() const { return mType == eHTMLOptGroupType; }
bool IsHTMLTable() const { return mType == eHTMLTableType; }

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

@ -671,7 +671,7 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
return;
}
if (aAttribute == nsGkAtoms::aria_disabled ||
if (aAttribute == nsGkAtoms::aria_disabled || aAttribute == nsGkAtoms::href ||
aAttribute == nsGkAtoms::disabled || aAttribute == nsGkAtoms::tabindex ||
aAttribute == nsGkAtoms::contenteditable) {
mPrevStateBits = accessible->State();
@ -732,7 +732,7 @@ void DocAccessible::AttributeChanged(dom::Element* aElement,
// Fire accessible events iff there's an accessible, otherwise we consider
// the accessible state wasn't changed, i.e. its state is initial state.
AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
AttributeChangedImpl(accessible, aNameSpaceID, aAttribute, aModType);
// Update dependent IDs cache. Take care of accessible elements because no
// accessible element means either the element is not accessible at all or
@ -748,7 +748,7 @@ void DocAccessible::AttributeChanged(dom::Element* aElement,
// DocAccessible protected member
void DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
int32_t aNameSpaceID,
nsAtom* aAttribute) {
nsAtom* aAttribute, int32_t aModType) {
// Fire accessible event after short timer, because we need to wait for
// DOM attribute & resulting layout to actually change. Otherwise,
// assistive technology will retrieve the wrong state/value/selection info.
@ -921,6 +921,23 @@ void DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
if (aAttribute == nsGkAtoms::value) {
if (aAccessible->IsProgress())
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
return;
}
if (aModType == dom::MutationEvent_Binding::REMOVAL ||
aModType == dom::MutationEvent_Binding::ADDITION) {
if (aAttribute == nsGkAtoms::href) {
if (aAccessible->IsHTMLLink() &&
!nsCoreUtils::HasClickListener(aAccessible->GetContent())) {
RefPtr<AccEvent> linkedChangeEvent =
new AccStateChangeEvent(aAccessible, states::LINKED);
FireDelayedEvent(linkedChangeEvent);
// Fire a focusable state change event if the previous state was
// different. It may be the same if there is tabindex on this link.
aAccessible->MaybeFireFocusableStateChange(
(mPrevStateBits & states::FOCUSABLE));
}
}
}
}
@ -1789,17 +1806,6 @@ bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return true;
}
if (aAttribute == nsGkAtoms::href) {
// Not worth the expense to ensure which namespace these are in. It doesn't
// kill use to recreate the accessible even if the attribute was used in
// the wrong namespace or an element that doesn't support it.
// Make sure the accessible is recreated asynchronously to allow the content
// to handle the attribute change.
RecreateAccessible(aElement);
return true;
}
if (aAttribute == nsGkAtoms::aria_multiselectable &&
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
// This affects whether the accessible supports SelectAccessible.

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

@ -472,9 +472,10 @@ class DocAccessible : public HyperTextAccessibleWrap,
* @param aAccessible [in] accessible the DOM attribute is changed for
* @param aNameSpaceID [in] namespace of changed attribute
* @param aAttribute [in] changed attribute
* @param aModType [in] modification type (changed/added/removed)
*/
void AttributeChangedImpl(Accessible* aAccessible, int32_t aNameSpaceID,
nsAtom* aAttribute);
nsAtom* aAttribute, int32_t aModType);
/**
* Fire accessible events when ARIA attribute is changed.

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

@ -23,7 +23,9 @@ using namespace mozilla::a11y;
HTMLLinkAccessible::HTMLLinkAccessible(nsIContent* aContent,
DocAccessible* aDoc)
: HyperTextAccessibleWrap(aContent, aDoc) {}
: HyperTextAccessibleWrap(aContent, aDoc) {
mType = eHTMLLinkType;
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessible
@ -107,7 +109,7 @@ already_AddRefed<nsIURI> HTMLLinkAccessible::AnchorURIAt(
}
////////////////////////////////////////////////////////////////////////////////
// Protected members
// HTMLLinkAccessible
bool HTMLLinkAccessible::IsLinked() const {
EventStates state = mContent->AsElement()->State();

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

@ -35,17 +35,21 @@ class HTMLLinkAccessible : public HyperTextAccessibleWrap {
virtual already_AddRefed<nsIURI> AnchorURIAt(
uint32_t aAnchorIndex) const override;
protected:
virtual ~HTMLLinkAccessible() {}
enum { eAction_Jump = 0 };
/**
* Returns true if the link has href attribute.
*/
bool IsLinked() const;
protected:
virtual ~HTMLLinkAccessible() {}
enum { eAction_Jump = 0 };
};
inline HTMLLinkAccessible* Accessible::AsHTMLLink() {
return IsHTMLLink() ? static_cast<HTMLLinkAccessible*>(this) : nullptr;
}
} // namespace a11y
} // namespace mozilla

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

@ -122,6 +122,10 @@
getNode("scrollable2").setAttribute("disabled", "true");
await p;
p = waitForEvent(...focusableStateChange("link", false));
getNode("link").removeAttribute("href");
await p;
SimpleTest.finish();
}
@ -152,5 +156,7 @@
<textarea id="textarea" rows="3" cols="30"></textarea>
<a id="link" href="#">A link</a>
</body>
</html>

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

@ -117,6 +117,20 @@
await p;
}
async function testLinked() {
let p = waitForEvent(...stateChange(STATE_LINKED, false, false, "link1"));
getNode("link1").removeAttribute("href");
await p;
p = waitForEvent(...stateChange(STATE_LINKED, false, false, "link2"));
getNode("link2").removeAttribute("onclick");
await p;
p = waitForEvent(...stateChange(STATE_LINKED, false, true, "link3"));
getNode("link3").setAttribute("href", "http://example.com");
await p;
}
async function doTests() {
// Test opening details objects
await openNode("detailsOpen", "summaryOpen", true);
@ -159,6 +173,8 @@
await echoingStateChange("text1", "aria-disabled", "disabled", null,
EXT_STATE_ENABLED, true, true);
await testLinked();
SimpleTest.finish();
}
@ -245,6 +261,10 @@
<input id="text1">
<a id="link1" href="#">I am a link link</a>
<a id="link2" onclick="console.log('hi')">I am a link-ish link</a>
<a id="link3">I am a non-link link</a>
<div id="eventdump"></div>
</body>
</html>