зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1571616 - Prune or reinsert accessibles in non-accessible container. r=Jamie
We need to visit the descendants of a container that has no accessible, but has accessible children. Also added a test for delayed removal where we can put all these nasty cases. Differential Revision: https://phabricator.services.mozilla.com/D41059 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
27fee7c535
Коммит
09554268a2
|
@ -1308,6 +1308,7 @@ void DocAccessible::ContentInserted(nsIContent* aStartChildNode,
|
|||
}
|
||||
|
||||
bool DocAccessible::PruneOrInsertSubtree(nsIContent* aRoot) {
|
||||
bool insert = false;
|
||||
// If we already have an accessible, check if we need to remove it, recreate
|
||||
// it, or keep it in place.
|
||||
Accessible* acc = GetAccessible(aRoot);
|
||||
|
@ -1349,7 +1350,19 @@ bool DocAccessible::PruneOrInsertSubtree(nsIContent* aRoot) {
|
|||
// If there is no current accessible, and the node has a frame, or is
|
||||
// display:contents, schedule it for insertion.
|
||||
if (aRoot->GetPrimaryFrame() || nsCoreUtils::IsDisplayContents(aRoot)) {
|
||||
return true;
|
||||
// This may be a new subtree, the insertion process will recurse through
|
||||
// its descendants.
|
||||
if (!GetAccessibleOrDescendant(aRoot)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Content is not an accessible, but has accessible descendants.
|
||||
// We schedule this container for insertion strictly for the case where it
|
||||
// itself now needs an accessible. We will still need to recurse into the
|
||||
// descendant content to prune accessibles, and in all likelyness to
|
||||
// insert accessibles since accessible insertions will likeley get missed
|
||||
// in an existing subtree.
|
||||
insert = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1368,11 +1381,7 @@ bool DocAccessible::PruneOrInsertSubtree(nsIContent* aRoot) {
|
|||
}
|
||||
}
|
||||
|
||||
// If we get here we either already have an accessible we don't want to touch,
|
||||
// or the content does not have a frame and is not display:contents.
|
||||
MOZ_ASSERT(acc || (!aRoot->GetPrimaryFrame() &&
|
||||
!nsCoreUtils::IsDisplayContents(aRoot)));
|
||||
return false;
|
||||
return insert;
|
||||
}
|
||||
|
||||
void DocAccessible::RecreateAccessible(nsIContent* aContent) {
|
||||
|
|
|
@ -21,6 +21,7 @@ support-files = test_bug1276857_subframe.html
|
|||
[test_contextmenu.xul]
|
||||
[test_cssoverflow.html]
|
||||
[test_deck.xul]
|
||||
[test_delayed_removal.html]
|
||||
[test_doc.html]
|
||||
[test_gencontent.html]
|
||||
[test_general.html]
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Test accessible delayed removal</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<style>
|
||||
.gentext:before {
|
||||
content: "START"
|
||||
}
|
||||
.gentext:after {
|
||||
content: "END"
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../promisified-events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
async function hideDivFromInsideSpan() {
|
||||
let msg = "hideDivFromInsideSpan";
|
||||
info(msg);
|
||||
let events = waitForOrderedEvents(
|
||||
[[EVENT_HIDE, "div1"], [EVENT_REORDER, "span1"]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("div1").style.display = "none";
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c1", { SECTION: [ { REGION: [] }, ] });
|
||||
}
|
||||
|
||||
async function showDivFromInsideSpan() {
|
||||
let msg = "showDivFromInsideSpan";
|
||||
info(msg);
|
||||
let events = waitForOrderedEvents(
|
||||
[[EVENT_SHOW, "div2"], [EVENT_REORDER, "span2"]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("div2").style.display = "block";
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c2",
|
||||
{ SECTION: [ { REGION: [{ SECTION: [ { TEXT_LEAF: [] } ] }] }, ] });
|
||||
}
|
||||
|
||||
async function removeDivFromInsideSpan() {
|
||||
let msg = "removeDivFromInsideSpan";
|
||||
info(msg);
|
||||
let events = waitForOrderedEvents(
|
||||
[[EVENT_HIDE, getNode("div3")], [EVENT_REORDER, "span3"]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("div3").remove();
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c3", { SECTION: [ { REGION: [] }, ] });
|
||||
}
|
||||
|
||||
// Test to see that generated content is inserted
|
||||
async function addCSSGeneratedContent() {
|
||||
let msg = "addCSSGeneratedContent";
|
||||
let c4_child = getAccessible("c4_child");
|
||||
info(msg);
|
||||
let events = waitForOrderedEvents([
|
||||
[EVENT_SHOW, evt => evt.accessible == c4_child.firstChild],
|
||||
[EVENT_SHOW, evt => evt.accessible == c4_child.lastChild],
|
||||
[EVENT_REORDER, c4_child]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("c4_child").classList.add('gentext');
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c4", { SECTION: [ // container
|
||||
{ SECTION: [ // inserted node
|
||||
{ STATICTEXT: [] }, // :before
|
||||
{ TEXT_LEAF: [] }, // primary text
|
||||
{ STATICTEXT: [] }, // :after
|
||||
] },
|
||||
] });
|
||||
}
|
||||
|
||||
// Test to see that generated content gets removed
|
||||
async function removeCSSGeneratedContent() {
|
||||
let msg = "removeCSSGeneratedContent";
|
||||
let c5_child = getAccessible("c5_child");
|
||||
info(msg);
|
||||
let events = waitForEvents([
|
||||
[EVENT_HIDE, c5_child.firstChild],
|
||||
[EVENT_HIDE, c5_child.lastChild],
|
||||
[EVENT_REORDER, c5_child]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("c5_child").classList.remove('gentext');
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c5",{ SECTION: [ // container
|
||||
{ SECTION: [ // inserted node
|
||||
{ TEXT_LEAF: [] }, // primary text
|
||||
] },
|
||||
] });
|
||||
}
|
||||
|
||||
// Test to see that a non-accessible intermediate container gets its accessible
|
||||
// descendants removed and inserted correctly.
|
||||
async function intermediateNonAccessibleContainers() {
|
||||
let msg = "intermediateNonAccessibleContainers";
|
||||
info(msg);
|
||||
|
||||
testAccessibleTree("c6",{ SECTION: [
|
||||
{ SECTION: [
|
||||
{ role: ROLE_PUSHBUTTON, name: "Hello" },
|
||||
] },
|
||||
] });
|
||||
|
||||
let events = waitForOrderedEvents(
|
||||
[[EVENT_HIDE, "b1"], [EVENT_SHOW, "b2"], [EVENT_REORDER, "scrollarea"]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
getNode("scrollarea").style.overflow = "auto";
|
||||
document.querySelector("#scrollarea > div > div:first-child").style.display = "none";
|
||||
document.querySelector("#scrollarea > div > div:last-child").style.display = "block";
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c6",{ SECTION: [
|
||||
{ SECTION: [
|
||||
{ role: ROLE_PUSHBUTTON, name: "Goodbye" },
|
||||
] },
|
||||
] });
|
||||
}
|
||||
|
||||
// Test to see that the button gets reparented into the new accessible container.
|
||||
async function intermediateNonAccessibleContainerBecomesAccessible() {
|
||||
let msg = "intermediateNonAccessibleContainerBecomesAccessible";
|
||||
info(msg);
|
||||
|
||||
testAccessibleTree("c7",{ SECTION: [
|
||||
{ role: ROLE_PUSHBUTTON, name: "Hello" },
|
||||
{ TEXT_LEAF: [] }
|
||||
] });
|
||||
|
||||
let events = waitForOrderedEvents(
|
||||
[[EVENT_HIDE, "b3"],
|
||||
// b3 show event coalesced into its new container
|
||||
[EVENT_SHOW, evt => evt.DOMNode.classList.contains('intermediate')],
|
||||
[EVENT_REORDER, "c7"]], msg);
|
||||
document.body.offsetTop; // Flush layout.
|
||||
document.querySelector("#c7 > div").style.display = "block";
|
||||
await events;
|
||||
|
||||
testAccessibleTree("c7",{ SECTION: [
|
||||
{ SECTION: [ { role: ROLE_PUSHBUTTON, name: "Hello" } ] }
|
||||
] });
|
||||
}
|
||||
|
||||
async function doTest() {
|
||||
await hideDivFromInsideSpan();
|
||||
|
||||
await showDivFromInsideSpan();
|
||||
|
||||
await removeDivFromInsideSpan();
|
||||
|
||||
await addCSSGeneratedContent();
|
||||
|
||||
await removeCSSGeneratedContent();
|
||||
|
||||
await intermediateNonAccessibleContainers();
|
||||
|
||||
await intermediateNonAccessibleContainerBecomesAccessible();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="c1">
|
||||
<span role="region" id="span1" aria-label="region"><div id="div1">hello</div></span>
|
||||
</div>
|
||||
|
||||
<div id="c2">
|
||||
<span role="region" id="span2" aria-label="region"><div id="div2" style="display: none">hello</div></span>
|
||||
</div>
|
||||
|
||||
<div id="c3">
|
||||
<span role="region" id="span3" aria-label="region"><div id="div3">hello</div></span>
|
||||
</div>
|
||||
|
||||
<div id="c4"><div id="c4_child">text</div></div>
|
||||
|
||||
<div id="c5"><div id="c5_child" class="gentext">text</div></div>
|
||||
|
||||
<div id="c6">
|
||||
<div id="scrollarea" style="overflow:hidden;">
|
||||
<div><div><button id="b1">Hello</button></div><div style="display: none"><button id="b2">Goodbye</button></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="c7">
|
||||
<div style="display: inline;" class="intermediate">
|
||||
<button id="b3">Hello</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -133,93 +133,6 @@
|
|||
};
|
||||
}
|
||||
|
||||
// This tests a case where a container (c5) gets re-framed
|
||||
// because an inline child has its block child set to display: none.
|
||||
function hideBlockChildOfInline() {
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, "div"),
|
||||
new invokerChecker(EVENT_REORDER, "span"),
|
||||
];
|
||||
|
||||
this.invoke = function hideBlockChildOfInline_invoke() {
|
||||
document.body.offsetTop; // Flush layout.
|
||||
|
||||
getNode("div").style.display = "none";
|
||||
|
||||
};
|
||||
|
||||
this.finalCheck = function hideBlockChildOfInline_finalCheck() {
|
||||
var accTree =
|
||||
{ SECTION: [ // container
|
||||
{ REGION: [] },
|
||||
] };
|
||||
testAccessibleTree("c5", accTree);
|
||||
};
|
||||
|
||||
this.getID = function hideBlockChildOfInline_getID() {
|
||||
return "hide div from inside span.";
|
||||
};
|
||||
}
|
||||
|
||||
// This tests a case where a container (c5) gets re-framed
|
||||
// because an inline child has its block child set to display: none.
|
||||
function reinsertBlockChildOfInline() {
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, "span"),
|
||||
];
|
||||
|
||||
this.invoke = function reinsertBlockChildOfInline_invoke() {
|
||||
document.body.offsetTop; // Flush layout.
|
||||
|
||||
getNode("div").style.display = "block";
|
||||
|
||||
};
|
||||
|
||||
this.finalCheck = function reinsertBlockChildOfInline_finalCheck() {
|
||||
var accTree =
|
||||
{ SECTION: [ // container
|
||||
{ REGION: [
|
||||
{ SECTION: [
|
||||
{ TEXT_LEAF: [] }
|
||||
] }
|
||||
] },
|
||||
] };
|
||||
testAccessibleTree("c5", accTree);
|
||||
};
|
||||
|
||||
this.getID = function reinsertBlockChildOfInline_getID() {
|
||||
return "reinsert div from inside span.";
|
||||
};
|
||||
}
|
||||
|
||||
// This tests a case where a container (c5) gets re-framed
|
||||
// because an inline child has its block child removed.
|
||||
function removeBlockChildOfInline() {
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("div")),
|
||||
new invokerChecker(EVENT_REORDER, "span"),
|
||||
];
|
||||
|
||||
this.invoke = function removeBlockChildOfInline_invoke() {
|
||||
document.body.offsetTop; // Flush layout.
|
||||
|
||||
getNode("div").remove();
|
||||
|
||||
};
|
||||
|
||||
this.finalCheck = function removeBlockChildOfInline_finalCheck() {
|
||||
var accTree =
|
||||
{ SECTION: [ // container
|
||||
{ REGION: [] },
|
||||
] };
|
||||
testAccessibleTree("c5", accTree);
|
||||
};
|
||||
|
||||
this.getID = function removeBlockChildOfInline_getID() {
|
||||
return "remove div from inside span.";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
@ -236,9 +149,6 @@
|
|||
gQueue.push(new removeRemove("c2"));
|
||||
gQueue.push(new insertInaccessibleAccessibleSiblings());
|
||||
gQueue.push(new displayContentsInsertion());
|
||||
gQueue.push(new hideBlockChildOfInline());
|
||||
gQueue.push(new reinsertBlockChildOfInline());
|
||||
gQueue.push(new removeBlockChildOfInline());
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
@ -259,9 +169,6 @@
|
|||
|
||||
<div id="c3"><input type="button" value="button"></div>
|
||||
<div id="c4"></div>
|
||||
<div id="c5">
|
||||
<span role="region" id="span" aria-label="region"><div id="div">hello</div></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче