Bug 854796: Anchor, area elements without href shouldn't have link role, r=Jamie,devtools-reviewers

Per the HTML-AAM spec, a and area elements without href attributes should have
generic roles. This revision implements this preference by creating hypertext
accessibles when said elements lack href attributes (or click listeners). A
byproduct of this change is recognizing that a elements have no intrinsic role
mapping; they could be generics or links. This revision handles situations
where href or click listeners might appear or dissapear, and recreates the
accessibles when necessary. Since image map areas are handled by their
containing image maps, this revision specializes HTMLAreaAccessible::NativeRole
to account for the discrepancy that we can't account for in the markup map.
This revision also changes the relevant WPT test expectations, updates existing
tests that this change affects, and adds tests to verify that changing href
and click listeners dynamically changes the role appropriately.

Differential Revision: https://phabricator.services.mozilla.com/D183550
This commit is contained in:
Nathan LaPre 2023-07-24 19:31:29 +00:00
parent ad3cf4e0b8
commit f8b9470054
22 changed files with 344 additions and 83 deletions

View file

@ -8,6 +8,12 @@
MARKUPMAP( MARKUPMAP(
a, a,
[](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
// An anchor element without an href attribute and without a click
// listener should be a generic.
if (!aElement->HasAttr(nsGkAtoms::href) &&
!nsCoreUtils::HasClickListener(aElement)) {
return new HyperTextAccessibleWrap(aElement, aContext->Document());
}
// Only some roles truly enjoy life as HTMLLinkAccessibles, for // Only some roles truly enjoy life as HTMLLinkAccessibles, for
// details see closed bug 494807. // details see closed bug 494807.
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement); const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aElement);
@ -18,7 +24,7 @@ MARKUPMAP(
return new HTMLLinkAccessible(aElement, aContext->Document()); return new HTMLLinkAccessible(aElement, aContext->Document());
}, },
roles::LINK) 0)
MARKUPMAP(abbr, New_HyperText, 0) MARKUPMAP(abbr, New_HyperText, 0)

View file

