bug 348901. Remove extra whitespace from source when exposing accessible text. r+sr=roc, r=surkov. a=dbaron

This commit is contained in:
aaronleventhal@moonset.net 2007-08-03 18:12:24 -07:00
Родитель 95f65866c6
Коммит 58bbdf1368
14 изменённых файлов: 351 добавлений и 124 удалений

Просмотреть файл

@ -79,6 +79,7 @@ endif
EXTRA_DSO_LIBS = \
gkgfx \
thebes \
$(NULL)
EXTRA_DSO_LDOPTS = \

Просмотреть файл

@ -41,7 +41,7 @@
interface nsIAccessible;
interface nsIAccessibleEvent;
[uuid(817ae493-b238-4fbc-a623-d20ed81eebcd)]
[uuid(03932812-53d1-4dc7-965d-6b6ad8a872b1)]
interface nsPIAccessible : nsISupports
{
/**
@ -100,6 +100,7 @@ interface nsPIAccessible : nsISupports
* Returns text of accessible if accessible has text role otherwise empty
* string.
*/
AString getContentText();
void appendTextTo(out AString aString, in unsigned long aStartOffset,
in unsigned long aLength);
};

Просмотреть файл

@ -1519,12 +1519,13 @@ nsresult nsAccessible::AppendFlatStringFromContentNode(nsIContent *aContent, nsA
}
}
if (aContent->TextLength() > 0) {
nsAutoString text;
aContent->AppendTextTo(text);
if (!text.IsEmpty())
aFlatString->Append(text);
if (isHTMLBlock && !aFlatString->IsEmpty())
nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
nsresult rv = frame->GetRenderedText(aFlatString);
NS_ENSURE_SUCCESS(rv, rv);
if (isHTMLBlock && !aFlatString->IsEmpty()) {
aFlatString->Append(PRUnichar(' '));
}
}
return NS_OK;
}
@ -3147,18 +3148,34 @@ PRInt32 nsAccessible::TextLength(nsIAccessible *aAccessible)
if (!IsText(aAccessible))
return 1;
nsCOMPtr<nsPIAccessNode> pAccNode(do_QueryInterface(aAccessible));
NS_ASSERTION(pAccNode, "QI to nsPIAccessNode failed");
nsIFrame *frame = pAccNode->GetFrame();
if (frame) { // Optimal way to get the text length -- no string copy
nsIContent *content = frame->GetContent();
if (content) {
PRUint32 length;
nsresult rv = nsHyperTextAccessible::ContentToRenderedOffset(frame, content->TextLength(), &length);
return NS_SUCCEEDED(rv) ? length : -1;
}
}
// For list bullets (or anything other accessible which would compute its own text
// They don't have their own frame.
// XXX In the future, list bullets may have frame and anon content, so
// we should be able to remove this at that point
nsCOMPtr<nsPIAccessible> pAcc(do_QueryInterface(aAccessible));
NS_ENSURE_TRUE(pAcc, NS_ERROR_FAILURE);
NS_ASSERTION(pAcc, "QI to nsPIAccessible failed");
nsAutoString text;
pAcc->GetContentText(text);
pAcc->AppendTextTo(text, 0, PR_UINT32_MAX); // Get all the text
return text.Length();
}
NS_IMETHODIMP
nsAccessible::GetContentText(nsAString& aText)
nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength)
{
aText.Truncate();
return NS_OK;
}

Просмотреть файл

@ -149,7 +149,7 @@ public:
static PRUint32 Role(nsIAccessible *aAcc) { PRUint32 role; aAcc->GetFinalRole(&role); return role; }
static PRBool IsText(nsIAccessible *aAcc) { PRUint32 role = Role(aAcc); return role == nsIAccessibleRole::ROLE_TEXT_LEAF || role == nsIAccessibleRole::ROLE_STATICTEXT; }
static PRBool IsEmbeddedObject(nsIAccessible *aAcc) { PRUint32 role = Role(aAcc); return role != nsIAccessibleRole::ROLE_TEXT_LEAF && role != nsIAccessibleRole::ROLE_WHITESPACE && role != nsIAccessibleRole::ROLE_STATICTEXT; }
static PRInt32 TextLength(nsIAccessible *aAccessible);
static PRInt32 TextLength(nsIAccessible *aAccessible); // Returns -1 on failure
static PRBool IsLeaf(nsIAccessible *aAcc) { PRInt32 numChildren; aAcc->GetChildCount(&numChildren); return numChildren > 0; }
static PRBool IsNodeRelevant(nsIDOMNode *aNode); // Is node something that could have an attached accessible

Просмотреть файл

