Bug 1730085 part 2: Implement support for BOUNDARY_LINE_END in TextLeafPoint and HyperTextAccessibleBase. r=eeejay

BOUNDARY_LINE_END is implemented using BOUNDARY_LINE_START and adjusting for line feed characters, which are line end boundaries where present.

Differential Revision: https://phabricator.services.mozilla.com/D138103
This commit is contained in:
James Teh 2022-02-11 02:25:21 +00:00
Родитель 9e62d53a3d
Коммит df5caa3102
6 изменённых файлов: 122 добавлений и 9 удалений

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

@ -424,6 +424,12 @@ bool TextLeafPoint::IsEmptyLastLine() const {
return text.CharAt(0) == '\n';
}
char16_t TextLeafPoint::GetChar() const {
nsAutoString text;
mAcc->AppendTextTo(text, mOffset, 1);
return text.CharAt(0);
}
TextLeafPoint TextLeafPoint::FindPrevLineStartSameLocalAcc(
bool aIncludeOrigin) const {
LocalAccessible* acc = mAcc->AsLocal();
@ -743,6 +749,9 @@ TextLeafPoint TextLeafPoint::FindBoundary(AccessibleTextBoundary aBoundaryType,
return ActualizeCaret().FindBoundary(aBoundaryType, aDirection,
aIncludeOrigin);
}
if (aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END) {
return FindLineEnd(aDirection, aIncludeOrigin);
}
if (aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START &&
aIncludeOrigin && aDirection == eDirPrevious && IsEmptyLastLine()) {
// If we're at an empty line at the end of an Accessible, we don't want to
@ -818,6 +827,46 @@ TextLeafPoint TextLeafPoint::FindBoundary(AccessibleTextBoundary aBoundaryType,
return TextLeafPoint();
}
TextLeafPoint TextLeafPoint::FindLineEnd(nsDirection aDirection,
bool aIncludeOrigin) const {
if (aDirection == eDirPrevious && IsEmptyLastLine()) {
// If we're at an empty line at the end of an Accessible, we don't want to
// walk into the previous line. For example, this can happen if the caret
// is positioned on an empty line at the end of a textarea.
// Because we want the line end, we must walk back to the line feed
// character.
return FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
}
if (aIncludeOrigin && IsLineFeedChar()) {
return *this;
}
if (aDirection == eDirPrevious && !aIncludeOrigin) {
// If there is a line feed immediately before us, return that.
TextLeafPoint prevChar =
FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
if (prevChar.IsLineFeedChar()) {
return prevChar;
}
}
TextLeafPoint searchFrom = *this;
if (aDirection == eDirNext && (IsLineFeedChar() || IsEmptyLastLine())) {
// If we search for the next line start from a line feed, we'll get the
// character immediately following the line feed. We actually want the
// next line start after that. Skip the line feed.
searchFrom = FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirNext);
}
TextLeafPoint lineStart = searchFrom.FindBoundary(
nsIAccessibleText::BOUNDARY_LINE_START, aDirection, aIncludeOrigin);
// If there is a line feed before this line start (at the end of the previous
// line), we must return that.
TextLeafPoint prevChar = lineStart.FindBoundary(
nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious, false);
if (prevChar && prevChar.IsLineFeedChar()) {
return prevChar;
}
return lineStart;
}
already_AddRefed<AccAttributes> TextLeafPoint::GetTextAttributesLocalAcc(
bool aIncludeDefaults) const {
LocalAccessible* acc = mAcc->AsLocal();

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

@ -146,9 +146,13 @@ class TextLeafPoint final {
const AccAttributes* aOriginAttrs = nullptr,
bool aIncludeDefaults = true) const;
bool IsLineFeedChar() const { return GetChar() == '\n'; }
private:
bool IsEmptyLastLine() const;
char16_t GetChar() const;
TextLeafPoint FindLineStartSameRemoteAcc(nsDirection aDirection,
bool aIncludeOrigin) const;
@ -158,6 +162,8 @@ class TextLeafPoint final {
*/
TextLeafPoint FindLineStartSameAcc(nsDirection aDirection,
bool aIncludeOrigin) const;
TextLeafPoint FindLineEnd(nsDirection aDirection, bool aIncludeOrigin) const;
};
/**

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

@ -216,6 +216,24 @@ std::pair<bool, int32_t> HyperTextAccessibleBase::TransformOffset(
return {false, aIsEndOffset ? static_cast<int32_t>(CharacterCount()) : 0};
}
void HyperTextAccessibleBase::AdjustOriginIfEndBoundary(
TextLeafPoint& aOrigin, AccessibleTextBoundary aBoundaryType) const {
if (aBoundaryType != nsIAccessibleText::BOUNDARY_LINE_END &&
aBoundaryType != nsIAccessibleText::BOUNDARY_WORD_END) {
return;
}
TextLeafPoint actualOrig =
aOrigin.IsCaret() ? aOrigin.ActualizeCaret(/* aAdjustAtEndOfLine */ false)
: aOrigin;
if (aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END) {
if (!actualOrig.IsLineFeedChar()) {
return;
}
aOrigin =
actualOrig.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious);
}
}
void HyperTextAccessibleBase::TextBeforeOffset(
int32_t aOffset, AccessibleTextBoundary aBoundaryType,
int32_t* aStartOffset, int32_t* aEndOffset, nsAString& aText) {
@ -238,12 +256,14 @@ void HyperTextAccessibleBase::TextBeforeOffset(
break;
case nsIAccessibleText::BOUNDARY_WORD_START:
case nsIAccessibleText::BOUNDARY_LINE_START:
case nsIAccessibleText::BOUNDARY_LINE_END:
TextLeafPoint orig;
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
orig = TextLeafPoint::GetCaret(Acc());
} else {
orig = ToTextLeafPoint(static_cast<int32_t>(adjustedOffset));
}
AdjustOriginIfEndBoundary(orig, aBoundaryType);
TextLeafPoint end = orig.FindBoundary(aBoundaryType, eDirPrevious,
/* aIncludeOrigin */ true);
bool ok;
@ -293,11 +313,14 @@ void HyperTextAccessibleBase::TextAtOffset(int32_t aOffset,
break;
case nsIAccessibleText::BOUNDARY_WORD_START:
case nsIAccessibleText::BOUNDARY_LINE_START:
TextLeafPoint origStart, end;
case nsIAccessibleText::BOUNDARY_LINE_END:
TextLeafPoint start, end;
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
origStart = end = TextLeafPoint::GetCaret(Acc());
start = TextLeafPoint::GetCaret(Acc());
AdjustOriginIfEndBoundary(start, aBoundaryType);
end = start;
} else {
origStart = ToTextLeafPoint(static_cast<int32_t>(adjustedOffset));
start = ToTextLeafPoint(static_cast<int32_t>(adjustedOffset));
Accessible* childAcc = GetChildAtOffset(adjustedOffset);
if (childAcc && childAcc->IsHyperText()) {
// We're searching for boundaries enclosing an embedded object.
@ -310,11 +333,12 @@ void HyperTextAccessibleBase::TextAtOffset(int32_t aOffset,
end = ToTextLeafPoint(static_cast<int32_t>(adjustedOffset),
/* aDescendToEnd */ true);
} else {
end = origStart;
AdjustOriginIfEndBoundary(start, aBoundaryType);
end = start;
}
}
TextLeafPoint start = origStart.FindBoundary(aBoundaryType, eDirPrevious,
/* aIncludeOrigin */ true);
start = start.FindBoundary(aBoundaryType, eDirPrevious,
/* aIncludeOrigin */ true);
bool ok;
std::tie(ok, *aStartOffset) = TransformOffset(start.mAcc, start.mOffset,
/* aIsEndOffset */ false);
@ -352,6 +376,7 @@ void HyperTextAccessibleBase::TextAfterOffset(
}
case nsIAccessibleText::BOUNDARY_WORD_START:
case nsIAccessibleText::BOUNDARY_LINE_START:
case nsIAccessibleText::BOUNDARY_LINE_END:
TextLeafPoint orig;
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
orig = TextLeafPoint::GetCaret(Acc());
@ -359,6 +384,7 @@ void HyperTextAccessibleBase::TextAfterOffset(
orig = ToTextLeafPoint(static_cast<int32_t>(adjustedOffset),
/* aDescendToEnd */ true);
}
AdjustOriginIfEndBoundary(orig, aBoundaryType);
TextLeafPoint start = orig.FindBoundary(aBoundaryType, eDirNext);
bool ok;
std::tie(ok, *aStartOffset) = TransformOffset(start.mAcc, start.mOffset,

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

@ -178,6 +178,16 @@ class HyperTextAccessibleBase {
std::pair<bool, int32_t> TransformOffset(Accessible* aDescendant,
int32_t aOffset,
bool aIsEndOffset) const;
/**
* Helper method for TextBefore/At/AfterOffset.
* If BOUNDARY_LINE_END was requested and the origin is itself a line end
* boundary, we must use the line which ends at the origin. We must do
* similarly for BOUNDARY_WORD_END. This method adjusts the origin
* accordingly.
*/
void AdjustOriginIfEndBoundary(TextLeafPoint& aOrigin,
AccessibleTextBoundary aBoundaryType) const;
};
} // namespace mozilla::a11y

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

@ -922,7 +922,8 @@ void HyperTextAccessible::TextBeforeOffset(int32_t aOffset,
nsAString& aText) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup() &&
(aBoundaryType == nsIAccessibleText::BOUNDARY_WORD_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START)) {
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END)) {
// This isn't strictly related to caching, but this new text implementation
// is being developed to make caching feasible. We put it behind this pref
// to make it easy to test while it's still under development.
@ -1011,7 +1012,8 @@ void HyperTextAccessible::TextAtOffset(int32_t aOffset,
int32_t* aEndOffset, nsAString& aText) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup() &&
(aBoundaryType == nsIAccessibleText::BOUNDARY_WORD_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START)) {
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END)) {
// This isn't strictly related to caching, but this new text implementation
// is being developed to make caching feasible. We put it behind this pref
// to make it easy to test while it's still under development.
@ -1108,7 +1110,8 @@ void HyperTextAccessible::TextAfterOffset(int32_t aOffset,
nsAString& aText) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup() &&
(aBoundaryType == nsIAccessibleText::BOUNDARY_WORD_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START)) {
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START ||
aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END)) {
// This isn't strictly related to caching, but this new text implementation
// is being developed to make caching feasible. We put it behind this pref
// to make it easy to test while it's still under development.

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

@ -57,6 +57,25 @@ ef gh</pre>
[0, 5, "ef gh", 6, 11],
[6, 11, "", 11, 11],
]);
if (isWinNoCache) {
todo(
false,
"Cache disabled, so RemoteAccessible doesn't support BOUNDARY_LINE_END on Windows"
);
} else {
testTextAtOffset(acc, BOUNDARY_LINE_END, [
[0, 5, "ab cd", 0, 5],
[6, 11, "\nef gh", 5, 11],
]);
testTextBeforeOffset(acc, BOUNDARY_LINE_END, [
[0, 5, "", 0, 0],
[6, 11, "ab cd", 0, 5],
]);
testTextAfterOffset(acc, BOUNDARY_LINE_END, [
[0, 5, "\nef gh", 5, 11],
[6, 11, "", 11, 11],
]);
}
testTextAtOffset(acc, BOUNDARY_WORD_START, [
[0, 2, "ab ", 0, 3],
[3, 5, "cd\n", 3, 6],