зеркало из https://github.com/mozilla/pjs.git
Bug 552707 While we're expanding selection by dragging, the selection root element should capture mouse events and all scrollable elements should be scrollable r=roc+smaug, ui=faaborg
This commit is contained in:
Родитель
4686066b93
Коммит
02f36526f8
|
@ -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<nsFrameSelection*>(frameselection);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetContentToCaptureForSelection() returns a content which should capture
|
||||
* mouse events for aSelectionRoot. E.g., the result is <input type="text">
|
||||
* 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 <input type="text">.
|
||||
*/
|
||||
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 <input type="text">
|
||||
* but user is selecting outside of the <input>:
|
||||
* * If aSelectionRoot is NULL, this returns the
|
||||
* selection root frame of the <input>.
|
||||
* * Otherwise, e.g., aSelectionRoot is the root
|
||||
* element of the document, the result is the
|
||||
* nearest ancestor scrollable element of the
|
||||
* <input> 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<nsFrameSelection> fc = const_cast<nsFrameSelection*>(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,
|
|||
nsCOMPtr<nsIContent>parentContent;
|
||||
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<nsFrameSelection> fs =
|
||||
FindDraggingFrameSelection(aPresContext->PresShell(), &target);
|
||||
if (!fs || !target || IsSelectionOff()) {
|
||||
return NS_OK; // not selecting now
|
||||
}
|
||||
nsIPresShell *presShell = aPresContext->PresShell();
|
||||
|
||||
nsRefPtr<nsFrameSelection> 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<nsMouseEvent*>(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<nsIContent> 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<nsIContent> 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<nsFrameSelection> 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<nsMouseEvent*>(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<nsFrameSelection> frameselection;
|
||||
ContentOffsets offsets;
|
||||
nsCOMPtr<nsIContent> 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<nsFrameSelection> frameSelection;
|
||||
if (activeFrame != this &&
|
||||
static_cast<nsFrame*>(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<nsFrameSelection> fs = GetFrameSelection();
|
||||
NS_ENSURE_TRUE(fs, PR_TRUE);
|
||||
return (fs->GetDisplaySelection() == nsISelectionController::SELECTION_OFF);
|
||||
}
|
||||
|
||||
struct NS_STACK_CLASS FrameContentRange {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<nsINode> mCellParent; //used to snap to table selection
|
||||
nsCOMPtr<nsIContent> mStartSelectedCell;
|
||||
nsCOMPtr<nsIContent> mEndSelectedCell;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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] + ";");
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<title>selection scrolling test</title>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.open("window_selection_scrolling.html", "_blank",
|
||||
"top=200,left=200,width=300,height=300,scrollbars=yes");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -169,7 +169,7 @@ nsAutoRepeatBoxFrame::HandleRelease(nsPresContext* aPresContext,
|
|||
if (!IsActivatedOnHover()) {
|
||||
StopRepeat();
|
||||
}
|
||||
return NS_OK;
|
||||
return nsButtonBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1044,7 +1044,7 @@ nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
|
|||
{
|
||||
StopRepeat();
|
||||
|
||||
return NS_OK;
|
||||
return nsBoxFrame::HandleRelease(aPresContext, aEvent, aEventStatus);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче