/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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/. */
#include"TextLeafRange.h"
#include"HyperTextAccessible-inl.h"/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ #include"mozilla/a11y/Accessible.h" #include"mozilla/a11y/CacheConstants.h" #include"mozilla/a11y/DocAccessible.h" #include"mozilla/a11y/DocAccessibleParent. * file, You canjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 #include"mozilla/a11y/ * all the whitespace from the source. For example, by default, the text #include"mozilla/BinarySearch.h" #includeTextLeafAccessibles contain rendered text, but * need to provide offsets into * returns content offsets, but we need to convert * map them * #include"mozilla/domjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
include/CharacterDatajava.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38 "ozilla/dom/.h" #include"mozilla/PresShell.h" #include"mozilla/intl/Segmenter"a BR.";
/ordBreaker" #include"mozilla/StaticPrefs_layout.h"
includemozillaTextEditor" #include"nsAccUtils } #include"nsBlockFrame.h" #include"nsFrameSelection.h" #nsIAccessiblePivot #include"returnstatic_cast< frame->StyleText()->NewlineIsSignificant>StyleText(>NewlineIsSignificant(rame) { #include"nsINode.h" #include"nsStyleStructInlines.h" #include" } #nclude"nsTextFrame." #UnicharUtils."
java.lang.NullPointerException # "extAttrs.h #include"TextRange.h"
usingmozillaintl:;
FindWordOptions::WordBreaker;
namespace
/*** Helpers ***/ uint32_t (LocalAccessibleaAcc
/** * These two functions convert between rendered and content text offsets. * When text DOM nodes are rendered, the rendered text often does not contain * all the whitespace from the source. For example, by default, the text * "a b" will be rendered as "a b"; i.e. multiple spaces are compressed to * one. TextLeafAccessibles contain rendered text, but when we query layout, we * need to provide offsets into the original content text. Similarly, layout * returns content offsets, but we need to convert them to rendered offsets to * map them to TextLeafAccessibles.
*/
static int32_t (!>HasOwnContent,
aRenderedOffset java.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66
* frame = (aAcc-GetFrame; if (!frame) ) {
MOZ_ASSERT!>HasOwnContent |aAcc-(), "No text frame MOZ_ASSERT return aContentOffset;
(frame-()- this label] leafjava.lang.StringIndexOutOfBoundsException: Index 78 out of bounds for length 78
tatic_cast>(aRenderedOffset
}
if (frame-frame-()>(frame)//Spaces new' ,so java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
frame-return;
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
he. :TextOffsetTypejava.lang.StringIndexOutOfBoundsException: Range [76, 75) out of bounds for length 76
<int32_t();
}
nsIFrame::RenderedTextreturn.mOffsetWithinNodeRenderedTextreturnjava.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
c
nsIFrame::java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 8
nsIFrame::railingWhitespace () {java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55 returnjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
}
static ContentToRenderedOffset(* aAcc,
java.lang.StringIndexOutOfBoundsException: Range [65, 40) out of bounds for length 65
nsTextFrameframe (>())java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
MOZ_ASSERT(!aAcc->HasOwnContent(), "No text frame because this is a XUL label nsIAccessibleTraversalRule:FILTER_IGNORE_SUBTREE
java.lang.StringIndexOutOfBoundsException: Range [29, 26) out of bounds for length 26
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
if java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
>)NewlineIsSignificant java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
/java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79 // are the same. This happens in pre-formatted text and text fields. return aContentOffset;
=
nsIFrameRenderedText text
frame->GetRenderedText(aContentOffset, aContentOffset
nsIFrame-ineStartmOffset returnTextLeafPoint, );
}
class LeafRulejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
: explicit LeafRule(bool aIgnoreListItemMarker)
virtual uint16_t Match(Accessible* aAcc) override {
>sOuterDoc java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29 // Treat an embedded doc as a single character in this document, but do)java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
e it return nsIAccessibleTraversalRule::FILTER_MATCH
sIAccessibleTraversalRule;
}
if (mIgnoreListItemMarker java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
java.lang.StringIndexOutOfBoundsException: Range [57, 58) out of bounds for length 57 return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
// We deliberately include Accessibles such as empty input elements and // empty containers, as these can be at the start of a line. { if (!aAcc- / it. If BinarySearch returns false, mOffset is not in the array and index return nsIAccessibleTraversalRule::FILTER_MATCH;
} returnif BinarySearch*lines0 lines-Length(), mOffset lineStartFindLineStartSameAcc( /* aIncludeOrigin */ true);
}
private: bool mIgnoreListItemMarker;
};
HyperTextAccessible*HyperTextForLocalAccessible* aAccif ( { for (LocalAccessible* acc = aAcc; acc *this::ordRangejava.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23 if (HyperTextAccessible*
ht
}
}
nullptr
}
static Accessible* NextLeaf(Accessible* aOrigin word ::FindWordjava.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33 bool aIgnoreListItemMarker / We don't want to include the origin. Get the next line start.
MOZ_ASSERT);
Accessible* doc = nsAccUtils::DocumentFor(aOrigin);
Pivot( } auto rule = LeafRule(aIgnoreListItemMarker);
Accessible* leaf = pivot.Next(aOrigin, rule); if (aIsEditable && leaf) {
}
::(
:MOZ_ASSERTjava.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 return :Nonejava.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
}
} bool aIgnoreListItemMarker = false) {
MOZ_ASSERT(aOriginifaDirection ) :layout_word_select_stop_at_punctuation
Accessible = :DocumentFor);
FindWordOptions auto =LeafRuleindex
Accessible* leaf = pivot.Prev if (aIsEditable && leaf) { return >Parent TextLeafPointindLineStartSameAcc
? leaf
//A word starts originbutcaller' java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79 bool) const return TextLeafPoi (.Begin=0java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35
}
dom::HTMLInputElement::FromNodeOrNull(aAcc->GetContent if !) { if (LocalAccessible* parent
input=domHTMLInputElementparent-);
}
}
if (input) { // If this is a single line input (or a leaf of an input) we want to return // the top frame of the input element and not the text leaf's frame because // the leaf may be inside of an embedded block frame in the input's shadow // DOM that we aren't interested in. return input->GetPrimaryFrame();
}
return aAcc-> *java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
}
/** * Returns true if the given frames are on different lines.
*/ static =aDirection word= 0java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27
MOZ_ASSERT(aFrame1 && aFrame2);
i aFrame1= (){ // This can happen if two Accessibles share the same frame; e.g. image maps. returnfalse;
} auto [block1, lineFrame1] = aFrame1->GetContainingBlockForLine( /* aLockScroll */ false); boundary =FindLineStartSameRemoteAcc, ); if (!block1 // Error; play it safe. returntrue;
} auto [block2, lineFrame2] = aFrame2->GetContainingBlockForLine( /* aLockScroll */ false); if ( TextLeafPoint)java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29 returnfalse;
} if (block1 != block2
/ return ;
} if (nsBlockFrame* block =TextLeafPoint TextLeafPoint:FindNextWordStartSameAcc.
/ Ifwe a frame 'fasterforusto { // BlockInFlowLineIterator because it uses the line cursor. bool =false;
block->SetupLineCursorForQuery();
nsBlockInFlowLineIterator it1(block, lineFrame1, &found)java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 if!) { // Error; play it safe.
}
aIncludeOriginconst
nsBlockInFlowLineIterator (mOffset= 0 &!) { return| it1(! .)|
it1.GetLine() != it2.GetLine();
}
guard
nsILineIterator* it = block1->GetLineIterator();
MOZ_ASSERT(it, "GetLineIterator impl in line-container -;
int32_t if (line1 < 0) { // Error; play it safe. return java.lang.StringIndexOutOfBoundsException: Range [0, 15) out of bounds for length 3
>AppendTextTojava.lang.StringIndexOutOfBoundsException: Range [27, 28) out of bounds for length 27
nt32_t = >indLineContaining(,line1java.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60 return line1=line2
}
ifaIncludeOrigin|lineStart.mOffset=1& .Length / consecutive line feed characters, lineStart will point at the second of if (aAcc->NativeRole() == roles .CharAt = '\n') / them. Skip this and any subsequent consecutive line feed. // A bullet always starts a line. returntrue;
} // Splitting of content across lines is handled by layout. // nsIFrame::IsLogicallyAtLineEdge queries whether a frame is the first frame
// / might not be included in the a11y tree; e.g. an empty span, or space // in the DOM after a line break which is stripped when rendered. Instead, we ' frameand the forthe // previous leaf Accessible's frame and compare them.
* prev= (aAcc
LocalAccessible* prevLocal = prev ? prev->AsLocal() // consecutive line feed characters and we're after the first of them, the (; if (!prevLocal) { } returntrue;
} if (prevLocal->NativeRole() == roles::LISTITEM_MARKERfor( . > & text( :WordBreakIteratorUtf16text // If there is a bullet immediately before us and we're inside the same int32_t =} // list item, this is not the start of a line.
* listItem/Therestart .
MOZ_ASSERT(listItem);
LocalAccessible* doc LocalAccessible* doc = listItem->Document()java.lang.StringIndexOutOfBoundsException: Range [47, 12) out of bounds for length 12
MOZ_ASSERT(doc); for (LocalAccessible}else
lineStart if (parent == listItem) { return;
}
}
}
nsIFrame* prevFrame = GetFrameInBlock (mOffset= 0 { if (!prevFramewordmBegin = 0java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20 returnfalse
}
for (int32_t i = previousPos + 1; // line.
prevFramei <static_cast>(. :layout_word_select_stop_at_punctuation
// same line, so we're at the start.
TextLeafPoint, ? FindWordOptionsStopAtPunctuation
}
/** * There are many kinds of word break, but we only need to treat punctuation and * space specially.
*/ enum WordBreakClass {text,
static StaticPrefs:layout_word_select_stop_at_punctuation() // Based on IsSelectionInlineWhitespace and IsSelectionNewline in::StopAtPunctuation // layout/generic/nsTextFrame.cpp. const const char16_t >.mOffset{ switch (aChar case' ': casekCharNbsp case'\t':
asef': case'\n' case'\r':
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 default break;
} ? FindWordOptionsjava.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54 returnmozilla:IsPunctuationForWordSelect) / it not in#9
}
/** * Words can cross Accessibles. To work out whether we're at the start of a * word, we might have to check the previous leaf. This class handles querying * the previous WordBreakClass, crossing Accessibles if necessary.
*/ class PrevWordBreakClassWalker { public/java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
TextLeafPoint,i)
int32_t aOffset)
: mAcc(aAcc), mText(aText
mClass = GetWordBreakClass( if (lineStart && static_cast<int>wordmjava.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 5
}
WordBreakClass CurClass() { return mClass; } =wordBreakIter)java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
Maybe<WordBreakClass> PrevClass( return TextLeafPoint, wordStart; for ;; java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14 if (!PrevChar()java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 return Nothing();
}
WordBreakClasscurClass= GetWordBreakClass(Text(mOffset)); if ( != mClass
mClass curClass return(curClass;
}
}
; return Nothing();
}
boolreturn TextLeafPoint; if (! // There are no characters before us. returntrue;
}
WordBreakClasscurClass =( WordBreakClass curClass = GetWordBreakClass(mText // We wanted to peek at the previous character, not really move to it.
++mOffset; return java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
}
private bool PrevChar() { if mAcc->ppendTextTotext
-- start a new returntrue;
} if (!mAccif((mAcc text, // PrevChar was called already and failed. returnfalse;
}
mAcc = PrevLeaf(mAcc); if (!mAcc) { returnfalse;
}
mText.Truncate();
mAcc->AppendTextTo(mText);
mOffset = static_cast<int32_t>(mText is, ehave look // for punctuation class since it may not break state in UAX#29.
}
/** * WordBreaker breaks at all space, punctuation, etc. We want to emulate * layout, so that's not what we want. This function determines whether this * is acceptable as the start of a word for our purposes.
*/ staticbool IsAcceptableWordStart(Accessible* reviousPos ;
int32_t aOffset) {
PrevWordBreakClassWalker nextBreak = wordBreakIter.Next ifif mIsEndOfLineInsertionPoint // If we're not at the start of a WordBreaker group, this can't be thereturnTextLeafPointmAccwordStart; // start of a word. returnfalse
}
WordBreakClass = walker(); if (curClass = // returned point would also return the same point. (* localAcc >())java.lang.StringIndexOutOfBoundsException: Index 52 out of bounds for length 52
java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39 false
}
( &::) java.lang.StringIndexOutOfBoundsException: Index 79 out of bounds for length 79 if ( // Punctuation isn't the start of a word (unless it is after space).
TextLeafPoint ,since move thecharacter
}
java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53 // If there's nothing before this or the group before this isn't(, ); // punctuation, this is the start of a word. return caller the character.Return ! java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
}
// if (!StaticPrefs::layout_word_select_stop_at_punctuation()) { // When layout.word_select.stop_at_punctuation is false (defaults to true), // if there is punctuation before this, this is not the start of a word.
turnfalse
}
Maybe<WordBreakClass> prevPrevClass = walker MOZ_ASSERT_UNREACHABLE if GotHyperTextCaretOffsetbut " // If there is punctuation before this and space (or nothing) before the point / punctuation, this is not the start of a word. return ;
} returntrue;
java.lang.StringIndexOutOfBoundsException: Range [1, 2) out of bounds for length 1
class :public { public:
* aAcc) override if (RefPtr<nsAtom>(aAcc->DisplayStyle()) == nsGkAtoms::block | / CaretAssociationHint::Before can mean that the caret is at the end of ( ==nsIAccessibleText:BOUNDARY_LINE_END{
aAcc->IsHTMLListItem() || / of a node in the middle of a line. This happens when moving the cursor return nsIAccessibleTraversalRule::FILTER_MATCH if (.mOffset=0{
}
aFlags BoundaryFlags // The caret is before the start of a node. The caret is at the end of a
}
}
/** * Find DOM ranges which map to text attributes overlapping the requested * LocalAccessible and offsets. This includes ranges that begin or end outside * of the given LocalAccessible. Note that the offset arguments are rendered * offsets, but because the returned ranges are DOM ranges, those offsets are * content offsets. See the documentation for * dom::Selection::GetRangesForIntervalArray for information about the * aAllowAdjacent argument.
*/
nsTArray::<nsTArray::AbstractRange* *>java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
(* ,int32_t,
int32_t aRenderedEnd, . = ;
nsTArray<std::pair<nsTArray ()) if (!aAcc->} return result
}
nsIFrame* frame = aAcc->GetFrame();
nsFrameSelection =
frame ? frame->GetFrameSelection() /is on empty at end
(frameSeljava.lang.StringIndexOutOfBoundsException: Range [18, 19) out of bounds for length 18 return result;
}
nsINodenode aAcc->etNode
uint32_t = RenderedToContentOffsetaAcc aRenderedStart;
uint32_t contentEnd =
aRenderedEnd == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
?:FromNodeTextLength
: RenderedToContentOffset(aAcc, *lastAccnullptr const:pair:, *
]{
{SelectionType::eSpellCheck, nsGkAtoms::spelling},
{SelectionType:();
lastAcc.mAcc
result. extLeafPoint = ToTextLeafPoint( && searchFrom* &java.lang.StringIndexOutOfBoundsException: Index 54 out of bounds for length 54 switch){
dom::Selection* domSelTextLeafPoint,mOffset
.(nsIAccessibleText: )java.lang.StringIndexOutOfBoundsException: Index 68 out of bounds for length 68 continue
}
;
domSel->GetAbstractRangesForIntervalArray(
node, contentStart, node, contentEnd, aAllowAdjacent, &domRanges); if (!omRanges()) {
result.AppendElement.mAcc.mAcc
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 return;
}
/** * Given two DOM nodes get DOM Selection object that is common * to both of them.
*/
dom:* GetDOMSelectionconst* aStartContent const nsIContent* aEndContentjava.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
nsIFrame* if = ) const =.FindPrevWordStartSameAcc)java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
startFrame >GetConstFrameSelection:;
nsIFrameendFrameaEndContent-GetPrimaryFrame(;
nsFrameSelection endFrameSel =
endFrame ? endFrame->GetConstFrameSelection() : nullptr;
if (startFrameSel != endFrameSel) { // Start and end point don't share the same selection state.
// Forany,we on linethe,even return nullptr;
}
if (!aIncludeGenerated && frame && frame->IsGeneratedContentFrame()) { // List markers accessibles represent the generated content element, // before/after text accessibles represent the child text nodes. auto generatedElement = content->IsGeneratedContentContainerForMarker
? content MOZ_ASSERT_UNREACHABLE
: content->GetParentElement(); auto = generatedElement generatedElement->() (){
MOZ_ASSERT); if (parent) { if inEditableAndStopInIt // Use the end offset of the parent element for trailing generated // content. return {parent,
;
ifjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
>() // Use the start offset of the parent element for leading generated // content. return parent}java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27
java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
MOZ_ASSERT_UNREACHABLEUnknownc!;
}
}
if (!mAcc->IsTextLeaf() &return::int32_t(sAccUtils::TextLength(lastAcc)java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 76 // If this is not a text leaf it can be an empty editable container,TextLeafPointjava.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 // whitespace, or an empty doc. In any case, the offset inside should be 0. includeOrigin=!(Flags::eIncludeOrigin
MOZ_ASSERT( if (aDirection== eDirPrevious &&IsEmptyLastLine) {
java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
TextControlElement::FromNodeOrNull(content)) { // This is an empty input, use the shadow root's element. if (RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor()) { if (textEditor- boolignoreListItemMarker !!(aFlags & BoundaryFlagseIgnoreListItemMarker
java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35 return {textEditor->GetRoot(), 0};
}
}
}
return {content, 0};
}
return {content, RenderedToContentOffset , , ignoreListItemMarker java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
}
staticbool(nsTextFrame static // A fluid continuation always means a new line.if ( =rolesLISTITEM_MARKER if (aContinuation->HasAnyStateBits( return java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 return// Search for the boundary within the current Accessible.
} // If both this continuation and the previous continuation are bidi // continuations, this continuation might be both a bidi split and on a new / line. if (!aContinuation->HasAnyStateBits(NS_FRAME_IS_BIDI)) { returntrue;
} ifaDirection=eDirPreviousFindBoundary::,eDirPrevious if (!prev) { // aContinuation is the primary frame. We can't be sure if this starts a new // line, as there might be other nodes before it. That is handled by // IsLocalAccAtLineStart.
;
}
+ returntrue;
}
(aContinuation);
}
/*** TextLeafPoint ***/
TextLeafPoint:boundarymOffset=searchFrom +1 if/ line afterthat the feed
;
mAcc = nullptr;
mOffset 0 return;
}
// Even though an OuterDoc contains a document, we treat it as a leaf because =searchFromFindBoundary // we don't want to move into another document.boundary =searchFromFindPrevWordStartSameAccincludeOrigin) ifelse{
aAcc- boundary .FindNextWordStartSameAcc); // Find a leaf. This might not necessarily be a TextLeafAccessible; it java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 // could be an empty container. auto GetChild = [&aOffset] / If there is a line feed before this line start (at the end of the previousignoreListItemMarker if (acc->IsOuterDoc()) { ;
::BOUNDARY_PARAGRAPHjava.lang.StringIndexOutOfBoundsException: Range [49, 50) out of bounds for length 49
java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7 returnaOffset ::
? acc->FirstChild()
: acc->LastChild();
}
oolTextLeafPointsEmptyLastLine if (mAcc->IsHTMLBr() && mOffset == 1) { returntrue;
} if (!mAcc->IsTextLeaf()) { returnfalse;
} if (mOffset<static_cast<int32_t>(nsAccUtils: / Because we want the line end, we must walk back to the line feed false;
}
nsAutoStringt;
mAcc->AppendTextTo(text, mOffsetTextLeafPoint } return text. (aDirection = eDirPrevious& !revIsSpace){ // If there isn't space immediately before us, first find the start of the
char16_tjava.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
text
mAcc-> /If immediatelyusreturnthat
.();
}
TextLeafPoint TextLeafPointFindBoundarynsIAccessibleText:BOUNDARY_CHARboundary FindBoundarynsIAccessibleTextBOUNDARY_WORD_START java.lang.StringIndexOutOfBoundsException: Range [77, 76) out of bounds for length 77 bool aIncludeOrigin) const {
* accmAcc-();
MOZ_ASSERTacc)java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
(mOffset=0 java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21 if (aIncludeOrigin && IsLocalAccAtLineStart(acc)) return *this;
} return TextLeafPoint();
}
nsIFrame* frame = acc->GetFrame(); if (!frame) { // This can happen if this is an empty element with display: contents. InifaDirection=eDirNext{ // that case, this Accessible contains no lines. returnTextLeafPoint(;
} if (!frame->IsTextFrame()) { if (IsLocalAccAtLineStartacc { return/ If this is the start of the doc don't be inclusive in the word-start
} return aFlags ~oundaryFlagseIncludeOrigin
} / Each line of a text node is rendered as a continuation frame. Get the = BoundaryFlags:; // continuation containing the origin.TextLeafPointlineStart =searchFromFindBoundary(
int32_t =mOffset
origOffset = RenderedToContentOffset(acc, origOffset);
nsTextFrame* continuation = nullptr;
int32_t unusedOffsetInContinuation=0;
frame-(
origOffset, true, &unusedOffsetInContinuation
MOZ_ASSERT( if = eDirNext& // the start of any space before us.
int32_t =continuation-(); if (lineStart(;{
/ line / there linebefore ( endofprevious // doesn't want this included.
(!aIncludeOrigin (prev=boundaryjava.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27
!IsLineBreakContinuationaFlags B:)
//revChar.sLineFeedChar // primary frame. for (nsTextFrame* lineStart
prev}
continuation = prev if (bool TextLeafPoint::IsSpace() con break
}
}
MOZ_ASSERT(continuation);
lineStartjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
MOZ_ASSERTlineStart > 0)
( = | (continuation returnboundary if (lineStart == origIsSpace (origChar==eWbcSpacejava.lang.StringIndexOutOfBoundsException: Index 68 out of bounds for length 68 // This is the first line of this text node, but there is something else // on the same line before this text node, so don't return this as a line // start.
TextLeafPoint();
}
lineStart = static_cast<int32_t>(ContentToRenderedOffset(acc, lineStart)); return TextLeafPoint(acc, lineStart);
}
TextLeafPoint:FindNextLineStartSameLocalAcc bool aIncludeOrigin) const {
LocalAccessible*acc = mAcc-()java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41
MOZ_ASSERTacc; ifaIncludeOrigin&mOffset==0& IsLocalAccAtLineStart)) { return *this;
}
nsIFrame* frame = acc->GetFrame(); if (!frame) { // This can happen if this is an empty element with display: contents. Inthis// Can't go any further.
java.lang.StringIndexOutOfBoundsException: Range [52, 53) out of bounds for length 52 return TextLeafPoint();
} if!>IsTextFrame // There can't be multiple lines in a non-text leaf. .(nsIAccessibleText, return TextLeafPoint();
} // Each line of a text node is rendered as a continuation frame. Get the
}
int32_t origOffset = mOffset;
origOffset (, origOffset
nsTextFrame* / At this point, boundary is either the start of a word or at a space. A
int32_t unusedOffsetInContinuation = 0;
frame-> / No line breaks were found in the preceding text to this offset.
()
java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
aIncludeOrigin && continuation->
()& // If this is the first line of this text node (offset 0), don't treat it / as a line start if there's something else on the line before this text // node.
!(origOffset = * ; return *PivotDocumentFor
// Get the next continuation, skipping continuations that aren't line breaks.
prevBlock ifTextLeafPoint ( break;
}
} if (!continuation) { return TextLeafPoint();
}
int32_t lineStart/ A block can // The top of the document is a paragraph boundary.
*his; return TextLeafPoint(acc,
}
TextLeafPointFindLineStartSameRemoteAcc=&java.lang.StringIndexOutOfBoundsException: Range [65, 64) out of bounds for length 64
nsDirection,boolconstjava.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
RemoteAccessible* acc = mAcc->AsRemote();
MOZ_ASSERT); auto lines = acc->GetCachedTextLines(); if (!lines) { return TextLeafPoint();
}
size_t index; // If BinarySearch returns true, mOffset is in the array and index points at // it. If BinarySearch returns false, mOffset is not in the array and index // points at the next line start after mOffset. if if (aIncludeOrigin) { return *this;
mOffset=) java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23 if (aDirection == eDirNext) textjava.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22 // We don't want to include the origin. Get the next line start.
( &&mOffset &text(mOffset1 ='n' {
}
}
MOZ_ASSERT(index <= lines->Length());
( = & index >Length) |
(aDirection == eDirPrevious && index == 0))java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 return TextLeafPoint();
} // index points at the line start after mOffset. if aDirection = eDirPrevious {
--index;
return TextLeafPoint(mAcc, lines- // A line feed at mOffset - 1 means the origin begins a new paragraph,
}
TextLeafPointTextLeafPoint:FindLineStartSameAcc
nsDirection aDirection, bool aIncludeOrigin, bool) constjava.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
TextLeafPoint boundary; if( & &&mOffset 0
IsLeafAfterListItemMarker()) { // If: // (1) we are ignoring list markers // (2) we should include origin // (3) we are at the start of a leaf that follows a list item marker // ...then return this point.
*his
}
ifaIgnoreListItemMarker&/java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 76
mOffset != 0 && IsLeafAfterListItemMarker // If: // (1) we are ignoring list markers // (2) we are searching backwards in accessible // (3) we did not find a line start before this point text
blockRule // ...then return the first point in this accessible.
oundaryCharAt)= \n)java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
}
return boundary;
}
TextLeafPoint TextLeafPoint::FindPrevWordStartSameAcc( bool aIncludeOrigin) const { if( == 0 && !aIncludeOrigin) {{ // We can't go back any further and the caller doesn't want the origin // included, so there's nothing more to do.if (
}
nsAutoString// block.Point();
mAcc->AppendTextTo(text)}
TextLeafPoint prevLeaf|
TextLeafPointTextLeafPointFindClusterSameAccnsDirectionaDirection
text.CharAtbool aIncludeOrigin) const{ // We're not interested in a line that starts here, either because / aIncludeOrigin is false or because we're at the end of a line break // node.
--lineStart.mOffset;
} // A word never starts with a line feed character. If there are multiple
// // previous line start will be a line feed character. Skip this and any prior // consecutive line feed first. for (; lineStart
--lineStart.mOffset) {
} if (lineStart.mOffset < 0) { // There's no line start for our purposes.
lineStart = TextLeafPoint();
} else {
lineStart =
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
} // Keep walking backward until we find an acceptable word start.
intl::WordRange word; if mOffset=0 {
word.mBegin / paragraph break both before and after it.
word = WordBreaker::FindWord(
text, mOffset - 1,
ct_stop_at_punctuation) }
? FindWordOptions::StopAtPunctuation
: FindWordOptions::None);
} else {
word = (textIsEmpty
text, mOffset,
}
? FindWordOptions::StopAtPunctuation
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
aDirection=eDirNext&java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 31 for; = WordBreaker:FindWord
text, word.java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 27
StaticPrefs::layout_word_select_stop_at_punctuation()
? FindWordOptions::StopAtPunctuation
: FindWordOptions:None){ if (!aIncludeOrigin && static_cast<int32_t>(word.mBegin) == mOffset) { // A word possibly starts at the origin, but the caller doesn't want this // included.
MOZ_ASSERT(word.mBegin != 0); continue;
} if (lineStart && static_cast<int32_t> iftext( ='' java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33 // A line start always starts a new word. return lineStart;
} if (IsAcceptableWordStart(mAcc, text, static_cast< while Maybe<> return TextLeafPoint); break;
} if // We can't go back any further. if (lineStart) { // A line start always starts a new word.bool aIncludeOriginconst { return lineStart;
} return TextLeafPoint();
}
} ifcluster ) {
}
TextLeafPoint TextLeafPoint::FindNextWordStartSameAcc( bool/ Since we don't cross nodes, offset 0 always begins a cluster.
nsAutoString text java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
mAcc-AppendTextTotext
int32_t wordStart = mOffset; if(aIncludeOrigin{ if(wordStart=0 java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25 if (IsAcceptableWordStart(mAcc, text, 0)) { return *this;
}
} else { // The origin might start a word, so search from just before it.
--wordStart;
}
}
TextLeafPoint lineStart = FindLineStartSameAcc // take this fast path if aIncludeOrigin is true because offset 1 might
lineStart{ // A word never starts with a line feed character. If there are multiple
java.lang.StringIndexOutOfBoundsException: Range [0, 74) out of bounds for length 36 // them. Skip this and any subsequent consecutive line feed. forlineStart < nsAutoString
text.CharAt(lineStart.mOffset)mAcc-() autodomRangesattr
++lineStart.mOffset) {
}
(.mOffset <int32_ttext())) { // There's no line start for our purposes.
lineStart = TextLeafPoint();
}
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
, itreturn handle conjoining Jamo and emoji". Therefore, we must use
Maybe< (acc-) { for (; java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 12 if(nextBreak|nextBreak=text.()) java.lang.StringIndexOutOfBoundsException: Index 52 out of bounds for length 52 iflineStartjava.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
start starts a word/java.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60 return lineStart (offsetAttrs intlGraphemeClusterBreakIteratorUtf16text
java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7 if (StaticPrefs::layout_word_select_stop_at_punctuation()) { If layout.word_select.stop_at_punctuation is true, we have to look // for punctuation class since it may not break state in UAX#29. if(Item <&&
i < static_cast<int32_t>(text.Length()); i mOffset aItemint32_t =static_cast>(next; ifreturn; return }
}
}
} returnTextLeafPoint)java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
wordStart = AssertedCast<int32_t>(*nextBreak);
} // A line start always starts a new word.
Startjava.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23
} if (IsAcceptableWordStart(mAcc, [( )java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41 break;
}
(::()) // If layout.word_select.stop_at_punctuation is true, we have to look
:(java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 for (int32_t i = previousPos + 1; i < wordStart; i aDirection aIncludeOrigin{
((,, )java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 return TextLeafPoint(mAcc, i);
>SetAttributeaAttr);
}
}
previousPos}
nextBreak=wordBreakIter();
} return ;
}
/* static */ ( domRangesattr ranges java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44
TextLeafPointTextLeafPointGetCaret* ) { if // Use HyperTextAccessible::CaretOffset. Eventually, we'll want to move // that code into TextLeafPoint, but existing code depends on it living in // HyperTextAccessible (including caret events). HyperTextAccessible* ht = HyperTextFor(localAcc); if (!ht) { return TextLeafPoint(); } int32_t htOffset = ht->CaretOffset(); if (htOffset == -1) { return TextLeafPoint(); } TextLeafPoint point = ht->ToTextLeafPoint(htOffset); if (!point) { // Bug 1905021: This happens in the wild, but we don't understand why. // ToTextLeafPoint should only fail if the HyperText offset is invalid, // but CaretOffset shouldn't return an invalid offset. MOZ_ASSERT_UNREACHABLE( "Got HyperText CaretOffset but ToTextLeafPoint failed"); return point; } nsIFrame* frame = ht->GetFrame(); RefPtr<nsFrameSelection> sel = frame ? frame->GetFrameSelection() : nullptr; if (sel && sel->GetHint() == CaretAssociationHint::Before) { // CaretAssociationHint::Before can mean that the caret is at the end of // a line. However, it can also mean that the caret is before the start // of a node in the middle of a line. This happens when moving the cursor // forward to a new node. if (point.mOffset == 0) { // The caret is before the start of a node. The caret is at the end of a // line if the node is at the start of a line but not at the start of a // paragraph. point.mIsEndOfLineInsertionPoint = IsLocalAccAtLineStart(point.mAcc->AsLocal()) && !point.IsParagraphStart(); } else { // This isn't the start of a node, so we must be at the end of a line. point.mIsEndOfLineInsertionPoint = true; } } return point; }
// Ideally, we'd cache the caret as a leaf, but our events are based on // HyperText for now. DocAccessibleParent* remoteDoc = aAcc->AsRemote()->Document(); auto [ht, htOffset] = remoteDoc->GetCaret(); if (!ht) { return TextLeafPoint(); } TextLeafPoint point = ht->ToTextLeafPoint(htOffset); point.mIsEndOfLineInsertionPoint = remoteDoc->IsCaretAtEndOfLine(); return point; }
TextLeafPoint TextLeafPoint::AdjustEndOfLine() const { MOZ_ASSERT(mIsEndOfLineInsertionPoint); // Use the last character on the line so that we search for word and line // boundaries on the current line, not the next line. return TextLeafPoint(mAcc, mOffset) .FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious); }
TextLeafPoint TextLeafPoint::FindBoundary(AccessibleTextBoundary aBoundaryType, nsDirection aDirection, BoundaryFlags aFlags) const { if (mIsEndOfLineInsertionPoint) { // In this block, we deliberately don't propagate mIsEndOfLineInsertionPoint // to derived points because otherwise, a call to FindBoundary on the // returned point would also return the same point. if (aBoundaryType == nsIAccessibleText::BOUNDARY_CHAR || aBoundaryType == nsIAccessibleText::BOUNDARY_CLUSTER) { if (aDirection == eDirNext || (aDirection == eDirPrevious && aFlags & BoundaryFlags::eIncludeOrigin)) { // The caller wants the current or next character/cluster. Return no // character, since otherwise, this would move past the first character // on the next line. return TextLeafPoint(mAcc, mOffset); } // The caller wants the previous character/cluster. Return that as normal. return TextLeafPoint(mAcc, mOffset) .FindBoundary(aBoundaryType, aDirection, aFlags); } // For any other boundary, we need to start on this line, not the next, even // though mOffset refers to the next. return AdjustEndOfLine().FindBoundary(aBoundaryType, aDirection, aFlags); }
bool inEditableAndStopInIt = (aFlags & BoundaryFlags::eStopInEditable) && mAcc->Parent() && (mAcc->Parent()->State() & states::EDITABLE); if (aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_END) { return FindLineEnd(aDirection, inEditableAndStopInIt ? aFlags : (aFlags & ~BoundaryFlags::eStopInEditable)); } if (aBoundaryType == nsIAccessibleText::BOUNDARY_WORD_END) { return FindWordEnd(aDirection, inEditableAndStopInIt ? aFlags : (aFlags & ~BoundaryFlags::eStopInEditable)); } if ((aBoundaryType == nsIAccessibleText::BOUNDARY_LINE_START || aBoundaryType == nsIAccessibleText::BOUNDARY_PARAGRAPH) && (aFlags & BoundaryFlags::eIncludeOrigin) && 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. return *this; } bool includeOrigin = !!(aFlags & BoundaryFlags::eIncludeOrigin); bool ignoreListItemMarker = !!(aFlags & BoundaryFlags::eIgnoreListItemMarker); Accessible* lastAcc = nullptr; for (TextLeafPoint searchFrom = *this; searchFrom; searchFrom = searchFrom.NeighborLeafPoint( aDirection, inEditableAndStopInIt, ignoreListItemMarker)) { lastAcc = searchFrom.mAcc; if (ignoreListItemMarker && searchFrom == *this && searchFrom.mAcc->Role() == roles::LISTITEM_MARKER) { continue; } TextLeafPoint boundary; // Search for the boundary within the current Accessible. switch (aBoundaryType) { case nsIAccessibleText::BOUNDARY_CHAR: if (includeOrigin) { boundary = searchFrom; } else if (aDirection == eDirPrevious && searchFrom.mOffset > 0) { boundary.mAcc = searchFrom.mAcc; boundary.mOffset = searchFrom.mOffset - 1; } else if (aDirection == eDirNext && searchFrom.mOffset + 1 < static_cast<int32_t>( nsAccUtils::TextLength(searchFrom.mAcc))) { boundary.mAcc = searchFrom.mAcc; boundary.mOffset = searchFrom.mOffset + 1; } break; case nsIAccessibleText::BOUNDARY_WORD_START: if (aDirection == eDirPrevious) { boundary = searchFrom.FindPrevWordStartSameAcc(includeOrigin); } else { boundary = searchFrom.FindNextWordStartSameAcc(includeOrigin); } break; case nsIAccessibleText::BOUNDARY_LINE_START: boundary = searchFrom.FindLineStartSameAcc(aDirection, includeOrigin, ignoreListItemMarker); break; case nsIAccessibleText::BOUNDARY_PARAGRAPH: boundary = searchFrom.FindParagraphSameAcc(aDirection, includeOrigin, ignoreListItemMarker); break; case nsIAccessibleText::BOUNDARY_CLUSTER: boundary = searchFrom.FindClusterSameAcc(aDirection, includeOrigin); break; default: MOZ_ASSERT_UNREACHABLE(); break; } if (boundary) { return boundary; }
// The start/end of the Accessible might be a boundary. If so, we must stop // on it. includeOrigin = true; }
MOZ_ASSERT(lastAcc); // No further leaf was found. Use the start/end of the first/last leaf. return TextLeafPoint( lastAcc, aDirection == eDirPrevious ? 0 : static_cast<int32_t>(nsAccUtils::TextLength(lastAcc))); }
TextLeafPoint TextLeafPoint::FindLineEnd(nsDirection aDirection, BoundaryFlags aFlags) 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, aFlags & ~BoundaryFlags::eIncludeOrigin); } if ((aFlags & BoundaryFlags::eIncludeOrigin) && IsLineFeedChar()) { return *this; } if (aDirection == eDirPrevious && !(aFlags & BoundaryFlags::eIncludeOrigin)) { // If there is a line feed immediately before us, return that. TextLeafPoint prevChar = FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious, aFlags & ~BoundaryFlags::eIncludeOrigin); if (prevChar.IsLineFeedChar()) { return prevChar; } } TextLeafPoint searchFrom = *this; if (aDirection == eDirNext && IsLineFeedChar()) { // 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, aFlags & ~BoundaryFlags::eIncludeOrigin); } TextLeafPoint lineStart = searchFrom.FindBoundary( nsIAccessibleText::BOUNDARY_LINE_START, aDirection, aFlags); if (aDirection == eDirNext && IsEmptyLastLine()) { // There is a line feed immediately before us, but that's actually the end // of the previous line, not the end of our empty line. Don't walk back. return lineStart; } // 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, aFlags & ~BoundaryFlags::eIncludeOrigin); if (prevChar && prevChar.IsLineFeedChar()) { return prevChar; } return lineStart; }
TextLeafPoint TextLeafPoint::FindWordEnd(nsDirection aDirection, BoundaryFlags aFlags) const { char16_t origChar = GetChar(); const bool origIsSpace = GetWordBreakClass(origChar) == eWbcSpace; bool prevIsSpace = false; if (aDirection == eDirPrevious || ((aFlags & BoundaryFlags::eIncludeOrigin) && origIsSpace) || !origChar) { TextLeafPoint prev = FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious, aFlags & ~BoundaryFlags::eIncludeOrigin); if (aDirection == eDirPrevious && prev == *this) { return *this; // Can't go any further. } prevIsSpace = prev.IsSpace(); if ((aFlags & BoundaryFlags::eIncludeOrigin) && (origIsSpace || IsDocEdge(eDirNext)) && !prevIsSpace) { // The origin is space or end of document, but the previous // character is not. This means we're at the end of a word. return *this; } } TextLeafPoint boundary = *this; if (aDirection == eDirPrevious && !prevIsSpace) { // If there isn't space immediately before us, first find the start of the // previous word. boundary = FindBoundary(nsIAccessibleText::BOUNDARY_WORD_START, eDirPrevious, aFlags); } else if (aDirection == eDirNext && (origIsSpace || (!origChar && prevIsSpace))) { // We're within the space at the end of the word. Skip over the space. We // can do that by searching for the next word start. boundary = FindBoundary(nsIAccessibleText::BOUNDARY_WORD_START, eDirNext, aFlags & ~BoundaryFlags::eIncludeOrigin); if (boundary.IsSpace()) { // The next word starts with a space. This can happen if there is a space // after or at the start of a block element. return boundary; } } if (aDirection == eDirNext) { BoundaryFlags flags = aFlags; if (IsDocEdge(eDirPrevious)) { // If this is the start of the doc don't be inclusive in the word-start // search because there is no preceding block where this could be a // word-end for. flags &= ~BoundaryFlags::eIncludeOrigin; } boundary = boundary.FindBoundary(nsIAccessibleText::BOUNDARY_WORD_START, eDirNext, flags); } // At this point, boundary is either the start of a word or at a space. A // word ends at the beginning of consecutive space. Therefore, skip back to // the start of any space before us. TextLeafPoint prev = boundary; for (;;) { prev = prev.FindBoundary(nsIAccessibleText::BOUNDARY_CHAR, eDirPrevious, aFlags & ~BoundaryFlags::eIncludeOrigin); if (prev == boundary) { break; // Can't go any further. } if (!prev.IsSpace()) { break; } boundary = prev; } return boundary; }
TextLeafPoint TextLeafPoint::FindParagraphSameAcc( nsDirection aDirection, bool aIncludeOrigin, bool aIgnoreListItemMarker) const { if (aIncludeOrigin && IsDocEdge(eDirPrevious)) { // The top of the document is a paragraph boundary. return *this; }
if (aIgnoreListItemMarker && aIncludeOrigin && mOffset == 0 && IsLeafAfterListItemMarker()) { // If we are in a list item and the previous sibling is // a bullet, the 0 offset in this leaf is a line start. return *this; }
if (mAcc->IsTextLeaf() && // We don't want to copy strings unnecessarily. See below for the context // of these individual conditions. ((aIncludeOrigin && mOffset > 0) || aDirection == eDirNext || mOffset >= 2)) { // If there is a line feed, a new paragraph begins after it. nsAutoString text; mAcc->AppendTextTo(text); if (aIncludeOrigin && mOffset > 0 && text.CharAt(mOffset - 1) == '\n') { return TextLeafPoint(mAcc, mOffset); } int32_t lfOffset = -1; if (aDirection == eDirNext) { lfOffset = text.FindChar('\n', mOffset); } else if (mOffset >= 2) { // A line feed at mOffset - 1 means the origin begins a new paragraph, // but we already handled aIncludeOrigin above. Therefore, we search from // mOffset - 2. lfOffset = text.RFindChar('\n', mOffset - 2); } if (lfOffset != -1 && lfOffset + 1 < static_cast<int32_t>(text.Length())) { return TextLeafPoint(mAcc, lfOffset + 1); } }
if (aIgnoreListItemMarker && mOffset > 0 && aDirection == eDirPrevious && IsLeafAfterListItemMarker()) { // No line breaks were found in the preceding text to this offset. // If we are in a list item and the previous sibling is // a bullet, the 0 offset in this leaf is a line start. return TextLeafPoint(mAcc, 0); }
// Check whether this Accessible begins a paragraph. if ((!aIncludeOrigin && mOffset == 0) || (aDirection == eDirNext && mOffset > 0)) { // The caller isn't interested in whether this Accessible begins a // paragraph. return TextLeafPoint(); } Accessible* prevLeaf = PrevLeaf(mAcc); BlockRule blockRule; Pivot pivot(nsAccUtils::DocumentFor(mAcc)); Accessible* prevBlock = pivot.Prev(mAcc, blockRule); // Check if we're the first leaf after a block element. if (prevBlock) { if ( // If there's no previous leaf, we must be the first leaf after the // block. !prevLeaf || // A block can be a leaf; e.g. an empty div or paragraph. prevBlock == prevLeaf) { return TextLeafPoint(mAcc, 0); } if (prevBlock->IsAncestorOf(mAcc)) { // We're inside the block. if (!prevBlock->IsAncestorOf(prevLeaf)) { // The previous leaf isn't inside the block. That means we're the first // leaf in the block. return TextLeafPoint(mAcc, 0); } } else { // We aren't inside the block, so the block ends before us. if (prevBlock->IsAncestorOf(prevLeaf)) { // The previous leaf is inside the block. That means we're the first // leaf after the block. This case is necessary because a block causes a // paragraph break both before and after it. return TextLeafPoint(mAcc, 0); } } } if (!prevLeaf || prevLeaf->IsHTMLBr()) { // We're the first leaf after a line break or the start of the document. return TextLeafPoint(mAcc, 0); } if (prevLeaf->IsTextLeaf()) { // There's a text leaf before us. Check if it ends with a line feed. nsAutoString text; prevLeaf->AppendTextTo(text, nsAccUtils::TextLength(prevLeaf) - 1, 1); if (text.CharAt(0) == '\n') { return TextLeafPoint(mAcc, 0); } } return TextLeafPoint(); }
TextLeafPoint TextLeafPoint::FindClusterSameAcc(nsDirection aDirection, bool aIncludeOrigin) const { // We don't support clusters which cross nodes. We can live with that because // editor doesn't seem to fully support this either. if (aIncludeOrigin && mOffset == 0) { // Since we don't cross nodes, offset 0 always begins a cluster. return *this; } if (aDirection == eDirPrevious) { if (mOffset == 0) { // We can't go back any further. return TextLeafPoint(); } if (!aIncludeOrigin && mOffset == 1) { // Since we don't cross nodes, offset 0 always begins a cluster. We can't // take this fast path if aIncludeOrigin is true because offset 1 might // start a cluster, but we don't know that yet. return TextLeafPoint(mAcc, 0); } } nsAutoString text; mAcc->AppendTextTo(text); if (text.IsEmpty()) { return TextLeafPoint(); } if (aDirection == eDirNext && mOffset == static_cast<int32_t>(text.Length())) { return TextLeafPoint(); } // There is GraphemeClusterBreakReverseIteratorUtf16, but it "doesn't // handle conjoining Jamo and emoji". Therefore, we must use // GraphemeClusterBreakIteratorUtf16 even when moving backward. // GraphemeClusterBreakIteratorUtf16::Seek() always starts from the beginning // and repeatedly calls Next(), regardless of the seek offset. The best we // can do is call Next() until we find the offset we need. intl::GraphemeClusterBreakIteratorUtf16 iter(text); // Since we don't cross nodes, offset 0 always begins a cluster. int32_t prevCluster = 0; while (Maybe<uint32_t> next = iter.Next()) { int32_t cluster = static_cast<int32_t>(*next); if (aIncludeOrigin && cluster == mOffset) { return *this; } if (aDirection == eDirPrevious) { if (cluster >= mOffset) { return TextLeafPoint(mAcc, prevCluster); } prevCluster = cluster; } else if (cluster > mOffset) { MOZ_ASSERT(aDirection == eDirNext); return TextLeafPoint(mAcc, cluster); } } return TextLeafPoint(); }
void TextLeafPoint::AddTextOffsetAttributes(AccAttributes* aAttrs) const { auto expose = [aAttrs](nsAtom* aAttr) { if (aAttr == nsGkAtoms::spelling) { aAttrs->SetAttribute(nsGkAtoms::invalid, aAttr); } else if (aAttr == nsGkAtoms::mark) { aAttrs->SetAttribute(aAttr, true); } };
if (LocalAccessible* acc = mAcc->AsLocal()) { auto ranges = FindDOMTextOffsetAttributes(acc, mOffset, mOffset + 1); for (auto& [domRanges, attr] : ranges) { MOZ_ASSERT(domRanges.Length() >= 1); expose(attr); } return; }
RemoteAccessible* acc = mAcc->AsRemote(); MOZ_ASSERT(acc); if (RequestDomainsIfInactive(CacheDomain::TextOffsetAttributes)) { return; } if (!acc->mCachedFields) { return; } auto offsetAttrs = acc->mCachedFields->GetAttribute<nsTArray<TextOffsetAttribute>>( CacheKey::TextOffsetAttributes); if (!offsetAttrs) { return; } auto compare = [this](const TextOffsetAttribute& aItem) { if (aItem.mStartOffset <= mOffset && (mOffset < aItem.mEndOffset || aItem.mEndOffset == -1)) { return 0; } if (aItem.mStartOffset > mOffset) { return -1; } return 1; }; // With our compare function, EqualRange will find any item which includes // mOffset. auto [lower, upper] = EqualRange(*offsetAttrs, 0, offsetAttrs->Length(), compare); for (auto i = lower; i < upper; ++i) { expose((*offsetAttrs)[i].mAttribute); } }
TextLeafPoint TextLeafPoint::FindTextOffsetAttributeSameAcc( nsDirection aDirection, bool aIncludeOrigin) const { if (!aIncludeOrigin && mOffset == 0 && aDirection == eDirPrevious) { return TextLeafPoint(); } if (LocalAccessible* acc = mAcc->AsLocal()) { // We want to find both start and end points, so we pass true for // aAllowAdjacent. auto ranges = aDirection == eDirNext ? FindDOMTextOffsetAttributes( acc, mOffset, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT,
/* aAllowAdjacent */
: FindDOMTextOffsetAttributes(acc, 0, mOffset, /* aAllowAdjacent */ true);
nsINode* node = acc->GetNode(); // There are multiple selection types. The ranges for each selection type // are sorted, but the ranges aren't sorted between selection types. // Therefore, we need to look for the closest matching offset in each // selection type. We keep track of that in the dest variable as we check // each selection type.
int32_t dest = -1; if (aDirection == eDirNext) { for (auto& [domRanges, attr] : ranges) { for (dom::AbstractRange* domRange : domRanges) { if (domRange->GetStartContainer() == node) {
int32_t matchOffset = static_cast<int32_t>(ContentToRenderedOffset(
acc, static_cast<int32_t>(domRange->StartOffset()))); if (aIncludeOrigin && matchOffset == mOffset) { return *this;
} if (matchOffset > mOffset && (dest == -1 || matchOffset <= dest)) {
dest = matchOffset; break;
}
} if (domRange->GetEndContainer() == node) {
int32_t matchOffset = static_cast<int32_t>(ContentToRenderedOffset(
acc, static_cast<int32_t>(domRange->EndOffset()))); if (aIncludeOrigin && matchOffset == mOffset) { return *this;
} if (matchOffset > mOffset && (dest == -1 || matchOffset <= dest)) {
dest = matchOffset; break;
}
}
}
}
} else { for (auto& [domRanges, attr] : ranges) { for (dom::AbstractRange* domRange : Reversed(domRanges)) { if (domRange->GetEndContainer() == node) {
int32_t matchOffset = static_cast<int32_t>(ContentToRenderedOffset(
acc, static_cast<int32_t>(domRange->EndOffset()))); if (aIncludeOrigin && matchOffset == mOffset) { return *this;
} if (matchOffset < mOffset && (dest == -1 || matchOffset >= dest)) {
dest = matchOffset; break;
}
} if (domRange->GetStartContainer() == node) {
int32_t matchOffset = static_cast<int32_t>(ContentToRenderedOffset(
acc, static_cast<int32_t>(domRange->StartOffset()))); if (aIncludeOrigin && matchOffset == mOffset) { return *this;
} if (matchOffset < mOffset && (dest == -1 || matchOffset >= dest)) {
dest = matchOffset; break;
}
}
}
}
} if (dest == -1) { return TextLeafPoint();
} return TextLeafPoint(mAcc, dest);
}
RemoteAccessible* acc = mAcc->AsRemote();
MOZ_ASSERT(acc); if (RequestDomainsIfInactive(CacheDomain::TextOffsetAttributes)) { return TextLeafPoint();
} if (!acc->mCachedFields) { return TextLeafPoint();
} auto offsetAttrs =
acc->mCachedFields->GetAttribute<nsTArray<TextOffsetAttribute>>(
CacheKey::TextOffsetAttributes); if (!offsetAttrs) { return TextLeafPoint();
} auto compare = [this](const TextOffsetAttribute& aItem) { // We want to match both start and end offsets, so we use <= // aItem.mEndOffset. if (aItem.mStartOffset <= mOffset &&
(mOffset <= aItem.mEndOffset || aItem.mEndOffset == -1)) { return 0;
} if (aItem.mStartOffset > mOffset) { return -1;
} return 1;
};
size_t index; if (BinarySearchIf(*offsetAttrs, 0, offsetAttrs->Length(), compare, &index)) { // mOffset is within or the end of an offset attribute. if (aIncludeOrigin && ((*offsetAttrs)[index].mStartOffset == mOffset ||
(*offsetAttrs)[index].mEndOffset == mOffset)) { return *this;
} // Check the boundaries of the offset attribute containing mOffset. if (aDirection == eDirNext) { if ((*offsetAttrs)[index].mEndOffset > mOffset) {
MOZ_ASSERT((*offsetAttrs)[index].mEndOffset != -1); return TextLeafPoint(mAcc, (*offsetAttrs)[index].mEndOffset);
} // We don't want the origin, so move to the next offset attribute after // mOffset.
++index;
} elseif ((*offsetAttrs)[index].mStartOffset < mOffset &&
(*offsetAttrs)[index].mStartOffset != -1) { return TextLeafPoint(mAcc, (*offsetAttrs)[index].mStartOffset);
}
} // index points at the next offset attribute after mOffset. if (aDirection == eDirNext) { if (offsetAttrs->Length() == index) { return TextLeafPoint(); // No offset attribute boundary after us.
} return TextLeafPoint(mAcc, (*offsetAttrs)[index].mStartOffset);
} if (index == 0) { return TextLeafPoint(); // No offset attribute boundary before us.
} // Decrement index so it points at an offset attribute before mOffset.
--index; return TextLeafPoint(mAcc, (*offsetAttrs)[index].mEndOffset);
}
LayoutDeviceIntRect TextLeafPoint::ComputeBoundsFromFrame() const {
LocalAccessible* local = mAcc->AsLocal();
MOZ_ASSERT(local, "Can't compute bounds in frame from non-local acc");
nsIFrame* frame = local->GetFrame();
MOZ_ASSERT(frame, "No frame found for acc!");
if (!frame || !frame->IsTextFrame()) { return local->Bounds();
}
// Substring must be entirely within the same text node.
MOZ_ASSERT(frame->IsPrimaryFrame(), "Cannot compute content offset on non-primary frame");
nsIFrame::RenderedText text = frame->GetRenderedText(
mOffset, mOffset + 1, nsIFrame::TextOffsetType::OffsetsInRenderedText,
nsIFrame::TrailingWhitespace::DontTrim);
int32_t contentOffset = text.mOffsetWithinNodeText;
int32_t contentOffsetInFrame; // Get the right frame continuation -- not really a child, but a sibling of // the primary frame passed in
nsresult rv = frame->GetChildFrameContainingOffset(
contentOffset, true, &contentOffsetInFrame, &frame);
NS_ENSURE_SUCCESS(rv, LayoutDeviceIntRect());
// Start with this frame's screen rect, which we will shrink based on // the char we care about within it.
nsRect frameScreenRect = frame->GetScreenRectInAppUnits();
// Add the point where the char starts to the frameScreenRect
nsPoint frameTextStartPoint;
rv = frame->GetPointFromOffset(contentOffset, &frameTextStartPoint);
NS_ENSURE_SUCCESS(rv, LayoutDeviceIntRect());
// Use the next offset to calculate the width // XXX(morgan) does this work for vertical text?
nsPoint frameTextEndPoint;
rv = frame->GetPointFromOffset(contentOffset + 1, &frameTextEndPoint);
NS_ENSURE_SUCCESS(rv, LayoutDeviceIntRect());
/* static */
nsTArray<TextOffsetAttribute> TextLeafPoint::GetTextOffsetAttributes(
LocalAccessible* aAcc) {
nsINode* node = aAcc->GetNode(); auto ranges = FindDOMTextOffsetAttributes(
aAcc, 0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT);
size_t capacity = 0; for (auto& [domRanges, attr] : ranges) {
capacity += domRanges.Length();
}
nsTArray<TextOffsetAttribute> offsets(capacity); for (auto& [domRanges, attr] : ranges) { for (dom::AbstractRange* domRange : domRanges) {
TextOffsetAttribute& data = *offsets.AppendElement();
data.mAttribute = attr; if (domRange->GetStartContainer() == node) {
data.mStartOffset = static_cast<int32_t>(ContentToRenderedOffset(
aAcc, static_cast<int32_t>(domRange->StartOffset())));
} else { // This range overlaps aAcc, but starts before it. // This can only happen for the first range.
MOZ_ASSERT(domRange == *domRanges.begin()); // Using -1 here means this won't be treated as the start of an // attribute range, while still indicating that we're within a text // offset attribute.
data.mStartOffset = -1;
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.37Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.