From a710fcd15d380fdeebbc3f90abdaae355cefd617 Mon Sep 17 00:00:00 2001 From: "roc+%cs.cmu.edu" Date: Wed, 7 Dec 2005 23:08:39 +0000 Subject: [PATCH] Bug 316281. Rework GetContentAndOffsetsFromPoint. r=uriber,sr=roc,patch by Eli Friedman --- layout/base/nsIFrameSelection.h | 17 +- layout/forms/nsTextControlFrame.cpp | 9 - layout/generic/nsBRFrame.cpp | 34 +- layout/generic/nsBlockFrame.cpp | 197 -------- layout/generic/nsBlockFrame.h | 28 -- layout/generic/nsFrame.cpp | 755 ++++++++++++++++------------ layout/generic/nsFrame.h | 5 + layout/generic/nsIFrame.h | 5 + layout/generic/nsSelection.cpp | 95 ---- layout/generic/nsTextFrame.cpp | 158 +----- 10 files changed, 489 insertions(+), 814 deletions(-) diff --git a/layout/base/nsIFrameSelection.h b/layout/base/nsIFrameSelection.h index 4e5155cdcdae..9381466f89c2 100644 --- a/layout/base/nsIFrameSelection.h +++ b/layout/base/nsIFrameSelection.h @@ -56,9 +56,8 @@ class nsIPresShell; // IID for the nsIFrameSelection interface #define NS_IFRAMESELECTION_IID \ -{ 0x18477ed4, 0x01ff, 0x4319, \ - { 0x95, 0xc0, 0x63, 0x9e, 0xe4, 0x33, 0xbe, 0x92 } } - +{ 0xe5d9fe4f, 0xf430, 0x41ab, \ + { 0x95, 0xab, 0x1e, 0x7c, 0x86, 0x80, 0x2d, 0xd7 } } //---------------------------------------------------------------------- @@ -289,18 +288,6 @@ public: */ NS_IMETHOD GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset)=0; - /** AdjustOffsetsFromStyle. Called after detecting that a click or drag will - * select the frame, this function looks for user-select style on that frame or a parent - * frame, and adjust the content and offsets accordingly. - * @param aFrame the frame that was clicked - * @param outContent content node to be selected - * @param outStartOffset selection start offset - * @param outEndOffset selection end offset - */ - NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection, - nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset)=0; - - NS_IMETHOD GetHint(HINT *aHint)=0; NS_IMETHOD SetHint(HINT aHint)=0; diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 13d7cb37ed1e..95e48a05ac9d 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -597,8 +597,6 @@ public: NS_IMETHOD GetLimiter(nsIContent **aLimiterContent); NS_IMETHOD GetTableCellSelection(PRBool *aState); NS_IMETHOD GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset); - NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection, - nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset); NS_IMETHOD GetHint(nsIFrameSelection::HINT *aHint); NS_IMETHOD SetHint(nsIFrameSelection::HINT aHint); NS_IMETHOD SetScrollableView(nsIScrollableView *aScrollableView); @@ -1148,13 +1146,6 @@ nsTextInputSelectionImpl::GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffs return mFrameSelection->GetFrameForNodeOffset(aNode, aOffset, aHint,aReturnFrame,aReturnOffset); } -NS_IMETHODIMP -nsTextInputSelectionImpl::AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection, - nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset) -{ - return mFrameSelection->AdjustOffsetsFromStyle(aFrame, changeSelection, outContent, outStartOffset, outEndOffset); -} - NS_IMETHODIMP nsTextInputSelectionImpl::GetHint(nsIFrameSelection::HINT *aHint) { return mFrameSelection->GetHint(aHint); diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp index c3648d0defc9..0d9a4aedb568 100644 --- a/layout/generic/nsBRFrame.cpp +++ b/layout/generic/nsBRFrame.cpp @@ -61,12 +61,11 @@ public: nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0); #endif - NS_IMETHOD GetContentAndOffsetsFromPoint(nsPresContext* aCX, - const nsPoint& aPoint, - nsIContent** aNewContent, - PRInt32& aContentOffset, - PRInt32& aContentOffsetEnd, - PRBool& aBeginFrameContent); + NS_IMETHOD GetPositionHelper(const nsPoint& aPoint, + nsIContent ** aNewContent, + PRInt32& aContentOffset, + PRInt32& aContentOffsetEnd); + NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos); @@ -210,22 +209,21 @@ BRFrame::GetType() const return nsLayoutAtoms::brFrame; } -NS_IMETHODIMP BRFrame::GetContentAndOffsetsFromPoint(nsPresContext* aCX, - const nsPoint& aPoint, - nsIContent ** aContent, - PRInt32& aOffsetBegin, - PRInt32& aOffsetEnd, - PRBool& aBeginFrameContent) +NS_IMETHODIMP BRFrame::GetPositionHelper(const nsPoint& aPoint, + nsIContent ** aNewContent, + PRInt32& aContentOffset, + PRInt32& aContentOffsetEnd) { if (!mContent) return NS_ERROR_NULL_POINTER; - NS_IF_ADDREF(*aContent = mContent->GetParent()); + NS_IF_ADDREF(*aNewContent = mContent->GetParent()); - if (*aContent) - aOffsetBegin = (*aContent)->IndexOf(mContent); - aOffsetEnd = aOffsetBegin; - aBeginFrameContent = PR_TRUE; - return NS_OK; + if (*aNewContent) { + aContentOffset = (*aNewContent)->IndexOf(mContent); + aContentOffsetEnd = aContentOffset; + return NS_OK; + } + return NS_ERROR_FAILURE; } NS_IMETHODIMP BRFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index f98cc668a87b..b6416b7ac9b5 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -6484,203 +6484,6 @@ nsBlockFrame::PaintChildren(nsPresContext* aPresContext, #endif } -// XXXldb Does this handle all overlap cases correctly? (How?) -nsresult -nsBlockFrame::GetClosestLine(nsILineIterator *aLI, - const nsPoint &aPoint, - PRInt32 &aClosestLine) -{ - if (!aLI) - return NS_ERROR_NULL_POINTER; - - nsRect rect; - PRInt32 numLines; - PRInt32 lineFrameCount; - nsIFrame *firstFrame; - PRUint32 flags; - - nsresult result = aLI->GetNumLines(&numLines); - - if (NS_FAILED(result) || numLines < 0) - return NS_OK;//do not handle - - PRInt32 shifted = numLines; - PRInt32 start = 0, midpoint = 0; - PRInt32 y = 0; - - while(shifted > 0) - { - // Cut the number of lines to look at in half and - // calculate the midpoint of the region we are looking at. - - shifted >>= 1; //divide by 2 - midpoint = start + shifted; - - // Get the dimensions of the line that is at the half - // point of the region we are looking at. - - result = aLI->GetLine(midpoint, &firstFrame, &lineFrameCount,rect,&flags); - if (NS_FAILED(result)) - break;//do not handle - - // Check to see if our point lies with the line's Y bounds. - - y = aPoint.y - rect.y; - if (y >=0 && (aPoint.y < (rect.y+rect.height))) - { - aClosestLine = midpoint; //spot on! - return NS_OK; - } - - if (y > 0) - { - // If we get here, no match was found above, so aPoint.y must - // be greater than the Y bounds of the current line rect. Move - // our starting point just beyond the midpoint of the current region. - - start = midpoint; - - if (numLines > 1 && start < (numLines - 1)) - ++start; - else - shifted = 0; - } - } - - // Make sure we don't go off the edge in either direction! - - NS_ASSERTION(start >=0 && start <= numLines, "Invalid start calculated."); - - if (start < 0) - start = 0; - else if (start >= numLines) - start = numLines - 1; - - aClosestLine = start; //close as we could come - - return NS_OK; -} - -NS_IMETHODIMP -nsBlockFrame::HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - - nsresult result; - nsIPresShell *shell = nsnull; - if (aEvent->message == NS_MOUSE_MOVE) { - shell = aPresContext->GetPresShell(); - if (!shell) - return NS_OK; - nsCOMPtr frameSelection; - PRBool mouseDown = PR_FALSE; -//check to see if we need to ask the selection controller.. - if (mState & NS_FRAME_INDEPENDENT_SELECTION) - { - nsCOMPtr selCon; - result = GetSelectionController(aPresContext, getter_AddRefs(selCon)); - if (NS_FAILED(result) || !selCon) - return result?result:NS_ERROR_FAILURE; - frameSelection = do_QueryInterface(selCon); - } - else - frameSelection = shell->FrameSelection(); - if (!frameSelection || NS_FAILED(frameSelection->GetMouseDownState(&mouseDown)) || !mouseDown) - return NS_OK;//do not handle - } - - if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN || aEvent->message == NS_MOUSE_MOVE || - aEvent->message == NS_MOUSE_LEFT_DOUBLECLICK ) { - - nsMouseEvent *me = (nsMouseEvent *)aEvent; - - nsIFrame *resultFrame = nsnull;//this will be passed the handle event when we - //can tell who to pass it to - nsIFrame *mainframe = this; - shell = aPresContext->GetPresShell(); - if (!shell) - return NS_OK; - nsCOMPtr it( do_QueryInterface(mainframe, &result) ); - nsPeekOffsetStruct pos; - - while(NS_OK == result) - { //we are starting aloop to allow us to "drill down to the one we want" - PRInt32 closestLine; - nsPoint pt = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mainframe); - if (NS_FAILED(result = GetClosestLine(it, pt, closestLine))) - return result; - - // XXX mDesiredX needs to be in GetOffsetFromView coords - nsPoint offset; - nsIView* view; - mainframe->GetOffsetFromView(offset, &view); - //we will now ask where to go. if we cant find what we want"aka another block frame" - //we drill down again - pos.mShell = shell; - pos.mDirection = eDirNext; - pos.mDesiredX = pt.x + offset.x; - pos.mScrollViewStop = PR_FALSE; - pos.mIsKeyboardSelect = PR_FALSE; - result = nsFrame::GetNextPrevLineFromeBlockFrame(aPresContext, - &pos, - mainframe, - closestLine-1, - 0 - ); - - if (NS_SUCCEEDED(result) && pos.mResultFrame){ - if (result == NS_OK) - it = do_QueryInterface(pos.mResultFrame, &result);//if this fails that's ok - resultFrame = pos.mResultFrame; - mainframe = resultFrame; - } - else - break;//time to go nothing was found - } - //end while loop. if nssucceeded resutl then keep going that means - //we have successfully hit another block frame and we should keep going. - - - if (resultFrame) - { - if (NS_POSITION_BEFORE_TABLE == result) - { - nsCOMPtr selCon; - result = GetSelectionController(aPresContext, getter_AddRefs(selCon)); - //get the selection controller - if (NS_SUCCEEDED(result) && selCon) - { - PRInt16 displayresult; - selCon->GetDisplaySelection(&displayresult); - if (displayresult == nsISelectionController::SELECTION_OFF) - return NS_OK;//nothing to do we cannot affect selection from here - } - PRBool mouseDown = aEvent->message == NS_MOUSE_MOVE; - result = shell->FrameSelection()->HandleClick(pos.mResultContent, - pos.mContentOffset, - pos.mContentOffsetEnd, - mouseDown || me->isShift, - PR_FALSE, - pos.mPreferLeft); - } - else - result = resultFrame->HandleEvent(aPresContext, aEvent, aEventStatus);//else let the frame/container do what it needs - /* Note that the above call to HandleEvent may capture the - mouse. If so, don't try to capture again. */ - if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN && !IsMouseCaptured(aPresContext)) - CaptureMouse(aPresContext, PR_TRUE); - return result; - } - else - { - return NS_OK; //just stop it - } - } - return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); -} - #ifdef ACCESSIBILITY NS_IMETHODIMP nsBlockFrame::GetAccessible(nsIAccessible** aAccessible) { diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 2f645feea163..e6981f8783b9 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -206,9 +206,6 @@ public: PRBool aConsiderSelf); virtual nsIFrame* GetFrameForPoint(const nsPoint& aPoint, nsFramePaintLayer aWhichLayer); - NS_IMETHOD HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus); NS_IMETHOD ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild); NS_IMETHOD IsVisibleForPainting(nsPresContext * aPresContext, @@ -229,21 +226,6 @@ public: nsIAtom* aAttribute, PRInt32 aModType); -#ifdef DO_SELECTION - NS_IMETHOD HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus); - - NS_IMETHOD HandleDrag(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus); - - nsIFrame * FindHitFrame(nsBlockFrame * aBlockFrame, - const nscoord aX, const nscoord aY, - const nsPoint & aPoint); - -#endif - virtual void DeleteNextInFlowChild(nsPresContext* aPresContext, nsIFrame* aNextInFlow); @@ -312,16 +294,6 @@ protected: nscoord aAscent, nscoord aSize); - /** - * GetClosestLine will return the line that VERTICALLY owns the point closest to aPoint.y - * aPoint is the point to search for, relative to the origin of the frame that aLI - * iterates over. - * aClosestLine is the result. - */ - nsresult GetClosestLine(nsILineIterator *aLI, - const nsPoint &aPoint, - PRInt32 &aClosestLine); - void TryAllLines(nsLineList::iterator* aIterator, nsLineList::iterator* aEndIterator, PRBool* aInOverflowLines); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 2b200c077529..1346eb113e6d 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -23,6 +23,7 @@ * Contributor(s): * Pierre Phaneuf * Uri Bernstein + * Eli Friedman * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -1112,28 +1113,6 @@ nsFrame::GetDataForTableSelection(nsIFrameSelection *aFrameSelection, return NS_OK; } -/* -NS_IMETHODIMP -nsFrame::FrameOrParentHasSpecialSelectionStyle(PRUint8 aSelectionStyle, nsIFrame* *foundFrame) -{ - nsIFrame* thisFrame = this; - - while (thisFrame) - { - if (thisFrame->GetStyleUserInterface()->mUserSelect == aSelectionStyle) - { - *foundFrame = thisFrame; - return NS_OK; - } - - thisFrame = thisFrame->GetParent(); - } - - *foundFrame = nsnull; - return NS_OK; -} -*/ - NS_IMETHODIMP nsFrame::IsSelectable(PRBool* aSelectable, PRUint8* aSelectStyle) const { @@ -1199,58 +1178,6 @@ nsFrame::IsSelectable(PRBool* aSelectable, PRUint8* aSelectStyle) const return NS_OK; } -PRBool -ContentContainsPoint(nsPresContext *aPresContext, - nsIContent *aContent, - const nsPoint &aPoint, - nsIView *aRelativeView) -{ - nsIPresShell *presShell = aPresContext->GetPresShell(); - - if (!presShell) return PR_FALSE; - - nsIFrame *frame = presShell->GetPrimaryFrameFor(aContent); - - if (!frame) return PR_FALSE; - - nsIView *frameView = nsnull; - nsPoint offsetPoint; - - // Get the view that contains the content's frame. - - nsresult rv = frame->GetOffsetFromView(offsetPoint, &frameView); - - if (NS_FAILED(rv) || !frameView) return PR_FALSE; - - // aPoint is relative to aRelativeView's upper left corner! Make sure - // that our point is in the same view space our content frame's - // rects are in. - - nsPoint point = aPoint + aRelativeView->GetOffsetTo(frameView); - - // Now check to see if the point is within the bounds of the - // content's primary frame, or any of it's continuation frames. - - while (frame) { - // Get the frame's rect and make it relative to the - // upper left corner of its parent view. - - nsRect frameRect = frame->GetRect(); - frameRect.x = offsetPoint.x; - frameRect.y = offsetPoint.y; - - if (frameRect.Contains(point)) { - // point is within this frame's rect! - return PR_TRUE; - } - - frame = frame->GetNextInFlow(); - - } - - return PR_FALSE; -} - /** * Handles the Mouse Press Event for the frame */ @@ -1366,19 +1293,6 @@ nsFrame::HandlePress(nsPresContext* aPresContext, rv = GetContentAndOffsetsFromPoint(aPresContext, pt, getter_AddRefs(content), startOffset, endOffset, beginFrameContent); - // do we have CSS that changes selection behaviour? - PRBool changeSelection = PR_FALSE; - { - nsCOMPtr selectContent; - PRInt32 newStart, newEnd; - if (NS_SUCCEEDED(frameselection->AdjustOffsetsFromStyle(this, &changeSelection, getter_AddRefs(selectContent), &newStart, &newEnd)) - && changeSelection) - { - content = selectContent; - startOffset = newStart; - endOffset = newEnd; - } - } if (NS_FAILED(rv)) return rv; @@ -1475,83 +1389,16 @@ nsFrame::HandlePress(nsPresContext* aPresContext, if (isEditor && !me->isShift && (endOffset - startOffset) == 1) { // A single node is selected and we aren't extending an existing - // selection, which means the user clicked directly on an object. - // Check if the user clicked in a -moz-user-select:all subtree, - // image, or hr. If so, we want to give the drag and drop - // code a chance to execute so we need to turn off selection extension - // when processing mouse move/drag events that follow this mouse - // down event. - - PRBool disableDragSelect = PR_FALSE; - - if (changeSelection) - { - // The click hilited a -moz-user-select:all subtree. - // - // XXX: We really should be able to just do a: - // - // disableDragSelect = PR_TRUE; - // - // but we are working around the fact that in some cases, - // selection selects a -moz-user-select:all subtree even - // when the click was outside of the subtree. An example of - // this case would be when the subtree is at the end of a - // line and the user clicks to the right of it. In this case - // I would expect the caret to be placed next to the root of - // the subtree, but right now the whole subtree gets selected. - // This means that we have to do geometric frame containment - // checks on the point to see if the user truly clicked - // inside the subtree. - - nsIView *view = nsnull; - nsPoint dummyPoint; - - // aEvent->point is relative to the upper left corner of the - // frame's parent view. Unfortunately, the only way to get - // the parent view is to call GetOffsetFromView(). - - nsPoint pt = nsLayoutUtils:: - GetEventCoordinatesForNearestView(aEvent, this, &view); - - GetOffsetFromView(dummyPoint, &view); - - // Now check to see if the point is truly within the bounds - // of any of the frames that make up the -moz-user-select:all subtree: - - if (view) - disableDragSelect = ContentContainsPoint(aPresContext, content, - pt, view); - } - else - { - // Check if click was in an image. - - nsIContent* frameContent = GetContent(); - nsCOMPtr img(do_QueryInterface(frameContent)); - - disableDragSelect = img != nsnull; - - if (!img) - { - // Check if click was in an hr. - - nsCOMPtr hr(do_QueryInterface(frameContent)); - disableDragSelect = hr != nsnull; - } - } - - if (disableDragSelect) - { - // Click was in one of our draggable objects, so disable - // selection extension during mouse moves. - - rv = frameselection->SetMouseDownState( PR_FALSE ); - } + // selection, which means the user clicked directly on an object (either + // -moz-user-select: all or a non-text node without children). + // Therefore, disable selection extension during mouse moves. + // XXX This is a bit hacky; shouldn't editor be able to deal with this? + rv = frameselection->SetMouseDownState( PR_FALSE ); } return rv; } - + /** * Multiple Mouse Press -- line or paragraph selection -- for the frame. * Wouldn't it be nice if this didn't have to be hardwired into Frame code? @@ -1575,28 +1422,31 @@ nsFrame::HandleMultiplePress(nsPresContext* aPresContext, // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph. // Otherwise, triple-click selects line, and quadruple-click selects paragraph // (on platforms that support quadruple-click). - PRBool selectPara = PR_FALSE; + nsSelectionAmount beginAmount, endAmount; nsMouseEvent *me = (nsMouseEvent *)aEvent; if (!me) return NS_OK; - if (me->clickCount == 4) - selectPara = PR_TRUE; - else if (me->clickCount == 3) - { - selectPara = - nsContentUtils::GetBoolPref("browser.triple_click_selects_paragraph"); - } - else + if (me->clickCount == 4) { + beginAmount = endAmount = eSelectParagraph; + } else if (me->clickCount == 3) { + if (nsContentUtils::GetBoolPref("browser.triple_click_selects_paragraph")) { + beginAmount = endAmount = eSelectParagraph; + } else { + beginAmount = eSelectBeginLine; + endAmount = eSelectEndLine; + } + } else if (me->clickCount == 2) { + // We only want inline frames; PeekBackwardAndForward dislikes blocks + beginAmount = endAmount = eSelectWord; + } else { return NS_OK; + } - // Line or paragraph selection: PRInt32 startPos = 0; PRInt32 contentOffsetEnd = 0; nsCOMPtr newContent; PRBool beginContent = PR_FALSE; - nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); - rv = GetContentAndOffsetsFromPoint(aPresContext, pt, getter_AddRefs(newContent), @@ -1604,13 +1454,19 @@ nsFrame::HandleMultiplePress(nsPresContext* aPresContext, contentOffsetEnd, beginContent); if (NS_FAILED(rv)) return rv; - - - return PeekBackwardAndForward(selectPara ? eSelectParagraph - : eSelectBeginLine, - selectPara ? eSelectParagraph - : eSelectEndLine, - startPos, aPresContext, PR_TRUE); + + nsIFrame* result; + PRInt32 offset; + // Maybe make this a static helper? + rv = GetPresContext()->GetPresShell()->FrameSelection()-> + GetFrameForNodeOffset(newContent, startPos, + nsIFrameSelection::HINT(beginContent), + &result, &offset); + NS_ENSURE_SUCCESS(rv, rv); + nsFrame* frame = NS_STATIC_CAST(nsFrame*, result); + + return frame->PeekBackwardAndForward(beginAmount, endAmount, + startPos, aPresContext, PR_TRUE); } NS_IMETHODIMP @@ -1711,6 +1567,9 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, PRBool selectable; PRUint8 selectStyle; IsSelectable(&selectable, &selectStyle); + // XXX Do we really need to exclude non-selectable content here? + // GetContentAndOffsetsFromPoint can handle it just fine, although some + // other stuff might not like it. if (!selectable) return NS_OK; if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { @@ -1851,20 +1710,6 @@ NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, beginFrameContent); if (NS_FAILED(result)) return result; - // do we have CSS that changes selection behaviour? - { - PRBool changeSelection; - nsCOMPtr selectContent; - PRInt32 newStart, newEnd; - if (NS_SUCCEEDED(frameselection->AdjustOffsetsFromStyle(this, &changeSelection, getter_AddRefs(selectContent), &newStart, &newEnd)) - && changeSelection) - { - content = selectContent; - startOffset = newStart; - endOffset = newEnd; - } - } - result = frameselection->HandleClick(content, startOffset , endOffset, me->isShift, PR_FALSE, beginFrameContent); if (NS_FAILED(result)) return result; } @@ -1897,139 +1742,431 @@ NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, return NS_OK; } +struct ContentOffsets { + ContentOffsets(nsIContent* aContent, PRInt32 aStart, PRInt32 aEnd) : + content(aContent), start(aStart), end(aEnd) { } + nsCOMPtr content; + PRInt32 start; + PRInt32 end; +}; + +// Retrieve the content offsets of a frame +static ContentOffsets GetOffsetsOfFrame(nsIFrame* aFrame) { + nsCOMPtr content, parent; + NS_ASSERTION(aFrame->GetContent(), "No content?!"); + content = aFrame->GetContent(); + if (aFrame->GetType() == nsLayoutAtoms::textFrame) { + PRInt32 offset, offsetEnd; + aFrame->GetOffsets(offset, offsetEnd); + return ContentOffsets(content, offset, offsetEnd); + } + // Loop to deal with anonymous content, which has no index; this loop + // probably won't run more than twice under normal conditions + do { + parent = content->GetParent(); + if (parent) { + PRInt32 beginOffset = parent->IndexOf(content); + if (beginOffset >= 0) + return ContentOffsets(parent, beginOffset, beginOffset + 1); + content = parent; + } + } while (parent); + + // The root content node must act differently + return ContentOffsets(content, 0, content->GetChildCount()); +} + +// The FrameTarget represents the closest frame to a point that can be selected +// The frame is the frame represented, frameEdge says whether one end of the +// frame is the result (in which case different handling is needed), and +// afterFrame says which end is repersented if frameEdge is true +struct FrameTarget { + FrameTarget(nsIFrame* aFrame, PRBool aFrameEdge, PRBool aAfterFrame) : + frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame) { } + static FrameTarget Null() { + return FrameTarget(nsnull, PR_FALSE, PR_FALSE); + } + PRBool IsNull() { + return !frame; + } + nsIFrame* frame; + PRPackedBool frameEdge; + PRPackedBool afterFrame; +}; + +// See function implementation for information +static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint); + +static PRBool SelfIsSelectable(nsIFrame* aFrame) +{ + return !(aFrame->IsGeneratedContentFrame() || + aFrame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE); +} + +static PRBool SelectionDescendToKids(nsIFrame* aFrame) { + PRUint8 style = aFrame->GetStyleUIReset()->mUserSelect; + nsIFrame* parent = aFrame->GetParent(); + // If we are only near (not directly over) then don't traverse + // frames with independent selection (e.g. text and list controls) + // unless we're already inside such a frame (see bug 268497). Note that this + // prevents any of the users of this method from entering form controls. + // XXX We might want some way to allow using the up-arrow to go into a form + // control, but the focus didn't work right anyway; it'd probably be enough + // if the left and right arrows could enter textboxes (which I don't believe + // they can at the moment) + return !aFrame->IsGeneratedContentFrame() && + style != NS_STYLE_USER_SELECT_ALL && + style != NS_STYLE_USER_SELECT_NONE && + ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || + !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)); +} + +static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild, + nsPoint aPoint) +{ + nsIFrame* parent = aChild->GetParent(); + if (SelectionDescendToKids(aChild)) { + nsPoint pt = aPoint - aChild->GetOffsetTo(parent); + return GetSelectionClosestFrame(aChild, pt); + } + return FrameTarget(aChild, PR_FALSE, PR_FALSE); +} + +// When the cursor needs to be at the beginning of a block, it shouldn't be +// before the first child. A click on a block whose first child is a block +// should put the cursor in the child. The cursor shouldn't be between the +// blocks, because that's not where it's expected. +// Note that this method is guaranteed to succeed. +static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, + PRBool aEndFrame) { + if (SelectionDescendToKids(aFrame)) { + nsIFrame* result = nsnull; + nsIFrame *frame = aFrame->GetFirstChild(nsnull); + if (!aEndFrame) { + while (frame && (!SelfIsSelectable(frame) || + frame->IsEmpty())) + frame = frame->GetNextSibling(); + if (frame) + result = frame; + } else { + // Because the frame tree is singly linked, to find the last frame, + // we have to iterate through all the frames + // XXX I have a feeling this could be slow for long blocks, although + // I can't find any slowdowns + while (frame) { + if (!frame->IsEmpty() && SelfIsSelectable(frame)) + result = frame; + frame = frame->GetNextSibling(); + } + } + if (result) + return DrillDownToSelectionFrame(result, aEndFrame); + } + // If the current frame has no targetable children, target the current frame + return FrameTarget(aFrame, PR_TRUE, aEndFrame); +} + +// This method finds the closest valid FrameTarget on a given line; if there is +// no valid FrameTarget on the line, it returns a null FrameTarget +static FrameTarget GetSelectionClosestFrameForLine( + nsBlockFrame* aParent, + nsBlockFrame::line_iterator aLine, + nsPoint aPoint) +{ + nsIFrame *frame = aLine->mFirstChild; + // Account for end of lines (any iterator from the block is valid) + if (aLine == aParent->end_lines()) + return DrillDownToSelectionFrame(aParent, PR_TRUE); + nsIFrame *closestFromLeft = nsnull, *closestFromRight = nsnull; + nsRect rect = aLine->mBounds; + nscoord closestLeft = rect.x, closestRight = rect.XMost(); + for (PRInt32 n = aLine->GetChildCount(); n; + --n, frame = frame->GetNextSibling()) { + if (!SelfIsSelectable(frame) || frame->IsEmpty()) + continue; + nsRect frameRect = frame->GetRect(); + if (aPoint.x >= frameRect.x) { + if (aPoint.x < frameRect.XMost()) { + return GetSelectionClosestFrameForChild(frame, aPoint); + } + if (frameRect.XMost() >= closestLeft) { + closestFromLeft = frame; + closestLeft = frameRect.XMost(); + } + } else { + if (frameRect.x <= closestRight) { + closestFromRight = frame; + closestRight = frameRect.x; + } + } + } + if (!closestFromLeft && !closestFromRight) { + // We should only get here if there are no selectable frames on a line + // XXX Do we need more elaborate handling here? + return FrameTarget::Null(); + } + if (closestFromLeft && + (!closestFromRight || + (abs(aPoint.x - closestLeft) <= abs(aPoint.x - closestRight)))) { + return GetSelectionClosestFrameForChild(closestFromLeft, aPoint); + } + return GetSelectionClosestFrameForChild(closestFromRight, aPoint); +} + +// This method is for the special handling we do for block frames; they're +// special because they represent paragraphs and because they are organized +// into lines, which have bounds that are not stored elsewhere in the +// frame tree. Returns a null FrameTarget for frames which are not +// blocks or blocks with no lines. +static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame, + nsPoint aPoint) +{ + nsresult rv; + nsBlockFrame* bf; // used only for QI + rv = aFrame->QueryInterface(kBlockFrameCID, (void**)&bf); + if (NS_FAILED(rv)) + return FrameTarget::Null(); + + // This code searches for the correct line + nsBlockFrame::line_iterator firstLine = bf->begin_lines(); + nsBlockFrame::line_iterator end = bf->end_lines(); + if (firstLine == end) + return FrameTarget::Null(); + nsBlockFrame::line_iterator curLine = firstLine; + nsBlockFrame::line_iterator closestLine = end; + while (curLine != end) { + // Check to see if our point lies with the line's Y bounds + nscoord y = aPoint.y - curLine->mBounds.y; + nscoord height = curLine->mBounds.height; + if (y >= 0 && y < height) { + closestLine = curLine; + break; // We found the line; stop looking + } + if (y < 0) + break; + ++curLine; + } + + if (closestLine == end) { + nsBlockFrame::line_iterator prevLine = curLine.prev(); + nsBlockFrame::line_iterator nextLine = curLine; + // Avoid empty lines + while (nextLine != end && nextLine->IsEmpty()) + ++nextLine; + while (prevLine != end && prevLine->IsEmpty()) + --prevLine; + + // This hidden pref dictates whether a point above or below all lines comes + // up with a line or the beginning or end of the frame; 0 on Windows, + // 1 on other platforms by default at the writing of this code + PRInt32 dragOutOfFrame = + nsContentUtils::GetIntPref("browser.drag_out_of_frame_style"); + + if (prevLine == end) { + if (dragOutOfFrame == 1 || nextLine == end) + return DrillDownToSelectionFrame(aFrame, PR_FALSE); + closestLine = nextLine; + } else if (nextLine == end) { + if (dragOutOfFrame == 1) + return DrillDownToSelectionFrame(aFrame, PR_TRUE); + closestLine = prevLine; + } else { // Figure out which line is closer + if (aPoint.y - prevLine->mBounds.YMost() < nextLine->mBounds.y - aPoint.y) + closestLine = prevLine; + else + closestLine = nextLine; + } + } + + do { + FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine, + aPoint); + if (!target.IsNull()) + return target; + ++closestLine; + } while (closestLine != end); + // Fall back to just targeting the last targetable place + return DrillDownToSelectionFrame(aFrame, PR_TRUE); +} + +// GetSelectionClosestFrame is the helper function that calculates the closest +// frame to the given point. +// It doesn't completely account for offset styles, so needs to be used in +// restricted environments. +// Cannot handle overlapping frames correctly, so it should recieve the output +// of GetFrameForPoint +// Guaranteed to return a valid FrameTarget +static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint) +{ + { + // Handle blocks; if the frame isn't a block, the method fails + FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint); + if (!target.IsNull()) + return target; + } + + nsIFrame *kid = aFrame->GetFirstChild(nsnull); + + if (kid) { + // Go through all the child frames to find the closest one + + // Large number to force the comparison to succeed + const nscoord HUGE_DISTANCE = nscoord_MAX; + nscoord closestXDistance = HUGE_DISTANCE; + nscoord closestYDistance = HUGE_DISTANCE; + nsIFrame *closestFrame = nsnull; + + do { + if (!SelfIsSelectable(kid) || kid->IsEmpty()) + continue; + + nsRect rect = kid->GetRect(); + + nscoord fromLeft = aPoint.x - rect.x; + nscoord fromRight = aPoint.x - rect.XMost(); + + nscoord xDistance; + if (fromLeft >= 0 && fromRight <= 0) { + xDistance = 0; + } else { + xDistance = PR_MIN(abs(fromLeft), abs(fromRight)); + } + + if (xDistance <= closestXDistance) + { + if (xDistance < closestXDistance) + closestYDistance = HUGE_DISTANCE; + + nscoord fromTop = aPoint.y - rect.y; + nscoord fromBottom = aPoint.y - rect.YMost(); + + nscoord yDistance; + if (fromTop >= 0 && fromBottom <= 0) + yDistance = 0; + else + yDistance = PR_MIN(abs(fromTop), abs(fromBottom)); + + if (yDistance < closestYDistance) + { + closestXDistance = xDistance; + closestYDistance = yDistance; + closestFrame = kid; + } + } + } while (kid = kid->GetNextSibling()); + if (closestFrame); + return GetSelectionClosestFrameForChild(closestFrame, aPoint); + } + return FrameTarget(aFrame, PR_FALSE, PR_FALSE); +} nsresult nsFrame::GetContentAndOffsetsFromPoint(nsPresContext* aCX, const nsPoint& aPoint, nsIContent ** aNewContent, PRInt32& aContentOffset, PRInt32& aContentOffsetEnd, - PRBool& aBeginFrameContent) + PRBool& aKeepWithAbove) { if (!aNewContent) return NS_ERROR_NULL_POINTER; - // Traverse through children and look for the best one to give this - // to if it fails the getposition call, make it yourself also only - // look at primary list - nsIFrame *closestFrame = nsnull; - nsIFrame *kid = GetFirstChild(nsnull); + // This section of code deals with special selection styles. Note that + // -moz-none and -moz-all exist, even though they don't need to be explicitly + // handled. + // The offset is forced not to end up in generated content; content offsets + // cannot represent content outside of the document's content tree. - if (kid) { -#define HUGE_DISTANCE 999999 //some HUGE number that will always fail first comparison - - PRInt32 closestXDistance = HUGE_DISTANCE; - PRInt32 closestYDistance = HUGE_DISTANCE; - - while (nsnull != kid) { - - // Skip over generated content kid frames, or frames - // that don't have a proper parent-child relationship! - - PRBool skipThisKid = (kid->GetStateBits() & NS_FRAME_GENERATED_CONTENT) != 0; - - if (skipThisKid) { - kid = kid->GetNextSibling(); - continue; - } - - // Kid frame has content that has a proper parent-child - // relationship. Now see if the aPoint inside it's bounding - // rect or close by. - - nsRect rect = kid->GetRect(); - - nscoord fromTop = aPoint.y - rect.y; - nscoord fromBottom = aPoint.y - rect.y - rect.height; - - PRInt32 yDistance; - if (fromTop > 0 && fromBottom < 0) - yDistance = 0; - else - yDistance = PR_MIN(abs(fromTop), abs(fromBottom)); - - if (yDistance <= closestYDistance && rect.width > 0 && rect.height > 0) - { - if (yDistance < closestYDistance) - closestXDistance = HUGE_DISTANCE; - - nscoord fromLeft = aPoint.x - rect.x; - nscoord fromRight = aPoint.x - rect.x - rect.width; - - PRInt32 xDistance; - if (fromLeft > 0 && fromRight < 0) - xDistance = 0; - else - xDistance = PR_MIN(abs(fromLeft), abs(fromRight)); - - if (xDistance == 0 && yDistance == 0) - { - closestFrame = kid; - break; - } - - if (xDistance < closestXDistance || (xDistance == closestXDistance && rect.x <= aPoint.x)) - { - // If we are only near (not directly over) then don't traverse a frame with independent - // selection (e.g. text and list controls) unless we're already inside such a frame, - // except in "browsewithcaret" mode, bug 268497. - if (!(kid->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || - (GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || - nsContentUtils::GetBoolPref("accessibility.browsewithcaret")) { - closestXDistance = xDistance; - closestYDistance = yDistance; - closestFrame = kid; - } - } - // else if (xDistance > closestXDistance) - // break;//done - } - - kid = kid->GetNextSibling(); - } - if (closestFrame) { - - // If we cross a view boundary, we need to adjust - // the coordinates because GetPosition() expects - // them to be relative to the closest view. - - nsPoint newPoint = aPoint - closestFrame->GetOffsetTo(this); - return closestFrame->GetContentAndOffsetsFromPoint(aCX, newPoint, - aNewContent, - aContentOffset, - aContentOffsetEnd, - aBeginFrameContent); + nsIFrame* adjustedFrame = this; + PRBool frameAdjusted = PR_FALSE; + for (nsIFrame* frame = this; frame; frame = frame->GetParent()) + { + // These are the conditions that make all children not able to handle + // a cursor. + if (frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE || + frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL || + frame->IsGeneratedContentFrame()) { + adjustedFrame = frame; + frameAdjusted = PR_TRUE; } } - if (!mContent) - return NS_ERROR_NULL_POINTER; + // -moz-user-select: all needs special handling, because clicking on it + // should lead to the whole frame being selected + if (adjustedFrame->GetStyleUIReset()->mUserSelect == + NS_STYLE_USER_SELECT_ALL) { + ContentOffsets selectOffset = GetOffsetsOfFrame(adjustedFrame); - NS_IF_ADDREF(*aNewContent = mContent->GetParent()); - if (*aNewContent){ - - PRInt32 contentOffset(aContentOffset); //temp to hold old value in case of failure - - contentOffset = (*aNewContent)->IndexOf(mContent); - if (contentOffset < 0) - { - return NS_ERROR_FAILURE; + NS_IF_ADDREF(*aNewContent = selectOffset.content); + aContentOffset = selectOffset.start; + aContentOffsetEnd = selectOffset.end; + aKeepWithAbove = PR_FALSE; + return NS_OK; + } + // For other cases, try to find a closest frame starting from the parent of + // the unselectable frame + if (frameAdjusted) + adjustedFrame = adjustedFrame->GetParent(); + + nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); + FrameTarget closest = GetSelectionClosestFrame(adjustedFrame, adjustedPoint); + + ContentOffsets offset = GetOffsetsOfFrame(closest.frame); + // If the correct offset is at one end of a frame, use offset-based + // calculation method + if (closest.frameEdge) { + NS_ADDREF(*aNewContent = offset.content); + if (closest.afterFrame) { + aContentOffset = offset.end; + aKeepWithAbove = PR_FALSE; + } else { + aContentOffset = offset.start; + aKeepWithAbove = PR_TRUE; } - aContentOffset = contentOffset; //its clear save the result + aContentOffsetEnd = aContentOffset; + return NS_OK; + } + nsPoint pt = aPoint - closest.frame->GetOffsetTo(this); + nsresult rv = closest.frame->GetPositionHelper(pt, aNewContent, + aContentOffset, + aContentOffsetEnd); + NS_ENSURE_SUCCESS(rv, rv); - aBeginFrameContent = PR_TRUE; - nsRect thisRect(nsPoint(0, 0), GetSize()); - if (thisRect.Contains(aPoint)) - aContentOffsetEnd = aContentOffset +1; - else - { - //if we are a collapsed frame then dont check to see if we need to skip past this content - //see bug http://bugzilla.mozilla.org/show_bug.cgi?id=103888 - if (thisRect.width && thisRect.height && ((thisRect.x + thisRect.width) < aPoint.x || thisRect.y > aPoint.y)) - { - aBeginFrameContent = PR_FALSE; - aContentOffset++; - } - aContentOffsetEnd = aContentOffset; + // XXX should I add some kind of offset standardization? + // consider xxxxxzzzzz; should any click between the last + // x and first z put the cursor in the same logical position in addition + // to the same visual position? + + NS_ASSERTION(*aNewContent == offset.content, + "There should only be one possible content base"); + aKeepWithAbove = (aContentOffset == offset.start); + return NS_OK; +} + +NS_IMETHODIMP nsFrame::GetPositionHelper(const nsPoint& aPoint, + nsIContent ** aNewContent, + PRInt32& aContentOffset, + PRInt32& aContentOffsetEnd) +{ + ContentOffsets offset = GetOffsetsOfFrame(this); + + NS_IF_ADDREF(*aNewContent = offset.content); + // Figure out whether the offsets should be over, after, or before the frame + nsRect rect(nsPoint(0, 0), GetSize()); + + if (rect.Contains(aPoint)) { + aContentOffset = offset.start; + aContentOffsetEnd = offset.end; + } else { + PRBool isBlock = (GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE); + PRBool isRtl = (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL); + if ((isBlock && rect.y < aPoint.y) || + (!isBlock && ((isRtl && rect.x + rect.width < aPoint.x) || + (!isRtl && rect.x < aPoint.x)))) { + aContentOffset = offset.end; + aContentOffsetEnd = offset.end; + } else { + aContentOffset = offset.start; + aContentOffsetEnd = offset.start; } } return NS_OK; diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 2a465772a127..b75842fbfd62 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -321,6 +321,11 @@ public: PRBool aJumpLines); + // Helper for GetContentAndOffsetsFromPoint + NS_IMETHOD GetPositionHelper(const nsPoint& aPoint, + nsIContent ** aNewContent, + PRInt32& aContentOffset, + PRInt32& aContentOffsetEnd); // Box layout methods NS_IMETHOD GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize); NS_IMETHOD GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 839f098d45c7..8c11b4d09670 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -760,6 +760,11 @@ public: PRInt32& aContentOffset, PRInt32& aContentOffsetEnd, PRBool& aBeginFrameContent) = 0; + // Helper for GetContentAndOffsetsFromPoint + NS_IMETHOD GetPositionHelper(const nsPoint& aPoint, + nsIContent ** aNewContent, + PRInt32& aContentOffset, + PRInt32& aContentOffsetEnd) = 0; /** * This structure holds information about a cursor. mContainer represents a diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index a902636085da..a42dddcef481 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -340,9 +340,6 @@ public: NS_IMETHOD GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset); NS_IMETHOD CommonPageMove(PRBool aForward, PRBool aExtend, nsIScrollableView *aScrollableView, nsIFrameSelection *aFrameSel); - NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection, - nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset); - NS_IMETHOD SetHint(HINT aHintRight); NS_IMETHOD GetHint(HINT *aHintRight); NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend); @@ -462,9 +459,6 @@ private: nsresult NotifySelectionListeners(SelectionType aType); // add parameters to say collapsed etc? - // utility method to lookup frame style - nsresult FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame); - nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES]; // Table selection support. @@ -2468,21 +2462,6 @@ nsSelection::HandleDrag(nsPresContext *aPresContext, nsIFrame *aFrame, nsPoint& AdjustForMaintainedSelection(newContent, startPos)) return NS_OK; - // do we have CSS that changes selection behaviour? - { - //add scope for nsCOMPtr - PRBool changeSelection; - nsCOMPtr selectContent; - PRInt32 newStart, newEnd; - if (NS_SUCCEEDED(AdjustOffsetsFromStyle(newFrame, &changeSelection, getter_AddRefs(selectContent), &newStart, &newEnd)) - && changeSelection) - { - newContent = selectContent; - startPos = newStart; - contentOffsetEnd = newEnd; - } - } - if (NS_SUCCEEDED(result)) { #ifdef VISUALSELECTION @@ -3056,27 +3035,6 @@ nsSelection::NotifySelectionListeners(SelectionType aType) return NS_ERROR_FAILURE; } -nsresult -nsSelection::FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame) -{ - nsIFrame* thisFrame = aFrame; - - while (thisFrame) - { - if (thisFrame->GetStyleUIReset()->mUserSelect == aSelectionStyle) - { - *foundFrame = thisFrame; - return NS_OK; - } - - thisFrame = thisFrame->GetParent(); - } - - *foundFrame = nsnull; - return NS_OK; -} - - // Start of Table Selection methods static PRBool IsCell(nsIContent *aContent) @@ -4093,59 +4051,6 @@ nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset) // End of Table Selection -NS_IMETHODIMP -nsSelection::AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection, - nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset) -{ - - *changeSelection = PR_FALSE; - *outContent = nsnull; - - nsresult rv; - nsIFrame* selectAllFrame; - rv = FrameOrParentHasSpecialSelectionStyle(aFrame, NS_STYLE_USER_SELECT_ALL, &selectAllFrame); - if (NS_FAILED(rv)) return rv; - - if (!selectAllFrame) - return NS_OK; - - nsIContent* selectAllContent = selectAllFrame->GetContent(); - if (selectAllContent) - { - nsCOMPtr parentContent = selectAllContent->GetParent(); - if (parentContent) - { - PRInt32 startOffset = parentContent->IndexOf(selectAllContent); - - if (startOffset < 0) - { - // hrmm, this is probably anonymous content. Let's go up another level - // do we need to do this if we get the right frameSelection to start with? - nsCOMPtr superParent = parentContent->GetParent(); - if (superParent) - { - PRInt32 superStartOffset = superParent->IndexOf(parentContent); - if (superStartOffset < 0) - return NS_ERROR_FAILURE; // give up - - parentContent = superParent; - startOffset = superStartOffset; - } - } - - NS_IF_ADDREF(*outContent = parentContent); - - *outStartOffset = startOffset; - *outEndOffset = startOffset + 1; - - *changeSelection = PR_TRUE; - } - } - - return NS_OK; -} - - NS_IMETHODIMP nsSelection::SetHint(HINT aHintRight) { diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index b2a9c862f61c..ea51f973e2f2 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -294,21 +294,12 @@ public: NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ; #endif - NS_IMETHOD GetPosition(nsPresContext* aPresContext, - const nsPoint& aPoint, + NS_IMETHOD GetPositionHelper(const nsPoint& aPoint, nsIContent ** aNewContent, PRInt32& aContentOffset, PRInt32& aContentOffsetEnd); - NS_IMETHOD GetContentAndOffsetsFromPoint(nsPresContext* aPresContext, - const nsPoint& aPoint, - nsIContent ** aNewContent, - PRInt32& aContentOffset, - PRInt32& aContentOffsetEnd, - PRBool& aBeginFrameContent); - - NS_IMETHOD GetPositionSlowly(nsPresContext* aPresContext, - nsIRenderingContext * aRendContext, + NS_IMETHOD GetPositionSlowly(nsIRenderingContext * aRendContext, const nsPoint& aPoint, nsIContent ** aNewContent, PRInt32& aOffset); @@ -322,10 +313,6 @@ public: NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos); NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval); - NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus); - NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const; virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end); @@ -3065,22 +3052,21 @@ nsTextFrame::PaintUnicodeText(nsPresContext* aPresContext, //measure Spaced Textvoid nsresult -nsTextFrame::GetPositionSlowly(nsPresContext* aPresContext, - nsIRenderingContext* aRendContext, +nsTextFrame::GetPositionSlowly(nsIRenderingContext* aRendContext, const nsPoint& aPoint, nsIContent** aNewContent, PRInt32& aOffset) { // pre-condition tests - NS_PRECONDITION(aPresContext && aRendContext && aNewContent, "null arg"); - if (!aPresContext || !aRendContext || !aNewContent) { + NS_PRECONDITION(aRendContext && aNewContent, "null arg"); + if (!aRendContext || !aNewContent) { return NS_ERROR_NULL_POINTER; } // initialize out param *aNewContent = nsnull; - nsTextStyle ts(aPresContext, *aRendContext, mStyleContext); + nsTextStyle ts(GetPresContext(), *aRendContext, mStyleContext); if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) { return NS_ERROR_INVALID_ARG; } @@ -3119,7 +3105,7 @@ nsTextFrame::GetPositionSlowly(nsPresContext* aPresContext, } // Transform text from content into renderable form - nsTextTransformer tx(aPresContext); + nsTextTransformer tx(GetPresContext()); PRInt32 textLength; PRIntn numJustifiableCharacter; @@ -3148,29 +3134,6 @@ nsTextFrame::GetPositionSlowly(nsPresContext* aPresContext, #endif // IBMBIDI ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter); - -//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... - PRInt32 prefInt = - nsContentUtils::GetIntPref("browser.drag_out_of_frame_style"); - - PRBool outofstylehandled = PR_FALSE; - - if (prefInt) - { - if (aPoint.y < 0)//above rectangle - { - aOffset = mContentOffset; - outofstylehandled = PR_TRUE; - } - else if (aPoint.y > mRect.height) - { - aOffset = mContentOffset + mContentLength; - outofstylehandled = PR_TRUE; - } - } - - if (!outofstylehandled) //then we drag to closest X point and dont worry about the 'Y' -//END STYLE RULE { //the following will first get the index into the PAINTBUFFER then the actual content nscoord adjustedX = PR_MAX(0,aPoint.x); @@ -4033,16 +3996,15 @@ nsTextFrame::PaintAsciiText(nsPresContext* aPresContext, // display of selection is based on the compressed text. //--------------------------------------------------------------------------- NS_IMETHODIMP -nsTextFrame::GetPosition(nsPresContext* aPresContext, - const nsPoint& aPoint, +nsTextFrame::GetPositionHelper(const nsPoint& aPoint, nsIContent ** aNewContent, PRInt32& aContentOffset, PRInt32& aContentOffsetEnd) { // pre-condition tests - NS_PRECONDITION(aPresContext && aNewContent, "null arg"); - if (!aPresContext || !aNewContent) { + NS_PRECONDITION(aNewContent, "null arg"); + if (!aNewContent) { return NS_ERROR_NULL_POINTER; } // initialize out param @@ -4052,14 +4014,14 @@ nsTextFrame::GetPosition(nsPresContext* aPresContext, if (mState & NS_FRAME_IS_DIRTY) return NS_ERROR_UNEXPECTED; - nsIPresShell *shell = aPresContext->GetPresShell(); + nsIPresShell *shell = GetPresContext()->GetPresShell(); if (shell) { nsCOMPtr rendContext; nsresult rv = shell->CreateRenderingContext(this, getter_AddRefs(rendContext)); if (NS_SUCCEEDED(rv)) { - nsTextStyle ts(aPresContext, *rendContext, mStyleContext); + nsTextStyle ts(GetPresContext(), *rendContext, mStyleContext); if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) { - nsresult result = GetPositionSlowly(aPresContext, rendContext, aPoint, aNewContent, + nsresult result = GetPositionSlowly(rendContext, aPoint, aNewContent, aContentOffset); aContentOffsetEnd = aContentOffset; return result; @@ -4077,39 +4039,17 @@ nsTextFrame::GetPosition(nsPresContext* aPresContext, SetFontFromStyle(rendContext, mStyleContext); // Get the renderable form of the text - nsTextTransformer tx(aPresContext); + nsTextTransformer tx(GetPresContext()); PRInt32 textLength; // no need to worry about justification, that's always on the slow path PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); -//IF STYLE SAYS TO SELECT TO END OF FRAME HERE... - PRInt32 prefInt = - nsContentUtils::GetIntPref("browser.drag_out_of_frame_style"); - PRBool outofstylehandled = PR_FALSE; - - if (prefInt) - { - if (aPoint.y < 0)//above rectangle - { - aContentOffset = mContentOffset; - aContentOffsetEnd = aContentOffset; - outofstylehandled = PR_TRUE; - } - else if (aPoint.y > mRect.height) - { - aContentOffset = mContentOffset + mContentLength; - aContentOffsetEnd = aContentOffset; - outofstylehandled = PR_TRUE; - } - } - if (textLength <= 0) { aContentOffset = mContentOffset; aContentOffsetEnd = aContentOffset; } - else if (!outofstylehandled) //then we need to track based on the X coord only + else { -//END STYLE IF PRInt32* ip = indexBuffer.mBuffer; PRInt32 indx; @@ -4196,42 +4136,6 @@ nsTextFrame::GetPosition(nsPresContext* aPresContext, return NS_OK; } -NS_IMETHODIMP -nsTextFrame::GetContentAndOffsetsFromPoint(nsPresContext* aPresContext, - const nsPoint& aPoint, - nsIContent ** aNewContent, - PRInt32& aContentOffset, - PRInt32& aContentOffsetEnd, - PRBool& aBeginFrameContent) -{ - if (!aNewContent) - return NS_ERROR_NULL_POINTER; - *aNewContent = nsnull;//initialize - aContentOffset = 0; - aContentOffsetEnd = 0; - aBeginFrameContent = 0; - - DEBUG_VERIFY_NOT_DIRTY(mState); - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; - - nsPoint newPoint; - newPoint.y = aPoint.y; - if (aPoint.x < 0) - newPoint.x = 0; - else - newPoint.x = aPoint.x; - nsresult rv = GetPosition(aPresContext, newPoint, aNewContent, aContentOffset, aContentOffsetEnd); - if (NS_FAILED(rv)) - return rv; - if (aContentOffset == mContentOffset) - aBeginFrameContent = PR_TRUE; - else - aBeginFrameContent = PR_FALSE; - return rv; -} - - // [HACK] Foward Declarations void ForceDrawFrame(nsFrame * aFrame); @@ -5120,38 +5024,6 @@ nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) return result; } -NS_IMETHODIMP -nsTextFrame::HandleMultiplePress(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { - return NS_OK; - } - - nsMouseEvent *me = (nsMouseEvent *)aEvent; - if (!me) return NS_OK; - - // Triple- and greater click counts are handled by nsFrame. - if (me->clickCount > 2) - return nsFrame::HandleMultiplePress(aPresContext, aEvent, aEventStatus); - - // Double-click: word selection, handled here: - PRInt32 startPos = 0; - PRInt32 contentOffsetEnd = 0; - nsCOMPtr newContent; - nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); - nsresult rv = GetPosition(aPresContext, pt, - getter_AddRefs(newContent), startPos, - contentOffsetEnd); - if (NS_FAILED(rv)) - return rv; - - return PeekBackwardAndForward(eSelectWord, eSelectWord, startPos, - aPresContext, PR_FALSE); -} - - NS_IMETHODIMP nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval) {