@ -442,17 +442,33 @@ nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges) {
content == document->DocumentNode()->GetRootElement())) { content == document->DocumentNode()->GetRootElement())) {
acc = document; acc = document;
} }
if (!acc && content->IsElement() &&
content->AsElement()->IsHTMLElement(nsGkAtoms::area)) {
// For area accessibles, we have to recreate the entire image map,
// since the image map accessible manages the tree itself. The click
// listener change may require us to update the role for the
// accessible associated with the area element.
LocalAccessible* areaAcc =
document->GetAccessibleEvenIfNotInMap(content);
if (areaAcc && areaAcc->LocalParent()) {
document->RecreateAccessible(areaAcc->LocalParent()->GetContent());
}
}
if (!acc && nsCoreUtils::HasClickListener(content)) { if (!acc && nsCoreUtils::HasClickListener(content)) {
// Create an accessible for a inaccessible element having click event // Create an accessible for a inaccessible element having click event
// handler. // handler.
document->ContentInserted(content, content->GetNextSibling()); document->ContentInserted(content, content->GetNextSibling());
} else if (acc) { } else if (acc) {
if (acc->IsHTMLLink() && !acc->AsHTMLLink()->IsLinked()) { if ((acc->IsHTMLLink() && !acc->AsHTMLLink()->IsLinked()) ||
// Notify of a LINKED state change if an HTML link gets a click (content->IsElement() &&
// listener but does not have an href attribute. content->AsElement()->IsHTMLElement(nsGkAtoms::a) &&
RefPtr<AccEvent> linkedChangeEvent = !acc->IsHTMLLink())) {
new AccStateChangeEvent(acc, states::LINKED); // An HTML link without an href attribute should have a generic
document->FireDelayedEvent(linkedChangeEvent); // role, unless it has a click listener. Since we might have gained
// or lost a click listener here, recreate the accessible so that we
// can create the correct type of accessible. If it was a link, it
// may no longer be one. If it wasn't, it may become one.
document->RecreateAccessible(content);
} }
// A click listener change might mean losing or gaining an action. // A click listener change might mean losing or gaining an action.

View file

@ -1803,6 +1803,33 @@ bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return true; return true;
} }
if (aAttribute == nsGkAtoms::href &&
!nsCoreUtils::HasClickListener(aElement)) {
// If the href is added or removed for a or area elements without click
// listeners, we need to recreate the accessible since the role might have
// changed. Without an href or click listener, the accessible must be a
// generic.
if (aElement->IsHTMLElement(nsGkAtoms::a)) {
LocalAccessible* acc = GetAccessible(aElement);
if (!acc) {
return false;
}
if (acc->IsHTMLLink() != aElement->HasAttr(nsGkAtoms::href)) {
RecreateAccessible(aElement);
return true;
}
} else if (aElement->IsHTMLElement(nsGkAtoms::area)) {
// For area accessibles, we have to recreate the entire image map, since
// the image map accessible manages the tree itself.
LocalAccessible* areaAcc = GetAccessibleEvenIfNotInMap(aElement);
if (!areaAcc || !areaAcc->LocalParent()) {
return false;
}
RecreateAccessible(areaAcc->LocalParent()->GetContent());
return true;
}
}
if (aElement->IsHTMLElement(nsGkAtoms::img) && aAttribute == nsGkAtoms::alt) { if (aElement->IsHTMLElement(nsGkAtoms::img) && aAttribute == nsGkAtoms::alt) {
// If alt text changes on an img element, we may want to create or remove an // If alt text changes on an img element, we may want to create or remove an
// accessible for that img. // accessible for that img.

View file

@ -9,6 +9,7 @@
#include "EventTree.h" #include "EventTree.h"
#include "Role.h" #include "Role.h"
#include "nsCoreUtils.h"
#include "nsIFrame.h" #include "nsIFrame.h"
#include "nsImageFrame.h" #include "nsImageFrame.h"
#include "nsImageMap.h" #include "nsImageMap.h"
@ -105,6 +106,19 @@ HTMLAreaAccessible::HTMLAreaAccessible(nsIContent* aContent,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// HTMLAreaAccessible: LocalAccessible // HTMLAreaAccessible: LocalAccessible
role HTMLAreaAccessible::NativeRole() const {
// A link element without an href attribute and without a click listener
// should be reported as a generic.
if (mContent->IsElement()) {
dom::Element* element = mContent->AsElement();
if (!element->HasAttr(nsGkAtoms::href) &&
!nsCoreUtils::HasClickListener(element)) {
return roles::TEXT;
}
}
return HTMLLinkAccessible::NativeRole();
}
ENameValueFlag HTMLAreaAccessible::NativeName(nsString& aName) const { ENameValueFlag HTMLAreaAccessible::NativeName(nsString& aName) const {
ENameValueFlag nameFlag = LocalAccessible::NativeName(aName); ENameValueFlag nameFlag = LocalAccessible::NativeName(aName);
if (!aName.IsEmpty()) return nameFlag; if (!aName.IsEmpty()) return nameFlag;

View file

@ -61,6 +61,9 @@ class HTMLAreaAccessible final : public HTMLLinkAccessible {
return false; return false;
} }
// LocalAccessible
virtual role NativeRole() const override;
protected: protected:
// LocalAccessible // LocalAccessible
virtual ENameValueFlag NativeName(nsString& aName) const override; virtual ENameValueFlag NativeName(nsString& aName) const override;

View file

@ -92,6 +92,10 @@ addAccessibleTask(
src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> src="http://example.com/a11y/accessible/tests/mochitest/moz.png">
</a> </a>
<a id="link4" onmousedown=""></a>
<a id="link5" onclick=""></a>
<a id="link6" onmouseup=""></a>
<div> <div>
<label for="TextBox_t2" id="label1"> <label for="TextBox_t2" id="label1">
<span>Explicit</span> <span>Explicit</span>
@ -117,6 +121,9 @@ addAccessibleTask(
await _testActions("link2", ["click"], gClickEvents); await _testActions("link2", ["click"], gClickEvents);
await _testActions("link3", ["jump"], gClickEvents); await _testActions("link3", ["jump"], gClickEvents);
await _testActions("link3img", ["click ancestor"], gClickEvents); await _testActions("link3img", ["click ancestor"], gClickEvents);
await _testActions("link4", ["click"], gClickEvents);
await _testActions("link5", ["click"], gClickEvents);
await _testActions("link6", ["click"], gClickEvents);
await _testActions("label1", ["click"], gClickEvents); await _testActions("label1", ["click"], gClickEvents);
await _testActions("p_in_clickable_div", ["click ancestor"], gClickEvents); await _testActions("p_in_clickable_div", ["click ancestor"], gClickEvents);
@ -171,16 +178,24 @@ addAccessibleTask(
await _testActions("onclick_img", ["showlongdesc"]); await _testActions("onclick_img", ["showlongdesc"]);
// Remove 'href' from link and test linkable child // Remove 'href' from link and test linkable child
const link1Acc = findAccessibleChildByID(docAcc, "link1"); let link1Acc = findAccessibleChildByID(docAcc, "link1");
is( is(
link1Acc.firstChild.getActionName(0), link1Acc.firstChild.getActionName(0),
"click ancestor", "click ancestor",
"linkable child has click ancestor action" "linkable child has click ancestor action"
); );
let onRecreation = waitForEvents({
expected: [
[EVENT_HIDE, link1Acc],
[EVENT_SHOW, "link1"],
],
});
await invokeContentTask(browser, [], () => { await invokeContentTask(browser, [], () => {
let link1 = content.document.getElementById("link1"); let link1 = content.document.getElementById("link1");
link1.removeAttribute("href"); link1.removeAttribute("href");
}); });
await onRecreation;
link1Acc = findAccessibleChildByID(docAcc, "link1");
await untilCacheIs(() => link1Acc.actionCount, 0, "link has no actions"); await untilCacheIs(() => link1Acc.actionCount, 0, "link has no actions");
is(link1Acc.firstChild.actionCount, 0, "linkable child's actions removed"); is(link1Acc.firstChild.actionCount, 0, "linkable child's actions removed");

View file

@ -338,6 +338,7 @@ const markupTests = [
<span id="l1">test2</span> <span id="l1">test2</span>
<span id="l2">test3</span> <span id="l2">test3</span>
<a id="a" <a id="a"
href=""
aria-label="test1" aria-label="test1"
aria-labelledby="l1 l2" aria-labelledby="l1 l2"
title="test4">test5</a>`, title="test4">test5</a>`,
@ -350,6 +351,7 @@ const markupTests = [
<span id="l1">test2</span> <span id="l1">test2</span>
<span id="l2">test3</span> <span id="l2">test3</span>
<a id="a-img" <a id="a-img"
href=""
aria-label="test1" aria-label="test1"
aria-labelledby="l1 l2" aria-labelledby="l1 l2"
title="test4"><img alt="test5"/></a>`, title="test4"><img alt="test5"/></a>`,

View file

@ -280,7 +280,7 @@ addAccessibleTask(
addAccessibleTask( addAccessibleTask(
`<a id="link" href="https://example.com/">Test</a>`, `<a id="link" href="https://example.com/">Test</a>`,
async function (browser, docAcc) { async function (browser, docAcc) {
const link = findAccessibleChildByID(docAcc, "link"); let link = findAccessibleChildByID(docAcc, "link");
is(link.value, "https://example.com/", "link initial value correct"); is(link.value, "https://example.com/", "link initial value correct");
const textLeaf = link.firstChild; const textLeaf = link.firstChild;
is(textLeaf.value, "https://example.com/", "link initial value correct"); is(textLeaf.value, "https://example.com/", "link initial value correct");
@ -294,11 +294,27 @@ addAccessibleTask(
); );
info("Removing link href"); info("Removing link href");
let onRecreation = waitForEvents({
expected: [
[EVENT_HIDE, link],
[EVENT_SHOW, "link"],
],
});
await invokeSetAttribute(browser, "link", "href"); await invokeSetAttribute(browser, "link", "href");
await onRecreation;
link = findAccessibleChildByID(docAcc, "link");
await untilCacheIs(() => link.value, "", "link value empty after removal"); await untilCacheIs(() => link.value, "", "link value empty after removal");
info("Setting link href"); info("Setting link href");
onRecreation = waitForEvents({
expected: [
[EVENT_HIDE, link],
[EVENT_SHOW, "link"],
],
});
await invokeSetAttribute(browser, "link", "href", "https://example.com/"); await invokeSetAttribute(browser, "link", "href", "https://example.com/");
await onRecreation;
link = findAccessibleChildByID(docAcc, "link");
await untilCacheIs( await untilCacheIs(
() => link.value, () => link.value,
"https://example.com/", "https://example.com/",

View file

@ -5,6 +5,6 @@
</head> </head>
<body id="body"> <body id="body">
<div id="container1"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img id="img1" src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> </div> <div id="container1"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img id="img1" src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> </div>
<div id="container2-parent"> <a id="container2"></a> <a><img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"></a> </div> <div id="container2-parent"> <a id="container2" href=""></a> <a href=""><img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"></a> </div>
</body> </body>
</html> </html>

View file

@ -39,18 +39,6 @@ addAccessibleTask(
} }
); );
function waitForLinkedChange(id, isEnabled) {
return waitForEvent(EVENT_STATE_CHANGE, e => {
e.QueryInterface(nsIAccessibleStateChangeEvent);
return (
e.state == STATE_LINKED &&
!e.isExtraState &&
isEnabled == e.isEnabled &&
id == getAccessibleDOMNodeID(e.accessible)
);
});
}
/** /**
* Test linked vs unlinked anchor tags * Test linked vs unlinked anchor tags
*/ */
@ -92,36 +80,39 @@ addAccessibleTask(
"bare <a> gets correct group role" "bare <a> gets correct group role"
); );
let stateChanged = waitForLinkedChange("link1", false); let onRecreation = waitForEvent(EVENT_SHOW, "link1");
await SpecialPowers.spawn(browser, [], () => { await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById("link1").removeAttribute("href"); content.document.getElementById("link1").removeAttribute("href");
}); });
await stateChanged; await onRecreation;
link1 = getNativeInterface(accDoc, "link1");
is( is(
link1.getAttributeValue("AXRole"), link1.getAttributeValue("AXRole"),
"AXGroup", "AXGroup",
"<a> stripped from href gets group role" "<a> stripped from href gets group role"
); );
stateChanged = waitForLinkedChange("link2", false); onRecreation = waitForEvent(EVENT_SHOW, "link2");
await SpecialPowers.spawn(browser, [], () => { await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById("link2").removeAttribute("onclick"); content.document.getElementById("link2").removeAttribute("onclick");
}); });
await stateChanged; await onRecreation;
link2 = getNativeInterface(accDoc, "link2");
is( is(
link2.getAttributeValue("AXRole"), link2.getAttributeValue("AXRole"),
"AXGroup", "AXGroup",
"<a> stripped from onclick gets group role" "<a> stripped from onclick gets group role"
); );
stateChanged = waitForLinkedChange("link3", true); onRecreation = waitForEvent(EVENT_SHOW, "link3");
await SpecialPowers.spawn(browser, [], () => { await SpecialPowers.spawn(browser, [], () => {
content.document content.document
.getElementById("link3") .getElementById("link3")
// eslint-disable-next-line @microsoft/sdl/no-insecure-url // eslint-disable-next-line @microsoft/sdl/no-insecure-url
.setAttribute("href", "http://example.com"); .setAttribute("href", "http://example.com");
}); });
await stateChanged; await onRecreation;
link3 = getNativeInterface(accDoc, "link3");
is( is(
link3.getAttributeValue("AXRole"), link3.getAttributeValue("AXRole"),
"AXLink", "AXLink",
@ -212,7 +203,7 @@ addAccessibleTask(
link5 link5
.getAttributeValue("AXLinkedUIElements")[0] .getAttributeValue("AXLinkedUIElements")[0]
.getAttributeValue("AXTitle"), .getAttributeValue("AXTitle"),
"I have a name", "",
"Link 5 is linked to a named element" "Link 5 is linked to a named element"
); );
is( is(

View file

@ -14,6 +14,7 @@ skip-if = true || (verify && !debug && (os == 'linux')) #Bug 1445513
[browser_css_content_visibility.js] [browser_css_content_visibility.js]
[browser_general.js] [browser_general.js]
[browser_lazy_tabs.js] [browser_lazy_tabs.js]
[browser_link.js]
[browser_searchbar.js] [browser_searchbar.js]
[browser_shadowdom.js] [browser_shadowdom.js]
[browser_test_nsIAccessibleDocument_URL.js] [browser_test_nsIAccessibleDocument_URL.js]

View file

@ -0,0 +1,208 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
/**
* Verify that an anchor element reports a generic role without an href
* attribute and reports a LINK role with it present. Verify that these roles
* change as the attribute appears and disappears.
*/
addAccessibleTask(
`
<a id="link">test</a>
`,
async function (browser, accDoc) {
let link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_TEXT, "Checking role of anchor element without href");
let onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, link],
[EVENT_SHOW, "link"],
],
});
info("Adding an href to the anchor element");
await invokeContentTask(browser, [], () => {
content.document.getElementById("link").setAttribute("href", "#");
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_LINK, "Checking role of anchor element with href");
onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, link],
[EVENT_SHOW, "link"],
],
});
info("Removing the href from the anchor element");
await invokeContentTask(browser, [], () => {
content.document.getElementById("link").removeAttribute("href");
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_TEXT, "Checking role of anchor element without href");
},
{
chrome: true,
topLevel: true,
iframe: true,
remoteIframe: true,
}
);
/**
* Verify that an anchor element reports a generic role without a click listener
* and reports a LINK role with it present. Verify that these roles change as
* the click listener appears.
*/
addAccessibleTask(
`
<a id="link">test</a>
`,
async function (browser, accDoc) {
let link = findAccessibleChildByID(accDoc, "link");
is(
link.role,
ROLE_TEXT,
"Checking role of anchor element without click listener"
);
let onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, link],
[EVENT_SHOW, "link"],
],
});
info("Adding a click listener to the anchor element");
await invokeContentTask(browser, [], () => {
content.document
.getElementById("link")
.addEventListener("click", () => {});
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(
link.role,
ROLE_LINK,
"Checking role of anchor element with click listener"
);
},
{
chrome: true,
topLevel: true,
iframe: true,
remoteIframe: true,
}
);
/**
* Verify that an area element reports a generic role without an href
* attribute and reports a LINK role with it present. Verify that these roles
* change as the attribute appears and disappears.
*/
addAccessibleTask(
`
<map name="map">
<area id="link">
</map>
<img id="img" usemap="#map" src="http://example.com/a11y/accessible/tests/mochitest/letters.gif">
`,
async function (browser, accDoc) {
let link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_TEXT, "Checking role of area element without href");
let img = findAccessibleChildByID(accDoc, "img");
let onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, img],
[EVENT_SHOW, "img"],
],
});
info("Adding an href to the area element");
await invokeContentTask(browser, [], () => {
content.document.getElementById("link").setAttribute("href", "#");
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_LINK, "Checking role of area element with href");
img = findAccessibleChildByID(accDoc, "img");
onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, img],
[EVENT_SHOW, "img"],
],
});
info("Removing the href from the area element");
await invokeContentTask(browser, [], () => {
content.document.getElementById("link").removeAttribute("href");
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(link.role, ROLE_TEXT, "Checking role of area element without href");
},
{
chrome: true,
topLevel: true,
iframe: true,
remoteIframe: true,
}
);
/**
* Verify that an area element reports a generic role without a click listener
* and reports a LINK role with it present. Verify that these roles change as
* the click listener appears.
*/
addAccessibleTask(
`
<map name="map">
<area id="link">
</map>
<img id="img" usemap="#map" src="http://example.com/a11y/accessible/tests/mochitest/letters.gif">
`,
async function (browser, accDoc) {
let link = findAccessibleChildByID(accDoc, "link");
is(
link.role,
ROLE_TEXT,
"Checking role of area element without click listener"
);
let img = findAccessibleChildByID(accDoc, "img");
let onHideAndShow = waitForEvents({
expected: [
[EVENT_HIDE, img],
[EVENT_SHOW, "img"],
],
});
info("Adding a click listener to the area element");
await invokeContentTask(browser, [], () => {
content.document
.getElementById("link")
.addEventListener("click", () => {});
});
await onHideAndShow;
link = findAccessibleChildByID(accDoc, "link");
is(
link.role,
ROLE_LINK,
"Checking role of area element with click listener"
);
},
{
chrome: true,
topLevel: true,
iframe: true,
remoteIframe: true,
}
);

