Bug 1951720: Don't exclude 0 width whitespace text nodes from the accessibility tree if they're at the end of a wrapped line. r=morgan
In bug 1951067, I pruned all 0 width whitespace text nodes from the accessibility tree. However, it turns out that whitespace text nodes at the end of wrapped lines can also be 0 width. These have semantic importance, since otherwise, words in separate inline nodes can be merged together without a space. To fix this, explicitly check that the node is before a hard line break, not a soft (wrapped) line break. Differential Revision: https://phabricator.services.mozilla.com/D242290
This commit is contained in:
@@ -823,11 +823,10 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
|
||||
if (textAcc) {
|
||||
// Remove the TextLeafAccessible if:
|
||||
// 1. The rendered text is empty; or
|
||||
// 2. The text is just a space, but its layout frame has a width of 0,
|
||||
// so it isn't visible. This can happen if there is whitespace before an
|
||||
// invisible element at the end of a block.
|
||||
// 2. The text is invisible, semantically irrelevant whitespace before a
|
||||
// hard line break.
|
||||
if (text.mString.IsEmpty() ||
|
||||
(text.mString.EqualsLiteral(" ") && textFrame->GetRect().IsEmpty())) {
|
||||
nsCoreUtils::IsTrimmedWhitespaceBeforeHardLineBreak(textFrame)) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node lost its content; doc: %p",
|
||||
|
||||
@@ -1306,7 +1306,7 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
|
||||
// Ignore not rendered text nodes and whitespace text nodes between table
|
||||
// cells.
|
||||
if (text.mString.IsEmpty() ||
|
||||
(text.mString.EqualsLiteral(" ") && frame->GetRect().IsEmpty()) ||
|
||||
nsCoreUtils::IsTrimmedWhitespaceBeforeHardLineBreak(frame) ||
|
||||
(aContext->IsTableRow() &&
|
||||
nsCoreUtils::IsWhitespaceString(text.mString))) {
|
||||
if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
|
||||
|
||||
@@ -661,3 +661,19 @@ Element* nsCoreUtils::GetAriaActiveDescendantElement(Element* aElement) {
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool nsCoreUtils::IsTrimmedWhitespaceBeforeHardLineBreak(nsIFrame* aFrame) {
|
||||
if (!aFrame->GetRect().IsEmpty() ||
|
||||
!aFrame->HasAnyStateBits(TEXT_END_OF_LINE)) {
|
||||
return false;
|
||||
}
|
||||
// Normally, accessibility calls nsIFrame::GetRenderedText with
|
||||
// TrailingWhitespace::NoTrim. Using TrailingWhitespace::Trim instead trims 0
|
||||
// width whitespace before a hard line break, resulting in an empty string if
|
||||
// that is all the frame contains. Note that TrailingWhitespace::Trim does
|
||||
// *not* trim whitespace before a soft line break (wrapped line).
|
||||
nsIFrame::RenderedText text = aFrame->GetRenderedText(
|
||||
0, UINT32_MAX, nsIFrame::TextOffsetType::OffsetsInContentText,
|
||||
nsIFrame::TrailingWhitespace::Trim);
|
||||
return text.mString.IsEmpty();
|
||||
}
|
||||
|
||||
@@ -336,6 +336,17 @@ class nsCoreUtils {
|
||||
nsINode* aStartAncestor);
|
||||
|
||||
static Element* GetAriaActiveDescendantElement(Element* aElement);
|
||||
|
||||
/**
|
||||
* Return true if the given text frame is 0 width whitespace before a hard
|
||||
* line break. This is not visible and is semantically irrelevant. This can
|
||||
* happen if there is whitespace before an invisible element at the end of a
|
||||
* block. For example:
|
||||
* <div><span>a</span> <span hidden>b</span></div>
|
||||
* This results in a text node for "a" and a text node for " ". This function
|
||||
* will return true for the latter node.
|
||||
*/
|
||||
static bool IsTrimmedWhitespaceBeforeHardLineBreak(nsIFrame* aFrame);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -69,24 +69,29 @@ addAccessibleTask(
|
||||
);
|
||||
|
||||
/**
|
||||
* Test whitespace before a hidden element at the end of a block.
|
||||
* Test whitespace before hard and soft line breaks.
|
||||
*/
|
||||
addAccessibleTask(
|
||||
`<div id="container"><span>a</span> <span id="b" hidden>b</span></div>`,
|
||||
async function testBeforeHiddenElementAtEnd(browser, docAcc) {
|
||||
const container = findAccessibleChildByID(docAcc, "container");
|
||||
testAccessibleTree(container, {
|
||||
`
|
||||
<div id="hardContainer"><span>a</span> <span id="b" hidden>b</span></div>
|
||||
<div id="softContainer" style="width: 1ch; font-family: monospace;">
|
||||
<span>c</span> <span>d</span>
|
||||
</div>
|
||||
`,
|
||||
async function testBeforeLineBreaks(browser, docAcc) {
|
||||
const hardContainer = findAccessibleChildByID(docAcc, "hardContainer");
|
||||
testAccessibleTree(hardContainer, {
|
||||
role: ROLE_SECTION,
|
||||
children: [{ role: ROLE_TEXT_LEAF, name: "a" }],
|
||||
});
|
||||
|
||||
info("Showing b");
|
||||
let reordered = waitForEvent(EVENT_REORDER, container);
|
||||
let reordered = waitForEvent(EVENT_REORDER, hardContainer);
|
||||
await invokeContentTask(browser, [], () => {
|
||||
content.document.getElementById("b").hidden = false;
|
||||
});
|
||||
await reordered;
|
||||
testAccessibleTree(container, {
|
||||
testAccessibleTree(hardContainer, {
|
||||
role: ROLE_SECTION,
|
||||
children: [
|
||||
{ role: ROLE_TEXT_LEAF, name: "a" },
|
||||
@@ -96,15 +101,25 @@ addAccessibleTask(
|
||||
});
|
||||
|
||||
info("Hiding b");
|
||||
reordered = waitForEvent(EVENT_REORDER, container);
|
||||
reordered = waitForEvent(EVENT_REORDER, hardContainer);
|
||||
await invokeContentTask(browser, [], () => {
|
||||
content.document.getElementById("b").hidden = true;
|
||||
});
|
||||
await reordered;
|
||||
testAccessibleTree(container, {
|
||||
testAccessibleTree(hardContainer, {
|
||||
role: ROLE_SECTION,
|
||||
children: [{ role: ROLE_TEXT_LEAF, name: "a" }],
|
||||
});
|
||||
|
||||
const softContainer = findAccessibleChildByID(docAcc, "softContainer");
|
||||
testAccessibleTree(softContainer, {
|
||||
role: ROLE_SECTION,
|
||||
children: [
|
||||
{ role: ROLE_TEXT_LEAF, name: "c" },
|
||||
{ role: ROLE_TEXT_LEAF, name: " " },
|
||||
{ role: ROLE_TEXT_LEAF, name: "d" },
|
||||
],
|
||||
});
|
||||
},
|
||||
{ chrome: true, topLevel: true }
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user