From fcf4d1b5afa5035c0735338e017ccdfdf7fa21f2 Mon Sep 17 00:00:00 2001 From: "uriber%gmail.com" Date: Mon, 11 Sep 2006 08:03:28 +0000 Subject: [PATCH] Backing out bug 300131 until I figure out how to deal with the bustage. git-svn-id: svn://10.0.0.236/trunk@209689 18797224-902f-48f8-a5cc-f745e15eee43 --- .../src/html/nsHyperTextAccessible.cpp | 2 +- mozilla/layout/base/nsCaret.cpp | 4 +- mozilla/layout/generic/nsBRFrame.cpp | 63 +- mozilla/layout/generic/nsFrame.cpp | 321 +++------ mozilla/layout/generic/nsFrame.h | 9 +- mozilla/layout/generic/nsFrameSelection.h | 17 +- mozilla/layout/generic/nsIFrame.h | 89 +-- mozilla/layout/generic/nsInlineFrame.cpp | 16 - mozilla/layout/generic/nsInlineFrame.h | 4 +- mozilla/layout/generic/nsSelection.cpp | 51 +- mozilla/layout/generic/nsTextFrame.cpp | 675 ++++++++++++------ 11 files changed, 641 insertions(+), 610 deletions(-) diff --git a/mozilla/accessible/src/html/nsHyperTextAccessible.cpp b/mozilla/accessible/src/html/nsHyperTextAccessible.cpp index f86f92fdd07..503a06c2657 100644 --- a/mozilla/accessible/src/html/nsHyperTextAccessible.cpp +++ b/mozilla/accessible/src/html/nsHyperTextAccessible.cpp @@ -511,7 +511,7 @@ PRInt32 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell, nsIFr kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi, wordMovementType); - nsresult rv = aFromFrame->PeekOffset(&pos); + nsresult rv = aFromFrame->PeekOffset(aPresShell->GetPresContext(), &pos); PRInt32 resultOffset = pos.mContentOffset; nsIContent *resultContent = nsnull; diff --git a/mozilla/layout/base/nsCaret.cpp b/mozilla/layout/base/nsCaret.cpp index c49625d116b..8cc85cd093a 100644 --- a/mozilla/layout/base/nsCaret.cpp +++ b/mozilla/layout/base/nsCaret.cpp @@ -719,7 +719,7 @@ nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode, { nsPeekOffsetStruct pos; pos.SetData(eSelectBeginLine, eDirPrevious, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE); - if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) { + if (NS_SUCCEEDED(frameAfter->PeekOffset(presContext, &pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; } @@ -751,7 +751,7 @@ nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode, { nsPeekOffsetStruct pos; pos.SetData(eSelectEndLine, eDirNext, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE); - if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) { + if (NS_SUCCEEDED(frameBefore->PeekOffset(presContext, &pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; } diff --git a/mozilla/layout/generic/nsBRFrame.cpp b/mozilla/layout/generic/nsBRFrame.cpp index 004532ec300..9fa0e85bd62 100644 --- a/mozilla/layout/generic/nsBRFrame.cpp +++ b/mozilla/layout/generic/nsBRFrame.cpp @@ -66,10 +66,8 @@ public: virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint); - virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType); + NS_IMETHOD PeekOffset(nsPresContext* aPresContext, + nsPeekOffsetStruct *aPos); NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -209,35 +207,42 @@ nsIFrame::ContentOffsets BRFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoin return offsets; } -PRBool -BRFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset) +NS_IMETHODIMP BRFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) { - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - // If we hit the end of a BR going backwards, go to its beginning and stay there. - if (!aForward && startOffset != 0) { - *aOffset = 0; - return PR_TRUE; + if (!aPos) + return NS_ERROR_NULL_POINTER; + + //BR is also a whitespace, but sometimes GetNextWord() can't handle this + //See bug 304891 + nsTextTransformer::Initialize(); + + if (aPos->mWordMovementType != eDefaultBehavior) { + // aPos->mWordMovementType possible values: + // eEndWord: eat the space if we're moving backwards + // eStartWord: eat the space if we're moving forwards + if ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)) { + aPos->mEatingWS = PR_TRUE; + } + } + else if (aPos->mDirection == eDirNext && nsTextTransformer::GetWordSelectEatSpaceAfter()) { + // Use the hidden preference which is based on operating system behavior. + // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. + // When going backwards, the start of the word is always used, on every operating system. + aPos->mEatingWS = PR_TRUE; } - // Otherwise, stop if we hit the beginning, continue (forward) if we hit the end. - return (startOffset == 0); -} -PRBool -BRFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - // Keep going. The actual line jumping will stop us. - return PR_FALSE; -} + //offset of this content in its parents child list. base 0 + PRInt32 offsetBegin = mContent->GetParent()->IndexOf(mContent); -PRBool -BRFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType) -{ - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - // Keep going. The actual line jumping will stop us. - return PR_FALSE; + if (aPos->mAmount != eSelectLine && aPos->mAmount != eSelectBeginLine + && aPos->mAmount != eSelectEndLine) //then we must do the adjustment to make sure we leave this frame + { + if (aPos->mDirection == eDirNext) + aPos->mStartOffset = offsetBegin +1;//go to end to make sure we jump to next node. + else + aPos->mStartOffset = offsetBegin; //we start at beginning to make sure we leave this frame. + } + return nsFrame::PeekOffset(aPresContext, aPos);//now we let the default take over. } #ifdef ACCESSIBILITY diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index 63bc5c06d86..18c6c5aafe5 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -2010,7 +2010,7 @@ nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, PR_TRUE, //limit on scrolled views PR_FALSE, PR_FALSE); - rv = PeekOffset(&startpos); + rv = PeekOffset(aPresContext, &startpos); if (NS_FAILED(rv)) return rv; nsPeekOffsetStruct endpos; @@ -2022,7 +2022,7 @@ nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, PR_TRUE, //limit on scrolled views PR_FALSE, PR_FALSE); - rv = PeekOffset(&endpos); + rv = PeekOffset(aPresContext, &endpos); if (NS_FAILED(rv)) return rv; @@ -3767,9 +3767,7 @@ nsFrame::GetPointFromOffset(nsPresContext* inPresContext, nsIRenderingContext* i if (newContent){ PRInt32 newOffset = newContent->IndexOf(mContent); - PRBool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1; - if (!isRTL && inOffset > newOffset || - isRTL && inOffset <= newOffset) + if (inOffset > newOffset) bottomLeft.x = GetRect().width; } } @@ -4058,10 +4056,11 @@ nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext, //we need to jump to new block frame. aPos->mAmount = eSelectLine; aPos->mStartOffset = 0; + aPos->mEatingWS = PR_FALSE; aPos->mAttachForward = !(aPos->mDirection == eDirNext); if (aPos->mDirection == eDirPrevious) aPos->mStartOffset = -1;//start from end - return aBlockFrame->PeekOffset(aPos); + return aBlockFrame->PeekOffset(aPresContext, aPos); } } return NS_OK; @@ -4141,7 +4140,8 @@ FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection) } nsresult -nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos) +nsFrame::PeekOffsetParagraph(nsPresContext* aPresContext, + nsPeekOffsetStruct *aPos) { nsIFrame* frame = this; nsBlockFrame* bf; // used only for QI @@ -4208,137 +4208,62 @@ nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos) return NS_OK; } -// Determine movement direction relative to frame -static PRBool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, PRBool aVisual) -{ - PRBool isReverseDirection = aVisual ? - (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : PR_FALSE; - return aDirection == (isReverseDirection ? eDirPrevious : eDirNext); -} - -NS_IMETHOD -nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) +NS_IMETHODIMP +nsFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) { if (!aPos) return NS_ERROR_NULL_POINTER; nsresult result = NS_ERROR_FAILURE; - - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; - - // Translate content offset to be relative to frame - FrameContentRange range = GetRangeForFrame(this); - PRInt32 offset = aPos->mStartOffset - range.start; - nsIFrame* current = this; - - switch (aPos->mAmount) { - case eSelectCharacter: + switch (aPos->mAmount){ + case eSelectCharacter : case eSelectWord: { - PRBool eatingNonRenderableWS = PR_FALSE; - PRBool done = PR_FALSE; - - while (!done) { - PRBool movingInFrameDirection = - IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); + if (mContent) + { + nsIContent* newContent = mContent->GetParent(); + if (newContent){ + aPos->mResultContent = newContent; - if (eatingNonRenderableWS) - done = current->PeekOffsetNoAmount(movingInFrameDirection, &offset); - else - done = current->PeekOffsetCharacter(movingInFrameDirection, &offset); + PRInt32 newOffset = newContent->IndexOf(mContent); + + if (aPos->mDirection == eDirNext) + aPos->mContentOffset = newOffset + 1; + else + aPos->mContentOffset = newOffset;//to beginning of frame + + nsTextTransformer::Initialize(); + if (nsTextTransformer::GetWordSelectEatSpaceAfter() && + aPos->mDirection == eDirNext && aPos->mEatingWS) + { + //If we want to stop at beginning of the next word + //GetFrameFromDirection should not return NS_ERROR_FAILURE + //at end of line + aPos->mEatingWS = PR_FALSE; + result = GetFrameFromDirection(aPresContext, aPos); + aPos->mEatingWS = PR_TRUE; + } + else + result = GetFrameFromDirection(aPresContext, aPos); - if (!done) { - PRBool jumpedLine; - result = - current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, - aPos->mJumpLines, aPos->mScrollViewStop, - ¤t, &offset, &jumpedLine); if (NS_FAILED(result)) return result; - - // If we jumped lines, it's as if we found a character, but we still need - // to eat non-renderable content on the new line. - if (jumpedLine) - eatingNonRenderableWS = PR_TRUE; + PRBool selectable = PR_FALSE; + if (aPos->mResultFrame) + aPos->mResultFrame->IsSelectable(&selectable, nsnull); + if (NS_FAILED(result) || !aPos->mResultFrame || !selectable) + { + return result?result:NS_ERROR_FAILURE; + } + return aPos->mResultFrame->PeekOffset(aPresContext, aPos); } } - - // Set outputs - range = GetRangeForFrame(current); - aPos->mResultFrame = current; - aPos->mResultContent = range.content; - // Output offset is relative to content, not frame - aPos->mContentOffset = offset < 0 ? range.end : range.start + offset; - break; } - case eSelectWord: + case eSelectNoAmount: { - // wordSelectEatSpace means "are we looking for a boundary between whitespace - // and non-whitespace (in the direction we're moving in)". - // It is true when moving forward and looking for a beginning of a word, or - // when moving backwards and looking for an end of a word. - PRBool wordSelectEatSpace; - if (aPos->mWordMovementType != eDefaultBehavior) { - // aPos->mWordMovementType possible values: - // eEndWord: eat the space if we're moving backwards - // eStartWord: eat the space if we're moving forwards - wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)); - } - else { - // Use the hidden preference which is based on operating system behavior. - // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. - // When going backwards, the start of the word is always used, on every operating system. - nsTextTransformer::Initialize(); - wordSelectEatSpace = aPos->mDirection == eDirNext && nsTextTransformer::GetWordSelectEatSpaceAfter(); - } - - // sawBeforeType means "we already saw characters of the type - // before the boundary we're looking for". Examples: - // 1. If we're moving forward, looking for a word beginning (i.e. a boundary - // between whitespace and non-whitespace), then eatingWS==PR_TRUE means - // "we already saw some whitespace". - // 2. If we're moving backward, looking for a word beginning (i.e. a boundary - // between non-whitespace and whitespace), then eatingWS==PR_TRUE means - // "we already saw some non-whitespace". - PRBool sawBeforeType = PR_FALSE; - - PRBool done = PR_FALSE; - while (!done) { - PRBool movingInFrameDirection = - IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); - - done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace, aPos->mIsKeyboardSelect, - &offset, &sawBeforeType); - - if (!done) { - nsIFrame* nextFrame; - PRInt32 nextFrameOffset; - PRBool jumpedLine; - result = - current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, - aPos->mJumpLines, aPos->mScrollViewStop, - &nextFrame, &nextFrameOffset, &jumpedLine); - // We can't jump lines if we're looking for whitespace following - // non-whitespace, and we already encountered non-whitespace. - if (NS_FAILED(result) || - jumpedLine && !wordSelectEatSpace && sawBeforeType) { - done = PR_TRUE; - } else { - current = nextFrame; - offset = nextFrameOffset; - // Jumping a line is equivalent to encountering whitespace - if (wordSelectEatSpace && jumpedLine) - sawBeforeType = PR_TRUE; - } - } - } - - // Set outputs - range = GetRangeForFrame(current); - aPos->mResultFrame = current; + FrameContentRange range = GetRangeForFrame(this); aPos->mResultContent = range.content; - // Output offset is relative to content, not frame - aPos->mContentOffset = offset < 0 ? range.end : range.start + offset; + aPos->mContentOffset = range.start; + result = range.content ? NS_OK : NS_ERROR_FAILURE; break; } case eSelectLine : @@ -4347,24 +4272,23 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) nsIFrame *blockFrame = this; while (NS_FAILED(result)){ - PRInt32 thisLine = nsFrame::GetLineNumber(blockFrame, &blockFrame); + PRInt32 thisLine = GetLineNumber(blockFrame, &blockFrame); if (thisLine < 0) return NS_ERROR_FAILURE; result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(iter)); NS_ASSERTION(NS_SUCCEEDED(result) && iter, "GetLineNumber() succeeded but no block frame?"); int edgeCase = 0;//no edge case. this should look at thisLine - PRBool doneLooping = PR_FALSE;//tells us when no more block frames hit. //this part will find a frame or a block frame. if it's a block frame //it will "drill down" to find a viable frame or it will return an error. nsIFrame *lastFrame = this; do { - result = nsFrame::GetNextPrevLineFromeBlockFrame(GetPresContext(), - aPos, - blockFrame, - thisLine, - edgeCase //start from thisLine + result = GetNextPrevLineFromeBlockFrame(aPresContext, + aPos, + blockFrame, + thisLine, + edgeCase //start from thisLine ); if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going { @@ -4429,11 +4353,11 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) } } while (!doneLooping); } - return result; + break; } case eSelectParagraph: - return PeekOffsetParagraph(aPos); + return PeekOffsetParagraph(aPresContext, aPos); case eSelectBeginLine: case eSelectEndLine: @@ -4441,7 +4365,7 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) nsCOMPtr it; // Adjusted so that the caret can't get confused when content changes nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this); - PRInt32 thisLine = nsFrame::GetLineNumber(blockFrame, &blockFrame); + PRInt32 thisLine = GetLineNumber(blockFrame, &blockFrame); if (thisLine < 0) return NS_ERROR_FAILURE; result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it)); @@ -4455,7 +4379,7 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) PRBool endOfLine = (eSelectEndLine == aPos->mAmount); #ifdef IBMBIDI - if (aPos->mVisual && GetPresContext()->BidiEnabled()) { + if (aPos->mVisual && aPresContext->BidiEnabled()) { PRBool lineIsRTL; it->GetDirection(&lineIsRTL); PRBool isReordered; @@ -4495,61 +4419,17 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) return NS_ERROR_FAILURE; return NS_OK; } + break; default: { - NS_ASSERTION(PR_FALSE, "Invalid amount"); - return NS_ERROR_FAILURE; + if (NS_SUCCEEDED(result)) + result = aPos->mResultFrame->PeekOffset(aPresContext, aPos); } - } - return NS_OK; + } + return result; } -PRBool -nsFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - // Sure, we can stop right here. - return PR_TRUE; -} - -PRBool -nsFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - // A negative offset means "end of frame", which in our case means offset 1. - if (startOffset < 0) - startOffset = 1; - if (aForward == (startOffset == 0)) { - // We're before the frame and moving forward, or after it and moving backwards: - // skip to the other side and we're done. - *aOffset = 1 - startOffset; - return PR_TRUE; - } - return PR_FALSE; -} - -PRBool -nsFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType) -{ - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - if (startOffset < 0) - startOffset = 1; - if (aForward == (startOffset == 0)) { - // We're before the frame and moving forward, or after it and moving backwards. - // If we're looking for non-whitespace, we found it (without skipping this frame). - if (aWordSelectEatSpace && *aSawBeforeType) - return PR_TRUE; - // Otherwise skip to the other side and note that we encountered non-whitespace. - *aOffset = 1 - startOffset; - if (!aWordSelectEatSpace) - *aSawBeforeType = PR_TRUE; - } - return PR_FALSE; -} NS_IMETHODIMP nsFrame::CheckVisibility(nsPresContext* , PRInt32 , PRInt32 , PRBool , PRBool *, PRBool *) @@ -4596,18 +4476,8 @@ nsFrame::GetLineNumber(nsIFrame *aFrame, nsIFrame** aContainingBlock) } nsresult -nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, - PRBool aJumpLines, PRBool aScrollViewStop, - nsIFrame** aOutFrame, PRInt32* aOutOffset, PRBool* aOutJumpedLine) -{ - if (!aOutFrame || !aOutOffset || !aOutJumpedLine) - return NS_ERROR_NULL_POINTER; - - nsPresContext* presContext = GetPresContext(); - *aOutFrame = nsnull; - *aOutOffset = 0; - *aOutJumpedLine = PR_FALSE; - +nsIFrame::GetFrameFromDirection(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) +{ // Find the prev/next selectable frame PRBool selectable = PR_FALSE; nsIFrame *traversedFrame = this; @@ -4625,19 +4495,19 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, nsIFrame *firstFrame; nsIFrame *lastFrame; #ifdef IBMBIDI - if (aVisual && presContext->BidiEnabled()) { + if (aPos->mVisual && aPresContext->BidiEnabled()) { PRBool lineIsRTL; it->GetDirection(&lineIsRTL); PRBool isReordered; result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); - nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame; + nsIFrame** framePtr = aPos->mDirection == eDirPrevious ? &firstFrame : &lastFrame; if (*framePtr) { nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr); if (((embeddingLevel & 1) && lineIsRTL || !(embeddingLevel & 1) && !lineIsRTL) == - (aDirection == eDirPrevious)) { - nsFrame::GetFirstLeaf(presContext, framePtr); + (aPos->mDirection == eDirPrevious)) { + nsFrame::GetFirstLeaf(aPresContext, framePtr); } else { - nsFrame::GetLastLeaf(presContext, framePtr); + nsFrame::GetLastLeaf(aPresContext, framePtr); } atLineEdge = *framePtr == traversedFrame; } else { @@ -4654,8 +4524,8 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, if (NS_FAILED(result)) return result; - if (aDirection == eDirPrevious) { - nsFrame::GetFirstLeaf(presContext, &firstFrame); + if (aPos->mDirection == eDirPrevious) { + nsFrame::GetFirstLeaf(aPresContext, &firstFrame); atLineEdge = firstFrame == traversedFrame; } else { // eDirNext lastFrame = firstFrame; @@ -4666,36 +4536,43 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, return NS_ERROR_FAILURE; } } - nsFrame::GetLastLeaf(presContext, &lastFrame); + nsFrame::GetLastLeaf(aPresContext, &lastFrame); atLineEdge = lastFrame == traversedFrame; } } - if (atLineEdge) { - *aOutJumpedLine = PR_TRUE; - if (!aJumpLines) - return NS_ERROR_FAILURE; //we are done. cannot jump lines + if (atLineEdge) + { + if (aPos->mJumpLines != PR_TRUE) + return NS_ERROR_FAILURE;//we are done. cannot jump lines + if (aPos->mAmount != eSelectWord) + { + aPos->mAmount = eSelectNoAmount; + } + else{ + if (aPos->mEatingWS)//done finding what we wanted + return NS_ERROR_FAILURE; + } } - nsCOMPtr frameTraversal; result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), - presContext, traversedFrame, + aPresContext, traversedFrame, eLeaf, - aVisual && presContext->BidiEnabled(), - aScrollViewStop, + aPos->mVisual && aPresContext->BidiEnabled(), + aPos->mScrollViewStop, PR_TRUE // aFollowOOFs ); if (NS_FAILED(result)) return result; - if (aDirection == eDirNext) + nsISupports *isupports = nsnull; + if (aPos->mDirection == eDirNext) result = frameTraversal->Next(); - else + else result = frameTraversal->Prev(); + if (NS_FAILED(result)) return result; - - nsISupports *isupports = nsnull; result = frameTraversal->CurrentItem(&isupports); if (NS_FAILED(result)) return result; @@ -4706,18 +4583,20 @@ nsIFrame::GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, traversedFrame = (nsIFrame *)isupports; traversedFrame->IsSelectable(&selectable, nsnull); } // while (!selectable) - - *aOutOffset = (aDirection == eDirNext) ? 0 : -1; - + + if (aPos->mDirection == eDirNext) + aPos->mStartOffset = 0; + else + aPos->mStartOffset = -1; #ifdef IBMBIDI - if (aVisual) { + if (aPos->mVisual) { PRUint8 newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame); PRUint8 newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame); if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end - *aOutOffset = -1 - *aOutOffset; + aPos->mStartOffset = -1 - aPos->mStartOffset; } #endif - *aOutFrame = traversedFrame; + aPos->mResultFrame = traversedFrame; return NS_OK; } diff --git a/mozilla/layout/generic/nsFrame.h b/mozilla/layout/generic/nsFrame.h index 950891d386e..e9857ff1021 100644 --- a/mozilla/layout/generic/nsFrame.h +++ b/mozilla/layout/generic/nsFrame.h @@ -250,14 +250,11 @@ public: NS_IMETHOD IsSelectable(PRBool* aIsSelectable, PRUint8* aSelectStyle) const; NS_IMETHOD GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon); - - virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType); - + NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) ; NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval); + NS_IMETHOD PeekOffsetParagraph(nsPresContext* aPresContext, + nsPeekOffsetStruct *aPos); NS_IMETHOD GetOffsets(PRInt32 &aStart, PRInt32 &aEnd) const; NS_IMETHOD ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild); diff --git a/mozilla/layout/generic/nsFrameSelection.h b/mozilla/layout/generic/nsFrameSelection.h index 71123949414..8b4ce2ccb27 100644 --- a/mozilla/layout/generic/nsFrameSelection.h +++ b/mozilla/layout/generic/nsFrameSelection.h @@ -91,6 +91,7 @@ struct nsPeekOffsetStruct mIsKeyboardSelect = aIsKeyboardSelect; mVisual = aVisual; mWordMovementType = aWordMovementType; + mEatingWS = PR_FALSE; } // Note: Most arguments (input and output) are only used with certain values @@ -104,14 +105,6 @@ struct nsPeekOffsetStruct nsSelectionAmount mAmount; // mDirection: eDirPrevious or eDirNext. - // * Note for visual bidi movement: - // eDirPrevious means 'left-then-up' if the containing block is LTR, - // 'right-then-up' if it is RTL. - // eDirNext means 'right-then-down' if the containing block is LTR, - // 'left-then-down' if it is RTL. - // Between paragraphs, eDirPrevious means "go to the visual end of the - // previous paragraph", and eDirNext means "go to the visual beginning - // of the next paragraph". // Used with: eSelectCharacter, eSelectWord, eSelectLine, eSelectParagraph. nsDirection mDirection; @@ -163,6 +156,14 @@ struct nsPeekOffsetStruct // PR_TRUE means "the beginning of the frame logically after the caret". // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine. PRBool mAttachForward; + + /*** Arguments only used internally ***/ + + // mEatingWS: Used only internally, for recursive calls into PeekOffset. + // Shold be PR_FALSE upon initial call to PeekOffset. + // Used with: eSelectWord. + PRBool mEatingWS; + }; struct nsPrevNextBidiLevels diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index b9808ea62bc..0df87d2ff20 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -100,10 +100,10 @@ struct nsMargin; typedef class nsIFrame nsIBox; // IID for the nsIFrame interface -// 48813f12-5e55-46af-8527-c8f408bac999 +// b7f1e31d-0dc8-466f-a46c-76a718a805a3 #define NS_IFRAME_IID \ -{ 0x48813f12, 0x5e55, 0x46af, \ - { 0x85, 0x27, 0xc8, 0xf4, 0x08, 0xba, 0xc9, 0x99 } } +{ 0xb7f1e31d, 0x0dc8, 0x466f, \ + { 0xa4, 0x6c, 0x76, 0xa7, 0x18, 0xa8, 0x05, 0xa3 } } /** * Indication of how the frame can be split. This is used when doing runaround @@ -1335,22 +1335,26 @@ public: * return NS_ERROR_FAILURE * @param aPOS is defined in nsFrameSelection */ - NS_IMETHOD PeekOffset(nsPeekOffsetStruct *aPos); + NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) = 0; /** * called to find the previous/next selectable leaf frame. - * @param aDirection [in] the direction to move in (eDirPrevious or eDirNext) - * @param aVisual [in] whether bidi caret behavior is visual (PR_TRUE) or logical (PR_FALSE) - * @param aJumpLines [in] whether to allow jumping across line boundaries - * @param aScrollViewStop [in] whether to stop when reaching a scroll frame boundary - * @param aOutFrame [out] the previous/next selectable leaf frame - * @param aOutOffset [out] 0 indicates that we arrived at the beginning of the output frame; - * -1 indicates that we arrived at its end. - * @param aOutJumpedLine [out] whether this frame and the returned frame are on different lines + * @param aPOS is defined in nsFrameSelection. + * Fields of aPos used as input are: + * mAmount: should be either eSelectWord or eSelectCharacter + * mDirection: eDirPrevious or eDirNext + * mJumpLines: whether to allow jumping across line boundaries + * mScrollViewStop: whether to stop when reaching a scroll view boundary + * mVisual: whether bidi caret behavior is visual (PR_TRUE) or logical (PR_FALSE) + * mEatingWS: indicates whether we're seeking a boundary between + * non-whitespace and whitespace. + * Fields of aPos set as output are: + * mResultFrame: the previous/next selectable leaf frame + * mAmount: might be set to eSelectNone after crossing a line boundary + * mStartOffset: set to 0 or -1 to indicate which edge of the result frame + * is closer to "this" frame. */ - nsresult GetFrameFromDirection(nsDirection aDirection, PRBool aVisual, - PRBool aJumpLines, PRBool aScrollViewStop, - nsIFrame** aOutFrame, PRInt32* aOutOffset, PRBool* aOutJumpedLine); + nsresult GetFrameFromDirection(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos); /** * called to see if the children of the frame are visible from indexstart to index end. @@ -1700,60 +1704,7 @@ protected: */ void InvalidateRoot(const nsRect& aDamageRect, nscoord aOffsetX, nscoord aOffsetY, - PRBool aImmediate); - - /** - * Can we stop inside this frame when we're skipping non-rendered whitespace? - * @param aForward [in] Are we moving forward (or backward) in content order. - * @param aOffset [in/out] At what offset into the frame to start looking. - * on output - what offset was reached (whether or not we found a place to stop). - * @return PR_TRUE: An appropriate offset was found within this frame, - * and is given by aOffset. - * PR_FALSE: Not found within this frame, need to try the next frame. - */ - virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset) = 0; - - /** - * Search the frame for the next character - * @param aForward [in] Are we moving forward (or backward) in content order. - * @param aOffset [in/out] At what offset into the frame to start looking. - * on output - what offset was reached (whether or not we found a place to stop). - * @return PR_TRUE: An appropriate offset was found within this frame, - * and is given by aOffset. - * PR_FALSE: Not found within this frame, need to try the next frame. - */ - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) = 0; - - /** - * Search the frame for the next word boundary - * @param aForward [in] Are we moving forward (or backward) in content order. - * @param aWordSelectEatSpace [in] PR_TRUE: look for non-whitespace following - * whitespace (in the direction of movement). - * PR_FALSE: look for whitespace following non-whitespace (in the - * direction of movement). - * @param aIsKeyboardSelect [in] Was the action initiated by a keyboard operation? - * If PR_TRUE, punctuation immediately following a word is considered part - * of that word. Otherwise, a sequence of punctuation is always considered - * as a word on its own. - * @param aOffset [in/out] At what offset into the frame to start looking. - * on output - what offset was reached (whether or not we found a place to stop). - * @param aSawBeforeType [in/out] Did we encounter a character of the pre-boundary type - * (whitespace if aWordSelectEatSpace is true, non-whitespace otherwise). - * @return PR_TRUE: An appropriate offset was found within this frame, - * and is given by aOffset. - * PR_FALSE: Not found within this frame, need to try the next frame. - */ - virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType) = 0; - - /** - * Search for the first paragraph boundary before or after the given position - * @param aPos See description in nsFrameSelection.h. The following fields are - * used by this method: - * Input: mDirection - * Output: mResultContent, mContentOffset - */ - nsresult PeekOffsetParagraph(nsPeekOffsetStruct *aPos); + PRBool aImmediate); private: NS_IMETHOD_(nsrefcnt) AddRef(void) = 0; diff --git a/mozilla/layout/generic/nsInlineFrame.cpp b/mozilla/layout/generic/nsInlineFrame.cpp index db2a2962ec8..e081517d446 100644 --- a/mozilla/layout/generic/nsInlineFrame.cpp +++ b/mozilla/layout/generic/nsInlineFrame.cpp @@ -175,22 +175,6 @@ nsInlineFrame::IsEmpty() return PR_TRUE; } -PRBool -nsInlineFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) -{ - // Override the implementation in nsFrame, to skip empty inline frames - NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - if (startOffset < 0) - startOffset = 1; - if (aForward == (startOffset == 0)) { - // We're before the frame and moving forward, or after it and moving backwards: - // skip to the other side, but keep going. - *aOffset = 1 - startOffset; - } - return PR_FALSE; -} - NS_IMETHODIMP nsInlineFrame::AppendFrames(nsIAtom* aListName, nsIFrame* aFrameList) diff --git a/mozilla/layout/generic/nsInlineFrame.h b/mozilla/layout/generic/nsInlineFrame.h index 2f2aef8654b..4d88af07294 100644 --- a/mozilla/layout/generic/nsInlineFrame.h +++ b/mozilla/layout/generic/nsInlineFrame.h @@ -48,7 +48,7 @@ class nsAnonymousBlockFrame; #define NS_INLINE_FRAME_CID \ - { 0x88b298af, 0x8b0e, 0x4592,{0x9e, 0xc6, 0xea, 0x4c, 0x4b, 0x3f, 0xf7, 0xa4}} + { 0xa6cf90e0, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} #define nsInlineFrameSuper nsHTMLContainerFrame @@ -113,8 +113,6 @@ public: virtual PRBool IsEmpty(); virtual PRBool IsSelfEmpty(); - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset); - // nsIHTMLReflow overrides NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/generic/nsSelection.cpp b/mozilla/layout/generic/nsSelection.cpp index cd1e013130e..b45ace73542 100644 --- a/mozilla/layout/generic/nsSelection.cpp +++ b/mozilla/layout/generic/nsSelection.cpp @@ -1287,6 +1287,9 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, } } + offsetused = mDomSelections[index]->FetchFocusOffset(); + weakNodeUsed = mDomSelections[index]->FetchFocusNode(); + PRBool visualMovement = (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE || aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE || @@ -1346,7 +1349,7 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, default :return NS_ERROR_FAILURE; } PostReason(nsISelectionListener::KEYPRESS_REASON); - if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent) + if (NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent) { nsIFrame *theFrame; PRInt32 currentOffset, frameStart, frameEnd; @@ -1413,8 +1416,6 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinueSelection, PR_FALSE); } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) { // Collapse selection if PeekOffset failed because we bumped into the BRFrame, bug 207623. - weakNodeUsed = mDomSelections[index]->FetchFocusNode(); - offsetused = mDomSelections[index]->FetchFocusOffset(); mDomSelections[index]->Collapse(weakNodeUsed, offsetused); tHint = mHint; // make the line below restore the original hint result = NS_OK; @@ -1547,8 +1548,10 @@ nsFrameSelection::VisualSequence(nsIFrame* aSelectFrame, PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); nsresult result = nsnull; + nsPresContext* presContext = mShell->GetPresContext(); + PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(aCurrentFrame); - result = aSelectFrame->PeekOffset(aPos); + result = aSelectFrame->PeekOffset(presContext, aPos); while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame)) { if (NS_FAILED(result)) @@ -1581,7 +1584,7 @@ nsFrameSelection::VisualSequence(nsIFrame* aSelectFrame, aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount aPos->mContentOffset = 0; - result = aSelectFrame->PeekOffset(aPos); + result = aSelectFrame->PeekOffset(presContext, aPos); } return NS_OK; @@ -1651,17 +1654,19 @@ nsFrameSelection::SelectLines(nsDirection aSelectionDirection, endOffset = aAnchorOffset; } + nsPresContext* presContext = mShell->GetPresContext(); + aPos.mStartOffset = startOffset; aPos.mDirection = eDirNext; aPos.mAmount = eSelectLine; - result = startFrame->PeekOffset(&aPos); + result = startFrame->PeekOffset(presContext, &aPos); if (NS_FAILED(result)) return result; startFrame = aPos.mResultFrame; aPos.mStartOffset = aPos.mContentOffset; aPos.mAmount = eSelectBeginLine; - result = startFrame->PeekOffset(&aPos); + result = startFrame->PeekOffset(presContext, &aPos); if (NS_FAILED(result)) return result; @@ -1685,14 +1690,14 @@ nsFrameSelection::SelectLines(nsDirection aSelectionDirection, aPos.mStartOffset = endOffset; aPos.mDirection = eDirPrevious; aPos.mAmount = eSelectLine; - result = endFrame->PeekOffset(&aPos); + result = endFrame->PeekOffset(presContext, &aPos); if (NS_FAILED(result)) return result; endFrame = aPos.mResultFrame; aPos.mStartOffset = aPos.mContentOffset; aPos.mAmount = eSelectEndLine; - result = endFrame->PeekOffset(&aPos); + result = endFrame->PeekOffset(presContext, &aPos); if (NS_FAILED(result)) return result; @@ -1736,6 +1741,8 @@ nsFrameSelection::VisualSelectFrames(nsIFrame* aCurrentFrame, nsDirection selectionDirection; PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + nsPresContext* presContext = mShell->GetPresContext(); + result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset); if (NS_FAILED(result)) return result; @@ -1811,7 +1818,7 @@ nsFrameSelection::VisualSelectFrames(nsIFrame* aCurrentFrame, aPos.mStartOffset = anchorOffset; aPos.mDirection = selectionDirection; - result = anchorFrame->PeekOffset(&aPos); + result = anchorFrame->PeekOffset(presContext, &aPos); if (NS_FAILED(result)) return result; @@ -1963,14 +1970,20 @@ nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode, return levels; } - nsIFrame *newFrame; - PRInt32 offset; - PRBool jumpedLine; - nsresult rv = currentFrame->GetFrameFromDirection(direction, PR_FALSE, - aJumpLines, PR_TRUE, - &newFrame, &offset, &jumpedLine); - if (NS_FAILED(rv)) - newFrame = nsnull; + nsPeekOffsetStruct pos; + pos.SetData(eSelectCharacter, + direction, + currentOffset, + 0, + aJumpLines, + PR_TRUE, // aScrollViewStop + PR_FALSE, // aIsKeyboardSelect + PR_FALSE); // aVisual + + nsresult rv = currentFrame->GetFrameFromDirection(mShell->GetPresContext(), &pos); + nsIFrame *newFrame = nsnull; + if (NS_SUCCEEDED(rv)) + newFrame = pos.mResultFrame; PRUint8 baseLevel = NS_GET_BASE_LEVEL(currentFrame); PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(currentFrame); @@ -2286,7 +2299,7 @@ nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint) pos.SetData(amount, direction, offset, 0, PR_FALSE, mLimiter != nsnull, PR_FALSE, PR_FALSE); - if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) { + if (frame && NS_SUCCEEDED(frame->PeekOffset(mShell->GetPresContext(), &pos)) && pos.mResultContent) { offsets.content = pos.mResultContent; offsets.offset = pos.mContentOffset; } diff --git a/mozilla/layout/generic/nsTextFrame.cpp b/mozilla/layout/generic/nsTextFrame.cpp index 85b68c8ff52..235be480e58 100644 --- a/mozilla/layout/generic/nsTextFrame.cpp +++ b/mozilla/layout/generic/nsTextFrame.cpp @@ -345,11 +345,7 @@ public: PRBool aSelected, nsSpread aSpread); - virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType); - + NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos); NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval); NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const; @@ -4467,273 +4463,480 @@ nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset, return NS_OK; } -PRBool -nsTextFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - nsresult rv = indexBuffer.GrowTo(mContentLength + 1); - if (NS_FAILED(rv)) { - return PR_FALSE; - } - PRInt32 textLength; - nsTextTransformer tx(GetPresContext()); - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); - return (textLength > 0); -} -PRBool -nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) +NS_IMETHODIMP +nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) { - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - // A negative offset means "end of frame". - if (startOffset < 0) - startOffset = mContentLength; + DEBUG_VERIFY_NOT_DIRTY(mState); + if (mState & NS_FRAME_IS_DIRTY) + return NS_ERROR_UNEXPECTED; - nsPresContext* presContext = GetPresContext(); +#ifdef IBMBIDI + // XXX TODO: - this explanation may not be accurate + // - need to better explain for what happens when you move + // from the end of a line to the beginning of the next + // despite not making a move logically within the text. + // + // When you move your caret by in some visual direction, and you find the + // new position is at the edge of a line (beginning or end), you usually + // want to stay on the current line rather than move to the next or the + // previous one; however, if you want to get to the edge of the next or + // the previous word (i.e. you're 'eating white space' when moving + // between words), and you couldn't find the word in the current line, + // you do _not_ want to stay on the current line - you want to get to that + // word. + // + // When your frames are ordered the same way logically as they are + // visually (e.g. when they're frames of LTR text displayed in + // left-then-down order), this translates to terms of 'prefer left' or + // 'prefer right', i.e. if you move to the left and hit the beginning of + // the line you have to choose between the frame "on the left" - the last + // frame on the previous line - and the frame "on the right" - the + // first frame of the current line. + // + // When the frames are displayed in right-then-down order (i.e. frames + // within an RTL line), the last sentence remains correct, but now + // the directions are reversed - if you're moving left, you hit the end + // of a line; the frame closer to your original position is the one + // "on the right" - the last frame on the current line - rather than the + // one "on the left" - the first frame on the next + // line. + // + // Note (for visual caret movement): + // eDirPrevious means 'left-then-up' if the containing block is LTR, + // 'right-then-up' if it is RTL. + // eDirNext means 'right-then-down' if the containing block is LTR, + // 'left-then-down' if it is RTL. + // Between paragraphs, eDirPrevious means "go to the visual end of the + // previous paragraph", and eDirNext means "go to the visual beginning of + // the next paragraph" + + PRBool isReverseDirection = aPos->mVisual ? + (NS_GET_EMBEDDING_LEVEL(this) & 1) != (NS_GET_BASE_LEVEL(this) & 1) : PR_FALSE; + PRBool movementIsInFrameDirection = + ((aPos->mDirection == eDirNext) && !isReverseDirection) || + ((aPos->mDirection == eDirPrevious) && isReverseDirection); + +#endif + + if (!aPos || !mContent) + return NS_ERROR_NULL_POINTER; + + // XXX TODO: explain this policy; why the assymetry between + // too high and too low start offsets? + // + // There are 4 possible ranges of aPos->mStartOffset: + // + // 0 mContentOffset mContentOffset+mContentLength + // | | | + // range #1 | range #2 | range #3 | range #4 + // *************** + // our frame's part + // of the content + // + // Range Policy + // ------------------------------------------------------------------------ + // #1 Assume the start position is at the end of the content of 'this' + // #2 Round up the position to the beginning of our frame + // #3 No change necessary + // #4 Delegate the PeekOffset() to the next frame (according + // to the direction, either to our left or our right) + // + // Note: the diagram above is drawn left-to-right, but advancing + // in the content may sometimes mean going right-to-left - // Transform text from content into renderable form + if (aPos->mStartOffset < 0 ) + aPos->mStartOffset = mContentLength + mContentOffset; + if (aPos->mStartOffset < mContentOffset){ + aPos->mStartOffset = mContentOffset; + } + if (aPos->mStartOffset > (mContentOffset + mContentLength)){ + nsIFrame *nextContinuation = GetNextContinuation(); + if (!nextContinuation){ + NS_ASSERTION(PR_FALSE,"nsTextFrame::PeekOffset no more continuation\n"); + return NS_ERROR_INVALID_ARG; + } + return nextContinuation->PeekOffset(aPresContext, aPos); + } + + // XXX TODO: explain the following: + // - if this frame is the first of the last in the line according + // to the content indices, why not handle the cases of + // eSelectBeginLine/eSelectEndLine + // - why can't we make the hand-off to the parent class' method + // before correcting aPos->mStartOffset? + + if (aPos->mAmount == eSelectLine || aPos->mAmount == eSelectBeginLine + || aPos->mAmount == eSelectEndLine || aPos->mAmount == eSelectParagraph) + { + return nsFrame::PeekOffset(aPresContext, aPos); + } + nsAutoTextBuffer paintBuffer; nsAutoIndexBuffer indexBuffer; nsresult rv = indexBuffer.GrowTo(mContentLength + 1); if (NS_FAILED(rv)) { - return PR_FALSE; - } - PRInt32 textLength; - nsTextTransformer tx(presContext); - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + return rv; + } PRInt32* ip = indexBuffer.mBuffer; - PRBool found = PR_TRUE; - - PRBool selectable; - PRUint8 selectStyle; - - IsSelectable(&selectable, &selectStyle); - if (selectStyle == NS_STYLE_USER_SELECT_ALL) - return PR_FALSE; - - if (!aForward) { - *aOffset = 0; - PRInt32 i; - - nsAutoPRUint8Buffer clusterBuffer; - rv = FillClusterBuffer(presContext, paintBuffer.mBuffer, - (PRUint32)textLength, clusterBuffer); - NS_ENSURE_SUCCESS(rv, rv); - - for (i = startOffset-1; i >= 0; i--){ - if ((ip[i] < ip[startOffset]) && - (clusterBuffer.mBuffer[ip[i] - mContentOffset]) && - (! IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) + PRInt32 textLength; + nsresult result(NS_ERROR_FAILURE); + aPos->mResultContent = mContent;//do this right off + switch (aPos->mAmount){ + case eSelectNoAmount: + { + if (!IsEmpty()) //if no renderable length, you can't park here. { - *aOffset = i; - break; + aPos->mContentOffset = aPos->mStartOffset; + result = NS_OK; + } + else + { + result = GetFrameFromDirection(aPresContext, aPos); + if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this) + return aPos->mResultFrame->PeekOffset(aPresContext, aPos); + else if (NS_FAILED(result)) + return result; } } - -#ifdef SUNCTL - static NS_DEFINE_CID(kLECID, NS_ULE_CID); - - nsCOMPtr ctlObj; - ctlObj = do_CreateInstance(kLECID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Cell based cursor movement will not be supported\n"); - ctlObj = nsnull; - } - else { - PRBool needsCTL = PR_FALSE; - PRInt32 previousOffset; - - ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - mContentOffset + startOffset, -1, &needsCTL); - - if (needsCTL) { - ctlObj->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - textLength, mContentOffset + startOffset, - &previousOffset); - *aOffset = i = previousOffset - mContentOffset; - } - } -#endif /* SUNCTL */ - - if (i < 0) - found = PR_FALSE; - } - else // aForward - { - PRInt32 i; - *aOffset = mContentLength; - - nsAutoPRUint8Buffer clusterBuffer; - rv = FillClusterBuffer(presContext, paintBuffer.mBuffer, - (PRUint32)textLength, clusterBuffer); - NS_ENSURE_SUCCESS(rv, rv); - - for (i = startOffset; i <= mContentLength; i++) { - if ((ip[i] > ip[startOffset]) && - ((i == mContentLength) || - (!IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i] - mContentOffset])) && - (clusterBuffer.mBuffer[ip[i] - mContentOffset]))) { - *aOffset = i; - break; - } - } - -#ifdef SUNCTL - static NS_DEFINE_CID(kLECID, NS_ULE_CID); - - nsCOMPtr ctlObj; - ctlObj = do_CreateInstance(kLECID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Cell based cursor movement will not be supported\n"); - ctlObj = nsnull; - } - else { - PRBool needsCTL = PR_FALSE; - PRInt32 nextOffset; - - ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - mContentOffset + startOffset, 0, &needsCTL); - - if (needsCTL) { - - ctlObj->NextCluster(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - textLength, mContentOffset + startOffset, - &nextOffset); - *aOffset = i = nextOffset - mContentOffset; - } - } -#endif /* SUNCTL */ - - if (i > mContentLength) - found = PR_FALSE; - } - return found; -} + break; -PRBool -nsTextFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType) -{ - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - if (startOffset < 0) - startOffset = mContentLength; - - nsTextTransformer tx(GetPresContext()); - PRBool keepSearching = PR_TRUE; //if you run out of chars before you hit the end of word, maybe next frame has more text to select? - PRBool found = PR_FALSE; - PRBool isWhitespace, wasTransformed; - PRInt32 wordLen, contentLen; - - PRBool selectable; - PRUint8 selectStyle; - IsSelectable(&selectable, &selectStyle); - if (selectStyle == NS_STYLE_USER_SELECT_ALL) - found = PR_FALSE; - else - { - tx.Init(this, mContent, mContentOffset + startOffset); - if (!aForward) { - *aOffset = 0; //initialize + case eSelectCharacter: + { + // Transform text from content into renderable form + nsIDocument* doc = mContent->GetDocument(); + if (!doc) { + return NS_OK; + } + nsTextTransformer tx(aPresContext); + PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + + PRBool found = PR_TRUE; + + PRBool selectable; + PRUint8 selectStyle; + + IsSelectable(&selectable, &selectStyle); + if ( selectStyle == NS_STYLE_USER_SELECT_ALL ) + found = PR_FALSE; + else + { + + #ifdef IBMBIDI + if (!movementIsInFrameDirection){ + #else + if (aPos->mDirection == eDirPrevious){ + #endif + aPos->mContentOffset = 0; + PRInt32 i; + + nsAutoPRUint8Buffer clusterBuffer; + rv = FillClusterBuffer(aPresContext, paintBuffer.mBuffer, + (PRUint32)textLength, clusterBuffer); + NS_ENSURE_SUCCESS(rv, rv); + + for (i = aPos->mStartOffset -1 - mContentOffset; i >=0; i--){ + if ((ip[i] < ip[aPos->mStartOffset - mContentOffset]) && + (clusterBuffer.mBuffer[ip[i] - mContentOffset]) && + (! IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) + { + aPos->mContentOffset = i + mContentOffset; + break; + } + } + + #ifdef SUNCTL + static NS_DEFINE_CID(kLECID, NS_ULE_CID); + + nsCOMPtr ctlObj; + ctlObj = do_CreateInstance(kLECID, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("Cell based cursor movement will not be supported\n"); + ctlObj = nsnull; + } + else { + PRBool needsCTL = PR_FALSE; + PRInt32 previousOffset; + + ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, + paintBuffer.mBuffer), + aPos->mStartOffset, -1, &needsCTL); + + if (needsCTL) { + ctlObj->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar*, + paintBuffer.mBuffer), + textLength,aPos->mStartOffset, + &previousOffset); + aPos->mContentOffset = i = previousOffset; + } + } + #endif /* SUNCTL */ + + if (i <0){ + found = PR_FALSE; + aPos->mContentOffset = mContentOffset;//in case next call fails we stop at this offset + } + } + #ifdef IBMBIDI + else if (movementIsInFrameDirection){ + #else + else if (aPos->mDirection == eDirNext){ + #endif + PRInt32 i; + aPos->mContentOffset = mContentLength; + + nsAutoPRUint8Buffer clusterBuffer; + rv = FillClusterBuffer(aPresContext, paintBuffer.mBuffer, + (PRUint32)textLength, clusterBuffer); + NS_ENSURE_SUCCESS(rv, rv); + + for (i = aPos->mStartOffset - mContentOffset; i <= mContentLength; i++) { + if ((ip[i] > ip[aPos->mStartOffset - mContentOffset]) && + ((i == mContentLength) || + (!IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i] - mContentOffset])) && + (clusterBuffer.mBuffer[ip[i] - mContentOffset]))) { + aPos->mContentOffset = i + mContentOffset; + break; + } + } + + #ifdef SUNCTL + static NS_DEFINE_CID(kLECID, NS_ULE_CID); + + nsCOMPtr ctlObj; + ctlObj = do_CreateInstance(kLECID, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("Cell based cursor movement will not be supported\n"); + ctlObj = nsnull; + } + else { + PRBool needsCTL = PR_FALSE; + PRInt32 nextOffset; + + ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, + paintBuffer.mBuffer), + aPos->mStartOffset, 0, &needsCTL); + + if (needsCTL) { + + ctlObj->NextCluster(NS_REINTERPRET_CAST(const PRUnichar*, + paintBuffer.mBuffer), + textLength, aPos->mStartOffset, + &nextOffset); + aPos->mContentOffset = i = nextOffset; + } + } + #endif /* SUNCTL */ + + /* if (aStartOffset == 0 && (mState & TEXT_SKIP_LEADING_WS)) + i--; //back up because we just skipped over some white space. why skip over the char also? + */ + if (i > mContentLength){ + found = PR_FALSE; + aPos->mContentOffset = mContentOffset + mContentLength;//in case next call fails we stop at this offset + } + } + } + if (!found) + { + result = GetFrameFromDirection(aPresContext, aPos); + if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this) + { + return aPos->mResultFrame->PeekOffset(aPresContext, aPos); + } + } + else + aPos->mResultContent = mContent; + } + break; + + case eSelectWord: + { + // Transform text from content into renderable form + nsIDocument* doc = mContent->GetDocument(); + if (!doc) { + return result; + } + + nsTextTransformer tx(aPresContext); + + PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); + PRBool keepSearching; //if you run out of chars before you hit the end of word, maybe next frame has more text to select? + PRBool found = PR_FALSE; + PRBool isWhitespace, wasTransformed; + PRInt32 wordLen, contentLen; + PRBool wordSelectEatSpace; + if (aPos->mWordMovementType != eDefaultBehavior) { + // aPos->mWordMovementType possible values: + // eEndWord: eat the space if we're moving backwards + // eStartWord: eat the space if we're moving forwards + wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)); + } + else { + // Use the hidden preference which is based on operating system behavior. + // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. + // When going backwards, the start of the word is always used, on every operating system. + wordSelectEatSpace = aPos->mDirection == eDirNext && nsTextTransformer::GetWordSelectEatSpaceAfter(); + } + + PRBool selectable; + PRUint8 selectStyle; + IsSelectable(&selectable, &selectStyle); + if ( selectStyle == NS_STYLE_USER_SELECT_ALL ) + found = PR_FALSE; + else + { + #ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; -#endif // IBMBIDI - if (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, - PR_FALSE, aIsKeyboardSelect) && - contentLen <= startOffset) { - if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType){ - *aOffset = startOffset - contentLen; + if (!movementIsInFrameDirection){ +#else + if (aPos->mDirection == eDirPrevious){ +#endif keepSearching = PR_TRUE; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; + tx.Init(this, mContent, aPos->mStartOffset); + aPos->mContentOffset = mContentOffset;//initialize #ifdef IBMBIDI wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; #endif // IBMBIDI - while (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, - &isWhitespace, PR_FALSE, - aIsKeyboardSelect)){ - if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType) - break; - if (contentLen >= startOffset) - goto TryNextFrame; - *aOffset -= contentLen; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; + if (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, + PR_FALSE, aPos->mIsKeyboardSelect) && + (aPos->mStartOffset - contentLen >= mContentOffset) ){ + if ((wordSelectEatSpace ? isWhitespace : !isWhitespace) || !aPos->mEatingWS){ + aPos->mContentOffset = aPos->mStartOffset - contentLen; + keepSearching = PR_TRUE; + if (wordSelectEatSpace ? isWhitespace : !isWhitespace) + aPos->mEatingWS = PR_TRUE; #ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; + wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; #endif // IBMBIDI + while (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, + &isWhitespace, PR_FALSE, + aPos->mIsKeyboardSelect)){ + if (wordSelectEatSpace ? !isWhitespace : aPos->mEatingWS) + break; + if (aPos->mStartOffset - contentLen <= mContentOffset) + goto TryNextFrame; + aPos->mContentOffset -= contentLen; + if (wordSelectEatSpace ? isWhitespace : !isWhitespace) + aPos->mEatingWS = PR_TRUE; +#ifdef IBMBIDI + wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; +#endif // IBMBIDI + } + keepSearching = aPos->mContentOffset <= mContentOffset; + if (!keepSearching) + found = PR_TRUE; + } + else { + aPos->mContentOffset = mContentLength + mContentOffset; + found = PR_TRUE; + } } - keepSearching = *aOffset <= 0; - if (!keepSearching) - found = PR_TRUE; } - else { - *aOffset = mContentLength; - found = PR_TRUE; - } - } - } - else { // aForward - *aOffset = mContentLength; //initialize - #ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; -#endif // IBMBIDI - if (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect) && - (startOffset + contentLen <= mContentLength)) { - - if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType) { - *aOffset = startOffset + contentLen; - keepSearching = PR_TRUE; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; + else if (movementIsInFrameDirection){ +#else + else if (aPos->mDirection == eDirNext) { +#endif + tx.Init(this, mContent, aPos->mStartOffset ); + aPos->mContentOffset = mContentOffset + mContentLength;//initialize + #ifdef IBMBIDI wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; #endif // IBMBIDI - while (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect)) - { - if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType) - break; - if (startOffset + contentLen >= mContentLength) - goto TryNextFrame; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; - *aOffset += contentLen; + if (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aPos->mIsKeyboardSelect) && + (aPos->mStartOffset + contentLen <= (mContentLength + mContentOffset))){ + + // On some platforms (mac, unix), we want the selection to end + // at the end of the word (not the beginning of the next one). + if ((wordSelectEatSpace ? isWhitespace : !isWhitespace) || !aPos->mEatingWS) { + aPos->mContentOffset = aPos->mStartOffset + contentLen; + keepSearching = PR_TRUE; + if (wordSelectEatSpace ? isWhitespace : !isWhitespace) + aPos->mEatingWS = PR_TRUE; #ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; + wordLen = (mState & NS_FRAME_IS_BIDI) + ? mContentOffset + mContentLength : -1; #endif // IBMBIDI - } - keepSearching = *aOffset >= mContentLength; - if (!keepSearching) - found = PR_TRUE; + while (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aPos->mIsKeyboardSelect)) + { + if (wordSelectEatSpace ? !isWhitespace : aPos->mEatingWS) + break; + if (aPos->mStartOffset + contentLen >= (mContentLength + mContentOffset)) + goto TryNextFrame; + if (wordSelectEatSpace ? isWhitespace : !isWhitespace) + aPos->mEatingWS = PR_TRUE; + aPos->mContentOffset += contentLen; +#ifdef IBMBIDI + wordLen = (mState & NS_FRAME_IS_BIDI) + ? mContentOffset + mContentLength : -1; +#endif // IBMBIDI + } + keepSearching = (mContentOffset + mContentLength) <= aPos->mContentOffset; + if (!keepSearching) + found = PR_TRUE; + } + else + { + aPos->mContentOffset = mContentOffset; + found = PR_TRUE; + } + } + } + } +TryNextFrame: + if (!found || + (aPos->mContentOffset > (mContentOffset + mContentLength)) || + (aPos->mContentOffset < mContentOffset)) + { + aPos->mContentOffset = PR_MIN(aPos->mContentOffset, mContentOffset + mContentLength); + aPos->mContentOffset = PR_MAX(aPos->mContentOffset, mContentOffset); + if (wordSelectEatSpace && aPos->mEatingWS) { + //If we want to stop at beginning of the next word + //GetFrameFromDirction should not return NS_ERROR_FAILURE at end of line + aPos->mEatingWS = PR_FALSE; + result = GetFrameFromDirection(aPresContext, aPos); + aPos->mEatingWS = PR_TRUE; } else + result = GetFrameFromDirection(aPresContext, aPos); + if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this) { - *aOffset = 0; - found = PR_TRUE; + if (NS_SUCCEEDED(result = aPos->mResultFrame->PeekOffset(aPresContext, aPos))) + return NS_OK;//else fall through +#ifdef IBMBIDI + else if (movementIsInFrameDirection) +#else + else if (aPos->mDirection == eDirNext) +#endif + aPos->mContentOffset = mContentOffset + mContentLength; + else + aPos->mContentOffset = mContentOffset; } - } + else + aPos->mResultContent = mContent; + } + else + { + aPos->mResultContent = mContent; + } } + break; + + default: + result = NS_ERROR_FAILURE; break; } -TryNextFrame: - if (!found || - *aOffset > mContentLength || - *aOffset < 0) - { - found = PR_FALSE; - *aOffset = PR_MIN(*aOffset, mContentLength); - *aOffset = PR_MAX(*aOffset, 0); + + if (NS_FAILED(result)){ + aPos->mResultContent = mContent; + //aPos->mContentOffset = aPos->mStartOffset; + result = NS_OK; } - return found; -} + aPos->mResultFrame = this; + + return result; +} NS_IMETHODIMP nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval)