Bug 1735101: Cache char, line coordinate info to support computing TextBounds and CharBounds in the parent process r=Jamie

Depends on D144767

Differential Revision: https://phabricator.services.mozilla.com/D143527
This commit is contained in:
Morgan Reschenberg 2022-05-09 20:30:27 +00:00
parent 2506454b92
commit 4e6096c73d
18 changed files with 512 additions and 67 deletions

View File

@ -337,7 +337,6 @@ static void getCharacterExtentsCB(AtkText* aText, gint aOffset, gint* aX,
}
*aX = *aY = *aWidth = *aHeight = -1;
LayoutDeviceIntRect rect;
uint32_t geckoCoordType;
if (aCoords == ATK_XY_SCREEN) {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
@ -345,20 +344,18 @@ static void getCharacterExtentsCB(AtkText* aText, gint aOffset, gint* aX,
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
rect = text->CharBounds(aOffset, geckoCoordType);
} else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
rect = proxy->CharBounds(aOffset, geckoCoordType);
} else {
Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
if (!acc) {
return;
}
HyperTextAccessibleBase* text = acc->AsHyperTextBase();
if (!text || !acc->IsTextRole()) {
return;
}
LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType);
*aX = rect.x;
*aY = rect.y;
*aWidth = rect.width;
@ -373,7 +370,6 @@ static void getRangeExtentsCB(AtkText* aText, gint aStartOffset,
}
aRect->x = aRect->y = aRect->width = aRect->height = -1;
LayoutDeviceIntRect rect;
uint32_t geckoCoordType;
if (aCoords == ATK_XY_SCREEN) {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
@ -381,20 +377,19 @@ static void getRangeExtentsCB(AtkText* aText, gint aStartOffset,
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
rect = text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
} else if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
rect = proxy->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
} else {
Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
if (!acc) {
return;
}
HyperTextAccessibleBase* text = acc->AsHyperTextBase();
if (!text || !acc->IsTextRole()) {
return;
}
LayoutDeviceIntRect rect =
text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
aRect->x = rect.x;
aRect->y = rect.y;
aRect->width = rect.width;

View File

@ -25,6 +25,8 @@ class CacheDomain {
static constexpr uint64_t TransformMatrix = ((uint64_t)0x1) << 10;
static constexpr uint64_t ScrollPosition = ((uint64_t)0x1) << 11;
static constexpr uint64_t Table = ((uint64_t)0x1) << 11;
static constexpr uint64_t TextBounds = ((uint64_t)0x1) << 12;
// TODO: Combine TextBounds updates with Bounds updates
static constexpr uint64_t All = ~((uint64_t)0x0);
};

View File

@ -391,6 +391,10 @@ bool TextLeafPoint::operator<(const TextLeafPoint& aPoint) const {
return mAcc->IsBefore(aPoint.mAcc);
}
bool TextLeafPoint::operator<=(const TextLeafPoint& aPoint) const {
return *this == aPoint || *this < aPoint;
}
bool TextLeafPoint::IsEmptyLastLine() const {
if (mAcc->IsHTMLBr() && mOffset == 1) {
return true;
@ -1114,4 +1118,18 @@ TextLeafPoint TextLeafPoint::FindTextAttrsStart(
: static_cast<int32_t>(nsAccUtils::TextLength(lastPoint.mAcc)));
}
LayoutDeviceIntRect TextLeafPoint::CharBounds() {
if (!mAcc || !mAcc->IsRemote() || !mAcc->AsRemote() ||
!mAcc->AsRemote()->mCachedFields) {
return LayoutDeviceIntRect();
}
RemoteAccessible* acc = mAcc->AsRemote();
if (Maybe<nsTArray<nsRect>> charBounds = acc->GetCachedCharData()) {
return acc->BoundsWithOffset(Some(charBounds->ElementAt(mOffset)));
}
return LayoutDeviceIntRect();
}
} // namespace mozilla::a11y

View File

@ -58,6 +58,8 @@ class TextLeafPoint final {
bool operator<(const TextLeafPoint& aPoint) const;
bool operator<=(const TextLeafPoint& aPoint) const;
/**
* A valid TextLeafPoint evaluates to true. An invalid TextLeafPoint
* evaluates to false.
@ -146,6 +148,14 @@ class TextLeafPoint final {
const AccAttributes* aOriginAttrs = nullptr,
bool aIncludeDefaults = true) const;
/**
* Returns a rect (in dev pixels) describing position and size of
* the character at mOffset in mAcc. This rect is screen-relative.
* This function only works on remote accessibles, and assumes caching
* is enabled.
*/
LayoutDeviceIntRect CharBounds();
bool IsLineFeedChar() const { return GetChar() == '\n'; }
bool IsSpace() const;

View File

@ -9,6 +9,7 @@
#include "mozilla/a11y/Accessible.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "nsAccUtils.h"
#include "mozilla/a11y/RemoteAccessible.h"
#include "TextLeafRange.h"
#include "TextRange.h"
@ -166,6 +167,82 @@ bool HyperTextAccessibleBase::CharAt(int32_t aOffset, nsAString& aChar,
return true;
}
LayoutDeviceIntRect HyperTextAccessibleBase::CharBounds(int32_t aOffset,
uint32_t aCoordType) {
TextLeafPoint point = ToTextLeafPoint(aOffset, false);
if (!point.mAcc || !point.mAcc->IsRemote() ||
!point.mAcc->AsRemote()->mCachedFields) {
return LayoutDeviceIntRect();
}
LayoutDeviceIntRect bounds = point.CharBounds();
nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, Acc());
return bounds;
}
LayoutDeviceIntRect HyperTextAccessibleBase::TextBounds(int32_t aStartOffset,
int32_t aEndOffset,
uint32_t aCoordType) {
if (CharacterCount() == 0 ||
(aEndOffset > -1 && aStartOffset >= aEndOffset)) {
return LayoutDeviceIntRect();
}
LayoutDeviceIntRect result = CharBounds(aStartOffset, aCoordType);
int32_t indexOfLastChar =
static_cast<int32_t>(ConvertMagicOffset(aEndOffset)) - 1;
if (aStartOffset == indexOfLastChar) {
// HyperTextAccessibleBase::CharBounds() above already
// does coord-type conversion, so simply return `result` here.
return result;
}
// Here's where things get complicated. We can't simply query the first
// and last character, and union their bounds. They might reside on different
// lines, and a simple union may yield an incorrect width. We
// should use the length of the longest spanned line for our width.
// TODO: This functionality should probably live in TextLeafRange
TextLeafPoint currPoint = ToTextLeafPoint(aStartOffset, false);
TextLeafPoint endPoint = ToTextLeafPoint(indexOfLastChar, false);
bool locatedFinalLine = false;
// Note: we have to reset `result` here because the initialisation
// above potentially performed a coord-type conversion, and the rest of
// this function expects to work in screen coords.
result = currPoint.CharBounds();
// Union the first and last chars of each line to create a line rect. Then,
// union the lines together.
while (!locatedFinalLine) {
// Fetch the last point in the current line by getting the
// start of the next line and going back one char. We don't
// use BOUNDARY_LINE_END here because it is equivalent to LINE_START when
// the line doesn't end with a line feed character.
TextLeafPoint lineStartPoint =
currPoint.FindBoundary(nsIAccessibleText::BOUNDARY_LINE_START, eDirNext,
/* aIncludeOrigin */ false);
TextLeafPoint lastPointInLine = lineStartPoint.FindBoundary(
nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious,
/* aIncludeOrigin */ false);
if (endPoint <= lastPointInLine) {
lastPointInLine = endPoint;
locatedFinalLine = true;
}
LayoutDeviceIntRect currLine = currPoint.CharBounds();
currLine.UnionRect(currLine, lastPointInLine.CharBounds());
result.UnionRect(result, currLine);
currPoint = lineStartPoint;
}
// Calls to TextLeafPoint::CharBounds() will construct screen coordinates.
// Perform any additional conversions here.
nsAccUtils::ConvertScreenCoordsTo(&result.x, &result.y, aCoordType, Acc());
return result;
}
TextLeafPoint HyperTextAccessibleBase::ToTextLeafPoint(int32_t aOffset,
bool aDescendToEnd) {
Accessible* thisAcc = Acc();

View File

@ -100,6 +100,20 @@ class HyperTextAccessibleBase {
bool CharAt(int32_t aOffset, nsAString& aChar,
int32_t* aStartOffset = nullptr, int32_t* aEndOffset = nullptr);
/**
* Return a rect (in dev pixels) for character at given offset relative given
* coordinate system.
*/
virtual LayoutDeviceIntRect CharBounds(int32_t aOffset, uint32_t aCoordType);
/**
* Return a rect (in dev pixels) of the given text range relative given
* coordinate system.
*/
virtual LayoutDeviceIntRect TextBounds(int32_t aStartOffset,
int32_t aEndOffset,
uint32_t aCoordType);
/**
* Get a TextLeafPoint for a given offset in this HyperTextAccessible.
* If the offset points to an embedded object and aDescendToEnd is true,

View File

@ -173,20 +173,13 @@ class HyperTextAccessible : public AccessibleWrap,
*/
int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType);
/**
* Return a rect (in dev pixels) of the given text range relative given
* coordinate system.
*/
LayoutDeviceIntRect TextBounds(
int32_t aStartOffset, int32_t aEndOffset,
uint32_t aCoordType =
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) override;
/**
* Return a rect (in dev pixels) for character at given offset relative given
* coordinate system.
*/
LayoutDeviceIntRect CharBounds(int32_t aOffset, uint32_t aCoordType) {
LayoutDeviceIntRect CharBounds(int32_t aOffset,
uint32_t aCoordType) override {
int32_t endOffset = aOffset == static_cast<int32_t>(CharacterCount())
? aOffset
: aOffset + 1;

View File

@ -54,6 +54,7 @@
#include "nsIStringBundle.h"
#include "nsPresContext.h"
#include "nsIFrame.h"
#include "nsTextFrame.h"
#include "nsView.h"
#include "nsIDocShellTreeItem.h"
#include "nsIScrollableFrame.h"
@ -3206,6 +3207,31 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
}
}
nsIFrame* frame = GetFrame();
if ((aCacheDomain & CacheDomain::TextBounds) && IsTextLeaf()) {
if (frame && frame->IsTextFrame()) {
nsTArray<int32_t> charData;
nsIFrame* currTextFrame = frame;
while (currTextFrame) {
nsTArray<nsRect> charBounds;
currTextFrame->GetCharacterRectsInRange(
0, static_cast<nsTextFrame*>(currTextFrame)->GetContentLength(),
charBounds);
for (const nsRect& rect : charBounds) {
charData.AppendElement(rect.x);
charData.AppendElement(rect.y);
charData.AppendElement(rect.width);
charData.AppendElement(rect.height);
}
currTextFrame = currTextFrame->GetNextContinuation();
}
if (charData.Length()) {
fields->SetAttribute(nsGkAtoms::characterData, std::move(charData));
}
}
}
bool boundsChanged = false;
if (aCacheDomain & CacheDomain::Bounds) {
nsRect newBoundsRect = ParentRelativeBounds();
@ -3293,7 +3319,6 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
}
}
nsIFrame* frame = GetFrame();
if (aCacheDomain & CacheDomain::TransformMatrix) {
if (frame && frame->IsTransformed()) {
// We need to find a frame to make our transform relative to.

View File

@ -377,7 +377,21 @@ void RemoteAccessibleBase<Derived>::ApplyScrollOffset(nsRect& aBounds) const {
}
template <class Derived>
LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
nsRect RemoteAccessibleBase<Derived>::GetBoundsInAppUnits() const {
dom::CanonicalBrowsingContext* cbc =
static_cast<dom::BrowserParent*>(mDoc->Manager())
->GetBrowsingContext()
->Top();
dom::BrowserParent* bp = cbc->GetBrowserParent();
nsPresContext* presContext =
bp->GetOwnerElement()->OwnerDoc()->GetPresContext();
return LayoutDeviceIntRect::ToAppUnits(Bounds(),
presContext->AppUnitsPerDevPixel());
}
template <class Derived>
LayoutDeviceIntRect RemoteAccessibleBase<Derived>::BoundsWithOffset(
Maybe<nsRect> aOffset) const {
if (mCachedFields) {
Maybe<nsRect> maybeBounds = RetrieveCachedBounds();
if (maybeBounds) {
@ -390,6 +404,18 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
nsPresContext* presContext =
bp->GetOwnerElement()->OwnerDoc()->GetPresContext();
if (aOffset.isSome()) {
// The rect we've passed in is in app units, so no conversion needed.
nsRect internalRect = *aOffset;
// XXX: Right now this is exclusively used for
// text bounds, which already have their zoom level
// applied. Remove it, because we'll multiply it back
// in later on.
internalRect.ScaleRoundOut(1 / cbc->GetFullZoom());
bounds.SetRectX(bounds.x + internalRect.x, internalRect.width);
bounds.SetRectY(bounds.y + internalRect.y, internalRect.height);
}
Unused << ApplyTransform(bounds);
LayoutDeviceIntRect devPxBounds;
@ -475,6 +501,11 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
return LayoutDeviceIntRect();
}
template <class Derived>
LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
return BoundsWithOffset(Nothing());
}
template <class Derived>
void RemoteAccessibleBase<Derived>::AppendTextTo(nsAString& aText,
uint32_t aStartOffset,
@ -530,6 +561,28 @@ RemoteAccessibleBase<Derived>::GetCachedTextLines() {
return mCachedFields->GetAttribute<nsTArray<int32_t>>(nsGkAtoms::line);
}
template <class Derived>
Maybe<nsTArray<nsRect>> RemoteAccessibleBase<Derived>::GetCachedCharData() {
MOZ_ASSERT(IsText());
if (!mCachedFields) {
return Nothing();
}
if (Maybe<const nsTArray<int32_t>&> maybeCharData =
mCachedFields->GetAttribute<nsTArray<int32_t>>(
nsGkAtoms::characterData)) {
const nsTArray<int32_t>& charData = *maybeCharData;
nsTArray<nsRect> rects;
for (int i = 0; i < static_cast<int32_t>(charData.Length()); i += 4) {
nsRect r(charData[i], charData[i + 1], charData[i + 2], charData[i + 3]);
rects.AppendElement(r);
}
return Some(std::move(rects));
}
return Nothing();
}
template <class Derived>
void RemoteAccessibleBase<Derived>::DOMNodeID(nsString& aID) const {
if (mCachedFields) {

View File

@ -169,6 +169,8 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
virtual LayoutDeviceIntRect Bounds() const override;
nsRect GetBoundsInAppUnits() const;
virtual uint64_t State() override;
virtual already_AddRefed<AccAttributes> Attributes() override;
@ -276,6 +278,7 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
uint32_t GetCachedTextLength();
Maybe<const nsTArray<int32_t>&> GetCachedTextLines();
Maybe<nsTArray<nsRect>> GetCachedCharData();
RefPtr<const AccAttributes> GetCachedTextAttributes();
virtual HyperTextAccessibleBase* AsHyperTextBase() override {
@ -325,6 +328,7 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
Maybe<nsRect> RetrieveCachedBounds() const;
bool ApplyTransform(nsRect& aBounds) const;
void ApplyScrollOffset(nsRect& aBounds) const;
LayoutDeviceIntRect BoundsWithOffset(Maybe<nsRect> aOffset) const;
virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,
int32_t* aPosInSet) const override;
@ -345,6 +349,8 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
friend Derived;
friend DocAccessibleParent;
friend TextLeafPoint;
friend HyperTextAccessibleBase;
friend class xpcAccessible;
friend class CachedTableCellAccessible;

View File

@ -91,13 +91,6 @@ virtual void TextBeforeOffset(int32_t aOffset,
char16_t CharAt(int32_t aOffset);
LayoutDeviceIntRect TextBounds(
int32_t aStartOffset, int32_t aEndOffset,
uint32_t aCoordType =
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
LayoutDeviceIntRect CharBounds(int32_t aOffset, uint32_t aCoordType);
int32_t OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType);
bool SetSelectionBoundsAt(int32_t aSelectionNum, int32_t aStartOffset,

View File

@ -286,6 +286,12 @@ already_AddRefed<AccAttributes> RemoteAccessible::DefaultTextAttributes() {
LayoutDeviceIntRect RemoteAccessible::TextBounds(int32_t aStartOffset,
int32_t aEndOffset,
uint32_t aCoordType) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
MOZ_ASSERT(IsHyperText(), "is not hypertext?");
return RemoteAccessibleBase<RemoteAccessible>::TextBounds(
aStartOffset, aEndOffset, aCoordType);
}
LayoutDeviceIntRect rect;
Unused << mDoc->SendTextBounds(mID, aStartOffset, aEndOffset, aCoordType,
&rect);
@ -294,6 +300,12 @@ LayoutDeviceIntRect RemoteAccessible::TextBounds(int32_t aStartOffset,
LayoutDeviceIntRect RemoteAccessible::CharBounds(int32_t aOffset,
uint32_t aCoordType) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
MOZ_ASSERT(IsHyperText(), "is not hypertext?");
return RemoteAccessibleBase<RemoteAccessible>::CharBounds(aOffset,
aCoordType);
}
LayoutDeviceIntRect rect;
Unused << mDoc->SendCharBounds(mID, aOffset, aCoordType, &rect);
return rect;

View File

@ -35,6 +35,13 @@ class RemoteAccessible : public RemoteAccessibleBase<RemoteAccessible> {
virtual uint32_t CharacterCount() const override;
LayoutDeviceIntRect TextBounds(
int32_t aStartOffset, int32_t aEndOffset,
uint32_t aCoordType =
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) override;
LayoutDeviceIntRect CharBounds(int32_t aOffset, uint32_t aCoordType) override;
virtual already_AddRefed<AccAttributes> TextAttributes(
bool aIncludeDefAttrs, int32_t aOffset, int32_t* aStartOffset,
int32_t* aEndOffset) override;

View File

@ -28,6 +28,7 @@ skip-if = (os == "linux" && bits == 64) || (debug && os == "mac") || (debug && o
[browser_caching_uniqueid.js]
[browser_caching_interfaces.js]
[browser_caching_domnodeid.js]
[browser_caching_text_bounds.js]
# Events tests
[browser_events_announcement.js]

View File

@ -0,0 +1,236 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* import-globals-from ../../mochitest/layout.js */
loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR });
async function testTextNode(accDoc, browser, id) {
await testTextRange(accDoc, browser, id, 0, -1);
}
async function testTextRange(accDoc, browser, id, start, end) {
const r = await invokeContentTask(
browser,
[id, start, end],
(_id, _start, _end) => {
const htNode = content.document.getElementById(_id);
let [eX, eY, eW, eH] = [
Number.MAX_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER,
0,
0,
];
let traversed = 0;
let localStart = _start;
let endTraversal = false;
for (let element of htNode.childNodes) {
// ignore whitespace, but not embedded elements
let isEmbeddedElement = false;
if (element.length == undefined) {
if (!element.firstChild) {
continue;
} else {
isEmbeddedElement = true;
}
}
if (element.length + traversed < _start) {
// If our start index is not within this
// node, keep looking.
traversed += element.length;
localStart -= element.length;
continue;
}
let rect;
if (isEmbeddedElement) {
rect = element.getBoundingClientRect();
} else {
const range = content.document.createRange();
range.setStart(element, localStart);
if (_end != -1 && _end - traversed <= element.length) {
// If the current node contains
// our end index, stop here.
endTraversal = true;
range.setEnd(element, _end - traversed);
} else {
range.setEnd(element, element.length);
}
rect = range.getBoundingClientRect();
}
const oldX = eX == Number.MAX_SAFE_INTEGER ? 0 : eX;
const oldY = eY == Number.MAX_SAFE_INTEGER ? 0 : eY;
eX = Math.min(eX, rect.x);
eY = Math.min(eY, rect.y);
eW = Math.abs(Math.max(oldX + eW, rect.x + rect.width) - eX);
eH = Math.abs(Math.max(oldY + eH, rect.y + rect.height) - eY);
if (endTraversal) {
break;
}
localStart = 0;
traversed += element.length;
}
return [Math.round(eX), Math.round(eY), Math.round(eW), Math.round(eH)];
}
);
let hyperTextNode = findAccessibleChildByID(accDoc, id);
// test against parent-relative coords, because getBoundingClientRect
// is relative to the document, not the screen. this won't work on nested
// elements (ie. any hypertext whose parent is not the doc).
testTextBounds(hyperTextNode, start, end, r, COORDTYPE_PARENT_RELATIVE);
}
/**
* Test the text range boundary for simple LtR text
*/
addAccessibleTask(
`
<p id='p1' style='font-family: monospace;'>Tilimilitryamdiya</p>
<p id='p2' style='font-family: monospace;'>ل</p>
<p id='p3' dir='ltr' style='font-family: monospace;'>Привіт Світ</p>
<pre id='p4' style='font-family: monospace;'>a%0abcdef</pre>
`,
async function(browser, accDoc) {
info("Testing simple LtR text");
if (isWinNoCache) {
ok(true, "skipping tests, running on windows without cache");
// We have to do this in at least one of these sub-tasks because
// otherwise the test harness complains this file is empty when
// it runs on windows without the cache enabled.
return;
}
await testTextNode(accDoc, browser, "p1");
await testTextNode(accDoc, browser, "p2");
await testTextNode(accDoc, browser, "p3");
await testTextNode(accDoc, browser, "p4");
},
{
iframe: true,
}
);
/**
* Test the partial text range boundary for LtR text
*/
addAccessibleTask(
`
<p id='p1' style='font-family: monospace;'>Tilimilitryamdiya</p>
<p id='p2' dir='ltr' style='font-family: monospace;'>Привіт Світ</p>
`,
async function(browser, accDoc) {
info("Testing partial ranges in LtR text");
await testTextRange(accDoc, browser, "p1", 0, 4);
await testTextRange(accDoc, browser, "p1", 2, 8);
await testTextRange(accDoc, browser, "p1", 12, 17);
await testTextRange(accDoc, browser, "p2", 0, 4);
await testTextRange(accDoc, browser, "p2", 2, 8);
await testTextRange(accDoc, browser, "p2", 6, 11);
},
{
topLevel: !isWinNoCache,
iframe: !isWinNoCache,
}
);
/**
* Test the text boundary for multiline LtR text
*/
addAccessibleTask(
`
<p id='p4' dir='ltr' style='font-family: monospace;'>Привіт Світ<br>Привіт Світ</p>
<p id='p5' dir='ltr' style='font-family: monospace;'>Привіт Світ<br> Я ще трохи тексту в другому рядку</p>
<p id='p6' style='font-family: monospace;'>hello world I'm on line one<br> and I'm a separate line two with slightly more text</p>
<p id='p7' style='font-family: monospace;'>hello world<br>hello world</p>
`,
async function(browser, accDoc) {
info("Testing multiline LtR text");
await testTextNode(accDoc, browser, "p4");
await testTextNode(accDoc, browser, "p5");
// await testTextNode(accDoc, browser, "p6"); // w/o cache, fails width (a 259, e 250), w/ cache wrong w, h in iframe (line wrapping)
await testTextNode(accDoc, browser, "p7");
},
{
topLevel: !isWinNoCache,
iframe: !isWinNoCache,
}
);
/**
* Test the text boundary for simple RtL text
*/
addAccessibleTask(
`
<p id='p1' dir='rtl' style='font-family: monospace;'>Tilimilitryamdiya</p>
<p id='p2' dir='rtl' style='font-family: monospace;'>ل</p>
<p id='p3' dir='rtl' style='font-family: monospace;'>لل لللل لل</p>
<pre id='p4' dir='rtl' style='font-family: monospace;'>a%0abcdef</pre>
`,
async function(browser, accDoc) {
info("Testing simple RtL text");
await testTextNode(accDoc, browser, "p1");
await testTextNode(accDoc, browser, "p2");
await testTextNode(accDoc, browser, "p3");
await testTextNode(accDoc, browser, "p4");
},
{
topLevel: !isWinNoCache,
iframe: !isWinNoCache,
}
);
/**
* Test the text boundary for multiline RtL text
*/
addAccessibleTask(
`
<p id='p4' dir='rtl' style='font-family: monospace;'>لل لللل لل<br>لل لللل لل</p>
<p id='p5' dir='rtl' style='font-family: monospace;'>لل لللل لل<br> لل لل لل لل ل لل لل لل</p>
<p id='p6' dir='rtl' style='font-family: monospace;'>hello world I'm on line one<br> and I'm a separate line two with slightly more text</p>
<p id='p7' dir='rtl' style='font-family: monospace;'>hello world<br>hello world</p>
`,
async function(browser, accDoc) {
info("Testing multiline RtL text");
await testTextNode(accDoc, browser, "p4");
if (!isCacheEnabled) {
await testTextNode(accDoc, browser, "p5"); // w/ cache fails x, w - off by one char
}
// await testTextNode(accDoc, browser, "p6"); // w/o cache, fails width (a 259, e 250), w/ cache fails w, h in iframe (line wrapping)
await testTextNode(accDoc, browser, "p7");
},
{
topLevel: !isWinNoCache,
iframe: !isWinNoCache,
}
);
/**
* Test the partial text range boundary for RtL text
*/
addAccessibleTask(
`
<p id='p1' dir='rtl' style='font-family: monospace;'>Tilimilitryamdiya</p>
<p id='p2' dir='rtl' style='font-family: monospace;'>لل لللل لل</p>
`,
async function(browser, accDoc) {
info("Testing partial ranges in RtL text");
await testTextRange(accDoc, browser, "p1", 0, 4);
await testTextRange(accDoc, browser, "p1", 2, 8);
await testTextRange(accDoc, browser, "p1", 12, 17);
await testTextRange(accDoc, browser, "p2", 0, 4);
await testTextRange(accDoc, browser, "p2", 2, 8);
await testTextRange(accDoc, browser, "p2", 6, 10);
},
{
topLevel: !isWinNoCache,
iframe: !isWinNoCache,
}
);

View File

@ -205,9 +205,10 @@ function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin) {
);
// x
is(
xObj.value,
isWithin(
expectedX,
xObj.value,
1,
"Wrong x coordinate of text between offsets (" +
aStartOffset +
", " +
@ -218,8 +219,8 @@ function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin) {
// y
isWithin(
yObj.value,
expectedY,
yObj.value,
1,
`y coord of text between offsets (${aStartOffset}, ${aEndOffset}) ` +
`for ${prettyName(aID)}`
@ -232,17 +233,22 @@ function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin) {
", " +
aEndOffset +
") for " +
prettyName(aID);
if (widthObj.value == expectedWidth) {
ok(true, msg);
prettyName(aID) +
" - Got " +
widthObj.value +
" Expected " +
expectedWidth;
if (!WIN) {
isWithin(expectedWidth, widthObj.value, 1, msg);
} else {
// fails on some windows machines
todo(false, msg);
} // fails on some windows machines
}
// Height
isWithin(
heightObj.value,
expectedHeight,
heightObj.value,
1,
`height of text between offsets (${aStartOffset}, ${aEndOffset}) ` +
`for ${prettyName(aID)}`

View File

@ -112,9 +112,9 @@ ia2AccessibleText::get_characterExtents(long aOffset,
? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
: nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
LayoutDeviceIntRect rect;
auto [textAcc, hr] = LocalTextAcc();
auto textAcc = TextAcc();
if (!textAcc) {
return hr;
return CO_E_OBJNOTCONNECTED;
}
rect = textAcc->CharBounds(aOffset, geckoCoordType);

View File

@ -215,16 +215,14 @@ xpcAccessibleHyperText::GetCharacterExtents(int32_t aOffset, int32_t* aX,
if (!mIntl) return NS_ERROR_FAILURE;
LayoutDeviceIntRect rect;
if (mIntl->IsLocal()) {
rect = IntlLocal()->CharBounds(aOffset, aCoordType);
} else {
#if defined(XP_WIN)
if (mIntl->IsRemote() &&
!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
return NS_ERROR_NOT_IMPLEMENTED;
#else
rect = mIntl->AsRemote()->CharBounds(aOffset, aCoordType);
#endif
}
#endif
LayoutDeviceIntRect rect = Intl()->CharBounds(aOffset, aCoordType);
rect.GetRect(aX, aY, aWidth, aHeight);
return NS_OK;
}
@ -242,16 +240,15 @@ xpcAccessibleHyperText::GetRangeExtents(int32_t aStartOffset,
if (!mIntl) return NS_ERROR_FAILURE;
LayoutDeviceIntRect rect;
if (mIntl->IsLocal()) {
rect = IntlLocal()->TextBounds(aStartOffset, aEndOffset, aCoordType);
} else {
#if defined(XP_WIN)
if (mIntl->IsRemote() &&
!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
return NS_ERROR_NOT_IMPLEMENTED;
#else
rect = mIntl->AsRemote()->TextBounds(aStartOffset, aEndOffset, aCoordType);
#endif
}
#endif
LayoutDeviceIntRect rect =
Intl()->TextBounds(aStartOffset, aEndOffset, aCoordType);
rect.GetRect(aX, aY, aWidth, aHeight);
return NS_OK;
}