View file

@ -74,7 +74,7 @@
}, },
{ {
ID: "link2", ID: "link2",
actionName: "click", actionName: "jump",
events: CLICK_EVENTS, events: CLICK_EVENTS,
}, },
{ {
@ -86,7 +86,7 @@
}, },
{ {
ID: "link3", ID: "link3",
actionName: "click", actionName: "jump",
events: CLICK_EVENTS, events: CLICK_EVENTS,
}, },
{ {
@ -98,7 +98,7 @@
}, },
{ {
ID: "link4", ID: "link4",
actionName: "click", actionName: "jump",
events: CLICK_EVENTS, events: CLICK_EVENTS,
}, },
{ {
@ -132,13 +132,13 @@
<a href="about:mozilla" id="link1" target="_blank" rel="opener"> <a href="about:mozilla" id="link1" target="_blank" rel="opener">
<img src="../moz.png" id="img1"> <img src="../moz.png" id="img1">
</a> </a>
<a id="link2" onmousedown=""> <a id="link2" href="" onmousedown="">
<img src="../moz.png" id="img2"> <img src="../moz.png" id="img2">
</a> </a>
<a id="link3" onclick=""> <a id="link3" href="" onclick="">
<img src="../moz.png" id="img3"> <img src="../moz.png" id="img3">
</a> </a>
<a id="link4" onmouseup=""> <a id="link4" href="" onmouseup="">
<img src="../moz.png" id="img4"> <img src="../moz.png" id="img4">
</a> </a>
</body> </body>

View file

@ -75,10 +75,6 @@
getNode("div").removeAttribute("tabindex"); getNode("div").removeAttribute("tabindex");
await p; await p;
p = waitForEvent(...focusableStateChange("link", false));
getNode("link").removeAttribute("href");
await p;
info("add contenteditable"); info("add contenteditable");
// Expect editable change on non-input, // Expect editable change on non-input,
// and don't expect event on a native input. // and don't expect event on a native input.
@ -121,8 +117,6 @@
<div id="div">Hello</div> <div id="div">Hello</div>
<a id="link" href="#">A link</a>
<input id="input" value="Hello"> <input id="input" value="Hello">
</body> </body>
</html> </html>

View file

@ -138,21 +138,6 @@
await p; await p;
} }
async function testLinked() {
let p = waitForStateChange("link1", STATE_LINKED, false, false);
getNode("link1").removeAttribute("href");
await p;
p = waitForStateChange("link2", STATE_LINKED, false, false);
getNode("link2").removeAttribute("onclick");
await p;
p = waitForStateChange("link3", STATE_LINKED, true, false);
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
getNode("link3").setAttribute("href", "http://example.com");
await p;
}
async function testHasPopup() { async function testHasPopup() {
let p = waitForStateChange("popupButton", STATE_HASPOPUP, true, false); let p = waitForStateChange("popupButton", STATE_HASPOPUP, true, false);
getNode("popupButton").setAttribute("aria-haspopup", "true"); getNode("popupButton").setAttribute("aria-haspopup", "true");
@ -433,8 +418,6 @@
await testReadonlyUntilEditable(); await testReadonlyUntilEditable();
await testLinked();
await testHasPopup(); await testHasPopup();
await toggleStateChange("textbox", "aria-multiline", EXT_STATE_MULTI_LINE, true); await toggleStateChange("textbox", "aria-multiline", EXT_STATE_MULTI_LINE, true);
@ -537,10 +520,6 @@
<input id="text1"> <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="textbox" role="textbox" aria-multiline="false">hello</div> <div id="textbox" role="textbox" aria-multiline="false">hello</div>
<form id="form"> <form id="form">

View file

@ -142,18 +142,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368
// Named anchor, should never have state_linked // Named anchor, should never have state_linked
var namedAnchorAcc = getAccessible("namedAnchor", var namedAnchorAcc = getAccessible("namedAnchor",
[nsIAccessibleHyperLink]); [nsIAccessibleHyperLink]);
testThis("namedAnchor", namedAnchorAcc, ROLE_LINK, 1, testThis("namedAnchor", namedAnchorAcc, ROLE_TEXT, 1,
"This should never be of state_linked", true, 196, 197); null, true, 196, 197);
testStates(namedAnchorAcc, STATE_SELECTABLE, testStates(namedAnchorAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED));
0, (STATE_FOCUSABLE | STATE_LINKED));
testAction("namedAnchor", namedAnchorAcc, ""); testAction("namedAnchor", namedAnchorAcc, "");
// //////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////
// No link (hasn't any attribute), should never have state_linked // No link (hasn't any attribute), should never have state_linked
var noLinkAcc = getAccessible("noLink", var noLinkAcc = getAccessible("noLink",
[nsIAccessibleHyperLink]); [nsIAccessibleHyperLink]);
testThis("noLink", noLinkAcc, ROLE_LINK, 1, testThis("noLink", noLinkAcc, ROLE_TEXT, 1,
"This should never be of state_linked", true, 254, 255); null, true, 254, 255);
testStates(noLinkAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED)); testStates(noLinkAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED));
testAction("noLink", noLinkAcc, ""); testAction("noLink", noLinkAcc, "");