@ -1208,7 +1208,7 @@ nsDocAccessible::FireTextChangedEventOnDOMCharacterDataModified(nsIContent *aCon
PRUint32 replaceLen = aInfo->mReplaceLength;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(node, start, &offset);
rv = textAccessible->DOMPointToHypertextOffset(node, start, &offset);
if (NS_FAILED(rv))
return;
@ -1269,7 +1269,7 @@ nsDocAccessible::FireTextChangedEventOnDOMNodeInserted(nsIContent *aChild,
return;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(parentNode, aIndexInContainer, &offset);
rv = textAccessible->DOMPointToHypertextOffset(parentNode, aIndexInContainer, &offset);
if (NS_FAILED(rv))
return;
@ -1323,7 +1323,7 @@ nsDocAccessible::FireTextChangedEventOnDOMNodeRemoved(nsIContent *aChild,
return;
PRInt32 offset = 0;
rv = textAccessible->DOMPointToOffset(parentNode, aIndexInContainer, &offset);
rv = textAccessible->DOMPointToHypertextOffset(parentNode, aIndexInContainer, &offset);
if (NS_FAILED(rv))
return;

Просмотреть файл

@ -89,16 +89,11 @@ NS_IMETHODIMP nsTextAccessible::GetChildCount(PRInt32 *_retval)
}
NS_IMETHODIMP
nsTextAccessible::GetContentText(nsAString& aText)
nsTextAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength)
{
nsresult rv = nsLinkableAccessible::GetContentText(aText);
NS_ENSURE_SUCCESS(rv, rv);
nsIFrame *frame = GetFrame();
if (!frame)
return NS_OK;
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
frame->GetContent()->AppendTextTo(aText);
return NS_OK;
return frame->GetRenderedText(&aText, nsnull, nsnull, aStartOffset, aLength);
}

Просмотреть файл

@ -60,7 +60,7 @@ public:
NS_IMETHOD GetChildCount(PRInt32 *_retval);
// nsPIAccessible
NS_IMETHOD GetContentText(nsAString& aText);
NS_IMETHOD AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength);
};

Просмотреть файл

@ -59,6 +59,7 @@ REQUIRES = content \
locale \
necko \
string \
thebes \
webshell \
widget \
xpcom \

Просмотреть файл

@ -56,24 +56,7 @@ nsTextAccessibleWrap(aDomNode, aShell)
NS_IMETHODIMP nsHTMLTextAccessible::GetName(nsAString& aName)
{
aName.Truncate();
if (!mDOMNode) {
return NS_ERROR_FAILURE;
}
nsIFrame *frame = GetFrame();
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
nsAutoString name;
nsresult rv = mDOMNode->GetNodeValue(name);
NS_ENSURE_SUCCESS(rv, rv);
if (!frame->GetStyleText()->WhiteSpaceIsSignificant()) {
// Replace \r\n\t in markup with space unless in this is preformatted text
// where those characters are significant
name.ReplaceChar("\r\n\t", ' ');
}
aName = name;
return rv;
return AppendTextTo(aName, 0, PR_UINT32_MAX);
}
NS_IMETHODIMP nsHTMLTextAccessible::GetRole(PRUint32 *aRole)
@ -367,9 +350,14 @@ nsHTMLListBulletAccessible::GetParent(nsIAccessible **aParentAccessible)
}
NS_IMETHODIMP
nsHTMLListBulletAccessible::GetContentText(nsAString& aText)
nsHTMLListBulletAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
PRUint32 aLength)
{
aText = mBulletText;
PRUint32 maxLength = mBulletText.Length() - aStartOffset;
if (aLength > maxLength) {
aLength = maxLength;
}
aText += nsDependentSubstring(mBulletText, aStartOffset, aLength);
return NS_OK;
}

Просмотреть файл

@ -111,7 +111,7 @@ public:
NS_IMETHOD GetParent(nsIAccessible **aParentAccessible);
// nsPIAccessible
NS_IMETHOD GetContentText(nsAString& aText);
NS_IMETHOD AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLength);
protected:
// XXX: Ideally we'd get the bullet text directly from the bullet frame via

Просмотреть файл

