diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 4b7d2738f35..78c5bdaf55f 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -933,18 +933,6 @@ nsFrame::GetChildList(nsIAtom* aListName) const return nsFrameList::EmptyList(); } -static nsIFrame* -GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame) -{ - nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); - if (capturingContent) { - nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent); - return activeFrame ? activeFrame : aFrame; - } - - return aFrame; -} - PRInt16 nsFrame::DisplaySelection(nsPresContext* aPresContext, PRBool isOkToTurnOn) { @@ -2048,6 +2036,147 @@ nsFrame::IsSelectable(PRBool* aSelectable, PRUint8* aSelectStyle) const return NS_OK; } +nsFrameSelection* +nsFrame::GetFrameSelectionForSelectingByMouse() +{ + PRBool selectable; + PRUint8 selectStyle; + nsresult rv = IsSelectable(&selectable, &selectStyle); + NS_ENSURE_SUCCESS(rv, nsnull); + if (!selectable) { + return nsnull; + } + + // When implementing NS_STYLE_USER_SELECT_ELEMENT, + // NS_STYLE_USER_SELECT_ELEMENTS and NS_STYLE_USER_SELECT_TOGGLE, need to + // change this logic + PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT); + + // XXX This is screwy; it really should use the selection frame, not the + // event frame + const nsFrameSelection* frameselection = + (selectStyle == NS_STYLE_USER_SELECT_TEXT) ? GetConstFrameSelection() : + PresContext()->PresShell()->ConstFrameSelection(); + + return const_cast(frameselection); +} + +/** + * GetContentToCaptureForSelection() returns a content which should capture + * mouse events for aSelectionRoot. E.g., the result is + * if the aSelectionRoot is anonymous div element in the editor. + */ +static nsIContent* +GetContentToCaptureForSelection(nsIContent* aSelectionRoot) +{ + return aSelectionRoot->FindFirstNonNativeAnonymous(); +} + +/** + * GetSelectionRootContentForCapturingContent() returns a selection root + * content for the capturing content. E.g., the result is anonymous div + * element if aCapturingContent is a . + */ +static nsIContent* +GetSelectionRootContentForCapturingContent(nsIPresShell* aPresShell, + nsIContent* aCapturingContent) +{ + if (!aCapturingContent->HasIndependentSelection()) { + return aCapturingContent; + } + return aCapturingContent->GetSelectionRootContent(aPresShell); +} + +/** + * FindNearestScrollableFrameForSelection() returns the nearest ancestor + * scrollable frame when user is dragging on aFrame. + * + * @param aFrame A frame which the user is dragging on. + * @param aSelectionRoot When this is not NULL, the result is guaranteed that + * the result belongs to the same selection root. + * For example, when aFrame is in + * but user is selecting outside of the : + * * If aSelectionRoot is NULL, this returns the + * selection root frame of the . + * * Otherwise, e.g., aSelectionRoot is the root + * element of the document, the result is the + * nearest ancestor scrollable element of the + * element. + * + * @return The nearest ancestor scrollable frame for aFrame. + * If it was not found, returns NULL. + */ +static nsIScrollableFrame* +FindNearestScrollableFrameForSelection(nsIFrame* aFrame, + nsIContent* aSelectionRoot = nsnull) +{ +#ifdef DEBUG + nsFrameSelection* draggingFrameSelection = + nsFrameSelection::GetMouseDownFrameSelection(); + NS_ASSERTION(!draggingFrameSelection || + draggingFrameSelection == aFrame->GetConstFrameSelection(), + "aFrame must be in dragging nsFrameSelection"); +#endif + PRBool foundCapturingContent = PR_FALSE; + nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); + // If the specified selection root content is capturing content, + // that might be different from the computed selection root. Then, we should + // replace aSelectionRoot with its computed selection root. + if (aSelectionRoot && aSelectionRoot == capturingContent) { + nsIFrame* selectionRootFrame = aSelectionRoot->GetPrimaryFrame(); + NS_ENSURE_TRUE(selectionRootFrame, nsnull); + nsIPresShell* ps = selectionRootFrame->PresContext()->PresShell(); + aSelectionRoot = aSelectionRoot->GetSelectionRootContent(ps); + } + nsIScrollableFrame* lastScrollableFrame = nsnull; + for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { + do { + nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame); + if (!scrollableFrame || !scrollableFrame->GetScrolledFrame()) { + break; // non-scrollable frame. + } + + if (aSelectionRoot) { + // If aSelectionRoot isn't null, find a scrollable frame whose + // selection root is the same as aSelectionRoot. + nsIContent* content = frame->GetContent(); + if (!content) { + break; + } + nsIPresShell* ps = frame->PresContext()->PresShell(); + if (content->GetSelectionRootContent(ps) != aSelectionRoot) { + break; + } + } + + lastScrollableFrame = scrollableFrame; + + // If the scrollable frame has independent selection, we should return it + // even if it's not actually scrollable. + if (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) { + return scrollableFrame; + } + + nsRect range = scrollableFrame->GetScrollRange(); + if (range.width == 0 && range.height == 0) { + // The scrollable frame cannot scroll actually. We should look for + // another scrollable frame which can scrollable, however, if there is + // no such frame, we should return top most scrollable frame. + break; + } + + return scrollableFrame; + } while (0); + + if (frame->GetContent() == capturingContent) { + foundCapturingContent = PR_TRUE; + } else if (foundCapturingContent) { + break; // There is no scrollable frame in the capturing content + } + } + return lastScrollableFrame; +} + /** * Handles the Mouse Press Event for the frame */ @@ -2094,49 +2223,52 @@ nsFrame::HandlePress(nsPresContext* aPresContext, } } - // check whether style allows selection - // if not, don't tell selection the mouse event even occurred. - PRBool selectable; - PRUint8 selectStyle; - rv = IsSelectable(&selectable, &selectStyle); - if (NS_FAILED(rv)) return rv; - - // check for select: none - if (!selectable) - return NS_OK; - - // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and - // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic - PRBool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT); - - // If the mouse is dragged outside the nearest enclosing scrollable area - // while making a selection, the area will be scrolled. To do this, capture - // the mouse on the nearest scrollable frame. If there isn't a scrollable - // frame, or something else is already capturing the mouse, there's no - // reason to capture. - if (!nsIPresShell::GetCapturingContent()) { - nsIFrame* checkFrame = this; - nsIScrollableFrame *scrollFrame = nsnull; - while (checkFrame) { - scrollFrame = do_QueryFrame(checkFrame); - if (scrollFrame) { - nsIPresShell::SetCapturingContent(checkFrame->GetContent(), CAPTURE_IGNOREALLOWED); - break; - } - checkFrame = checkFrame->GetParent(); - } + nsFrameSelection* fs = GetFrameSelectionForSelectingByMouse(); + if (!fs) { + return NS_OK; // maybe, select: none } - // XXX This is screwy; it really should use the selection frame, not the - // event frame - const nsFrameSelection* frameselection = nsnull; - if (useFrameSelection) - frameselection = GetConstFrameSelection(); - else - frameselection = shell->ConstFrameSelection(); + // If the mouse is dragged outside the selection root's scrollable area + // while making a selection, the area will be scrolled. To do this, capture + // the mouse on the selection root frame. However, in table selection mode, + // a nearest scrollable frame should be captured the mouse because each + // scrollable frame except the nearest one doesn't need to scroll during + // selection. - if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) + // If something else is already capturing the mouse, the current selection + // root must be the capturing content. However, it might be outside of the + // this frame's selection root content. Then, we should do nothing. + nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); + PRBool captureMouse = !capturingContent; + NS_ASSERTION(mContent, "mContent must not be null"); + nsIContent* selectionRootOfThisFrame = + mContent->GetSelectionRootContent(shell); + NS_ASSERTION(selectionRootOfThisFrame, + "mContent must have a selection root content"); + if (capturingContent) { + nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame(); + NS_ENSURE_TRUE(capturingFrame, NS_OK); + nsIPresShell* capturedPresShell = + capturingFrame->PresContext()->PresShell(); + NS_ASSERTION(capturedPresShell, + "The captured content must have a presShell"); + nsIContent* selectionRootOfCapturedContent = + capturingContent->GetSelectionRootContent(capturedPresShell); + NS_ASSERTION(selectionRootOfCapturedContent, + "The captured content must have a selection root content"); + if (selectionRootOfThisFrame != selectionRootOfCapturedContent) { + return NS_OK; + } + } else { + nsIContent* contentToCaptureForSelection = + GetContentToCaptureForSelection(selectionRootOfThisFrame); + nsIPresShell::SetCapturingContent(contentToCaptureForSelection, + CAPTURE_IGNOREALLOWED); + } + + if (fs->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) { return NS_OK;//nothing to do we cannot affect selection from here + } nsMouseEvent *me = (nsMouseEvent *)aEvent; @@ -2148,13 +2280,12 @@ nsFrame::HandlePress(nsPresContext* aPresContext, PRBool control = me->isControl; #endif - nsRefPtr fc = const_cast(frameselection); if (me->clickCount >1 ) { // These methods aren't const but can't actually delete anything, // so no need for nsWeakFrame. - fc->SetMouseDownState(PR_TRUE); - fc->SetMouseDoubleDown(PR_TRUE); + fs->SetMouseDownState(PR_TRUE); + fs->SetMouseDoubleDown(PR_TRUE); return HandleMultiplePress(aPresContext, aEvent, aEventStatus, control); } @@ -2168,14 +2299,24 @@ nsFrame::HandlePress(nsPresContext* aPresContext, nsCOMPtrparentContent; PRInt32 contentOffset; PRInt32 target; - rv = GetDataForTableSelection(frameselection, shell, me, getter_AddRefs(parentContent), &contentOffset, &target); + rv = GetDataForTableSelection(fs, shell, me, getter_AddRefs(parentContent), + &contentOffset, &target); if (NS_SUCCEEDED(rv) && parentContent) { - fc->SetMouseDownState(PR_TRUE); - return fc->HandleTableSelection(parentContent, contentOffset, target, me); + // In table selection mode, a nearest scrollable frame should capture the + // mouse events. + if (captureMouse) { + nsIScrollableFrame* scrollableFrame = + FindNearestScrollableFrameForSelection(this); + nsIFrame* frame = do_QueryFrame(scrollableFrame); + nsIPresShell::SetCapturingContent(frame->GetContent(), + CAPTURE_IGNOREALLOWED); + } + fs->SetMouseDownState(PR_TRUE); + return fs->HandleTableSelection(parentContent, contentOffset, target, me); } - fc->SetDelayedCaretData(0); + fs->SetDelayedCaretData(0); // Check if any part of this frame is selected, and if the // user clicked inside the selected region. If so, we delay @@ -2188,8 +2329,8 @@ nsFrame::HandlePress(nsPresContext* aPresContext, if (isSelected) { PRBool inSelection = PR_FALSE; - details = frameselection->LookUpSelection(offsets.content, 0, - offsets.EndOffset(), PR_FALSE); + details = + fs->LookUpSelection(offsets.content, 0, offsets.EndOffset(), PR_FALSE); // // If there are any details, check to see if the user clicked @@ -2221,17 +2362,17 @@ nsFrame::HandlePress(nsPresContext* aPresContext, } if (inSelection) { - fc->SetMouseDownState(PR_FALSE); - fc->SetDelayedCaretData(me); + fs->SetMouseDownState(PR_FALSE); + fs->SetDelayedCaretData(me); return NS_OK; } } - fc->SetMouseDownState(PR_TRUE); + fs->SetMouseDownState(PR_TRUE); // Do not touch any nsFrame members after this point without adding // weakFrame checks. - rv = fc->HandleClick(offsets.content, offsets.StartOffset(), + rv = fs->HandleClick(offsets.content, offsets.StartOffset(), offsets.EndOffset(), me->isShift, control, offsets.associateWithNext); @@ -2239,7 +2380,7 @@ nsFrame::HandlePress(nsPresContext* aPresContext, return rv; if (offsets.offset != offsets.secondaryOffset) - fc->MaintainSelection(); + fs->MaintainSelection(); if (isEditor && !me->isShift && (offsets.EndOffset() - offsets.StartOffset()) == 1) @@ -2249,7 +2390,7 @@ nsFrame::HandlePress(nsPresContext* aPresContext, // -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? - fc->SetMouseDownState(PR_FALSE); + fs->SetMouseDownState(PR_FALSE); } return rv; @@ -2401,46 +2542,56 @@ nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, return frameSelection->MaintainSelection(aAmountBack); } -NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) +NS_IMETHODIMP +nsFrame::HandleDrag(nsPresContext* aPresContext, + nsGUIEvent* aEvent, + nsEventStatus* aEventStatus) { - PRBool selectable; - PRUint8 selectStyle; - IsSelectable(&selectable, &selectStyle); - // XXX Do we really need to exclude non-selectable content here? - // GetContentOffsetsFromPoint can handle it just fine, although some - // other stuff might not like it. - if (!selectable) - return NS_OK; - if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { - return NS_OK; + nsFrame* target; + nsRefPtr fs = + FindDraggingFrameSelection(aPresContext->PresShell(), &target); + if (!fs || !target || IsSelectionOff()) { + return NS_OK; // not selecting now } - nsIPresShell *presShell = aPresContext->PresShell(); - nsRefPtr frameselection = GetFrameSelection(); - PRBool mouseDown = frameselection->GetMouseDownState(); - if (!mouseDown) - return NS_OK; + // Stop auto scrolling, first. + fs->StopAutoScrollTimer(); - frameselection->StopAutoScrollTimer(); + return target->ExpandSelectionByMouseMove(fs, fs->GetShell(), + static_cast(aEvent), + aEventStatus); +} + +nsresult +nsFrame::ExpandSelectionByMouseMove(nsFrameSelection* aFrameSelection, + nsIPresShell* aPresShell, + nsMouseEvent* aEvent, + nsEventStatus* aEventStatus) +{ +#ifdef DEBUG + nsFrameSelection* draggingFrameSelection = + nsFrameSelection::GetMouseDownFrameSelection(); + NS_ASSERTION(draggingFrameSelection && + draggingFrameSelection == GetConstFrameSelection(), + "aFrameSelection must be handling current drag for selection"); +#endif // Check if we are dragging in a table cell nsCOMPtr parentContent; PRInt32 contentOffset; PRInt32 target; - nsMouseEvent *me = (nsMouseEvent *)aEvent; - nsresult result; - result = GetDataForTableSelection(frameselection, presShell, me, - getter_AddRefs(parentContent), - &contentOffset, &target); + nsresult rv = GetDataForTableSelection(aFrameSelection, aPresShell, + aEvent, getter_AddRefs(parentContent), + &contentOffset, &target); + PRBool handleTableSelection = NS_SUCCEEDED(rv) && parentContent; nsWeakFrame weakThis = this; - if (NS_SUCCEEDED(result) && parentContent) { - frameselection->HandleTableSelection(parentContent, contentOffset, target, me); + if (handleTableSelection) { + aFrameSelection->HandleTableSelection(parentContent, contentOffset, + target, aEvent); } else { nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); - frameselection->HandleDrag(this, pt); + aFrameSelection->HandleDrag(this, pt); } // The frameselection object notifies selection listeners synchronously above @@ -2449,175 +2600,312 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, return NS_OK; } - // get the nearest scrollframe - nsIFrame* checkFrame = this; - nsIScrollableFrame *scrollFrame = nsnull; - while (checkFrame) { - scrollFrame = do_QueryFrame(checkFrame); - if (scrollFrame) { - break; - } - checkFrame = checkFrame->GetParent(); + nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); + if (!capturingContent) { + return NS_OK; // The capture was canceled. } + nsIContent* selectionRoot = + GetSelectionRootContentForCapturingContent(aPresShell, capturingContent); - if (scrollFrame) { - nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame(); - if (capturingFrame) { - nsPoint pt = - nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, capturingFrame); - frameselection->StartAutoScrollTimer(capturingFrame, pt, 30); - } - } - - return NS_OK; -} - -/** - * This static method handles part of the nsFrame::HandleRelease in a way - * which doesn't rely on the nsFrame object to stay alive. - */ -static nsresult -HandleFrameSelection(nsFrameSelection* aFrameSelection, - nsIFrame::ContentOffsets& aOffsets, - PRBool aHandleTableSel, - PRInt32 aContentOffsetForTableSel, - PRInt32 aTargetForTableSel, - nsIContent* aParentContentForTableSel, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - if (!aFrameSelection) { + nsIScrollableFrame* scrollableFrame = + FindNearestScrollableFrameForSelection(this, selectionRoot); + // If a non-scrollable content captures by script, we may not be able to find + // any scrollable frame. + if (!scrollableFrame) { return NS_OK; } - nsresult rv = NS_OK; + const PRUint32 kAutoScrollTimerDelay = 30; - if (nsEventStatus_eConsumeNoDefault != *aEventStatus) { - if (!aHandleTableSel) { - nsMouseEvent *me = aFrameSelection->GetDelayedCaretData(); - if (!aOffsets.content || !me) { - return NS_ERROR_FAILURE; + if (!handleTableSelection) { + nsIScrollableFrame* selectionRootScrollableFrame = + FindNearestScrollableFrameForSelection(selectionRoot->GetPrimaryFrame()); + NS_ENSURE_TRUE(selectionRootScrollableFrame, NS_OK); + while (scrollableFrame) { + // We don't need to scroll the selection root frame when the mouse cursor + // is on its edge because selection root frame will be scrolled when the + // mouse cursor is outside of the frame. And user may want slower scroll + // than the "on edge" scroll speed. + if (selectionRootScrollableFrame == scrollableFrame) { + break; } - // We are doing this to simulate what we would have done on HandlePress. - // We didn't do it there to give the user an opportunity to drag - // the text, but since they didn't drag, we want to place the - // caret. - // However, we'll use the mouse position from the release, since: - // * it's easier - // * that's the normal click position to use (although really, in - // the normal case, small movements that don't count as a drag - // can do selection) - aFrameSelection->SetMouseDownState(PR_TRUE); + nsPoint scrollTo; + if (IsOnScrollableFrameEdge(scrollableFrame, aEvent, scrollTo)) { + aFrameSelection->StartAutoScrollTimer( + scrollableFrame->GetScrolledFrame(), scrollTo, kAutoScrollTimerDelay); + return NS_OK; + } - rv = aFrameSelection->HandleClick(aOffsets.content, - aOffsets.StartOffset(), - aOffsets.EndOffset(), - me->isShift, PR_FALSE, - aOffsets.associateWithNext); - if (NS_FAILED(rv)) { - return rv; - } - } else if (aParentContentForTableSel) { - aFrameSelection->SetMouseDownState(PR_FALSE); - rv = aFrameSelection->HandleTableSelection(aParentContentForTableSel, - aContentOffsetForTableSel, - aTargetForTableSel, - (nsMouseEvent *)aEvent); - if (NS_FAILED(rv)) { - return rv; - } + nsIFrame* frame = do_QueryFrame(scrollableFrame); + scrollableFrame = + FindNearestScrollableFrameForSelection(frame->GetParent(), + selectionRoot); } - aFrameSelection->SetDelayedCaretData(0); + scrollableFrame = selectionRootScrollableFrame; } - aFrameSelection->SetMouseDownState(PR_FALSE); - aFrameSelection->StopAutoScrollTimer(); + if (!scrollableFrame) { + return NS_OK; + } + + nsIFrame* scrolledFrame = scrollableFrame->GetScrolledFrame(); + NS_ASSERTION(scrolledFrame, + "The found scrollable frame doesn't have scrolled frame"); + nsPoint scrollTo = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrolledFrame); + aFrameSelection->StartAutoScrollTimer(scrolledFrame, scrollTo, + kAutoScrollTimerDelay); return NS_OK; } -NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) +nsFrame* +nsFrame::FindSelectableAncestor(nsIFrame* aFrame, + nsFrameSelection* aFrameSelection) { - nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this); + // If we're not selecting by mouse dragging, our ancestor must be selecting, + // so, we should handle it on the first selectable ancestor frame of the + // selecting document. + for (nsIFrame* frame = aFrame; frame; + frame = nsLayoutUtils::GetCrossDocParentFrame(frame)) { + PRBool selectable = PR_FALSE; + if (frame->GetConstFrameSelection() == aFrameSelection && + NS_SUCCEEDED(frame->IsSelectable(&selectable, nsnull)) && + selectable) { + nsFrame* result = do_QueryFrame(frame); + if (result) { + return result; + } + } + } + return nsnull; +} +nsFrameSelection* +nsFrame::FindDraggingFrameSelection(nsIPresShell* aPresShell, + nsFrame** aEventTarget) +{ + *aEventTarget = nsnull; + nsFrameSelection* fs = nsFrameSelection::GetMouseDownFrameSelection(); + if (!fs) { + return nsnull; // not dragging now + } + NS_ASSERTION(fs->GetMouseDownState(), + "Wrong nsFrameSelection was returned by GetMouseDownFrameSelection()"); + + nsIFrame* selectingFrame = this; + + // If this frame is for capturing content and it has independent selection, + // the actual selection root element might be its child or descendant which + // is a native anonymous element. + if (mContent == nsIPresShell::GetCapturingContent()) { + nsIContent* selectionRoot = + GetSelectionRootContentForCapturingContent(aPresShell, mContent); + if (selectionRoot) { + nsIFrame* frame = selectionRoot->GetPrimaryFrame(); + if (frame) { + selectingFrame = frame; + } + } + } + *aEventTarget = FindSelectableAncestor(selectingFrame, fs); + if (!*aEventTarget) { + *aEventTarget = this; + } + return fs; +} + +PRBool +nsFrame::IsOnScrollableFrameEdge(nsIScrollableFrame* aScrollableFrame, + nsGUIEvent* aEvent, + nsPoint &aScrollIntoView) +{ + nsIFrame* scrollableFrame = do_QueryFrame(aScrollableFrame); + nsPoint ptInScrollableFrame = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrollableFrame); + nsPoint scrollPosition = aScrollableFrame->GetScrollPosition(); + nsRect scrollRange = aScrollableFrame->GetScrollRange(); + nsRect scrollPort = aScrollableFrame->GetScrollPortRect(); + + nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame(); + NS_ENSURE_TRUE(scrolledFrame, PR_FALSE); + + aScrollIntoView = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, scrolledFrame); + + // The edge width (or height) is defined by pref, however, if the value + // is too thick for the frame, we should use 1/4 width (or height) of + // the frame. + static const char kPrefName_EdgeWidth[] = + "layout.selection.drag.autoscroll.inner_frame.edge_width"; + nsPresContext* pc = PresContext(); + PRInt32 edgePixel = Preferences::GetInt(kPrefName_EdgeWidth); + nscoord edgeApp = pc->DevPixelsToAppUnits(edgePixel); + nscoord onePixel = pc->DevPixelsToAppUnits(1); + + nscoord edgeH = NS_MAX(onePixel, NS_MIN(edgeApp, scrollPort.width / 4)); + nscoord edgeV = NS_MAX(onePixel, NS_MIN(edgeApp, scrollPort.height / 4)); + + // The scrolling mouse is defined by pref, however, if the amount is + // too big for the frame, we should use 1/2 width (or height) of the + // frame. + static const char kPrefName_ScrollAmount[] = + "layout.selection.drag.autoscroll.inner_frame.amount"; + PRInt32 scrollAmountPixel = + NS_MAX(Preferences::GetInt(kPrefName_ScrollAmount), 1); + nscoord scrollAmountApp = pc->DevPixelsToAppUnits(scrollAmountPixel); + + nscoord scrollAmountH = + NS_MAX(onePixel, NS_MIN(scrollAmountApp, scrollPort.width / 2)); + nscoord scrollAmountV = + NS_MAX(onePixel, NS_MIN(scrollAmountApp, scrollPort.height / 2)); + + PRBool isOnEdge = PR_FALSE; + if (ptInScrollableFrame.x < scrollPort.x + edgeH) { + if (scrollRange.x < scrollPosition.x) { + // Scroll to left. + aScrollIntoView.x = scrollPosition.x - scrollAmountH; + isOnEdge = PR_TRUE; + } + } else if (ptInScrollableFrame.x > scrollPort.x + scrollPort.width - edgeH) { + if (scrollRange.width > scrollPosition.x) { + // Scroll to right. + aScrollIntoView.x = scrollPosition.x + scrollPort.width + scrollAmountH; + isOnEdge = PR_TRUE; + } + } + + if (ptInScrollableFrame.y < scrollPort.y + edgeV) { + if (scrollRange.y < scrollPosition.y) { + // Scroll to top. + aScrollIntoView.y = scrollPosition.y - scrollAmountV; + isOnEdge = PR_TRUE; + } + } else if (ptInScrollableFrame.y > scrollPort.y + scrollPort.height - edgeV) { + if (scrollRange.height > scrollPosition.y) { + // Scroll to bottom. + aScrollIntoView.y = scrollPosition.y + scrollPort.height + scrollAmountV; + isOnEdge = PR_TRUE; + } + } + return isOnEdge; +} + +NS_IMETHODIMP +nsFrame::HandleRelease(nsPresContext* aPresContext, + nsGUIEvent* aEvent, + nsEventStatus* aEventStatus) +{ nsCOMPtr captureContent = nsIPresShell::GetCapturingContent(); // We can unconditionally stop capturing because // we should never be capturing when the mouse button is up nsIPresShell::SetCapturingContent(nsnull, 0); - PRBool selectionOff = - (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF); + nsFrame* targetFrame; + nsRefPtr fs = + FindDraggingFrameSelection(aPresContext->PresShell(), &targetFrame); + if (!fs) { + // If mouse button was pressed on selected text and released without + // mousemove event, there is no dragging frame selection. At that time, + // we need to clean up the pressed state with the frame selection for this + // frame. + fs = GetFrameSelectionForSelectingByMouse(); + if (!fs) { + return NS_OK; // maybe, select: none + } + targetFrame = FindSelectableAncestor(this, fs); + if (!targetFrame) { + // XXX At this time, can we just return? + targetFrame = this; + } + } + NS_ASSERTION(targetFrame, "targetFrame must be non-null"); + + nsMouseEvent* mouseEvent = static_cast(aEvent); + return targetFrame->EndSelectionChangeByMouse(fs, mouseEvent, aEventStatus); +} + +nsresult +nsFrame::EndSelectionChangeByMouse(nsFrameSelection* aFrameSelection, + nsMouseEvent* aMouseEvent, + nsEventStatus* aEventStatus) +{ + PRBool wasMouseDown = aFrameSelection->GetMouseDownState(); + + // First, stop expanding selection if necessary + aFrameSelection->SetMouseDownState(PR_FALSE); + aFrameSelection->StopAutoScrollTimer(); + + if (IsSelectionOff() || nsEventStatus_eConsumeNoDefault == *aEventStatus) { + return NS_OK; + } + + // Check if the frameselection recorded the mouse going down. + // If not, the user must have clicked in a part of the selection. + // Place the caret before continuing! + nsresult rv = NS_OK; + nsMouseEvent* delayedEvent = aFrameSelection->GetDelayedCaretData(); + if (!wasMouseDown && delayedEvent && delayedEvent->clickCount < 2) { + nsPoint pt = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aMouseEvent, this); + ContentOffsets offsets = GetContentOffsetsFromPoint(pt); + NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE); + + // We are doing this to simulate what we would have done on HandlePress. + // We didn't do it there to give the user an opportunity to drag + // the text, but since they didn't drag, we want to place the + // caret. + // However, we'll use the mouse position from the release, since: + // * it's easier + // * that's the normal click position to use (although really, in + // the normal case, small movements that don't count as a drag + // can do selection) + aFrameSelection->SetMouseDownState(PR_TRUE); + + // XXX Do not call any methods of the current object after this point!!! + // The object is perhaps dead! + rv = aFrameSelection->HandleClick(offsets.content, + offsets.StartOffset(), + offsets.EndOffset(), + delayedEvent->isShift, + PR_FALSE, + offsets.associateWithNext); + + aFrameSelection->SetMouseDownState(PR_FALSE); + aFrameSelection->SetDelayedCaretData(0); + + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; + } - nsRefPtr frameselection; - ContentOffsets offsets; nsCOMPtr parentContent; PRInt32 contentOffsetForTableSel = 0; PRInt32 targetForTableSel = 0; - PRBool handleTableSelection = PR_TRUE; - - if (!selectionOff) { - frameselection = GetFrameSelection(); - if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) { - // Check if the frameselection recorded the mouse going down. - // If not, the user must have clicked in a part of the selection. - // Place the caret before continuing! - - PRBool mouseDown = frameselection->GetMouseDownState(); - nsMouseEvent *me = frameselection->GetDelayedCaretData(); - - if (!mouseDown && me && me->clickCount < 2) { - nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); - offsets = GetContentOffsetsFromPoint(pt); - handleTableSelection = PR_FALSE; - } else { - GetDataForTableSelection(frameselection, PresContext()->PresShell(), - (nsMouseEvent *)aEvent, - getter_AddRefs(parentContent), - &contentOffsetForTableSel, - &targetForTableSel); - } - } + GetDataForTableSelection(aFrameSelection, PresContext()->PresShell(), + aMouseEvent, getter_AddRefs(parentContent), + &contentOffsetForTableSel, &targetForTableSel); + if (parentContent) { + // XXX Do not call any methods of the current object after this point!!! + // The object is perhaps dead! + rv = aFrameSelection->HandleTableSelection(parentContent, + contentOffsetForTableSel, + targetForTableSel, + aMouseEvent); } + aFrameSelection->SetDelayedCaretData(nsnull); - // We might be capturing in some other document and the event just happened to - // trickle down here. Make sure that document's frame selection is notified. - // Note, this may cause the current nsFrame object to be deleted, bug 336592. - nsRefPtr frameSelection; - if (activeFrame != this && - static_cast(activeFrame)->DisplaySelection(activeFrame->PresContext()) - != nsISelectionController::SELECTION_OFF) { - frameSelection = activeFrame->GetFrameSelection(); - } + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} - // Also check the selection of the capturing content which might be in a - // different document. - if (!frameSelection && captureContent) { - nsIDocument* doc = captureContent->GetCurrentDoc(); - if (doc) { - nsIPresShell* capturingShell = doc->GetShell(); - if (capturingShell && capturingShell != PresContext()->GetPresShell()) { - frameSelection = capturingShell->FrameSelection(); - } - } - } - - if (frameSelection) { - frameSelection->SetMouseDownState(PR_FALSE); - frameSelection->StopAutoScrollTimer(); - } - - // Do not call any methods of the current object after this point!!! - // The object is perhaps dead! - - return selectionOff - ? NS_OK - : HandleFrameSelection(frameselection, offsets, handleTableSelection, - contentOffsetForTableSel, targetForTableSel, - parentContent, aEvent, aEventStatus); +PRBool +nsFrame::IsSelectionOff() +{ + nsRefPtr fs = GetFrameSelection(); + NS_ENSURE_TRUE(fs, PR_TRUE); + return (fs->GetDisplaySelection() == nsISelectionController::SELECTION_OFF); } struct NS_STACK_CLASS FrameContentRange { diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 4064abf8227..b11573096eb 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -599,6 +599,53 @@ protected: nsIContent **aParentContent, PRInt32 *aContentOffset, PRInt32 *aTarget); + // Returns nsFrameSelection which is handling drag for selection. + // If it's not dragging for selection, this returns NULL. Otherwise, + // this returns the handling nsFrameSelection and a frame which is ancestor + // and should handle mouse events for selection. + nsFrameSelection* FindDraggingFrameSelection(nsIPresShell* aPresShell, + nsFrame** aEventTarget); + + // ExpandSelectionByMouseMove() will expand selection by aEvent. + // This should be called ONLY when the frame is mouse event target which is + // found by FindDraggingFrameSelection(). And aFrameSelection must be the + // drag handling nsFrameSelection. + nsresult ExpandSelectionByMouseMove(nsFrameSelection* aFrameSelection, + nsIPresShell* aPresShell, + nsMouseEvent* aEvent, + nsEventStatus* aEventStatus); + + // EndSelectionChangeByMouse() will stop the drag for selection if during + // that. And also cancel the selection if it's clicked on the selection + // range. + // This can be called on every frame. However, if an nsFrameSelection is + // handling drag for selection, this is called on the mouse event target + // frame which is computed by FindDraggingFrameSelection(). + // Otherwise, nearest ancestor selectable frame's should be called. + nsresult EndSelectionChangeByMouse(nsFrameSelection* aFrameSelection, + nsMouseEvent* aMouseEvent, + nsEventStatus* aEventStatus); + + // IsOnScrollableFrameEdge() checks whether the aEvent is fired on the + // edge of aScrollableFrame and it can be scrolled to the direction of the + // edge. If aEvent is fired on the edge and scrollable, this returns TRUE. + // Otherwise, FALSE. When this returns TRUE, this computes aScrollIntoView. + PRBool IsOnScrollableFrameEdge(nsIScrollableFrame* aScrollableFrame, + nsGUIEvent* aEvent, + nsPoint &aScrollIntoView); + + // FindSelectableAncestor() returns a frame which is the nearest selectable + // ancestor of aFrame. + static nsFrame* FindSelectableAncestor(nsIFrame* aFrame, + nsFrameSelection* aFrameSelection); + + // Returns nsFrameSelection for selecting by mouse in the frame. + nsFrameSelection* GetFrameSelectionForSelectingByMouse(); + + // If selection is off, returns TRUE. Otherwise, FALSE. + // This method always uses the frame's nsFrameSelection. + PRBool IsSelectionOff(); + // Fills aCursor with the appropriate information from ui static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui, nsIFrame::Cursor& aCursor); diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h index 5a9553a7f26..245e99a2617 100644 --- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -369,6 +369,15 @@ public: */ PRBool GetMouseDownState() const { return mMouseDownState; } + /** + * GetMouseDownedFrameSelection returns an instance which is handling + * mouse dragging. + */ + static nsFrameSelection* GetMouseDownFrameSelection() + { + return sDraggingFrameSelection; + } + /** if we are in table cell selection mode. aka ctrl click in table cell */ @@ -598,6 +607,7 @@ public: nsFrameSelection(); + virtual ~nsFrameSelection(); void StartBatchChanges(); void EndBatchChanges(); @@ -696,6 +706,8 @@ private: nsresult CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset); nsresult ClearNormalSelection(); + static nsFrameSelection* sDraggingFrameSelection; + nsCOMPtr mCellParent; //used to snap to table selection nsCOMPtr mStartSelectedCell; nsCOMPtr mEndSelectedCell; diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 8a9ab458b1f..ba07eca956b 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -140,7 +140,7 @@ static void printRange(nsIRange *aDomRange); #define DEBUG_OUT_RANGE(x) #endif //MOZ_DEBUG - +nsFrameSelection* nsFrameSelection::sDraggingFrameSelection = nsnull; //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend. //#define DEBUG_NAVIGATION @@ -425,7 +425,8 @@ public: } // aPoint is relative to aPresContext's root frame - nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint) + nsresult Start(nsPresContext *aPresContext, nsIContent *aContent, + nsPoint &aPoint) { mPoint = aPoint; @@ -433,7 +434,7 @@ public: // stopped by the selection if the prescontext is destroyed. mPresContext = aPresContext; - mContent = nsIPresShell::GetCapturingContent(); + mContent = aContent; if (!mTimer) { @@ -747,6 +748,13 @@ nsFrameSelection::nsFrameSelection() mSelectionChangeReason = nsISelectionListener::NO_REASON; } +nsFrameSelection::~nsFrameSelection() +{ + if (this == sDraggingFrameSelection) { + sDraggingFrameSelection = nsnull; + } +} + NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection) @@ -916,41 +924,10 @@ nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame, if (anchorRoot == contentRoot) { - // If the aFrame's content isn't the capturing content, it should be - // a descendant. At this time, we can return simply. - nsIContent* capturedContent = nsIPresShell::GetCapturingContent(); - if (capturedContent != content) - { - return NS_OK; - } - - // Find the frame under the mouse cursor with the root frame. - // At this time, don't use the anchor's frame because it may not have - // fixed positioned frames. - nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame(); - nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame); - nsIFrame* cursorFrame = - nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); - - // If the mouse cursor in on a frame which is descendant of same - // selection root, we can expand the selection to the frame. - if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell) - { - nsIContent* cursorContent = cursorFrame->GetContent(); - NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE); - nsIContent* cursorContentRoot = - cursorContent->GetSelectionRootContent(mShell); - NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED); - if (cursorContentRoot == anchorRoot) - { - *aRetFrame = cursorFrame; - aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame); - return NS_OK; - } - } - // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse - // cursor is out of the window), we should use the frame of the anchor - // root. + // The anchor and aFrame's root are the same. There + // is no need to constrain, simply return aFrame. + *aRetFrame = aFrame; + return NS_OK; } } @@ -1943,10 +1920,16 @@ nsFrameSelection::SetMouseDownState(PRBool aState) if (mMouseDownState == aState) return; + NS_ASSERTION((aState && !sDraggingFrameSelection) || + (!aState && sDraggingFrameSelection), + "Unexpected state happened"); + mMouseDownState = aState; - - if (!mMouseDownState) - { + + if (mMouseDownState) { + sDraggingFrameSelection = this; + } else { + sDraggingFrameSelection = nsnull; mDragSelectingCells = PR_FALSE; PostReason(nsISelectionListener::MOUSEUP_REASON); NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please. @@ -4716,7 +4699,8 @@ nsTypedSelection::DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint) { nsPoint presContextPoint = globalPoint - presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame); - mAutoScrollTimer->Start(presContext, presContextPoint); + mAutoScrollTimer->Start(presContext, aFrame->GetContent(), + presContextPoint); } return NS_OK; diff --git a/layout/generic/test/Makefile.in b/layout/generic/test/Makefile.in index d943e0b211f..18f8f89a991 100644 --- a/layout/generic/test/Makefile.in +++ b/layout/generic/test/Makefile.in @@ -135,6 +135,8 @@ _CHROME_FILES = \ test_backspace_delete.xul \ test_bug514732-2.xul \ file_bug514732_window.xul \ + test_selection_scrolling.html \ + window_selection_scrolling.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/layout/generic/test/test_selection_expanding.html b/layout/generic/test/test_selection_expanding.html index 832030235c0..4a0b6a8da05 100644 --- a/layout/generic/test/test_selection_expanding.html +++ b/layout/generic/test/test_selection_expanding.html @@ -184,28 +184,21 @@ function test() // selection starting at div1 synthesizeMouse(div1, 30, 5, { type: "mousedown" }); - // XXX if we move the mouse cursor to another document, the - // nsFrameSelection::HandleDrag method is called on the another document's. - // to iframe synthesizeMouse(iframe, 30, 5, { type: "mousemove" }); - check(kTrue | kToDo, kFalse, kFalse, + check(kTrue, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, "div1-iframe, all boxes are overflow: " + kOverflows[i] + ";"); - // XXX if the overflow is visible, synthesizeMouse with the input element - // or textarea element doesn't work fine. - var isVisibleTesting = kOverflows[i] == "visible"; - var todoFlag = isVisibleTesting ? kToDo : 0; // to input synthesizeMouse(input, 30, 5, { type: "mousemove" }); - check(kTrue | todoFlag, kTrue | todoFlag, kFalse, + check(kTrue, kTrue, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, "div1-input, all boxes are overflow: " + kOverflows[i] + ";"); // to textarea synthesizeMouse(textarea, 30, 5, { type: "mousemove" }); - check(kTrue | todoFlag, kTrue | todoFlag, kTrue | todoFlag, + check(kTrue, kTrue, kTrue, kFalse, kFalse, kFalse, kFalse, kFalse, kFalse, "div1-textarea, all boxes are overflow: " + kOverflows[i] + ";"); diff --git a/layout/generic/test/test_selection_scrolling.html b/layout/generic/test/test_selection_scrolling.html new file mode 100644 index 00000000000..70bd0374ec2 --- /dev/null +++ b/layout/generic/test/test_selection_scrolling.html @@ -0,0 +1,22 @@ + + + +selection scrolling test + + + + + + +
+
+
+ + diff --git a/layout/generic/test/window_selection_scrolling.html b/layout/generic/test/window_selection_scrolling.html new file mode 100644 index 00000000000..96c9c3450a1 --- /dev/null +++ b/layout/generic/test/window_selection_scrolling.html @@ -0,0 +1,1485 @@ + + + +selection scrolling test + + + + + + +
+
+
+
+ +
+
+
+
+
+ +
+
+ + +
+
+
+
+ + diff --git a/layout/xul/base/src/nsScrollBoxFrame.cpp b/layout/xul/base/src/nsScrollBoxFrame.cpp index 9a0114b8d43..10081e28439 100644 --- a/layout/xul/base/src/nsScrollBoxFrame.cpp +++ b/layout/xul/base/src/nsScrollBoxFrame.cpp @@ -169,7 +169,7 @@ nsAutoRepeatBoxFrame::HandleRelease(nsPresContext* aPresContext, if (!IsActivatedOnHover()) { StopRepeat(); } - return NS_OK; + return nsButtonBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus); } NS_IMETHODIMP diff --git a/layout/xul/base/src/nsScrollbarButtonFrame.cpp b/layout/xul/base/src/nsScrollbarButtonFrame.cpp index f25ca909d28..47da72bf226 100644 --- a/layout/xul/base/src/nsScrollbarButtonFrame.cpp +++ b/layout/xul/base/src/nsScrollbarButtonFrame.cpp @@ -82,11 +82,10 @@ nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext, return NS_OK; } - // XXX hack until handle release is actually called in nsframe. - if (aEvent->message == NS_MOUSE_EXIT_SYNTH || - aEvent->message == NS_MOUSE_BUTTON_UP) - HandleRelease(aPresContext, aEvent, aEventStatus); - + if (aEvent->message == NS_MOUSE_EXIT_SYNTH) { + Deactivate(); + } + // if we didn't handle the press ourselves, pass it on to the superclass if (!HandleButtonPress(aPresContext, aEvent, aEventStatus)) return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); @@ -191,10 +190,15 @@ nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus) { - // we're not active anymore + Deactivate(); + return nsButtonBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus); +} + +void +nsScrollbarButtonFrame::Deactivate() +{ mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, PR_TRUE); StopRepeat(); - return NS_OK; } void nsScrollbarButtonFrame::Notify() diff --git a/layout/xul/base/src/nsScrollbarButtonFrame.h b/layout/xul/base/src/nsScrollbarButtonFrame.h index 242d38a0c4e..1155355474e 100644 --- a/layout/xul/base/src/nsScrollbarButtonFrame.h +++ b/layout/xul/base/src/nsScrollbarButtonFrame.h @@ -81,10 +81,6 @@ public: nsEventStatus* aEventStatus, PRBool aControlHeld) { return NS_OK; } - NS_IMETHOD HandleDrag(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus) { return NS_OK; } - NS_IMETHOD HandleRelease(nsPresContext* aPresContext, nsGUIEvent * aEvent, nsEventStatus* aEventStatus); @@ -105,6 +101,8 @@ protected: } PRInt32 mIncrement; + + void Deactivate(); }; #endif diff --git a/layout/xul/base/src/nsScrollbarFrame.cpp b/layout/xul/base/src/nsScrollbarFrame.cpp index 66975ba67d8..3b11bf56ad3 100644 --- a/layout/xul/base/src/nsScrollbarFrame.cpp +++ b/layout/xul/base/src/nsScrollbarFrame.cpp @@ -158,22 +158,6 @@ nsScrollbarFrame::HandleMultiplePress(nsPresContext* aPresContext, return NS_OK; } -NS_IMETHODIMP -nsScrollbarFrame::HandleDrag(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsScrollbarFrame::HandleRelease(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - return NS_OK; -} - void nsScrollbarFrame::SetScrollbarMediatorContent(nsIContent* aMediator) { diff --git a/layout/xul/base/src/nsScrollbarFrame.h b/layout/xul/base/src/nsScrollbarFrame.h index e37c725fe79..925690b0532 100644 --- a/layout/xul/base/src/nsScrollbarFrame.h +++ b/layout/xul/base/src/nsScrollbarFrame.h @@ -78,14 +78,6 @@ public: nsEventStatus* aEventStatus, PRBool aControlHeld); - NS_IMETHOD HandleDrag(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus); - - NS_IMETHOD HandleRelease(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus); - NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow); diff --git a/layout/xul/base/src/nsSliderFrame.cpp b/layout/xul/base/src/nsSliderFrame.cpp index 4522d760ab7..d332a1ac1fb 100644 --- a/layout/xul/base/src/nsSliderFrame.cpp +++ b/layout/xul/base/src/nsSliderFrame.cpp @@ -1044,7 +1044,7 @@ nsSliderFrame::HandleRelease(nsPresContext* aPresContext, { StopRepeat(); - return NS_OK; + return nsBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus); } void diff --git a/layout/xul/base/src/nsSliderFrame.h b/layout/xul/base/src/nsSliderFrame.h index e3959c5da20..bde8adf6509 100644 --- a/layout/xul/base/src/nsSliderFrame.h +++ b/layout/xul/base/src/nsSliderFrame.h @@ -148,10 +148,6 @@ public: nsEventStatus* aEventStatus, PRBool aControlHeld) { return NS_OK; } - NS_IMETHOD HandleDrag(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus) { return NS_OK; } - NS_IMETHOD HandleRelease(nsPresContext* aPresContext, nsGUIEvent * aEvent, nsEventStatus* aEventStatus); diff --git a/layout/xul/base/src/nsSplitterFrame.cpp b/layout/xul/base/src/nsSplitterFrame.cpp index 9c4b45ca2aa..413d63641a1 100644 --- a/layout/xul/base/src/nsSplitterFrame.cpp +++ b/layout/xul/base/src/nsSplitterFrame.cpp @@ -379,22 +379,6 @@ nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext, return NS_OK; } -NS_IMETHODIMP -nsSplitterFrame::HandleDrag(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsSplitterFrame::HandleRelease(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus) -{ - return NS_OK; -} - NS_IMETHODIMP nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, diff --git a/layout/xul/base/src/nsSplitterFrame.h b/layout/xul/base/src/nsSplitterFrame.h index 0dd11494dc5..9ecadc3d4c6 100644 --- a/layout/xul/base/src/nsSplitterFrame.h +++ b/layout/xul/base/src/nsSplitterFrame.h @@ -86,14 +86,6 @@ public: nsEventStatus* aEventStatus, PRBool aControlHeld); - NS_IMETHOD HandleDrag(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus); - - NS_IMETHOD HandleRelease(nsPresContext* aPresContext, - nsGUIEvent * aEvent, - nsEventStatus* aEventStatus); - NS_IMETHOD HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 07babd44288..f818e0fba8d 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1293,6 +1293,17 @@ pref("layout.word_select.stop_at_punctuation", true); // deletes the selection (Unix default) pref("layout.selection.caret_style", 0); +// Prefs for auto scrolling by mouse drag. When the mouse cursor is on edge of +// inner scrollable frame than the selection root, the frame will be scrolled. +// |.edge_width| defines the edge width by CSS pixels. +// |.amout| defines the scrolling speed by CSS pixels. The auto scroll method +// uses scroll to a point function. When the mouse cursor is on the edge, it +// tries to scroll the frame to the point which is away from the edge. The +// value means how far the point is from edge in CSS pixels. +// I.e., larger value makes faster scroll. +pref("layout.selection.drag.autoscroll.inner_frame.edge_width", 32); +pref("layout.selection.drag.autoscroll.inner_frame.amount", 8); + // pref to control whether or not to replace backslashes with Yen signs // in documents encoded in one of Japanese legacy encodings (EUC-JP, // Shift_JIS, ISO-2022-JP) diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index 49919a93908..d7d564db12d 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -220,6 +220,13 @@ function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) var left = rect.left + aOffsetX; var top = rect.top + aOffsetY; + // body's bounding client rect depends its scroll position. + var body = aTarget.ownerDocument.body; + if (body == aTarget) { + left += aTarget.scrollLeft; + top += aTarget.scrollTop; + } + if (aEvent.type) { utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers); } @@ -282,8 +289,14 @@ function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) var rect = aTarget.getBoundingClientRect(); - var left = rect.left; - var top = rect.top; + var left = rect.left + aOffsetX; + var top = rect.top + aOffsetY; + + // body's bounding client rect depends its scroll position. + if (aTarget.ownerDocument.body == aTarget) { + left += aTarget.scrollLeft; + top += aTarget.scrollTop; + } var type = aEvent.type || "DOMMouseScroll"; var axis = aEvent.axis || "vertical"; @@ -294,7 +307,7 @@ function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) if (aEvent.isMomentum) { scrollFlags |= kIsMomentum; } - utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button, + utils.sendMouseScrollEvent(type, left, top, button, scrollFlags, aEvent.delta, modifiers); } }