View file

@ -66,7 +66,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428248
testThis("LinkWithSpan", 116, 5, "Heise Online"); testThis("LinkWithSpan", 116, 5, "Heise Online");
// Named anchor // Named anchor
testThis("namedAnchor", 193, 6, "This should never be of state_linked"); testThis("namedAnchor", 193, 6, null);
// Paragraph with link // Paragraph with link
var p2 = getAccessible("p2", [nsIAccessibleHyperText]); var p2 = getAccessible("p2", [nsIAccessibleHyperText]);

View file

@ -214,7 +214,6 @@
// strange cases // strange cases
testStates("aria_link_link", STATE_LINKED); testStates("aria_link_link", STATE_LINKED);
testStates("aria_link_anchor", STATE_SELECTABLE);
// some landmarks that break accessibility for these native elements // some landmarks that break accessibility for these native elements
// Note that these are illegal uses by web authors as per WAI-ARIA in HTML // Note that these are illegal uses by web authors as per WAI-ARIA in HTML
@ -553,7 +552,6 @@
<!-- strange edge case: please don't do this in the wild --> <!-- strange edge case: please don't do this in the wild -->
<a id="aria_link_link" role="link" href="foo">link</a> <a id="aria_link_link" role="link" href="foo">link</a>
<a id="aria_link_anchor" role="link" name="link_anchor">link</a>
<!-- landmarks: links --> <!-- landmarks: links -->
<a id="aria_application_link" role="application" href="foo">app</a> <a id="aria_application_link" role="application" href="foo">app</a>