@ -56,6 +56,7 @@
#include "nsIPlaintextEditor.h"
#include "nsIServiceManager.h"
#include "nsTextFragment.h"
#include "gfxSkipChars.h"
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
@ -238,13 +239,24 @@ void nsHyperTextAccessible::CacheChildren()
}
// Substring must be entirely within the same text node
nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRInt32 aStartOffset, PRInt32 aLength)
nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRInt32 aStartContentOffset,
PRInt32 aEndContentOffset)
{
nsIntRect screenRect;
NS_ENSURE_TRUE(aFrame, screenRect);
PRUint32 startRenderedOFfset, endRenderedOFfset;
nsresult rv = ContentToRenderedOffset(aFrame, aStartContentOffset, &startRenderedOFfset);
NS_ENSURE_SUCCESS(rv, screenRect);
rv = ContentToRenderedOffset(aFrame, aEndContentOffset, &endRenderedOFfset);
NS_ENSURE_SUCCESS(rv, screenRect);
nsIFrame *frame;
PRInt32 startOffsetInFrame;
nsresult rv = aFrame->GetChildFrameContainingOffset(aStartOffset, PR_FALSE,
&startOffsetInFrame, &frame);
PRInt32 startRenderedOFfsetInFrame;
// Get the right frame continuation -- not really a child, but a sibling of
// the primary frame passed in
rv = aFrame->GetChildFrameContainingOffset(startRenderedOFfset, PR_FALSE,
&startRenderedOFfsetInFrame, &frame);
NS_ENSURE_SUCCESS(rv, screenRect);
nsCOMPtr<nsIPresShell> shell = GetPresShell();
@ -262,7 +274,7 @@ nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRInt32 aS
nsPresContext *context = shell->GetPresContext();
while (frame && aLength > 0) {
while (frame && startRenderedOFfset < endRenderedOFfset) {
// Start with this frame's screen rect, which we will
// shrink based on the substring we care about within it.
// We will then add that frame to the total screenRect we
@ -273,26 +285,26 @@ nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRInt32 aS
PRInt32 startFrameTextOffset, endFrameTextOffset;
frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
PRInt32 frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
PRInt32 frameSubStringLength = PR_MIN(frameTotalTextLength - startOffsetInFrame, aLength);
PRInt32 seekLength = endRenderedOFfset - startRenderedOFfset;
PRInt32 frameSubStringLength = PR_MIN(frameTotalTextLength - startRenderedOFfsetInFrame, seekLength);
// Add the point where the string starts to the frameScreenRect
nsPoint frameTextStartPoint;
rv = frame->GetPointFromOffset(context, rc, aStartOffset, &frameTextStartPoint);
rv = frame->GetPointFromOffset(context, rc, startRenderedOFfset, &frameTextStartPoint);
NS_ENSURE_SUCCESS(rv, nsRect());
frameScreenRect.x += context->AppUnitsToDevPixels(frameTextStartPoint.x);
// Use the point for the end offset to calculate the width
nsPoint frameTextEndPoint;
rv = frame->GetPointFromOffset(context, rc, aStartOffset + frameSubStringLength, &frameTextEndPoint);
rv = frame->GetPointFromOffset(context, rc, startRenderedOFfset + frameSubStringLength, &frameTextEndPoint);
NS_ENSURE_SUCCESS(rv, nsRect());
frameScreenRect.width = context->AppUnitsToDevPixels(frameTextEndPoint.x - frameTextStartPoint.x);
screenRect.UnionRect(frameScreenRect, screenRect);
// Get ready to loop back for next frame continuation
aStartOffset += frameSubStringLength;
startOffsetInFrame = 0;
aLength -= frameSubStringLength;
startRenderedOFfset += frameSubStringLength;
startRenderedOFfsetInFrame = 0;
frame = frame->GetNextContinuation();
}
@ -331,6 +343,9 @@ nsIFrame* nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& a
nsIntRect unionRect;
nsCOMPtr<nsIAccessible> accessible;
gfxSkipChars skipChars;
gfxSkipCharsIterator iter;
// Loop through children and collect valid offsets, text and bounds
// depending on what we need for out parameters
while (NextChild(accessible)) {
@ -340,56 +355,58 @@ nsIFrame* nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& a
continue;
}
if (IsText(accessible)) {
nsCOMPtr<nsPIAccessible> pAcc(do_QueryInterface(accessible));
nsAutoString newText;
pAcc->GetContentText(newText);
PRInt32 substringEndOffset = newText.Length();
// We only need info up to rendered offset -- that is what we're converting to content offset
PRInt32 substringEndOffset;
nsresult rv = frame->GetRenderedText(nsnull, &skipChars, &iter);
PRUint32 ourRenderedStart = iter.GetSkippedOffset();
PRInt32 ourContentStart = iter.GetOriginalOffset();
if (NS_SUCCEEDED(rv)) {
substringEndOffset = iter.ConvertOriginalToSkipped(skipChars.GetOriginalCharCount() + ourContentStart) -
ourRenderedStart;
}
else {
// XXX for non-textframe text like list bullets, should go away after list bullet rewrite
substringEndOffset = TextLength(accessible);
}
if (startOffset < substringEndOffset) {
// Our start is within this substring
// XXX Can we somehow optimize further by getting the nsTextFragment
// and use CopyTo to a PRUnichar buffer to copy it directly to
// the string?
if (startOffset > 0 || endOffset < substringEndOffset) {
// XXX the Substring operation is efficient, but does the
// reassignment to the original nsAutoString cause a copy?
// We don't want the whole string for this accessible
// Get out the continuing text frame with this offset
PRInt32 outStartLineUnused;
frame->GetChildFrameContainingOffset(startOffset, PR_TRUE, &outStartLineUnused, &frame);
if (endOffset < substringEndOffset) {
// Don't take entire substring: stop before the end
substringEndOffset = endOffset;
}
if (aText) {
newText = Substring(newText, startOffset,
substringEndOffset - startOffset);
}
PRInt32 contentOffset = iter.ConvertSkippedToOriginal(startOffset) + ourRenderedStart - ourContentStart;
frame->GetChildFrameContainingOffset(contentOffset, PR_TRUE, &outStartLineUnused, &frame);
if (aEndFrame) {
*aEndFrame = frame; // We ended in the current frame
}
if (substringEndOffset > endOffset) {
// Need to stop before the end of the available text
substringEndOffset = endOffset;
}
aEndOffset = endOffset;
}
if (aText) {
if (!frame->GetStyleText()->WhiteSpaceIsSignificant()) {
// Replace \r\n\t in markup with space unless in this is
// preformatted text where those characters are significant
newText.ReplaceChar("\r\n\t", ' ');
}
*aText += newText;
nsCOMPtr<nsPIAccessible> pAcc(do_QueryInterface(accessible));
pAcc->AppendTextTo(*aText, startOffset, substringEndOffset - startOffset);
}
if (aBoundsRect) {
if (aBoundsRect) { // Caller wants the bounds of the text
aBoundsRect->UnionRect(*aBoundsRect, GetBoundsForString(frame, startOffset,
substringEndOffset - startOffset));
substringEndOffset));
}
if (!startFrame) {
startFrame = frame;
aStartOffset = startOffset;
}
// 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 {
@ -448,7 +465,9 @@ NS_IMETHODIMP nsHyperTextAccessible::GetCharacterCount(PRInt32 *aCharacterCount)
nsCOMPtr<nsIAccessible> accessible;
while (NextChild(accessible)) {
*aCharacterCount += TextLength(accessible);
PRInt32 textLength = TextLength(accessible);
NS_ENSURE_TRUE(textLength >= 0, nsnull);
*aCharacterCount += textLength;
}
return NS_OK;
}
@ -464,24 +483,28 @@ NS_IMETHODIMP nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset, PRUni
nsAutoString text;
nsresult rv = GetText(aOffset, aOffset + 1, text);
NS_ENSURE_SUCCESS(rv, rv);
if (text.IsEmpty()) {
return NS_ERROR_FAILURE;
}
*aCharacter = text.First();
return NS_OK;
}
nsresult nsHyperTextAccessible::DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32* aResult,
nsIAccessible **aFinalAccessible)
nsresult nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset,
PRInt32* aHyperTextOffset,
nsIAccessible **aFinalAccessible)
{
// Turn a DOM Node and offset into an offset into this hypertext.
// On failure, return null. On success, return the DOM node which contains the offset.
NS_ENSURE_ARG_POINTER(aResult);
*aResult = 0;
NS_ENSURE_ARG_POINTER(aHyperTextOffset);
*aHyperTextOffset = 0;
NS_ENSURE_ARG_POINTER(aNode);
NS_ENSURE_TRUE(aNodeOffset >= 0, NS_ERROR_INVALID_ARG);
if (aFinalAccessible) {
*aFinalAccessible = nsnull;
}
PRInt32 addTextOffset = 0;
PRUint32 addTextOffset = 0;
nsCOMPtr<nsIDOMNode> findNode;
unsigned short nodeType;
@ -489,7 +512,15 @@ nsresult nsHyperTextAccessible::DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNod
if (nodeType == nsIDOMNode::TEXT_NODE) {
// For text nodes, aNodeOffset comes in as a character offset
// Text offset will be added at the end, if we find the offset in this hypertext
addTextOffset = aNodeOffset;
// We want the "skipped" offset into the text (rendered text without the extra whitespace)
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
NS_ASSERTION(content, "No nsIContent for dom node");
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
nsIFrame *frame = presShell->GetPrimaryFrameFor(content);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &addTextOffset);
NS_ENSURE_SUCCESS(rv, rv);
// Get the child node and
findNode = aNode;
}
@ -505,7 +536,7 @@ nsresult nsHyperTextAccessible::DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNod
findNode = do_QueryInterface(parentContent->GetChildAt(aNodeOffset));
if (!findNode && !aNodeOffset) {
NS_ASSERTION(!SameCOMIdentity(parentContent, mDOMNode), "Cannot find child for DOMPointToOffset search");
NS_ASSERTION(!SameCOMIdentity(parentContent, mDOMNode), "Cannot find child for DOMPointToHypertextOffset search");
findNode = do_QueryInterface(parentContent); // Case #2: there are no children
}
}
@ -543,12 +574,14 @@ nsresult nsHyperTextAccessible::DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNod
// came after the last accessible child's node
nsCOMPtr<nsIAccessible> accessible;
while (NextChild(accessible) && accessible != childAccessible) {
*aResult += TextLength(accessible);
PRInt32 textLength = TextLength(accessible);
NS_ENSURE_TRUE(textLength >= 0, nsnull);
*aHyperTextOffset += textLength;
}
if (accessible) {
*aResult += addTextOffset;
*aHyperTextOffset += addTextOffset;
NS_ASSERTION(accessible == childAccessible, "These should be equal whenever we exit loop and accessible != nsnull");
if (aFinalAccessible && (NextChild(accessible) || addTextOffset < TextLength(childAccessible))) {
if (aFinalAccessible && (NextChild(accessible) || static_cast<PRInt32>(addTextOffset) < TextLength(childAccessible))) {
// If not at end of last text node, we will return the accessible we were in
NS_ADDREF(*aFinalAccessible = childAccessible);
}
@ -571,10 +604,15 @@ PRInt32 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell, nsIFr
// Ask layout for the new node and offset, after moving the appropriate amount
nsPeekOffsetStruct pos;
pos.SetData(aAmount, aDirection, aFromOffset, 0, kIsJumpLinesOk,
kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
PRInt32 contentOffset;
nsresult rv = RenderedToContentOffset(aFromFrame, aFromOffset, &contentOffset);
NS_ENSURE_SUCCESS(rv, -1);
pos.SetData(aAmount, aDirection, contentOffset,
0, kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
wordMovementType);
nsresult rv = aFromFrame->PeekOffset(&pos);
rv = aFromFrame->PeekOffset(&pos);
if (NS_FAILED(rv)) {
if (aDirection == eDirPrevious) {
// Use passed-in frame as starting point in failure case for now,
@ -586,7 +624,7 @@ PRInt32 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell, nsIFr
aFromFrame->GetOffsets(pos.mContentOffset, endOffsetUnused);
}
else {
return rv;
return -1;
}
}
@ -596,8 +634,8 @@ PRInt32 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell, nsIFr
NS_ENSURE_TRUE(resultNode, -1);
nsCOMPtr<nsIAccessible> finalAccessible;
rv = DOMPointToOffset(resultNode, pos.mContentOffset, &hyperTextOffset, getter_AddRefs(finalAccessible));
// If finalAccessible == nsnull, then DOMPointToOffset() searched through the hypertext
rv = DOMPointToHypertextOffset(resultNode, pos.mContentOffset, &hyperTextOffset, getter_AddRefs(finalAccessible));
// If finalAccessible == nsnull, then DOMPointToHypertextOffset() searched through the hypertext
// children without finding the node/offset position
NS_ENSURE_SUCCESS(rv, -1);
@ -713,7 +751,11 @@ nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTe
// If not text, then it's represented by an embedded object char
// (length of 1)
// XXX did this mean to check for eTEXT?
// XXX This is completely wrong, needs to be reimplemented
PRInt32 textLength = textContent ? textContent->TextLength() : 1;
if (textLength < 0) {
return NS_ERROR_FAILURE;
}
*aStartOffset = aOffset - startOffset;
*aEndOffset = *aStartOffset + textLength;
startOffset = *aStartOffset;
@ -812,6 +854,7 @@ NS_IMETHODIMP nsHyperTextAccessible::GetAttributeRange(PRInt32 aOffset, PRInt32
while (NextChild(accessible)) {
PRInt32 length = TextLength(accessible);
NS_ENSURE_TRUE(length >= 0, NS_ERROR_FAILURE);
if (*aRangeStartOffset + length > aOffset) {
*aRangeEndOffset = *aRangeStartOffset + length;
NS_ADDREF(*aAccessibleWithAttrs = accessible);
@ -993,14 +1036,19 @@ nsHyperTextAccessible::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY,
if (contentOffsets.IsNull() || contentOffsets.content != content) {
return NS_OK; // Not found, will return -1
}
offset += contentOffsets.offset;
PRUint32 addToOffset;
nsresult rv = ContentToRenderedOffset(frame, contentOffsets.offset, &addToOffset);
NS_ENSURE_SUCCESS(rv, rv);
offset += addToOffset;
}
*aOffset = offset;
return NS_OK;
}
frame = frame->GetNextContinuation();
}
offset += TextLength(accessible);
PRInt32 textLength = TextLength(accessible);
NS_ENSURE_TRUE(textLength >= 0, NS_ERROR_FAILURE);
offset += textLength;
}
return NS_OK; // Not found, will return -1
@ -1059,7 +1107,9 @@ NS_IMETHODIMP nsHyperTextAccessible::GetLinkIndex(PRInt32 aCharIndex, PRInt32 *a
PRUint32 role = Role(accessible);
if (role == nsIAccessibleRole::ROLE_TEXT_LEAF ||
role == nsIAccessibleRole::ROLE_STATICTEXT) {
characterCount += TextLength(accessible);
PRInt32 textLength = TextLength(accessible);
NS_ENSURE_TRUE(textLength >= 0, NS_ERROR_FAILURE);
characterCount += textLength;
}
else {
if (characterCount ++ == aCharIndex) {
@ -1208,7 +1258,7 @@ NS_IMETHODIMP nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
PRInt32 caretOffset;
domSel->GetFocusOffset(&caretOffset);
return DOMPointToOffset(caretNode, caretOffset, aCaretOffset);
return DOMPointToHypertextOffset(caretNode, caretOffset, aCaretOffset);
}
nsresult nsHyperTextAccessible::GetSelections(nsISelectionController **aSelCon, nsISelection **aDomSel)
@ -1301,7 +1351,7 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, P
range->GetStartContainer(getter_AddRefs(startNode));
PRInt32 startOffset;
range->GetStartOffset(&startOffset);
rv = DOMPointToOffset(startNode, startOffset, aStartOffset);
rv = DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> endNode;
@ -1313,7 +1363,7 @@ NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, P
*aEndOffset = *aStartOffset;
return NS_OK;
}
return DOMPointToOffset(endNode, endOffset, aEndOffset);
return DOMPointToHypertextOffset(endNode, endOffset, aEndOffset);
}
/*
@ -1414,3 +1464,39 @@ NS_IMETHODIMP nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
return domSel->RemoveRange(range);
}
nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
PRUint32 *aRenderedOffset)
{
gfxSkipChars skipChars;
gfxSkipCharsIterator iter;
// Only get info up to original ofset, we know that will be larger than skipped offset
nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aContentOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 ourRenderedStart = iter.GetSkippedOffset();
PRInt32 ourContentStart = iter.GetOriginalOffset();
*aRenderedOffset = iter.ConvertOriginalToSkipped(aContentOffset + ourContentStart) -
ourRenderedStart;
return NS_OK;
}
nsresult nsHyperTextAccessible::RenderedToContentOffset(nsIFrame *aFrame, PRUint32 aRenderedOffset,
PRInt32 *aContentOffset)
{
gfxSkipChars skipChars;
gfxSkipCharsIterator iter;
// We only need info up to skipped offset -- that is what we're converting to original offset
nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aRenderedOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 ourRenderedStart = iter.GetSkippedOffset();
PRInt32 ourContentStart = iter.GetOriginalOffset();
*aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
return NS_OK;
}

Просмотреть файл

@ -86,6 +86,14 @@ public:
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
void CacheChildren();
// Convert content offset to rendered text offset
static nsresult ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
PRUint32 *aRenderedOffset);
// Convert rendered text offset to content offset
static nsresult RenderedToContentOffset(nsIFrame *aFrame, PRUint32 aRenderedOffset,
PRInt32 *aContentOffset);
/**
* Turn a DOM Node and offset into a character offset into this hypertext.
* Will look for closest match when the DOM node does not have an accessible
@ -101,9 +109,9 @@ public:
* the current nsHyperTextAccessible,
* otherwise it is set to nsnull.
*/
nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset,
PRInt32 *aResultOffset,
nsIAccessible **aFinalAccessible = nsnull);
nsresult DOMPointToHypertextOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset,
PRInt32 *aHypertextOffset,
nsIAccessible **aFinalAccessible = nsnull);
protected:
PRBool IsHyperText();
@ -138,9 +146,9 @@ protected:
* Given a start offset and end offset, get substring information. Different info is returned depending
* on what optional paramters are provided.
* @param aStartOffset, the start offset into the hyper text. This is also an out parameter used to return
* the offset into the start frame's text content (start frame is the @return)
* @param aEndOffset, the endoffset into the hyper text. This is also an out parameter used to return
* the offset into the end frame's text content
* the offset into the start frame's rendered text content (start frame is the @return)
* @param aEndHyperOffset, the endoffset 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 (optional), return the substring's text
* @param aEndFrame (optional), return the end frame for this substring
* @param aBoundsRect (optional), return the bounds rectangle for this substring
@ -149,7 +157,7 @@ protected:
nsIFrame* GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset, nsAString *aText = nsnull,
nsIFrame **aEndFrame = nsnull, nsIntRect *aBoundsRect = nsnull);
nsIntRect GetBoundsForString(nsIFrame *aFrame, PRInt32 aStartOffset, PRInt32 aLength);
nsIntRect GetBoundsForString(nsIFrame *aFrame, PRInt32 aStartContentOffset, PRInt32 aEndContentOffset);
// Editor helpers, subclasses of nsHyperTextAccessible may have editor
virtual void SetEditor(nsIEditor *aEditor) { return; }

