зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1834874 - P1: Make text navigation work in UI thread with cached TextLeafPoint API. r=Jamie,geckoview-reviewers,m_kato
Differential Revision: https://phabricator.services.mozilla.com/D181320
This commit is contained in:
Родитель
0e87c46b2a
Коммит
85a5ad4790
|
@ -24,10 +24,12 @@
|
|||
#include "nsTextEquivUtils.h"
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "RootAccessible.h"
|
||||
#include "TextLeafRange.h"
|
||||
|
||||
#include "mozilla/a11y/PDocAccessibleChild.h"
|
||||
#include "mozilla/jni/GeckoBundleUtils.h"
|
||||
#include "mozilla/a11y/DocAccessibleParent.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
// icu TRUE conflicting with java::sdk::Boolean::TRUE()
|
||||
// https://searchfox.org/mozilla-central/rev/ce02064d8afc8673cef83c92896ee873bd35e7ae/intl/icu/source/common/unicode/umachine.h#265
|
||||
|
@ -37,6 +39,7 @@
|
|||
#endif
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
using mozilla::Maybe;
|
||||
|
||||
//-----------------------------------------------------
|
||||
// construction
|
||||
|
@ -298,100 +301,92 @@ void AccessibleWrap::ExploreByTouch(float aX, float aY) {
|
|||
}
|
||||
}
|
||||
|
||||
void AccessibleWrap::NavigateText(int32_t aGranularity, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward,
|
||||
bool aSelect) {
|
||||
a11y::Pivot pivot(RootAccessible());
|
||||
static TextLeafPoint ToTextLeafPoint(Accessible* aAccessible, int32_t aOffset) {
|
||||
if (HyperTextAccessibleBase* ht = aAccessible->AsHyperTextBase()) {
|
||||
return ht->ToTextLeafPoint(aOffset);
|
||||
}
|
||||
|
||||
HyperTextAccessible* editable =
|
||||
(State() & states::EDITABLE) != 0 ? AsHyperText() : nullptr;
|
||||
return TextLeafPoint(aAccessible, aOffset);
|
||||
}
|
||||
|
||||
Maybe<std::pair<int32_t, int32_t>> AccessibleWrap::NavigateText(
|
||||
Accessible* aAccessible, int32_t aGranularity, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward, bool aSelect) {
|
||||
int32_t startOffset = aStartOffset;
|
||||
int32_t endOffset = aEndOffset;
|
||||
if (startOffset == -1) {
|
||||
MOZ_ASSERT(endOffset == -1,
|
||||
"When start offset is unset, end offset should be too");
|
||||
startOffset = aForward ? 0 : nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT;
|
||||
endOffset = aForward ? 0 : nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT;
|
||||
}
|
||||
|
||||
int32_t start = aStartOffset, end = aEndOffset;
|
||||
// If the accessible is an editable, set the virtual cursor position
|
||||
// to its caret offset. Otherwise use the document's virtual cursor
|
||||
// position as a starting offset.
|
||||
if (editable) {
|
||||
start = end = editable->CaretOffset();
|
||||
if (aAccessible->State() & states::EDITABLE) {
|
||||
startOffset = endOffset = aAccessible->AsHyperTextBase()->CaretOffset();
|
||||
}
|
||||
|
||||
uint16_t pivotGranularity = nsIAccessiblePivot::LINE_BOUNDARY;
|
||||
TextLeafRange currentRange =
|
||||
TextLeafRange(ToTextLeafPoint(aAccessible, startOffset),
|
||||
ToTextLeafPoint(aAccessible, endOffset));
|
||||
uint16_t startBoundaryType = nsIAccessibleText::BOUNDARY_LINE_START;
|
||||
uint16_t endBoundaryType = nsIAccessibleText::BOUNDARY_LINE_END;
|
||||
switch (aGranularity) {
|
||||
case 1: // MOVEMENT_GRANULARITY_CHARACTER
|
||||
pivotGranularity = nsIAccessiblePivot::CHAR_BOUNDARY;
|
||||
startBoundaryType = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
endBoundaryType = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
break;
|
||||
case 2: // MOVEMENT_GRANULARITY_WORD
|
||||
pivotGranularity = nsIAccessiblePivot::WORD_BOUNDARY;
|
||||
startBoundaryType = nsIAccessibleText::BOUNDARY_WORD_START;
|
||||
endBoundaryType = nsIAccessibleText::BOUNDARY_WORD_END;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t newOffset;
|
||||
Accessible* newAnchorBase = nullptr;
|
||||
TextLeafRange resultRange;
|
||||
|
||||
if (aForward) {
|
||||
newAnchorBase = pivot.NextText(this, &start, &end, pivotGranularity);
|
||||
newOffset = end;
|
||||
resultRange.SetEnd(
|
||||
currentRange.End().FindBoundary(endBoundaryType, eDirNext));
|
||||
resultRange.SetStart(
|
||||
resultRange.End().FindBoundary(startBoundaryType, eDirPrevious));
|
||||
} else {
|
||||
newAnchorBase = pivot.PrevText(this, &start, &end, pivotGranularity);
|
||||
newOffset = start;
|
||||
}
|
||||
LocalAccessible* newAnchor =
|
||||
newAnchorBase ? newAnchorBase->AsLocal() : nullptr;
|
||||
|
||||
if (newAnchor && (start != aStartOffset || end != aEndOffset)) {
|
||||
if (IsTextLeaf() && newAnchor == LocalParent()) {
|
||||
// For paragraphs, divs, spans, etc., we put a11y focus on the text leaf
|
||||
// node instead of the HyperTextAccessible. However, Pivot will always
|
||||
// return a HyperTextAccessible. Android doesn't support text navigation
|
||||
// landing on an accessible which is different to the originating
|
||||
// accessible. Therefore, if we're still within the same text leaf,
|
||||
// translate the offsets to the text leaf.
|
||||
int32_t thisChild = IndexInParent();
|
||||
HyperTextAccessible* newHyper = newAnchor->AsHyperText();
|
||||
MOZ_ASSERT(newHyper);
|
||||
int32_t startChild = newHyper->GetChildIndexAtOffset(start);
|
||||
// We use end - 1 because the end offset is exclusive, so end itself
|
||||
// might be associated with the next child.
|
||||
int32_t endChild = newHyper->GetChildIndexAtOffset(end - 1);
|
||||
if (startChild == thisChild && endChild == thisChild) {
|
||||
// We've landed within the same text leaf.
|
||||
newAnchor = this;
|
||||
int32_t thisOffset = newHyper->GetChildOffset(thisChild);
|
||||
start -= thisOffset;
|
||||
end -= thisOffset;
|
||||
}
|
||||
}
|
||||
RefPtr<AccEvent> event = new AccVCChangeEvent(
|
||||
newAnchor->Document(), this, aStartOffset, aEndOffset, newAnchor, start,
|
||||
end, nsIAccessiblePivot::REASON_NONE, pivotGranularity, eFromUserInput);
|
||||
nsEventShell::FireEvent(event);
|
||||
resultRange.SetStart(
|
||||
currentRange.Start().FindBoundary(startBoundaryType, eDirPrevious));
|
||||
resultRange.SetEnd(
|
||||
resultRange.Start().FindBoundary(endBoundaryType, eDirNext));
|
||||
}
|
||||
|
||||
// If we are in an editable, move the caret to the new virtual cursor
|
||||
// offset.
|
||||
if (editable) {
|
||||
if (aSelect) {
|
||||
int32_t anchor = editable->CaretOffset();
|
||||
if (editable->SelectionCount()) {
|
||||
int32_t startSel, endSel;
|
||||
GetSelectionOrCaret(&startSel, &endSel);
|
||||
anchor = startSel == anchor ? endSel : startSel;
|
||||
}
|
||||
editable->SetSelectionBoundsAt(0, anchor, newOffset);
|
||||
} else {
|
||||
editable->SetCaretOffset(newOffset);
|
||||
}
|
||||
if (!resultRange.Crop(aAccessible)) {
|
||||
// If the new range does not intersect at all with the given
|
||||
// accessible/container this navigation has failed or reached an edge.
|
||||
return Nothing();
|
||||
}
|
||||
}
|
||||
|
||||
void AccessibleWrap::GetSelectionOrCaret(int32_t* aStartOffset,
|
||||
int32_t* aEndOffset) {
|
||||
*aStartOffset = *aEndOffset = -1;
|
||||
if (HyperTextAccessible* textAcc = AsHyperText()) {
|
||||
if (!textAcc->SelectionBoundsAt(0, aStartOffset, aEndOffset)) {
|
||||
*aStartOffset = *aEndOffset = textAcc->CaretOffset();
|
||||
}
|
||||
if (resultRange == currentRange || resultRange.Start() == resultRange.End()) {
|
||||
// If the result range equals the current range, or if the result range is
|
||||
// collapsed, we failed or reached an edge.
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
if (HyperTextAccessibleBase* ht = aAccessible->AsHyperTextBase()) {
|
||||
DebugOnly<bool> ok = false;
|
||||
std::tie(ok, startOffset) = ht->TransformOffset(
|
||||
resultRange.Start().mAcc, resultRange.Start().mOffset, false);
|
||||
MOZ_ASSERT(ok, "Accessible of range start should be in container.");
|
||||
|
||||
std::tie(ok, endOffset) = ht->TransformOffset(
|
||||
resultRange.End().mAcc, resultRange.End().mOffset, false);
|
||||
MOZ_ASSERT(ok, "Accessible range end should be in container.");
|
||||
} else {
|
||||
startOffset = resultRange.Start().mOffset;
|
||||
endOffset = resultRange.End().mOffset;
|
||||
}
|
||||
|
||||
return Some(std::make_pair(startOffset, endOffset));
|
||||
}
|
||||
|
||||
uint32_t AccessibleWrap::GetFlags(role aRole, uint64_t aState,
|
||||
|
|
|
@ -30,9 +30,6 @@ class AccessibleWrap : public LocalAccessible {
|
|||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
virtual bool PivotTo(int32_t aGranularity, bool aForward, bool aInclusive);
|
||||
|
||||
virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward, bool aSelect);
|
||||
|
||||
void ExploreByTouch(float aX, float aY);
|
||||
|
||||
static uint32_t GetFlags(role aRole, uint64_t aState, uint8_t aActionCount);
|
||||
|
@ -54,6 +51,10 @@ class AccessibleWrap : public LocalAccessible {
|
|||
static Accessible* DoPivot(Accessible* aAccessible, int32_t aGranularity,
|
||||
bool aForward, bool aInclusive);
|
||||
|
||||
static Maybe<std::pair<int32_t, int32_t>> NavigateText(
|
||||
Accessible* aAccessible, int32_t aGranularity, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward, bool aSelect);
|
||||
|
||||
protected:
|
||||
int32_t mID;
|
||||
|
||||
|
@ -61,8 +62,6 @@ class AccessibleWrap : public LocalAccessible {
|
|||
void GetTextEquiv(nsString& aText);
|
||||
|
||||
bool HandleLiveRegionEvent(AccEvent* aEvent);
|
||||
|
||||
void GetSelectionOrCaret(int32_t* aStartOffset, int32_t* aEndOffset);
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -225,14 +225,6 @@ void SessionAccessibility::ExploreByTouch(int32_t aID, float aX, float aY) {
|
|||
}
|
||||
}
|
||||
|
||||
void SessionAccessibility::NavigateText(int32_t aID, int32_t aGranularity,
|
||||
int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward,
|
||||
bool aSelect) {
|
||||
FORWARD_EXT_ACTION_TO_ACCESSIBLE(NavigateText, aGranularity, aStartOffset,
|
||||
aEndOffset, aForward, aSelect);
|
||||
}
|
||||
|
||||
static void GetSelectionOrCaret(HyperTextAccessibleBase* aHyperTextAcc,
|
||||
int32_t* aStartOffset, int32_t* aEndOffset) {
|
||||
if (!aHyperTextAcc->SelectionBoundsAt(0, aStartOffset, aEndOffset)) {
|
||||
|
@ -240,6 +232,80 @@ static void GetSelectionOrCaret(HyperTextAccessibleBase* aHyperTextAcc,
|
|||
}
|
||||
}
|
||||
|
||||
static void AdjustCaretToTextNavigation(Accessible* aAccessible,
|
||||
int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward,
|
||||
bool aSelect) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!(aAccessible->State() & states::EDITABLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HyperTextAccessibleBase* editable = aAccessible->AsHyperTextBase();
|
||||
MOZ_ASSERT(editable);
|
||||
if (!editable) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t newOffset = aForward ? aEndOffset : aStartOffset;
|
||||
if (aSelect) {
|
||||
int32_t anchor = editable->CaretOffset();
|
||||
if (editable->SelectionCount()) {
|
||||
int32_t startSel, endSel;
|
||||
GetSelectionOrCaret(editable, &startSel, &endSel);
|
||||
anchor = startSel == anchor ? endSel : startSel;
|
||||
}
|
||||
editable->SetSelectionBoundsAt(0, anchor, newOffset);
|
||||
} else {
|
||||
editable->SetCaretOffset(newOffset);
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionAccessibility::NavigateText(int32_t aID, int32_t aGranularity,
|
||||
int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward,
|
||||
bool aSelect) {
|
||||
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
|
||||
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
|
||||
RefPtr<SessionAccessibility> self(this);
|
||||
if (Accessible* acc = GetAccessibleByID(aID)) {
|
||||
if (acc->IsLocal()) {
|
||||
nsAppShell::PostEvent([this, self, aID, aGranularity, aStartOffset,
|
||||
aEndOffset, aForward, aSelect] {
|
||||
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
|
||||
if (Accessible* _acc = GetAccessibleByID(aID)) {
|
||||
auto result = AccessibleWrap::NavigateText(
|
||||
_acc, aGranularity, aStartOffset, aEndOffset, aForward, aSelect);
|
||||
|
||||
if (result) {
|
||||
SendTextTraversedEvent(_acc, result->first, result->second);
|
||||
AdjustCaretToTextNavigation(_acc, result->first, result->second,
|
||||
aForward, aSelect);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
auto result = AccessibleWrap::NavigateText(
|
||||
acc, aGranularity, aStartOffset, aEndOffset, aForward, aSelect);
|
||||
if (result) {
|
||||
nsAppShell::PostEvent([this, self, aID, result, aForward, aSelect] {
|
||||
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
|
||||
if (Accessible* _acc = GetAccessibleByID(aID)) {
|
||||
SendTextTraversedEvent(_acc, result->first, result->second);
|
||||
AdjustCaretToTextNavigation(_acc, result->first, result->second,
|
||||
aForward, aSelect);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SessionAccessibility::SetSelection(int32_t aID, int32_t aStart,
|
||||
int32_t aEnd) {
|
||||
if (Accessible* acc = GetAccessibleByID(aID)) {
|
||||
|
|
|
@ -57,7 +57,7 @@ class SessionAccessibility final
|
|||
void Click(int32_t aID);
|
||||
bool Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive);
|
||||
void ExploreByTouch(int32_t aID, float aX, float aY);
|
||||
void NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset,
|
||||
bool NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aForward, bool aSelect);
|
||||
void SetSelection(int32_t aID, int32_t aStart, int32_t aEnd);
|
||||
void Cut(int32_t aID);
|
||||
|
@ -107,6 +107,10 @@ class SessionAccessibility final
|
|||
|
||||
void SetAttached(bool aAttached, already_AddRefed<Runnable> aRunnable);
|
||||
|
||||
bool DoNavigateText(Accessible* aAccessible, int32_t aGranularity,
|
||||
int32_t aStartOffset, int32_t aEndOffset, bool aForward,
|
||||
bool aSelect);
|
||||
|
||||
jni::NativeWeakPtr<widget::GeckoViewSupport> mWindow; // Parent only
|
||||
java::SessionAccessibility::NativeProvider::GlobalRef mSessionAccessibility;
|
||||
|
||||
|
|
|
@ -1768,6 +1768,31 @@ bool TextLeafPoint::ContainsPoint(int32_t aX, int32_t aY) {
|
|||
return CharBounds().Contains(aX, aY);
|
||||
}
|
||||
|
||||
bool TextLeafRange::Crop(Accessible* aContainer) {
|
||||
TextLeafPoint containerStart(aContainer, 0);
|
||||
TextLeafPoint containerEnd(aContainer,
|
||||
nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT);
|
||||
|
||||
if (mEnd < containerStart || containerEnd < mStart) {
|
||||
// The range ends before the container, or starts after it.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mStart < containerStart) {
|
||||
// If range start is before container start, adjust range start to
|
||||
// start of container.
|
||||
mStart = containerStart;
|
||||
}
|
||||
|
||||
if (containerEnd < mEnd) {
|
||||
// If range end is after container end, adjust range end to end of
|
||||
// container.
|
||||
mEnd = containerEnd;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LayoutDeviceIntRect TextLeafRange::Bounds() const {
|
||||
if (mEnd == mStart || mEnd < mStart) {
|
||||
return LayoutDeviceIntRect();
|
||||
|
|
|
@ -277,11 +277,17 @@ class TextLeafRange final {
|
|||
return mEnd != aOther.mEnd || mStart != aOther.mStart;
|
||||
}
|
||||
|
||||
bool operator==(const TextLeafRange& aOther) const {
|
||||
return mEnd == aOther.mEnd && mStart == aOther.mStart;
|
||||
}
|
||||
|
||||
TextLeafPoint Start() const { return mStart; }
|
||||
void SetStart(const TextLeafPoint& aStart) { mStart = aStart; }
|
||||
TextLeafPoint End() const { return mEnd; }
|
||||
void SetEnd(const TextLeafPoint& aEnd) { mEnd = aEnd; }
|
||||
|
||||
bool Crop(Accessible* aContainer);
|
||||
|
||||
/**
|
||||
* Returns a union rect (in dev pixels) of all character bounds in this range.
|
||||
* This rect is screen-relative and inclusive of mEnd. This function only
|
||||
|
|
|
@ -184,6 +184,17 @@ class HyperTextAccessibleBase {
|
|||
return child ? LinkIndexOf(child) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the given a11y point into an offset relative to this hypertext.
|
||||
* Returns {success, offset}, where success is true if successful.
|
||||
* If unsuccessful, the returned offset will be CharacterCount() if
|
||||
* aIsEndOffset is true, 0 otherwise. This means most callers can ignore the
|
||||
* success return value.
|
||||
*/
|
||||
std::pair<bool, int32_t> TransformOffset(Accessible* aDescendant,
|
||||
int32_t aOffset,
|
||||
bool aIsEndOffset) const;
|
||||
|
||||
/**
|
||||
* Return text attributes for the given text range.
|
||||
*/
|
||||
|
@ -276,17 +287,6 @@ class HyperTextAccessibleBase {
|
|||
virtual nsTArray<int32_t>& GetCachedHyperTextOffsets() = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Transform the given a11y point into an offset relative to this hypertext.
|
||||
* Returns {success, offset}, where success is true if successful.
|
||||
* If unsuccessful, the returned offset will be CharacterCount() if
|
||||
* aIsEndOffset is true, 0 otherwise. This means most callers can ignore the
|
||||
* success return value.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -127,7 +127,7 @@ class GeckoTextMarkerRange final {
|
|||
* Return true if successfully cropped. false if the range does not intersect
|
||||
* with the container.
|
||||
*/
|
||||
bool Crop(Accessible* aContainer);
|
||||
bool Crop(Accessible* aContainer) { return mRange.Crop(aContainer); }
|
||||
|
||||
TextLeafRange mRange;
|
||||
};
|
||||
|
|
|
@ -509,29 +509,5 @@ NSValue* GeckoTextMarkerRange::Bounds() const {
|
|||
|
||||
void GeckoTextMarkerRange::Select() const { mRange.SetSelection(0); }
|
||||
|
||||
bool GeckoTextMarkerRange::Crop(Accessible* aContainer) {
|
||||
TextLeafPoint containerStart(aContainer, 0);
|
||||
TextLeafPoint containerEnd(aContainer,
|
||||
nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT);
|
||||
|
||||
if (mRange.End() < containerStart || containerEnd < mRange.Start()) {
|
||||
// The range ends before the container, or starts after it.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mRange.Start() < containerStart) {
|
||||
// If range start is before container start, adjust range start to
|
||||
// start of container.
|
||||
mRange.SetStart(containerStart);
|
||||
}
|
||||
|
||||
if (containerEnd < mRange.End()) {
|
||||
// If range end is after container end, adjust range end to end of
|
||||
// container.
|
||||
mRange.SetEnd(containerEnd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -297,18 +297,7 @@ public class SessionAccessibility {
|
|||
AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
|
||||
final boolean next =
|
||||
action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
|
||||
// We must return false if we're already at the edge.
|
||||
if (next) {
|
||||
if (mAtEndOfText) {
|
||||
return false;
|
||||
}
|
||||
if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD && mAtLastWord) {
|
||||
return false;
|
||||
}
|
||||
} else if (mAtStartOfText) {
|
||||
return false;
|
||||
}
|
||||
nativeProvider.navigateText(
|
||||
return nativeProvider.navigateText(
|
||||
virtualViewId, granularity, mStartOffset, mEndOffset, next, extendSelection);
|
||||
}
|
||||
return true;
|
||||
|
@ -401,9 +390,6 @@ public class SessionAccessibility {
|
|||
private int mFocusedNode = 0;
|
||||
private int mStartOffset = -1;
|
||||
private int mEndOffset = -1;
|
||||
private boolean mAtStartOfText = false;
|
||||
private boolean mAtEndOfText = false;
|
||||
private boolean mAtLastWord = false;
|
||||
private boolean mViewFocusRequested = false;
|
||||
|
||||
/* package */ SessionAccessibility(final GeckoSession session) {
|
||||
|
@ -623,9 +609,6 @@ public class SessionAccessibility {
|
|||
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
|
||||
mStartOffset = -1;
|
||||
mEndOffset = -1;
|
||||
mAtStartOfText = false;
|
||||
mAtEndOfText = false;
|
||||
mAtLastWord = false;
|
||||
mAccessibilityFocusedNode = sourceId;
|
||||
break;
|
||||
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
|
||||
|
@ -638,24 +621,6 @@ public class SessionAccessibility {
|
|||
case AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY:
|
||||
mStartOffset = event.getFromIndex();
|
||||
mEndOffset = event.getToIndex();
|
||||
// We must synchronously return false for text navigation
|
||||
// actions if the user attempts to navigate past the edge.
|
||||
// Because we do navigation async, we can't query this
|
||||
// on demand when the action is performed. Therefore, we cache
|
||||
// whether we're at either edge here.
|
||||
mAtStartOfText = mStartOffset == 0;
|
||||
final CharSequence text = event.getText().get(0);
|
||||
mAtEndOfText = mEndOffset >= text.length();
|
||||
mAtLastWord = mAtEndOfText;
|
||||
if (!mAtLastWord) {
|
||||
// Words exclude trailing spaces. To figure out whether
|
||||
// we're at the last word, we need to get the text after
|
||||
// our end offset and check if it's just spaces.
|
||||
final CharSequence afterText = text.subSequence(mEndOffset, text.length());
|
||||
if (TextUtils.getTrimmedLength(afterText) == 0) {
|
||||
mAtLastWord = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -716,8 +681,8 @@ public class SessionAccessibility {
|
|||
@WrapForJNI(dispatchTo = "gecko")
|
||||
public native void exploreByTouch(int id, float x, float y);
|
||||
|
||||
@WrapForJNI(dispatchTo = "gecko")
|
||||
public native void navigateText(
|
||||
@WrapForJNI(dispatchTo = "current")
|
||||
public native boolean navigateText(
|
||||
int id, int granularity, int startOffset, int endOffset, boolean forward, boolean select);
|
||||
|
||||
@WrapForJNI(dispatchTo = "gecko")
|
||||
|
|
Загрузка…
Ссылка в новой задаче