Bug 526394. Part 26: Rework nsSelection to use frames only. r=mats
This commit is contained in:
@@ -901,30 +901,6 @@ nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame,
|
||||
return MatrixTransformPoint(aPoint, ctm.Invert(), aFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
}
|
||||
|
||||
nsPoint
|
||||
nsLayoutUtils::GetEventCoordinatesForNearestView(nsEvent* aEvent,
|
||||
nsIFrame* aFrame,
|
||||
nsIView** aView)
|
||||
{
|
||||
if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
|
||||
aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
|
||||
aEvent->eventStructType != NS_DRAG_EVENT))
|
||||
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
||||
|
||||
nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
|
||||
if (!GUIEvent->widget)
|
||||
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
||||
|
||||
nsPoint viewToFrame;
|
||||
nsIView* frameView;
|
||||
aFrame->GetOffsetFromView(viewToFrame, &frameView);
|
||||
if (aView)
|
||||
*aView = frameView;
|
||||
|
||||
return TranslateWidgetToView(aFrame->PresContext(), GUIEvent->widget,
|
||||
GUIEvent->refPoint, frameView);
|
||||
}
|
||||
|
||||
static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
|
||||
nsIntPoint offset(0, 0);
|
||||
nsIWidget* parent = aWidget->GetParent();
|
||||
|
||||
@@ -396,24 +396,7 @@ public:
|
||||
static nsPoint GetEventCoordinatesRelativeTo(const nsEvent* aEvent,
|
||||
nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Get the coordinates of a given native mouse event, relative to the nearest
|
||||
* view for a given frame.
|
||||
* The "nearest view" is the view returned by nsFrame::GetOffsetFromView.
|
||||
* XXX this is extremely BOGUS because "nearest view" is a mess; every
|
||||
* use of this method is really a bug!
|
||||
* @param aEvent the event
|
||||
* @param aFrame the frame to make coordinates relative to
|
||||
* @param aView view to which returned coordinates are relative
|
||||
* @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
|
||||
* for some reason the coordinates for the mouse are not known (e.g.,
|
||||
* the event is not a GUI event).
|
||||
*/
|
||||
static nsPoint GetEventCoordinatesForNearestView(nsEvent* aEvent,
|
||||
nsIFrame* aFrame,
|
||||
nsIView** aView = nsnull);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Translate from widget coordinates to the view's coordinates
|
||||
* @param aPresContext the PresContext for the view
|
||||
* @param aWidget the widget
|
||||
|
||||
@@ -94,6 +94,7 @@ _TEST_FILES = \
|
||||
test_bug514127.html \
|
||||
test_bug518777.html \
|
||||
test_flush_on_paint.html \
|
||||
test_scroll_selection_into_view.html \
|
||||
test_scrolling.html \
|
||||
$(NULL)
|
||||
# test_bug396024.html is currently disabled because it interacts badly with
|
||||
|
||||
90
layout/base/tests/test_scroll_selection_into_view.html
Normal file
90
layout/base/tests/test_scroll_selection_into_view.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for scrolling selection into view</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="c1" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:200px; left:0;">
|
||||
<div style="height:400px;"></div>
|
||||
<div><span id="target1"
|
||||
style="display:inline-block; vertical-align:top; height:20px;">target</span>
|
||||
</div>
|
||||
<div style="height:400px;"></div>
|
||||
</div>
|
||||
<div id="c2" style="overflow:hidden; width:200px; height:200px; position:absolute; top:200px; left:200px;">
|
||||
<div style="height:400px;"></div>
|
||||
<div><span id="target2"
|
||||
style="display:inline-block; vertical-align:top; height:20px;">target2</span>
|
||||
</div>
|
||||
<div style="height:400px;"></div>
|
||||
</div>
|
||||
<div id="c3" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:200px; left:400px;">
|
||||
<div style="height:400px;"></div>
|
||||
<div><span id="target3"
|
||||
style="display:inline-block; vertical-align:top; height:300px;">target3</span>
|
||||
</div>
|
||||
<div style="height:400px;"></div>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var ANCHOR = 0;
|
||||
var FOCUS = 1;
|
||||
|
||||
function testCollapsed(id, vPercent, startAt, expected) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var selection = window.getSelection().QueryInterface(Components.interfaces.nsISelection2);
|
||||
|
||||
var c = document.getElementById("c" + id);
|
||||
var target = document.getElementById("target" + id);
|
||||
selection.collapse(target.parentNode, 0);
|
||||
c.scrollTop = startAt;
|
||||
selection.scrollIntoView(FOCUS, true, vPercent, 0);
|
||||
is(c.scrollTop, expected, "Scrolling " + target.id +
|
||||
" into view with vPercent " + vPercent + ", starting at " + startAt);
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
// Test scrolling an element smaller than the scrollport
|
||||
testCollapsed("1", 0, 0, 400);
|
||||
testCollapsed("1", 100, 0, 220);
|
||||
testCollapsed("1", -1, 0, 220);
|
||||
testCollapsed("1", 0, 500, 400);
|
||||
testCollapsed("1", 100, 500, 220);
|
||||
testCollapsed("1", -1, 500, 400);
|
||||
|
||||
// overflow:hidden elements should not be scrolled by selection
|
||||
// scrolling-into-view
|
||||
testCollapsed("2", 0, 0, 0);
|
||||
testCollapsed("2", 100, 0, 0);
|
||||
testCollapsed("2", -1, 0, 0);
|
||||
testCollapsed("2", 0, 500, 500);
|
||||
testCollapsed("2", 100, 500, 500);
|
||||
testCollapsed("2", -1, 500, 500);
|
||||
|
||||
// Test scrolling an element larger than the scrollport
|
||||
testCollapsed("3", 0, 0, 400);
|
||||
testCollapsed("3", 100, 0, 500);
|
||||
testCollapsed("3", -1, 0, 400);
|
||||
testCollapsed("3", 0, 1000, 400);
|
||||
testCollapsed("3", 100, 1000, 500);
|
||||
// If the element can't be completely visible, we make the top edge
|
||||
// visible.
|
||||
testCollapsed("3", -1, 1000, 400);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -2253,14 +2253,11 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
if (scrollFrame) {
|
||||
nsIView* capturingView = scrollFrame->GetScrollableView()->View();
|
||||
if (capturingView) {
|
||||
// Get the view that aEvent->point is relative to. This is disgusting.
|
||||
nsIView* eventView = nsnull;
|
||||
nsPoint pt = nsLayoutUtils::GetEventCoordinatesForNearestView(aEvent, this,
|
||||
&eventView);
|
||||
nsPoint capturePt = pt + eventView->GetOffsetTo(capturingView);
|
||||
frameselection->StartAutoScrollTimer(capturingView, capturePt, 30);
|
||||
nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
|
||||
if (capturingFrame) {
|
||||
nsPoint pt =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, capturingFrame);
|
||||
frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -325,16 +325,18 @@ public:
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex);
|
||||
|
||||
/** StartAutoScrollTimer is responsible for scrolling views so that aPoint is always
|
||||
* visible, and for selecting any frame that contains aPoint. The timer will also reset
|
||||
* itself to fire again if we have not scrolled to the end of the document.
|
||||
* @param aView is view to use when searching for the closest frame to the point,
|
||||
* which is the view that is capturing the mouse
|
||||
* @param aPoint is relative to the view.
|
||||
/** StartAutoScrollTimer is responsible for scrolling frames so that
|
||||
* aPoint is always visible, and for selecting any frame that contains
|
||||
* aPoint. The timer will also reset itself to fire again if we have
|
||||
* not scrolled to the end of the document.
|
||||
* @param aFrame is the outermost frame to use when searching for
|
||||
* the closest frame for the point, i.e. the frame that is capturing
|
||||
* the mouse
|
||||
* @param aPoint is relative to aFrame.
|
||||
* @param aDelay is the timer's interval.
|
||||
*/
|
||||
/*unsafe*/
|
||||
nsresult StartAutoScrollTimer(nsIView *aView,
|
||||
nsresult StartAutoScrollTimer(nsIFrame *aFrame,
|
||||
nsPoint aPoint,
|
||||
PRUint32 aDelay);
|
||||
|
||||
|
||||
@@ -93,9 +93,6 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
|
||||
#include "nsCaret.h"
|
||||
|
||||
|
||||
// included for view scrolling
|
||||
#include "nsIViewManager.h"
|
||||
#include "nsIScrollableView.h"
|
||||
#include "nsIDeviceContext.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIServiceManager.h"
|
||||
@@ -196,14 +193,9 @@ public:
|
||||
// utility methods for scrolling the selection into view
|
||||
nsresult GetPresContext(nsPresContext **aPresContext);
|
||||
nsresult GetPresShell(nsIPresShell **aPresShell);
|
||||
nsresult GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aXOffset, nscoord *aYOffset);
|
||||
nsresult GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint);
|
||||
nsresult GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView);
|
||||
nsresult ScrollRectIntoView(nsIScrollableView *aScrollableView,
|
||||
nsRect& aRect,
|
||||
PRIntn aVPercent, PRIntn aHPercent,
|
||||
PRBool aScrollParentViews,
|
||||
PRBool* aDidScroll);
|
||||
// Returns the position of the region, and frame that that position is relative
|
||||
// to. The 'position' is a zero-width rectangle.
|
||||
nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
|
||||
|
||||
nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
|
||||
// aDoFlush only matters if aIsSynchronous is true. If not, we'll just flush
|
||||
@@ -253,9 +245,8 @@ public:
|
||||
SelectionDetails **aReturnDetails, SelectionType aType, PRBool aSlowCheck);
|
||||
NS_IMETHOD Repaint(nsPresContext* aPresContext);
|
||||
|
||||
// Note: StartAutoScrollTimer might destroy arbitrary frames, views etc.
|
||||
nsresult StartAutoScrollTimer(nsPresContext *aPresContext,
|
||||
nsIView *aView,
|
||||
// Note: StartAutoScrollTimer might destroy arbitrary frames etc.
|
||||
nsresult StartAutoScrollTimer(nsIFrame *aFrame,
|
||||
nsPoint& aPoint,
|
||||
PRUint32 aDelay);
|
||||
|
||||
@@ -265,13 +256,8 @@ public:
|
||||
private:
|
||||
friend class nsAutoScrollTimer;
|
||||
|
||||
// Note: DoAutoScrollView might destroy arbitrary frames, views etc.
|
||||
nsresult DoAutoScrollView(nsPresContext *aPresContext,
|
||||
nsIView *aView,
|
||||
nsPoint& aPoint,
|
||||
PRBool aScrollParentViews);
|
||||
|
||||
nsresult GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset);
|
||||
// Note: DoAutoScroll might destroy arbitrary frames etc.
|
||||
nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
|
||||
|
||||
public:
|
||||
SelectionType GetType(){return mType;}
|
||||
@@ -416,6 +402,7 @@ public:
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
// aPoint is relative to aPresContext's root frame
|
||||
nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
|
||||
{
|
||||
mPoint = aPoint;
|
||||
@@ -472,24 +459,12 @@ public:
|
||||
mContent = nsnull;
|
||||
|
||||
mFrameSelection->HandleDrag(frame, mPoint);
|
||||
|
||||
if (!frame.IsAlive())
|
||||
return NS_OK;
|
||||
|
||||
nsIFrame* checkFrame = frame;
|
||||
nsIScrollableFrame *scrollFrame = nsnull;
|
||||
while (checkFrame) {
|
||||
scrollFrame = do_QueryFrame(checkFrame);
|
||||
if (scrollFrame) {
|
||||
break;
|
||||
}
|
||||
checkFrame = checkFrame->GetParent();
|
||||
}
|
||||
nsIView* capturingView = scrollFrame ? scrollFrame->GetScrollableView()->View() : nsnull;
|
||||
|
||||
nsPoint pnt;
|
||||
mSelection->DoAutoScrollView(mPresContext, capturingView,
|
||||
mPoint, PR_TRUE);
|
||||
nsPoint pt = mPoint -
|
||||
frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
|
||||
mSelection->DoAutoScroll(frame, pt);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -497,6 +472,7 @@ private:
|
||||
nsFrameSelection *mFrameSelection;
|
||||
nsTypedSelection *mSelection;
|
||||
nsPresContext *mPresContext;
|
||||
// relative to mPresContext's root frame
|
||||
nsPoint mPoint;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
@@ -1748,17 +1724,15 @@ nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::StartAutoScrollTimer(nsIView *aView,
|
||||
nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
|
||||
nsPoint aPoint,
|
||||
PRUint32 aDelay)
|
||||
{
|
||||
NS_ENSURE_STATE(mShell);
|
||||
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
|
||||
if (!mDomSelections[index])
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
return mDomSelections[index]->StartAutoScrollTimer(mShell->GetPresContext(),
|
||||
aView, aPoint, aDelay);
|
||||
return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2111,40 +2085,24 @@ nsFrameSelection::CommonPageMove(PRBool aForward,
|
||||
nsresult result;
|
||||
//get the frame from the scrollable view
|
||||
|
||||
nsIFrame* mainframe = nsnull;
|
||||
|
||||
// The view's client data points back to its frame
|
||||
nsIView *scrolledView;
|
||||
result = aScrollableFrame->GetScrollableView()->GetScrolledView(scrolledView);
|
||||
|
||||
if (NS_FAILED(result))
|
||||
return;
|
||||
|
||||
if (scrolledView)
|
||||
mainframe = static_cast<nsIFrame*>(scrolledView->GetClientData());
|
||||
|
||||
if (!mainframe)
|
||||
nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
|
||||
if (!scrolledFrame)
|
||||
return;
|
||||
|
||||
// find out where the caret is.
|
||||
// we should know mDesiredX value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
|
||||
nsISelection* domSel = GetSelection(nsISelectionController::SELECTION_NORMAL);
|
||||
|
||||
if (!domSel)
|
||||
return;
|
||||
|
||||
nsRefPtr<nsCaret> caret;
|
||||
nsRect caretPos;
|
||||
PRBool isCollapsed;
|
||||
result = mShell->GetCaret(getter_AddRefs(caret));
|
||||
|
||||
if (NS_FAILED(result))
|
||||
return;
|
||||
|
||||
nsIView *caretView;
|
||||
result = caret->GetCaretCoordinates(nsCaret::eClosestViewCoordinates, domSel, &caretPos, &isCollapsed, &caretView);
|
||||
|
||||
if (NS_FAILED(result))
|
||||
nsRect caretPos;
|
||||
nsIFrame* caretFrame = caret->GetGeometry(domSel, &caretPos);
|
||||
if (!caretFrame)
|
||||
return;
|
||||
|
||||
//need to adjust caret jump by percentage scroll
|
||||
@@ -2155,18 +2113,14 @@ nsFrameSelection::CommonPageMove(PRBool aForward,
|
||||
else
|
||||
caretPos.y -= scrollDelta.height;
|
||||
|
||||
|
||||
if (caretView)
|
||||
{
|
||||
caretPos += caretView->GetOffsetTo(scrolledView);
|
||||
}
|
||||
caretPos += caretFrame->GetOffsetTo(scrolledFrame);
|
||||
|
||||
// get a content at desired location
|
||||
nsPoint desiredPoint;
|
||||
desiredPoint.x = caretPos.x;
|
||||
desiredPoint.y = caretPos.y + caretPos.height/2;
|
||||
nsIFrame::ContentOffsets offsets =
|
||||
mainframe->GetContentOffsetsFromPoint(desiredPoint);
|
||||
scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
|
||||
|
||||
if (!offsets.content)
|
||||
return;
|
||||
@@ -4586,7 +4540,7 @@ nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoi
|
||||
{
|
||||
// Recalculate frame offset and cache it. Don't cache a frame offset if
|
||||
// GetPointFromOffset fails, though.
|
||||
rv = GetPointFromOffset(aFrame, inOffset, &aPoint);
|
||||
rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
|
||||
if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
|
||||
mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
|
||||
mCachedOffsetForFrame->mLastCaretFrame = aFrame;
|
||||
@@ -4637,12 +4591,11 @@ nsTypedSelection::SetTextRangeStyle(nsIDOMRange *aRange,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
|
||||
nsIView *aView,
|
||||
nsTypedSelection::StartAutoScrollTimer(nsIFrame *aFrame,
|
||||
nsPoint& aPoint,
|
||||
PRUint32 aDelay)
|
||||
{
|
||||
NS_PRECONDITION(aView, "Need a view");
|
||||
NS_PRECONDITION(aFrame, "Need a frame");
|
||||
|
||||
nsresult result;
|
||||
if (!mFrameSelection)
|
||||
@@ -4666,7 +4619,7 @@ nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
return DoAutoScrollView(aPresContext, aView, aPoint, PR_TRUE);
|
||||
return DoAutoScroll(aFrame, aPoint);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -4679,67 +4632,24 @@ nsTypedSelection::StopAutoScrollTimer()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset)
|
||||
nsTypedSelection::DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint)
|
||||
{
|
||||
// Note: A NULL aAncestorView pointer means that the caller wants
|
||||
// the view's global offset.
|
||||
NS_PRECONDITION(aFrame, "Need a frame");
|
||||
|
||||
if (!aView || !aXOffset || !aYOffset)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsPoint offset = aView->GetOffsetTo(aAncestorView);
|
||||
|
||||
*aXOffset = offset.x;
|
||||
*aYOffset = offset.y;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::DoAutoScrollView(nsPresContext *aPresContext,
|
||||
nsIView *aView,
|
||||
nsPoint& aPoint,
|
||||
PRBool aScrollParentViews)
|
||||
{
|
||||
if (!aPresContext || !aView)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsresult result;
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mAutoScrollTimer)
|
||||
result = mAutoScrollTimer->Stop();
|
||||
|
||||
//
|
||||
// Calculate the global offset of the view.
|
||||
//
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
nsIFrame* rootmostFrame =
|
||||
presContext->RootPresContext()->PresShell()->FrameManager()->GetRootFrame();
|
||||
nsPoint globalPoint = aPoint + aFrame->GetOffsetTo(rootmostFrame);
|
||||
|
||||
nsPoint globalOffset;
|
||||
result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
//
|
||||
// Convert aPoint into global coordinates so we can get back
|
||||
// to the same point after all the parent views have scrolled.
|
||||
//
|
||||
nsPoint globalPoint = aPoint + globalOffset;
|
||||
//
|
||||
// Now scroll aPoint into view.
|
||||
//
|
||||
|
||||
PRBool didScroll = PR_FALSE;
|
||||
|
||||
nsIView* scrollableView = aView;
|
||||
while (scrollableView && !scrollableView->ToScrollableView()) {
|
||||
scrollableView = scrollableView->GetParent();
|
||||
}
|
||||
if (!scrollableView)
|
||||
return NS_OK;
|
||||
nsRect r(aPoint, nsSize(1,1));
|
||||
result = ScrollRectIntoView(scrollableView->ToScrollableView(), r,
|
||||
NS_PRESSHELL_SCROLL_ANYWHERE,
|
||||
NS_PRESSHELL_SCROLL_ANYWHERE,
|
||||
aScrollParentViews, &didScroll);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
PRBool didScroll = presContext->PresShell()->
|
||||
ScrollFrameRectIntoView(aFrame, nsRect(aPoint, nsSize(1,1)),
|
||||
NS_PRESSHELL_SCROLL_ANYWHERE,
|
||||
NS_PRESSHELL_SCROLL_ANYWHERE, 0);
|
||||
|
||||
//
|
||||
// Start the AutoScroll timer if necessary.
|
||||
@@ -4747,16 +4657,9 @@ nsTypedSelection::DoAutoScrollView(nsPresContext *aPresContext,
|
||||
|
||||
if (didScroll && mAutoScrollTimer)
|
||||
{
|
||||
//
|
||||
// Map the globalPoint back into aView's coordinate system. We
|
||||
// have to get the globalOffsets again because aView's
|
||||
// window and its parents may have changed their offsets.
|
||||
//
|
||||
result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
nsPoint svPoint = globalPoint - globalOffset;
|
||||
mAutoScrollTimer->Start(aPresContext, svPoint);
|
||||
nsPoint presContextPoint = globalPoint -
|
||||
presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetTo(rootmostFrame);
|
||||
mAutoScrollTimer->Start(presContext, presContextPoint);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -5514,116 +5417,16 @@ nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aX, nscoord *aY)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (!mFrameSelection)
|
||||
return NS_ERROR_FAILURE;//nothing to do
|
||||
|
||||
if (!aScrollableView || !aFrame || !aX || !aY) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
*aX = 0;
|
||||
*aY = 0;
|
||||
|
||||
nsIView* scrolledView;
|
||||
nsPoint offset;
|
||||
nsIView* closestView;
|
||||
|
||||
// Determine the offset from aFrame to the scrolled view. We do that by
|
||||
// getting the offset from its closest view and then walking up
|
||||
aScrollableView->GetScrolledView(scrolledView);
|
||||
nsIPresShell *shell = mFrameSelection->GetShell();
|
||||
|
||||
if (!shell)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
aFrame->GetOffsetFromView(offset, &closestView);
|
||||
|
||||
// XXX Deal with the case where there is a scrolled element, e.g., a
|
||||
// DIV in the middle...
|
||||
offset += closestView->GetOffsetTo(scrolledView);
|
||||
|
||||
*aX = offset.x;
|
||||
*aY = offset.y;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (!mFrameSelection)
|
||||
return NS_ERROR_FAILURE;//nothing to do
|
||||
if (!aFrame || !aPoint)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
aPoint->x = 0;
|
||||
aPoint->y = 0;
|
||||
|
||||
//
|
||||
// Now get the closest view with a widget so we can create
|
||||
// a rendering context.
|
||||
//
|
||||
|
||||
nsIWidget* widget = nsnull;
|
||||
nsIView *closestView = nsnull;
|
||||
nsPoint offset(0, 0);
|
||||
|
||||
rv = aFrame->GetOffsetFromView(offset, &closestView);
|
||||
|
||||
while (!widget && closestView)
|
||||
{
|
||||
widget = closestView->GetWidget();
|
||||
|
||||
if (!widget)
|
||||
{
|
||||
closestView = closestView->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!closestView)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
//
|
||||
// Now get the point and return!
|
||||
//
|
||||
|
||||
rv = aFrame->GetPointFromOffset(aContentOffset, aPoint);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static nsIScrollableView *
|
||||
GetNearestScrollableView(nsIView *aView)
|
||||
{
|
||||
for (nsIView *v = aView; v; v = v->GetParent()) {
|
||||
nsIScrollableView* sv = v->ToScrollableView();
|
||||
if (sv) {
|
||||
nsIScrollableFrame *sf = nsLayoutUtils::GetScrollableFrameFor(sv);
|
||||
if (sf &&
|
||||
sf->GetScrollbarStyles() !=
|
||||
nsPresContext::ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
|
||||
NS_STYLE_OVERFLOW_HIDDEN))
|
||||
return sv;
|
||||
}
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView)
|
||||
nsIFrame *
|
||||
nsTypedSelection::GetSelectionAnchorGeometry(SelectionRegion aRegion,
|
||||
nsRect *aRect)
|
||||
{
|
||||
if (!mFrameSelection)
|
||||
return NS_ERROR_FAILURE; // nothing to do
|
||||
return nsnull; // nothing to do
|
||||
|
||||
NS_ENSURE_TRUE(aRect && aScrollableView, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(aRect, nsnull);
|
||||
|
||||
aRect->SetRect(0, 0, 0, 0);
|
||||
*aScrollableView = nsnull;
|
||||
|
||||
nsINode *node = nsnull;
|
||||
PRInt32 nodeOffset = 0;
|
||||
@@ -5639,29 +5442,20 @@ nsTypedSelection::GetSelectionRegionRectAndScrollableView(SelectionRegion aRegio
|
||||
nodeOffset = GetFocusOffset();
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (!node)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
||||
NS_ENSURE_TRUE(content.get(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(content.get(), nsnull);
|
||||
PRInt32 frameOffset = 0;
|
||||
frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
|
||||
mFrameSelection->GetHint(),
|
||||
&frameOffset);
|
||||
if (!frame)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Get the frame's nearest scrollable view.
|
||||
nsIFrame* parentWithView = frame->GetAncestorWithView();
|
||||
if (!parentWithView)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsIView* view = parentWithView->GetView();
|
||||
*aScrollableView = GetNearestScrollableView(view);
|
||||
if (!*aScrollableView)
|
||||
return NS_OK;
|
||||
return nsnull;
|
||||
|
||||
// Figure out what node type we have, then get the
|
||||
// appropriate rect for it's nodeOffset.
|
||||
@@ -5676,224 +5470,28 @@ nsTypedSelection::GetSelectionRegionRectAndScrollableView(SelectionRegion aRegio
|
||||
mFrameSelection->GetHint(),
|
||||
&frameOffset, &childFrame);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
return nsnull;
|
||||
if (!childFrame)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
return nsnull;
|
||||
|
||||
frame = childFrame;
|
||||
|
||||
// Get the x coordinate of the offset into the text frame.
|
||||
rv = GetCachedFrameOffset(frame, nodeOffset, pt);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// Get the frame's rect in scroll view coordinates.
|
||||
*aRect = frame->GetRect();
|
||||
nsresult rv = GetFrameToScrolledViewOffsets(*aScrollableView, frame,
|
||||
&aRect->x, &aRect->y);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Return the rect relative to the frame, with zero width.
|
||||
if (isText) {
|
||||
aRect->x += pt.x;
|
||||
}
|
||||
else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
|
||||
aRect->x = pt.x;
|
||||
} else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
|
||||
// It's the frame's right edge we're interested in.
|
||||
aRect->x += aRect->width;
|
||||
aRect->x = frame->GetRect().width;
|
||||
}
|
||||
aRect->height = frame->GetRect().height;
|
||||
|
||||
nsRect clipRect = (*aScrollableView)->View()->GetBounds();
|
||||
rv = (*aScrollableView)->GetScrollPosition(clipRect.x, clipRect.y);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If the point we are interested in is outside the clip region, we aim
|
||||
// to over-scroll it by a quarter of the clip's width.
|
||||
PRInt32 pad = clipRect.width / 4;
|
||||
|
||||
if (pad == 0)
|
||||
pad = 3; // Arbitrary
|
||||
|
||||
if (aRect->x >= clipRect.XMost()) {
|
||||
aRect->width = pad;
|
||||
}
|
||||
else if (aRect->x <= clipRect.x) {
|
||||
aRect->x -= pad;
|
||||
aRect->width = pad;
|
||||
}
|
||||
else {
|
||||
aRect->width = 60; // Arbitrary
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
ClampPointInsideRect(nsPoint& aPoint, const nsRect& aRect)
|
||||
{
|
||||
if (aPoint.x < aRect.x)
|
||||
aPoint.x = aRect.x;
|
||||
if (aPoint.x > aRect.XMost())
|
||||
aPoint.x = aRect.XMost();
|
||||
if (aPoint.y < aRect.y)
|
||||
aPoint.y = aRect.y;
|
||||
if (aPoint.y > aRect.YMost())
|
||||
aPoint.y = aRect.YMost();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTypedSelection::ScrollRectIntoView(nsIScrollableView *aScrollableView,
|
||||
nsRect& aRect,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent,
|
||||
PRBool aScrollParentViews,
|
||||
PRBool *aDidScroll)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (!mFrameSelection)
|
||||
return NS_OK;//nothing to do
|
||||
|
||||
if (!aScrollableView)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// Determine the visible rect in the scrolled view's coordinate space.
|
||||
// The size of the visible area is the clip view size
|
||||
nsRect visibleRect = aScrollableView->View()->GetBounds();
|
||||
aScrollableView->GetScrollPosition(visibleRect.x, visibleRect.y);
|
||||
|
||||
// The actual scroll offsets
|
||||
nscoord scrollOffsetX = visibleRect.x;
|
||||
nscoord scrollOffsetY = visibleRect.y;
|
||||
|
||||
nsPresContext::ScrollbarStyles ss =
|
||||
nsLayoutUtils::GetScrollableFrameFor(aScrollableView)->GetScrollbarStyles();
|
||||
|
||||
// See how aRect should be positioned vertically
|
||||
if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
|
||||
if (NS_PRESSHELL_SCROLL_ANYWHERE == aVPercent) {
|
||||
// The caller doesn't care where aRect is positioned vertically,
|
||||
// so long as it's fully visible
|
||||
if (aRect.y < visibleRect.y) {
|
||||
// Scroll up so aRect's top edge is visible
|
||||
scrollOffsetY = aRect.y;
|
||||
} else if (aRect.YMost() > visibleRect.YMost()) {
|
||||
// Scroll down so aRect's bottom edge is visible. Make sure
|
||||
// aRect's top edge is still visible
|
||||
scrollOffsetY += aRect.YMost() - visibleRect.YMost();
|
||||
if (scrollOffsetY > aRect.y) {
|
||||
scrollOffsetY = aRect.y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Align the aRect edge according to the specified percentage
|
||||
nscoord frameAlignY = aRect.y + (aRect.height * aVPercent) / 100;
|
||||
scrollOffsetY = frameAlignY - (visibleRect.height * aVPercent) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
// See how the aRect should be positioned horizontally
|
||||
if (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
|
||||
if (NS_PRESSHELL_SCROLL_ANYWHERE == aHPercent) {
|
||||
// The caller doesn't care where the aRect is positioned horizontally,
|
||||
// so long as it's fully visible
|
||||
if (aRect.x < visibleRect.x) {
|
||||
// Scroll left so the aRect's left edge is visible
|
||||
scrollOffsetX = aRect.x;
|
||||
} else if (aRect.XMost() > visibleRect.XMost()) {
|
||||
// Scroll right so the aRect's right edge is visible. Make sure the
|
||||
// aRect's left edge is still visible
|
||||
scrollOffsetX += aRect.XMost() - visibleRect.XMost();
|
||||
if (scrollOffsetX > aRect.x) {
|
||||
scrollOffsetX = aRect.x;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Align the aRect edge according to the specified percentage
|
||||
nscoord frameAlignX = aRect.x + (aRect.width * aHPercent) / 100;
|
||||
scrollOffsetX = frameAlignX - (visibleRect.width * aHPercent) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (aDidScroll &&
|
||||
(scrollOffsetX != visibleRect.x || scrollOffsetY != visibleRect.y)) {
|
||||
*aDidScroll = PR_TRUE;
|
||||
}
|
||||
aScrollableView->ScrollTo(scrollOffsetX, scrollOffsetY, 0);
|
||||
|
||||
if (aScrollParentViews)
|
||||
{
|
||||
//
|
||||
// Get aScrollableView's scrolled view.
|
||||
//
|
||||
|
||||
nsIView *scrolledView = 0;
|
||||
|
||||
rv = aScrollableView->GetScrolledView(scrolledView);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!scrolledView)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
//
|
||||
// Check if aScrollableRect has a parent scrollable view!
|
||||
//
|
||||
|
||||
nsIView *view = aScrollableView->View()->GetParent();
|
||||
|
||||
if (view)
|
||||
{
|
||||
nsIScrollableView *parentSV = GetNearestScrollableView(view);
|
||||
|
||||
if (parentSV)
|
||||
{
|
||||
//
|
||||
// Clip the x dimensions of aRect so that they are
|
||||
// completely within the bounds of the scrolledView.
|
||||
// This helps avoid unnecessary scrolling of parent
|
||||
// scrolled views.
|
||||
//
|
||||
nsRect svRect = scrolledView->GetBounds() - scrolledView->GetPosition();
|
||||
nsPoint topLeft = aRect.TopLeft();
|
||||
nsPoint bottomRight = aRect.BottomRight();
|
||||
ClampPointInsideRect(topLeft, svRect);
|
||||
ClampPointInsideRect(bottomRight, svRect);
|
||||
nsRect newRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x,
|
||||
bottomRight.y - topLeft.y);
|
||||
|
||||
//
|
||||
// We have a parent scrollable view, so now map aRect
|
||||
// into it's scrolled view's coordinate space.
|
||||
//
|
||||
|
||||
rv = parentSV->GetScrolledView(view);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!view)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nscoord offsetX, offsetY;
|
||||
rv = GetViewAncestorOffset(scrolledView, view, &offsetX, &offsetY);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
newRect.x += offsetX;
|
||||
newRect.y += offsetY;
|
||||
|
||||
//
|
||||
// Now scroll the rect into the parent's view.
|
||||
//
|
||||
|
||||
rv = ScrollRectIntoView(parentSV, newRect, aVPercent, aHPercent, aScrollParentViews, aDidScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
return frame;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -5981,21 +5579,12 @@ nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
|
||||
//
|
||||
|
||||
nsRect rect;
|
||||
nsIScrollableView *scrollableView = 0;
|
||||
nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
|
||||
if (!frame)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
result = GetSelectionRegionRectAndScrollableView(aRegion, &rect, &scrollableView);
|
||||
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
//
|
||||
// It's ok if we don't have a scrollable view, just return early.
|
||||
//
|
||||
if (!scrollableView)
|
||||
return NS_OK;
|
||||
|
||||
result = ScrollRectIntoView(scrollableView, rect, aVPercent, aHPercent,
|
||||
PR_TRUE, nsnull);
|
||||
presShell->ScrollFrameRectIntoView(frame, rect, aVPercent, aHPercent, 0);
|
||||
return NS_OK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user