View file

@ -187,7 +187,7 @@
on the image cache state and what optimizations layout was able to on the image cache state and what optimizations layout was able to
apply. --> apply. -->
<div id="container1"><img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"></div> <div id="container1"><img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"></div>
<div><a id="container2"></a> <a><img src="../moz.png"></a></div> <div><a id="container2" href=""></a> <a href=""><img src="../moz.png"></a></div>
<div id="container3"> <div id="container3">
<div id="c3_inner" role="presentation"> <div id="c3_inner" role="presentation">

View file

@ -16,7 +16,6 @@ const {
[KEYBOARD]: { [KEYBOARD]: {
FOCUSABLE_NO_SEMANTICS, FOCUSABLE_NO_SEMANTICS,
FOCUSABLE_POSITIVE_TABINDEX, FOCUSABLE_POSITIVE_TABINDEX,
INTERACTIVE_NO_ACTION,
INTERACTIVE_NOT_FOCUSABLE, INTERACTIVE_NOT_FOCUSABLE,
MOUSE_INTERACTIVE_ONLY, MOUSE_INTERACTIVE_ONLY,
NO_FOCUS_VISIBLE, NO_FOCUS_VISIBLE,
@ -61,10 +60,7 @@ add_task(async function () {
[ [
"Interactive accesible (link with no attributes) with no accessible actions.", "Interactive accesible (link with no attributes) with no accessible actions.",
"#link-1", "#link-1",
{ null,
score: FAIL,
issue: INTERACTIVE_NO_ACTION,
},
], ],
["Interactive accessible (link with valid href).", "#link-2", null], ["Interactive accessible (link with valid href).", "#link-2", null],
["Interactive accessible (link with # as href).", "#link-3", null], ["Interactive accessible (link with # as href).", "#link-3", null],

View file

@ -483,11 +483,7 @@ add_task(async function () {
], ],
["<embed> with video data type and aria-label", "#embed-3", null], ["<embed> with video data type and aria-label", "#embed-3", null],
["<embed> with video data type and aria-labelledby", "#embed-4", null], ["<embed> with video data type and aria-labelledby", "#embed-4", null],
[ ["Link with no inner content", "#link-1", null],
"Link with no inner content",
"#link-1",
{ score: FAIL, issue: INTERACTIVE_NO_NAME },
],
["Link with inner content", "#link-2", null], ["Link with inner content", "#link-2", null],
[ [
"Link with href and no inner content", "Link with href and no inner content",

View file

@ -15,7 +15,7 @@
expected: FAIL expected: FAIL
[el-a-no-href] [el-a-no-href]
expected: FAIL expected: PASS
[el-search] [el-search]
expected: FAIL expected: FAIL