Bug 1151648: Construct accessible objects for SVG elements which have title or desc. r=MarcoZ,yzen

According to SVG Accessibility API Mappings, an SVG element which has title or desc element must be exposed.
https://w3c.github.io/svg-aam/#include_elements
Previously, we didn't expose <g> elements at all even if they had a title/desc, and we unconditionally exposed some other SVG elements even when they didn't.

This removes the Dev Tools A11y Panel code which explicitly allowed unlabelled descendants of role="img" <svg> elements, since we don't create descendants if they don't have a label now anyway.
The associated tests had to be tweaked as well, since now we don't create unlabelled descendants.

Original patch by Takeshi Kurosawa.

Differential Revision: https://phabricator.services.mozilla.com/D77763
This commit is contained in:
James Teh 2020-06-03 13:18:38 +00:00
Родитель 7c97f420c8
Коммит c45ca37cf4
6 изменённых файлов: 155 добавлений и 82 удалений

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

@ -147,6 +147,20 @@ static bool MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument) {
return false;
}
/**
* Return true if the SVG element should be accessible
*/
static bool MustSVGElementBeAccessible(nsIContent* aContent) {
// https://w3c.github.io/svg-aam/#include_elements
for (nsIContent* childElm = aContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (childElm->IsAnyOfSVGElements(nsGkAtoms::title, nsGkAtoms::desc)) {
return true;
}
}
return false;
}
/**
* Used by XULMap.h to map both menupopup and popup elements
*/
@ -1140,7 +1154,7 @@ Accessible* nsAccessibilityService::CreateAccessible(nsINode* aNode,
if (!newAcc) {
if (content->IsSVGElement()) {
SVGGeometryFrame* geometryFrame = do_QueryFrame(frame);
if (geometryFrame) {
if (geometryFrame && MustSVGElementBeAccessible(content)) {
// A graphic elements: rect, circle, ellipse, line, path, polygon,
// polyline and image. A 'use' and 'text' graphic elements require
// special support.
@ -1149,6 +1163,9 @@ Accessible* nsAccessibilityService::CreateAccessible(nsINode* aNode,
newAcc = new HyperTextAccessibleWrap(content->AsElement(), document);
} else if (content->IsSVGElement(nsGkAtoms::svg)) {
newAcc = new EnumRoleAccessible<roles::DIAGRAM>(content, document);
} else if (content->IsSVGElement(nsGkAtoms::g) &&
MustSVGElementBeAccessible(content)) {
newAcc = new EnumRoleAccessible<roles::GROUPING>(content, document);
}
} else if (content->IsMathMLElement()) {

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

@ -16,6 +16,7 @@
<script type="application/javascript">
function doTests() {
testRole("svg", ROLE_DIAGRAM);
testRole("g", ROLE_GROUPING);
testRole("rect", ROLE_GRAPHIC);
testRole("circle", ROLE_GRAPHIC);
testRole("ellipse", ROLE_GRAPHIC);
@ -47,21 +48,40 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="g">
<title>g</title>
</g>
<rect width="300" height="100" id="rect"
style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/>
style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)">
<title>rect</title>
</rect>
<circle cx="100" cy="50" r="40" stroke="black" id="circle"
stroke-width="2" fill="red"/>
stroke-width="2" fill="red">
<title>circle</title>
</circle>
<ellipse cx="300" cy="80" rx="100" ry="50" id="ellipse"
style="fill:yellow;stroke:purple;stroke-width:2"/>
style="fill:yellow;stroke:purple;stroke-width:2">
<title>ellipse</title>
</ellipse>
<line x1="0" y1="0" x2="200" y2="200" id="line"
style="stroke:rgb(255,0,0);stroke-width:2"/>
style="stroke:rgb(255,0,0);stroke-width:2">
<title>line</title>
</line>
<polygon points="200,10 250,190 160,210" id="polygon"
style="fill:lime;stroke:purple;stroke-width:1"/>
style="fill:lime;stroke:purple;stroke-width:1">
<title>polygon</title>
</polygon>
<polyline points="20,20 40,25 60,40 80,120 120,140 200,180" id="polyline"
style="fill:none;stroke:black;stroke-width:3" />
<path d="M150 0 L75 200 L225 200 Z" id="path"/>
style="fill:none;stroke:black;stroke-width:3" >
<title>polyline</title>
</polyline>
<path d="M150 0 L75 200 L225 200 Z" id="path">
<title>path</title>
</path>
<image x1="25" y1="80" width="50" height="20" id="image"
xlink:href="../moz.png"/>
xlink:href="../moz.png">
<title>image</title>
</image>
</svg>
</body>

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

@ -13,6 +13,7 @@
<script type="application/javascript">
function doTest() {
// svgText
var accTree = {
role: ROLE_DIAGRAM,
children: [
@ -26,7 +27,55 @@
},
],
};
testAccessibleTree("svgItem", accTree);
testAccessibleTree("svgText", accTree);
// svg1
accTree = {
role: ROLE_DIAGRAM,
children: []
};
testAccessibleTree("svg1", accTree);
// svg2
accTree = {
role: ROLE_DIAGRAM,
children: [
{
role: ROLE_GROUPING,
children: []
}
]
};
testAccessibleTree("svg2", accTree);
// svg3
accTree = {
role: ROLE_DIAGRAM,
children: [
{
role: ROLE_GRAPHIC,
children: []
}
]
};
testAccessibleTree("svg3", accTree);
// svg4
accTree = {
role: ROLE_DIAGRAM,
children: [
{
role: ROLE_GROUPING,
children: [
{
role: ROLE_GRAPHIC,
children: []
}
]
}
]
};
testAccessibleTree("svg4", accTree);
SimpleTest.finish();
}
@ -40,8 +89,39 @@
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<svg id="svgItem">
<svg id="svgText">
<text>This is some text</text>
</svg>
<!-- no accessible objects -->
<svg id="svg1">
<g id="g1">
<rect width="300" height="100" id="rect1" style="fill:#00f" />
</g>
</svg>
<svg id="svg2">
<g id="g2">
<title>g</title>
<rect width="300" height="100" id="rect2" style="fill:#00f" />
</g>
</svg>
<svg id="svg3">
<g id="g3">
<rect width="300" height="100" id="rect3" style="fill:#00f">
<title>rect</title>
</rect>
</g>
</svg>
<svg id="svg4">
<g id="g4">
<title>g</title>
<rect width="300" height="100" id="rect4" style="fill:#00f">
<title>rect</title>
</rect>
</g>
</svg>
</body>
</html>

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

@ -6,13 +6,6 @@
const { Ci } = require("chrome");
loader.lazyRequireGetter(
this,
"getAriaRoles",
"devtools/server/actors/utils/accessibility",
true
);
const {
accessibility: {
AUDIT_TYPE: { TEXT_LABEL },
@ -145,23 +138,6 @@ const dialogRule = shouldHaveNonEmptyNameRule.bind(null, DIALOG_NO_NAME);
*/
const imageRule = function(accessible) {
const name = getAccessibleName(accessible);
const { DOMNode } = accessible;
if (
DOMNode instanceof DOMNode.ownerGlobal.SVGElement &&
DOMNode.ownerSVGElement
) {
let ownerSVGAccessible = accessible.parent;
while (ownerSVGAccessible.DOMNode.ownerSVGElement) {
ownerSVGAccessible = ownerSVGAccessible.parent;
}
const ariaRoles = getAriaRoles(ownerSVGAccessible);
if (ariaRoles && ariaRoles.includes("img")) {
// Do not require a defined name if a wrapping SVG has a role="img".
return null;
}
}
return name != null ? null : { score: FAIL, issue: IMAGE_NO_NAME };
};

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

@ -1065,63 +1065,43 @@ add_task(async function() {
],
["Non-unique aria toolbar with aria-labelledby", "#toolbar-4", null],
["SVGElement with role=img that has a title", "#svg-1", null],
[
"SVGElement with no name and with ownerSVGElement with role=img that has a title",
"#svg-2",
null,
],
["SVGElement without role=img that has a title", "#svg-3", null],
[
"SVGElement with no name and with ownerSVGElement without role=img",
"#svg-4",
{ score: FAIL, issue: IMAGE_NO_NAME },
],
["SVGElement without role=img that has a title", "#svg-2", null],
[
"SVGElement with role=img and no name",
"#svg-5",
"#svg-3",
{ score: FAIL, issue: IMAGE_NO_NAME },
],
[
"SVGElement with no name and with ownerSVGElement with role=img",
"#svg-6",
null,
],
[
"SVGElement with no name",
"#svg-7",
"#svg-4",
{ score: FAIL, issue: IMAGE_NO_NAME },
],
[
"SVGElement with no name and with ownerSVGElement with no name",
"#svg-8",
{ score: FAIL, issue: IMAGE_NO_NAME },
],
["SVGElement with a name", "#svg-9", null],
["SVGElement with a name", "#svg-5", null],
[
"SVGElement with a name and with ownerSVGElement with a name",
"#svg-10",
"#svg-6",
null,
],
["SVGElement with a title", "#svg-11", null],
["SVGElement with a title", "#svg-7", null],
[
"SVGElement with a name and with ownerSVGElement with a title",
"#svg-12",
"#svg-8",
null,
],
["SVGElement with role=img that has a title", "#svg-13", null],
["SVGElement with role=img that has a title", "#svg-9", null],
[
"SVGElement with a name and with ownerSVGElement with role=img that has a title",
"#svg-14",
"#svg-10",
null,
],
[
"SVGElement with role=img and no title",
"#svg-15",
"#svg-11",
{ score: FAIL, issue: IMAGE_NO_NAME },
],
[
"SVGElement with a name and with ownerSVGElement with role=img and no title",
"#svg-16",
"#svg-12",
null,
],
];

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

@ -435,31 +435,31 @@
<span id="toolbar-4" role="toolbar" aria-labelledby="toolbar-4-label"></span>
<svg id="svg-1" role="img" viewbox="0 0 100 10" height="10px">
<title id="siteLogoTitle">Site Logo</title>
<rect id="svg-2" x="0" y="00" width="100" height="10" fill="red"></rect>
<rect x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-3" viewbox="0 0 100 10" height="10px">
<svg id="svg-2" viewbox="0 0 100 10" height="10px">
<title id="siteLogoTitle">Site Logo</title>
<rect id="svg-4" x="0" y="00" width="100" height="10" fill="red"></rect>
<rect x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-5" role="img" viewbox="0 0 100 10" height="10px">
<rect id="svg-6" x="0" y="00" width="100" height="10" fill="red"></rect>
<svg id="svg-3" role="img" viewbox="0 0 100 10" height="10px">
<rect x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-4" viewbox="0 0 100 10" height="10px">
<rect x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-5" aria-label="foo" viewbox="0 0 100 10" height="10px">
<rect id="svg-6" aria-label="bar" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-7" viewbox="0 0 100 10" height="10px">
<rect id="svg-8" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-9" aria-label="foo" viewbox="0 0 100 10" height="10px">
<rect id="svg-10" aria-label="bar" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-11" viewbox="0 0 100 10" height="10px">
<title id="siteLogoTitle">Site Logo</title>
<rect id="svg-12" aria-label="foo" x="0" y="00" width="100" height="10" fill="red"></rect>
<rect id="svg-8" aria-label="foo" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-13" role="img" viewbox="0 0 100 10" height="10px">
<svg id="svg-9" role="img" viewbox="0 0 100 10" height="10px">
<title id="siteLogoTitle">Site Logo</title>
<rect aria-label="foo" id="svg-14" x="0" y="00" width="100" height="10" fill="red"></rect>
<rect aria-label="foo" id="svg-10" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
<svg id="svg-15" role="img" viewbox="0 0 100 10" height="10px">
<rect aria-label="foo" id="svg-16" x="0" y="00" width="100" height="10" fill="red"></rect>
<svg id="svg-11" role="img" viewbox="0 0 100 10" height="10px">
<rect aria-label="foo" id="svg-12" x="0" y="00" width="100" height="10" fill="red"></rect>
</svg>
</body>
</html>