Просмотреть файл

@ -90,6 +90,8 @@ class nsIAccessible;
class nsDisplayListBuilder;
class nsDisplayListSet;
class nsDisplayList;
class gfxSkipChars;
class gfxSkipCharsIterator;
struct nsPeekOffsetStruct;
struct nsPoint;
@ -1398,6 +1400,28 @@ public:
nscoord& aDeltaWidth,
PRBool& aLastCharIsJustifiable) = 0;
/**
* Append the rendered text to the passed-in string.
* The appended text will often not contain all the whitespace from source,
* depending on whether the CSS rule "white-space: pre" is active for this frame.
* if aStartOffset + aLength goes past end, or if aLength is not specified
* then use the text up to the string's end.
* Call this on the primary frame for a text node.
* @param aAppendToString String to append text to, or null if text should not be returned
* @param aSkipChars if aSkipIter is non-null, this must also be non-null.
* This gets used as backing data for the iterator so it should outlive the iterator.
* @param aSkipIter Where to fill in the gfxSkipCharsIterator info, or null if not needed by caller
* @param aStartOffset Skipped (rendered text) start offset
* @param aSkippedMaxLength Maximum number of characters to return
* The iterator can be used to map content offsets to offsets in the returned string, or vice versa.
*/
virtual nsresult GetRenderedText(nsAString* aAppendToString = nsnull,
gfxSkipChars* aSkipChars = nsnull,
gfxSkipCharsIterator* aSkipIter = nsnull,
PRUint32 aSkippedStartOffset = 0,
PRUint32 aSkippedMaxLength = PR_UINT32_MAX)
{ return NS_ERROR_NOT_IMPLEMENTED; }
/**
* Accessor functions to get/set the associated view object
*

Просмотреть файл

@ -81,6 +81,7 @@
#include "nsFrameManager.h"
#include "nsTextFrameTextRunCache.h"
#include "nsExpirationTracker.h"
#include "nsICaseConversion.h"
#include "nsTextFragment.h"
#include "nsGkAtoms.h"
@ -449,6 +450,11 @@ public:
nsIRenderingContext& aRC,
nscoord& aDeltaWidth,
PRBool& aLastCharIsJustifiable);
virtual nsresult GetRenderedText(nsAString* aString = nsnull,
gfxSkipChars* aSkipChars = nsnull,
gfxSkipCharsIterator* aSkipIter = nsnull,
PRUint32 aSkippedStartOffset = 0,
PRUint32 aSkippedMaxLength = PR_UINT32_MAX);
void AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
nsIFrame::InlineMinWidthData *aData);
@ -539,6 +545,7 @@ public:
struct TrimmedOffsets {
PRInt32 mStart;
PRInt32 mLength;
PRInt32 GetEnd() { return mStart + mLength; }
};
TrimmedOffsets GetTrimmedOffsets(const nsTextFragment* aFrag,
PRBool aTrimAfter);
@ -2048,7 +2055,7 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE) &&
textStyle->WhiteSpaceCanWrap()) {
PRInt32 whitespaceCount =
GetTrimmableWhitespaceCount(aFrag, offsets.mStart + offsets.mLength - 1,
GetTrimmableWhitespaceCount(aFrag, offsets.GetEnd() - 1,
offsets.mLength, -1);
offsets.mLength -= whitespaceCount;
}
@ -3260,6 +3267,13 @@ public:
virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
InlinePrefWidthData *aData);
virtual nsresult GetRenderedText(nsAString* aString = nsnull,
gfxSkipChars* aSkipChars = nsnull,
gfxSkipCharsIterator* aSkipIter = nsnull,
PRUint32 aSkippedStartOffset = 0,
PRUint32 aSkippedMaxLength = PR_UINT32_MAX)
{ return NS_ERROR_NOT_IMPLEMENTED; } // Call on a primary text frame only
protected:
nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
nsIFrame* mPrevContinuation;
@ -4589,7 +4603,7 @@ nsTextFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset)
TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), PR_TRUE);
// Check whether there are nonskipped characters in the trimmmed range
return iter.ConvertOriginalToSkipped(trimmed.mStart + trimmed.mLength) >
return iter.ConvertOriginalToSkipped(trimmed.GetEnd()) >
iter.ConvertOriginalToSkipped(trimmed.mStart);
}
@ -4644,7 +4658,7 @@ nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
if (!aForward) {
PRInt32 i;
for (i = PR_MIN(trimmed.mStart + trimmed.mLength, startOffset) - 1;
for (i = PR_MIN(trimmed.GetEnd(), startOffset) - 1;
i >= trimmed.mStart; --i) {
iter.SetOriginalOffset(i);
if (!iter.IsOriginalCharSkipped() &&
@ -4656,12 +4670,12 @@ nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
*aOffset = 0;
} else {
PRInt32 i;
for (i = startOffset + 1; i <= trimmed.mStart + trimmed.mLength; ++i) {
for (i = startOffset + 1; i <= trimmed.GetEnd(); ++i) {
iter.SetOriginalOffset(i);
// XXX we can't necessarily stop at the end of this frame,
// but we really have no choice right now. We need to do a deeper
// fix/restructuring of PeekOffsetCharacter
if (i == trimmed.mStart + trimmed.mLength ||
if (i == trimmed.GetEnd() ||
(!iter.IsOriginalCharSkipped() &&
mTextRun->IsClusterStart(iter.GetSkippedOffset()))) {
*aOffset = i - mContentOffset;
@ -4717,7 +4731,7 @@ ClusterIterator::NextCluster()
while (PR_TRUE) {
if (mDirection > 0) {
if (mIterator.GetOriginalOffset() >= mTrimmed.mStart + mTrimmed.mLength)
if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
return PR_FALSE;
if (mIterator.IsOriginalCharSkipped() ||
mIterator.GetOriginalOffset() < mTrimmed.mStart ||
@ -4732,7 +4746,7 @@ ClusterIterator::NextCluster()
return PR_FALSE;
mIterator.AdvanceOriginal(-1);
if (mIterator.IsOriginalCharSkipped() ||
mIterator.GetOriginalOffset() >= mTrimmed.mStart + mTrimmed.mLength ||
mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() ||
!textRun->IsClusterStart(mIterator.GetSkippedOffset()))
continue;
mCharIndex = mIterator.GetOriginalOffset();
@ -5581,13 +5595,13 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
const nsTextFragment* frag = mContent->GetText();
TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE);
gfxSkipCharsIterator iter = start;
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.mStart + trimmed.mLength);
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.GetEnd());
const nsStyleText* textStyle = GetStyleText();
gfxFloat delta = 0;
if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) {
aLastCharIsJustifiable = PR_TRUE;
} else if (trimmed.mStart + trimmed.mLength < mContentOffset + mContentLength) {
} else if (trimmed.GetEnd() < GetContentEnd()) {
gfxSkipCharsIterator end = iter;
PRUint32 endOffset = end.ConvertOriginalToSkipped(mContentOffset + mContentLength);
if (trimmedEnd < endOffset) {
@ -5613,7 +5627,7 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
provider.FindEndOfJustificationRange(&justificationEnd);
PRInt32 i;
for (i = justificationEnd.GetOriginalOffset(); i < trimmed.mStart + trimmed.mLength; ++i) {
for (i = justificationEnd.GetOriginalOffset(); i < trimmed.GetEnd(); ++i) {
if (IsJustifiableCharacter(frag, i, isCJK)) {
aLastCharIsJustifiable = PR_TRUE;
}
@ -5653,6 +5667,98 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
return NS_OK;
}
static PRUnichar TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
PRUint32 aSkippedOffset, PRUnichar aChar)
{
if (aChar == '\n' || aChar == '\r') {
return aStyle->WhiteSpaceIsSignificant() ? aChar : ' ';
}
switch (aStyle->mTextTransform) {
case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
nsContentUtils::GetCaseConv()->ToLower(aChar, &aChar);
break;
case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
nsContentUtils::GetCaseConv()->ToUpper(aChar, &aChar);
break;
case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
if (aTextRun->CanBreakLineBefore(aSkippedOffset)) {
nsContentUtils::GetCaseConv()->ToTitle(aChar, &aChar);
}
break;
}
return aChar;
}
nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
gfxSkipChars* aSkipChars,
gfxSkipCharsIterator* aSkipIter,
PRUint32 aSkippedStartOffset,
PRUint32 aSkippedMaxLength)
{
// The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient...
gfxSkipCharsBuilder skipCharsBuilder;
nsTextFrame* textFrame;
const nsTextFragment* textFrag = mContent->GetText();
PRInt32 keptCharsLength = 0;
PRInt32 validCharsLength = 0;
// Build skipChars and copy text, for each text frame in this continuation block
for (textFrame = this; textFrame;
textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
// For each text frame continuation in this block ...
// Ensure the text run and grab the gfxSkipCharsIterator for it
gfxSkipCharsIterator iter = textFrame->EnsureTextRun();
if (!textFrame->mTextRun)
return NS_ERROR_FAILURE;
// Skip to the start of the text run, past ignored chars at start of line
// XXX In the future we may decide to trim extra spaces before a hard line
// break, in which case we need to accurately detect those sitations and
// call GetTrimmedOffsets() with PR_TRUE to trim whitespace at the line's end
TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, PR_FALSE);
PRInt32 startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset;
if (startOfLineSkipChars > 0) {
skipCharsBuilder.SkipChars(startOfLineSkipChars);
iter.SetOriginalOffset(trimmedContentOffsets.mStart);
}
// Keep and copy the appropriate chars withing the caller's requested range
const nsStyleText* textStyle = textFrame->GetStyleText();
while (iter.GetOriginalOffset() < trimmedContentOffsets.GetEnd() &&
keptCharsLength < aSkippedMaxLength) {
// For each original char from content text
if (iter.IsOriginalCharSkipped() || ++ validCharsLength <= aSkippedStartOffset) {
skipCharsBuilder.SkipChar();
} else {
++ keptCharsLength;
skipCharsBuilder.KeepChar();
if (aAppendToString) {
aAppendToString->Append(
TransformChar(textStyle, textFrame->mTextRun, iter.GetSkippedOffset(),
textFrag->CharAt(iter.GetOriginalOffset())));
}
}
iter.AdvanceOriginal(1);
}
if (keptCharsLength >= aSkippedMaxLength) {
break; // Already past the end, don't build string or gfxSkipCharsIter anymore
}
}
if (aSkipChars) {
aSkipChars->TakeFrom(&skipCharsBuilder); // Copy skipChars into aSkipChars
if (aSkipIter) {
// Caller must provide both pointers in order to retrieve a gfxSkipCharsIterator,
// because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipCars.
*aSkipIter = gfxSkipCharsIterator(*aSkipChars, GetContentLength());
}
}
return NS_OK;
}
#ifdef DEBUG
// Translate the mapped content into a string that's printable
void