Bug 526394. Part 26: Rework nsSelection to use frames only. r=mats

This commit is contained in:
Robert O'Callahan
2009-09-10 17:16:18 +12:00
parent 7986bb5fe1
commit 9a37af643c
7 changed files with 168 additions and 530 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View 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>

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;
}