Bug 935375 - Word offsets broken if the next word is an embedded object, r=tbsaunde

This commit is contained in:
Alexander Surkov 2013-12-16 21:32:12 -05:00
parent cd068df067
commit 1a330301a9
3 changed files with 43 additions and 238 deletions

View File

@ -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) {

View File

@ -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.

View File

@ -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>