зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1822340 part 2: When hit testing using the cache, only match a TextLeafAccessible if one of its lines includes the requested point. r=morgan
Previously, we always used the Accessible's rect. However, if the text wraps across lines, its rect might cover a wider area than the actual text. That meant we were matching a wrapped text leaf when we shouldn't in some cases. Now, we restrict text leaf matches to the rects occupied by text. Differential Revision: https://phabricator.services.mozilla.com/D173097
This commit is contained in:
Родитель
3151b74de4
Коммит
ba7744a7e8
|
@ -329,6 +329,65 @@ double RemoteAccessibleBase<Derived>::Step() const {
|
|||
return UnspecifiedNaN<double>();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
bool RemoteAccessibleBase<Derived>::ContainsPoint(int32_t aX, int32_t aY) {
|
||||
if (!Bounds().Contains(aX, aY)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsTextLeaf()) {
|
||||
return true;
|
||||
}
|
||||
// This is a text leaf. The text might wrap across lines, which means our
|
||||
// rect might cover a wider area than the actual text. For example, if the
|
||||
// text begins in the middle of the first line and wraps on to the second,
|
||||
// the rect will cover the start of the first line and the end of the second.
|
||||
auto lines = GetCachedTextLines();
|
||||
if (!lines) {
|
||||
// This means the text is empty or occupies a single line (but does not
|
||||
// begin the line). In that case, the Bounds check above is sufficient,
|
||||
// since there's only one rect.
|
||||
return true;
|
||||
}
|
||||
uint32_t length = lines->Length();
|
||||
MOZ_ASSERT(length > 0,
|
||||
"Line starts shouldn't be in cache if there aren't any");
|
||||
if (length == 0 || (length == 1 && (*lines)[0] == 0)) {
|
||||
// This means the text begins and occupies a single line. Again, the Bounds
|
||||
// check above is sufficient.
|
||||
return true;
|
||||
}
|
||||
// Walk the lines of the text. Even if this text doesn't start at the
|
||||
// beginning of a line (i.e. lines[0] > 0), we always want to consider its
|
||||
// first line.
|
||||
int32_t lineStart = 0;
|
||||
for (uint32_t index = 0; index <= length; ++index) {
|
||||
int32_t lineEnd;
|
||||
if (index < length) {
|
||||
int32_t nextLineStart = (*lines)[index];
|
||||
if (nextLineStart == 0) {
|
||||
// This Accessible starts at the beginning of a line. Here, we always
|
||||
// treat 0 as the first line start anyway.
|
||||
MOZ_ASSERT(index == 0);
|
||||
continue;
|
||||
}
|
||||
lineEnd = nextLineStart - 1;
|
||||
} else {
|
||||
// This is the last line.
|
||||
lineEnd = static_cast<int32_t>(nsAccUtils::TextLength(this)) - 1;
|
||||
}
|
||||
MOZ_ASSERT(lineEnd >= lineStart);
|
||||
nsRect lineRect = GetCachedCharRect(lineStart);
|
||||
if (lineEnd > lineStart) {
|
||||
lineRect.UnionRect(lineRect, GetCachedCharRect(lineEnd));
|
||||
}
|
||||
if (BoundsWithOffset(Some(lineRect)).Contains(aX, aY)) {
|
||||
return true;
|
||||
}
|
||||
lineStart = lineEnd + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
Accessible* RemoteAccessibleBase<Derived>::ChildAtPoint(
|
||||
int32_t aX, int32_t aY, LocalAccessible::EWhichChildAtPoint aWhichChild) {
|
||||
|
@ -399,7 +458,7 @@ Accessible* RemoteAccessibleBase<Derived>::ChildAtPoint(
|
|||
break;
|
||||
}
|
||||
|
||||
if (acc->Bounds().Contains(aX, aY)) {
|
||||
if (acc->ContainsPoint(aX, aY)) {
|
||||
// Because our rects are in hittesting order, the
|
||||
// first match we encounter is guaranteed to be the
|
||||
// deepest match.
|
||||
|
|
|
@ -440,6 +440,7 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
|
|||
void ApplyCrossDocOffset(nsRect& aBounds) const;
|
||||
LayoutDeviceIntRect BoundsWithOffset(Maybe<nsRect> aOffset) const;
|
||||
bool IsFixedPos() const;
|
||||
bool ContainsPoint(int32_t aX, int32_t aY);
|
||||
|
||||
virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,
|
||||
int32_t* aPosInSet) const override;
|
||||
|
|
|
@ -101,6 +101,11 @@ async function runTests(browser, accDoc) {
|
|||
containerWithInaccessibleChildP2,
|
||||
containerWithInaccessibleChildP2.firstChild
|
||||
);
|
||||
|
||||
info("Testing wrapped text");
|
||||
const wrappedTextP = findAccessibleChildByID(accDoc, "wrappedTextP");
|
||||
const wrappedTextA = findAccessibleChildByID(accDoc, "wrappedTextA");
|
||||
await hitTest(browser, wrappedTextP, wrappedTextA, wrappedTextA.firstChild);
|
||||
}
|
||||
|
||||
addAccessibleTask(
|
||||
|
@ -142,6 +147,10 @@ addAccessibleTask(
|
|||
<p aria-hidden="true">hi</p>
|
||||
<p id="containerWithInaccessibleChild_p2">bye</p>
|
||||
</div>
|
||||
|
||||
<p id="wrappedTextP" style="width: 3ch; font-family: monospace;">
|
||||
<a id="wrappedTextA" href="https://example.com/">a</a>b cd
|
||||
</p>
|
||||
`,
|
||||
runTests,
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче