mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 02:09:28 +00:00
Bug 935375 - Word offsets broken if the next word is an embedded object, r=tbsaunde
This commit is contained in:
parent
cd068df067
commit
1a330301a9
@ -182,187 +182,6 @@ HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
|
||||
return screenRect.ToNearestPixels(presContext->AppUnitsPerDevPixel());
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the specified text.
|
||||
*/
|
||||
nsIFrame*
|
||||
HyperTextAccessible::GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
|
||||
nsAString* aText, nsIFrame** aEndFrame,
|
||||
Accessible** aStartAcc,
|
||||
Accessible** aEndAcc)
|
||||
{
|
||||
aStartOffset = ConvertMagicOffset(aStartOffset);
|
||||
aEndOffset = ConvertMagicOffset(aEndOffset);
|
||||
|
||||
int32_t startOffset = aStartOffset;
|
||||
int32_t endOffset = aEndOffset;
|
||||
// XXX this prevents text interface usage on <input type="password">
|
||||
bool isPassword = (Role() == roles::PASSWORD_TEXT);
|
||||
|
||||
// Clear out parameters and set up loop
|
||||
if (aText) {
|
||||
aText->Truncate();
|
||||
}
|
||||
if (endOffset < 0) {
|
||||
const int32_t kMaxTextLength = 32767;
|
||||
endOffset = kMaxTextLength; // Max end offset
|
||||
}
|
||||
else if (startOffset > endOffset) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIFrame *startFrame = nullptr;
|
||||
nsIFrame* endFrame = nullptr;
|
||||
if (aEndFrame) {
|
||||
*aEndFrame = nullptr;
|
||||
}
|
||||
if (aStartAcc)
|
||||
*aStartAcc = nullptr;
|
||||
if (aEndAcc)
|
||||
*aEndAcc = nullptr;
|
||||
|
||||
nsIntRect unionRect;
|
||||
Accessible* lastAccessible = nullptr;
|
||||
|
||||
gfxSkipChars skipChars;
|
||||
gfxSkipCharsIterator iter;
|
||||
|
||||
// Loop through children and collect valid offsets, text and bounds
|
||||
// depending on what we need for out parameters.
|
||||
uint32_t childCount = ChildCount();
|
||||
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
||||
Accessible* childAcc = mChildren[childIdx];
|
||||
lastAccessible = childAcc;
|
||||
|
||||
nsIFrame *frame = childAcc->GetFrame();
|
||||
if (!frame) {
|
||||
continue;
|
||||
}
|
||||
endFrame = frame;
|
||||
if (!nsAccUtils::IsEmbeddedObject(childAcc)) {
|
||||
// We only need info up to rendered offset -- that is what we're
|
||||
// converting to content offset
|
||||
int32_t substringEndOffset = -1;
|
||||
uint32_t ourRenderedStart = 0;
|
||||
int32_t ourContentStart = 0;
|
||||
if (frame->GetType() == nsGkAtoms::textFrame) {
|
||||
nsresult rv = frame->GetRenderedText(nullptr, &skipChars, &iter);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
ourRenderedStart = iter.GetSkippedOffset();
|
||||
ourContentStart = iter.GetOriginalOffset();
|
||||
substringEndOffset =
|
||||
iter.ConvertOriginalToSkipped(skipChars.GetOriginalCharCount() +
|
||||
ourContentStart) - ourRenderedStart;
|
||||
}
|
||||
}
|
||||
if (substringEndOffset < 0) {
|
||||
// XXX for non-textframe text like list bullets,
|
||||
// should go away after list bullet rewrite
|
||||
substringEndOffset = nsAccUtils::TextLength(childAcc);
|
||||
}
|
||||
if (startOffset < substringEndOffset ||
|
||||
(startOffset == substringEndOffset && (childIdx == childCount - 1))) {
|
||||
// Our start is within this substring
|
||||
if (startOffset > 0 || endOffset < substringEndOffset) {
|
||||
// We don't want the whole string for this accessible
|
||||
// Get out the continuing text frame with this offset
|
||||
int32_t outStartLineUnused;
|
||||
int32_t contentOffset;
|
||||
if (frame->GetType() == nsGkAtoms::textFrame) {
|
||||
contentOffset = iter.ConvertSkippedToOriginal(startOffset) +
|
||||
ourRenderedStart - ourContentStart;
|
||||
}
|
||||
else {
|
||||
contentOffset = startOffset;
|
||||
}
|
||||
frame->GetChildFrameContainingOffset(contentOffset, true,
|
||||
&outStartLineUnused, &frame);
|
||||
if (aEndFrame) {
|
||||
*aEndFrame = frame; // We ended in the current frame
|
||||
if (aEndAcc)
|
||||
NS_ADDREF(*aEndAcc = childAcc);
|
||||
}
|
||||
if (substringEndOffset > endOffset) {
|
||||
// Need to stop before the end of the available text
|
||||
substringEndOffset = endOffset;
|
||||
}
|
||||
aEndOffset = endOffset;
|
||||
}
|
||||
if (aText) {
|
||||
if (isPassword) {
|
||||
for (int32_t count = startOffset; count < substringEndOffset; count ++)
|
||||
*aText += '*'; // Show *'s only for password text
|
||||
}
|
||||
else {
|
||||
childAcc->AppendTextTo(*aText, startOffset,
|
||||
substringEndOffset - startOffset);
|
||||
}
|
||||
}
|
||||
if (!startFrame) {
|
||||
startFrame = frame;
|
||||
aStartOffset = startOffset;
|
||||
if (aStartAcc)
|
||||
NS_ADDREF(*aStartAcc = childAcc);
|
||||
}
|
||||
// We already started copying in this accessible's string,
|
||||
// for the next accessible we'll start at offset 0
|
||||
startOffset = 0;
|
||||
}
|
||||
else {
|
||||
// We have not found the start position yet, get the new startOffset
|
||||
// that is relative to next accessible
|
||||
startOffset -= substringEndOffset;
|
||||
}
|
||||
// The endOffset needs to be relative to the new startOffset
|
||||
endOffset -= substringEndOffset;
|
||||
}
|
||||
else {
|
||||
// Embedded object, append marker
|
||||
// XXX Append \n for <br>'s
|
||||
if (startOffset >= 1) {
|
||||
-- startOffset;
|
||||
}
|
||||
else {
|
||||
if (endOffset > 0) {
|
||||
if (aText) {
|
||||
// XXX: should use nsIAccessible::AppendTextTo.
|
||||
if (frame->GetType() == nsGkAtoms::brFrame) {
|
||||
*aText += kForcedNewLineChar;
|
||||
} else if (nsAccUtils::MustPrune(this)) {
|
||||
*aText += kImaginaryEmbeddedObjectChar;
|
||||
// Expose imaginary embedded object character if the accessible
|
||||
// hans't children.
|
||||
} else {
|
||||
*aText += kEmbeddedObjectChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!startFrame) {
|
||||
startFrame = frame;
|
||||
aStartOffset = 0;
|
||||
if (aStartAcc)
|
||||
NS_ADDREF(*aStartAcc = childAcc);
|
||||
}
|
||||
}
|
||||
-- endOffset;
|
||||
}
|
||||
if (endOffset <= 0 && startFrame) {
|
||||
break; // If we don't have startFrame yet, get that in next loop iteration
|
||||
}
|
||||
}
|
||||
|
||||
if (aStartAcc && !*aStartAcc) {
|
||||
NS_IF_ADDREF(*aStartAcc = lastAccessible);
|
||||
}
|
||||
if (aEndFrame && !*aEndFrame) {
|
||||
*aEndFrame = endFrame;
|
||||
if (aEndAcc && !*aEndAcc)
|
||||
NS_IF_ADDREF(*aEndAcc = lastAccessible);
|
||||
}
|
||||
|
||||
return startFrame;
|
||||
}
|
||||
|
||||
void
|
||||
HyperTextAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
|
||||
nsAString& aText)
|
||||
@ -622,46 +441,47 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType)
|
||||
{
|
||||
// Convert hypertext offset to frame-relative offset.
|
||||
int32_t offsetInFrame = aOffset, notUsedOffset = aOffset;
|
||||
nsRefPtr<Accessible> accAtOffset;
|
||||
nsIFrame* frameAtOffset =
|
||||
GetPosAndText(offsetInFrame, notUsedOffset, nullptr, nullptr,
|
||||
getter_AddRefs(accAtOffset));
|
||||
if (!frameAtOffset) {
|
||||
if (aOffset == CharacterCount()) {
|
||||
// Asking for start of line, while on last character.
|
||||
if (accAtOffset)
|
||||
frameAtOffset = accAtOffset->GetFrame();
|
||||
}
|
||||
NS_ASSERTION(frameAtOffset, "No start frame for text getting!");
|
||||
if (!frameAtOffset)
|
||||
// Find a leaf accessible frame to start with. PeekOffset wants this.
|
||||
HyperTextAccessible* text = this;
|
||||
Accessible* child = nullptr;
|
||||
int32_t innerOffset = aOffset;
|
||||
|
||||
do {
|
||||
int32_t childIdx = text->GetChildIndexAtOffset(innerOffset);
|
||||
NS_ASSERTION(childIdx != -1, "Bad in offset!");
|
||||
if (childIdx == -1)
|
||||
return -1;
|
||||
|
||||
// We're on the last continuation since we're on the last character.
|
||||
frameAtOffset = frameAtOffset->LastContinuation();
|
||||
child = text->GetChildAt(childIdx);
|
||||
innerOffset -= text->GetChildOffset(childIdx);
|
||||
|
||||
text = child->AsHyperText();
|
||||
} while (text);
|
||||
|
||||
nsIFrame* childFrame = child->GetFrame();
|
||||
NS_ENSURE_TRUE(childFrame, -1);
|
||||
|
||||
int32_t innerContentOffset = innerOffset;
|
||||
if (child->IsTextLeaf()) {
|
||||
NS_ASSERTION(childFrame->GetType() == nsGkAtoms::textFrame, "Wrong frame!");
|
||||
RenderedToContentOffset(childFrame, innerOffset, &innerContentOffset);
|
||||
}
|
||||
|
||||
// Return hypertext offset of the boundary of the found word.
|
||||
int32_t contentOffset = offsetInFrame;
|
||||
nsIFrame* primaryFrame = accAtOffset->GetFrame();
|
||||
NS_ENSURE_TRUE(primaryFrame, -1);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (primaryFrame->GetType() == nsGkAtoms::textFrame) {
|
||||
rv = RenderedToContentOffset(primaryFrame, offsetInFrame, &contentOffset);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
}
|
||||
nsIFrame* frameAtOffset = childFrame;
|
||||
int32_t unusedOffsetInFrame = 0;
|
||||
childFrame->GetChildFrameContainingOffset(innerContentOffset, true,
|
||||
&unusedOffsetInFrame,
|
||||
&frameAtOffset);
|
||||
|
||||
const bool kIsJumpLinesOk = true; // okay to jump lines
|
||||
const bool kIsScrollViewAStop = false; // do not stop at scroll views
|
||||
const bool kIsKeyboardSelect = true; // is keyboard selection
|
||||
const bool kIsVisualBidi = false; // use visual order for bidi text
|
||||
nsPeekOffsetStruct pos(aAmount, aDirection, contentOffset,
|
||||
nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
|
||||
0, kIsJumpLinesOk, kIsScrollViewAStop,
|
||||
kIsKeyboardSelect, kIsVisualBidi,
|
||||
aWordMovementType);
|
||||
rv = frameAtOffset->PeekOffset(&pos);
|
||||
nsresult rv = frameAtOffset->PeekOffset(&pos);
|
||||
|
||||
// PeekOffset fails on last/first lines of the text in certain cases.
|
||||
if (NS_FAILED(rv) && aAmount == eSelectLine) {
|
||||
|
@ -448,35 +448,6 @@ protected:
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType = eDefaultBehavior);
|
||||
|
||||
/**
|
||||
* Provides information for substring that is defined by the given start
|
||||
* and end offsets for this hyper text.
|
||||
*
|
||||
* @param aStartOffset [inout] the start offset into the hyper text. This
|
||||
* is also an out parameter used to return the offset
|
||||
* into the start frame's rendered text content
|
||||
* (start frame is the @return)
|
||||
*
|
||||
* @param aEndOffset [inout] the end offset into the hyper text. This is
|
||||
* also an out parameter used to return
|
||||
* the offset into the end frame's rendered
|
||||
* text content.
|
||||
*
|
||||
* @param aText [out, optional] return the substring's text
|
||||
* @param aEndFrame [out, optional] return the end frame for this
|
||||
* substring
|
||||
* @param aStartAcc [out, optional] return the start accessible for this
|
||||
* substring
|
||||
* @param aEndAcc [out, optional] return the end accessible for this
|
||||
* substring
|
||||
* @return the start frame for this substring
|
||||
*/
|
||||
nsIFrame* GetPosAndText(int32_t& aStartOffset, int32_t& aEndOffset,
|
||||
nsAString *aText = nullptr,
|
||||
nsIFrame **aEndFrame = nullptr,
|
||||
Accessible** aStartAcc = nullptr,
|
||||
Accessible** aEndAcc = nullptr);
|
||||
|
||||
/**
|
||||
* Return the boundaries of the substring in case of textual frame or
|
||||
* frame boundaries in case of non textual frame, offsets are ignored.
|
||||
|
@ -222,6 +222,19 @@
|
||||
[ 18, "ml_edivbr1", kTodo, kTodo, kOk ] ] ],
|
||||
[ 19, 19, "", 19, 19 ] ]);
|
||||
|
||||
// a <a href="#">b</a>
|
||||
// a *
|
||||
testTextBeforeOffset("cntr_1", BOUNDARY_WORD_START,
|
||||
[ [ 0, 1, "", 0, 0 ],
|
||||
[ 2, 3, "a ", 0, 2 ] ]);
|
||||
|
||||
testTextAtOffset("cntr_1", BOUNDARY_WORD_START,
|
||||
[ [ 0, 1, "a ", 0, 2 ],
|
||||
[ 2, 3, kEmbedChar, 2, 3 ] ]);
|
||||
testTextAfterOffset("cntr_1", BOUNDARY_WORD_START,
|
||||
[ [ 0, 1, kEmbedChar, 2, 3 ],
|
||||
[ 2, 3, "", 3, 3 ] ]);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -280,5 +293,6 @@ two words
|
||||
</textarea>
|
||||
</pre>
|
||||
|
||||
<div id="cntr_1">a <a href="#">b</a></div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user