Fix for bug #17989: Selection AutoScrolling broken when using GFX Scrollbars

layout/base/src/nsRangeList.cpp
        - Added ConstrainFrameAndPointToAnchorSubtree() and various utility
          methods.
        - Modified HandleDrag() to call ConstrainFrameAndPointToAnchorSubtree().

    layout/html/base/src/nsFrame.cpp
        - Modified GetContentAndOffsetFromPoint() to skip over generated
          and anonymous content frames.

    layout/html/base/src/nsGfxScrollFrame.cpp
    layout/html/base/src/nsGfxScrollFrame.h
        - Override the default nsFrame::GetContentAndOffsetsFromPoint()
          implementation with a version that calls
          mInner->GetContentAndOffsetsFromPoint(). This prevents the
          scrollbar frames from being included in the search.

    view/src/nsScrollPortView.cpp
        - Fixed bug where GetContainerSize() was returning the size of the
          ScrollPortView instead of the ScrolledView.

r=evaughan@netscape.com
This commit is contained in:
kin%netscape.com 1999-11-18 19:40:52 +00:00
Родитель e7d9c4d3be
Коммит 9ce2f08c0b
8 изменённых файлов: 444 добавлений и 8 удалений

Просмотреть файл

@ -255,7 +255,10 @@ private:
void InvalidateDesiredX(); //do not listen to mDesiredX you must get another.
void SetDesiredX(nscoord aX); //set the mDesiredX
nsresult GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent);
nsresult GetGlobalViewOffsetsFromFrame(nsIPresContext *aPresContext, nsIFrame *aFrame, nscoord *offsetX, nscoord *offsetY);
nsresult ConstrainFrameAndPointToAnchorSubtree(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint);
PRUint32 GetBatching(){return mBatching;}
PRBool GetNotifyFrames(){return mNotifyFrames;}
void SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
@ -744,6 +747,256 @@ nsRangeList::SetDesiredX(nscoord aX) //set the mDesiredX
mDesiredXSet = PR_TRUE;
}
nsresult
nsRangeList::GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent)
{
// This method returns the root of the sub-tree containing aContent.
// We do this by searching up through the parent hierarchy, and stopping
// when there are no more parents, or we hit a situation where the
// parent/child relationship becomes invalid.
//
// An example of an invalid parent/child relationship is anonymous content.
// Anonymous content has a pointer to it's parent, but it is not listed
// as a child of it's parent. In this case, the anonymous content would
// be considered the root of the subtree.
nsresult result;
if (!aContent || !aParent)
return NS_ERROR_NULL_POINTER;
*aParent = 0;
nsCOMPtr<nsIContent> parent = do_QueryInterface(aContent);
nsCOMPtr<nsIContent> child = parent;
while (child)
{
result = child->GetParent(*getter_AddRefs(parent));
if (NS_FAILED(result))
return result;
if (!parent)
break;
PRInt32 childIndex = 0;
PRInt32 childCount = 0;
result = parent->ChildCount(childCount);
if (NS_FAILED(result))
return result;
if (childCount < 1)
break;
result = parent->IndexOf(child, childIndex);
if (NS_FAILED(result))
return result;
if (childIndex < 0 || childIndex >= childCount)
break;
child = parent;
}
*aParent = child;
NS_IF_ADDREF(*aParent);
return result;
}
nsresult
nsRangeList::GetGlobalViewOffsetsFromFrame(nsIPresContext *aPresContext, nsIFrame *aFrame, nscoord *offsetX, nscoord *offsetY)
{
//
// The idea here is to figure out what the offset of aFrame's view
// is within the global space. Where I define the global space to
// be the coordinate system that exists above all views.
//
// The offsets are calculated by walking up the view parent hierarchy,
// adding up all the view positions, until there are no more views.
//
// A point in a view's coordinate space can be converted to the global
// coordinate space by simply adding the offsets returned by this method
// to the point itself.
//
if (!aPresContext || !aFrame || !offsetX || !offsetY)
return NS_ERROR_NULL_POINTER;
*offsetX = *offsetY = 0;
nsresult result;
nsIFrame *frame = aFrame;
nsIView *view;
while (frame)
{
result = frame->GetParentWithView(aPresContext, &frame);
if (NS_FAILED(result))
return result;
if (frame) {
view = 0;
result = frame->GetView(aPresContext, &view);
if (NS_FAILED(result))
return result;
if (view)
{
nscoord vX = 0, vY = 0;
result = view->GetPosition(&vX, &vY);
if (NS_FAILED(result))
return result;
*offsetX += vX;
*offsetY += vY;
}
}
}
return NS_OK;
}
nsresult
nsRangeList::ConstrainFrameAndPointToAnchorSubtree(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint)
{
//
// The whole point of this method is to return a frame and point that
// that lie within the same valid subtree as the anchor node's frame,
// for use with the method GetContentAndOffsetsFromPoint().
//
// A valid subtree is defined to be one where all the content nodes in
// the tree have a valid parent-child relationship.
//
// If the anchor frame and aFrame are in the same subtree, aFrame will
// be returned in aRetFrame. If they are in different subtrees, we
// return the frame for the root of the subtree.
//
if (!aFrame || !aRetFrame)
return NS_ERROR_NULL_POINTER;
*aRetFrame = aFrame;
aRetPoint = aPoint;
//
// Get the frame and content for the selection's anchor point!
//
nsresult result;
nsCOMPtr<nsIDOMNode> anchorNode;
PRInt32 anchorOffset = 0;
if (! mDomSelections[SELECTION_NORMAL])
return NS_ERROR_NULL_POINTER;
result = mDomSelections[SELECTION_NORMAL]->GetAnchorNode(getter_AddRefs(anchorNode));
if (NS_FAILED(result))
return result;
if (!anchorNode)
return NS_OK;
result = mDomSelections[SELECTION_NORMAL]->GetAnchorOffset(&anchorOffset);
if (NS_FAILED(result))
return result;
nsIFrame *anchorFrame = 0;
nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
if (!anchorContent)
return NS_ERROR_FAILURE;
result = GetFrameForNodeOffset(anchorContent, anchorOffset, &anchorFrame);
//
// Now find the root of the subtree containing the anchor's content.
//
nsCOMPtr<nsIContent> anchorRoot;
result = GetRootForContentSubtree(anchorContent, getter_AddRefs(anchorRoot));
if (NS_FAILED(result))
return result;
//
// Now find the root of the subtree containing aFrame's content.
//
nsCOMPtr<nsIContent> content;
result = aFrame->GetContent(getter_AddRefs(content));
if (NS_FAILED(result))
return result;
if (content)
{
nsCOMPtr<nsIContent> contentRoot;
result = GetRootForContentSubtree(content, getter_AddRefs(contentRoot));
if (anchorRoot == contentRoot)
{
//
// The anchor and AFrame's root are the same. There
// is no need to constrain, simply return aFrame.
//
*aRetFrame = aFrame;
return NS_OK;
}
}
//
// aFrame's root does not match the anchor's root, or there is no
// content associated with aFrame. Just return the primary frame
// for the anchor's root. We'll let GetContentAndOffsetsFromPoint()
// find the closest frame aPoint.
//
result = mTracker->GetPrimaryFrameFor(anchorRoot, aRetFrame);
if (NS_FAILED(result))
return result;
if (! *aRetFrame)
return NS_ERROR_FAILURE;
//
// Now make sure that aRetPoint is converted to the same coordinate
// system used by aRetFrame.
//
nsPoint frameOffset;
nsPoint retFrameOffset;
result = GetGlobalViewOffsetsFromFrame(aPresContext, aFrame, &frameOffset.x, &frameOffset.y);
if (NS_FAILED(result))
return result;
result = GetGlobalViewOffsetsFromFrame(aPresContext, *aRetFrame, &retFrameOffset.x, &retFrameOffset.y);
if (NS_FAILED(result))
return result;
aRetPoint = aPoint + frameOffset - retFrameOffset;
return NS_OK;
}
#ifdef XP_MAC
#pragma mark -
@ -1050,7 +1303,20 @@ nsRangeList::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
NS_IMETHODIMP
nsRangeList::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
{
if (!aPresContext || !aFrame)
return NS_ERROR_NULL_POINTER;
nsresult result;
nsIFrame *newFrame = 0;
nsPoint newPoint;
result = ConstrainFrameAndPointToAnchorSubtree(aPresContext, aFrame, aPoint, &newFrame, newPoint);
if (NS_FAILED(result))
return result;
if (!newFrame)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresShell> presShell;
@ -1064,9 +1330,9 @@ nsRangeList::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint&
PRBool beginOfContent;
nsCOMPtr<nsIContent> newContent;
result = aFrame->GetContentAndOffsetsFromPoint(*aPresContext, aPoint,
getter_AddRefs(newContent),
startPos, contentOffsetEnd,beginOfContent);
result = newFrame->GetContentAndOffsetsFromPoint(*aPresContext, newPoint,
getter_AddRefs(newContent),
startPos, contentOffsetEnd,beginOfContent);
if (NS_SUCCEEDED(result))
result = HandleClick(newContent, startPos, contentOffsetEnd , PR_TRUE, PR_FALSE,beginOfContent);

Просмотреть файл

@ -1035,6 +1035,64 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
PRInt32 closestYDistance = HUGE_DISTANCE;
while (nsnull != kid) {
// Skip over generated content kid frames, or frames
// that don't have a proper parent-child relationship!
PRBool skipThisKid = PR_FALSE;
nsFrameState frameState;
result = kid->GetFrameState(&frameState);
if (NS_FAILED(result))
return result;
if (frameState & NS_FRAME_GENERATED_CONTENT) {
// It's generated content, so skip it!
skipThisKid = PR_TRUE;
}
else {
// The frame's content is not generated. Now check
// if it is anonymous content!
nsCOMPtr<nsIContent> kidContent;
result = kid->GetContent(getter_AddRefs(kidContent));
if (NS_SUCCEEDED(result) && kidContent) {
nsCOMPtr<nsIContent> content;
result = kidContent->GetParent(*getter_AddRefs(content));
if (NS_SUCCEEDED(result) && content) {
PRInt32 kidCount = 0;
result = content->ChildCount(kidCount);
if (NS_SUCCEEDED(result)) {
PRInt32 kidIndex = 0;
result = content->IndexOf(kidContent, kidIndex);
// IndexOf() should return -1 for the index if it doesn't
// find kidContent in it's child list.
if (NS_SUCCEEDED(result) && (kidIndex < 0 || kidIndex >= kidCount)) {
// Must be anonymous content! So skip it!
skipThisKid = PR_TRUE;
}
}
}
}
}
if (skipThisKid) {
kid->GetNextSibling(&kid);
continue;
}
// Kid frame has content that has a proper parent-child
// relationship. Now see if the aPoint inside it's bounding
// rect or close by.
nsRect rect;
nsPoint offsetPoint(0,0);
nsIView * kidView = nsnull;
@ -1066,7 +1124,7 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
PRInt32 xDistance = PR_MIN(abs(xa - aPoint.x),abs(xb - aPoint.x));
if (xDistance < closestXDistance)
if (xDistance < closestXDistance || (xDistance == closestXDistance && rect.x <= aPoint.x))
{
closestXDistance = xDistance;
closestYDistance = yDistance;

Просмотреть файл

@ -602,6 +602,19 @@ nsGfxScrollFrame::Paint(nsIPresContext& aPresContext,
aWhichLayer);
}
NS_IMETHODIMP
nsGfxScrollFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
const nsPoint& aPoint,
nsIContent ** aNewContent,
PRInt32& aContentOffset,
PRInt32& aContentOffsetEnd,
PRBool& aBeginFrameContent)
{
if (! mInner)
return NS_ERROR_NULL_POINTER;
return mInner->mScrollAreaFrame->GetContentAndOffsetsFromPoint(aCX, aPoint, aNewContent, aContentOffset, aContentOffsetEnd, aBeginFrameContent);
}
PRIntn
nsGfxScrollFrame::GetSkipSides() const

Просмотреть файл

@ -93,6 +93,13 @@ public:
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer);
NS_IMETHOD GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
const nsPoint& aPoint,
nsIContent ** aNewContent,
PRInt32& aContentOffset,
PRInt32& aContentOffsetEnd,
PRBool& aBeginFrameContent);
// nsIAnonymousContentCreator
NS_IMETHOD CreateAnonymousContent(nsISupportsArray& aAnonymousItems);

Просмотреть файл

@ -1035,6 +1035,64 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
PRInt32 closestYDistance = HUGE_DISTANCE;
while (nsnull != kid) {
// Skip over generated content kid frames, or frames
// that don't have a proper parent-child relationship!
PRBool skipThisKid = PR_FALSE;
nsFrameState frameState;
result = kid->GetFrameState(&frameState);
if (NS_FAILED(result))
return result;
if (frameState & NS_FRAME_GENERATED_CONTENT) {
// It's generated content, so skip it!
skipThisKid = PR_TRUE;
}
else {
// The frame's content is not generated. Now check
// if it is anonymous content!
nsCOMPtr<nsIContent> kidContent;
result = kid->GetContent(getter_AddRefs(kidContent));
if (NS_SUCCEEDED(result) && kidContent) {
nsCOMPtr<nsIContent> content;
result = kidContent->GetParent(*getter_AddRefs(content));
if (NS_SUCCEEDED(result) && content) {
PRInt32 kidCount = 0;
result = content->ChildCount(kidCount);
if (NS_SUCCEEDED(result)) {
PRInt32 kidIndex = 0;
result = content->IndexOf(kidContent, kidIndex);
// IndexOf() should return -1 for the index if it doesn't
// find kidContent in it's child list.
if (NS_SUCCEEDED(result) && (kidIndex < 0 || kidIndex >= kidCount)) {
// Must be anonymous content! So skip it!
skipThisKid = PR_TRUE;
}
}
}
}
}
if (skipThisKid) {
kid->GetNextSibling(&kid);
continue;
}
// Kid frame has content that has a proper parent-child
// relationship. Now see if the aPoint inside it's bounding
// rect or close by.
nsRect rect;
nsPoint offsetPoint(0,0);
nsIView * kidView = nsnull;
@ -1066,7 +1124,7 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
PRInt32 xDistance = PR_MIN(abs(xa - aPoint.x),abs(xb - aPoint.x));
if (xDistance < closestXDistance)
if (xDistance < closestXDistance || (xDistance == closestXDistance && rect.x <= aPoint.x))
{
closestXDistance = xDistance;
closestYDistance = yDistance;

Просмотреть файл

@ -602,6 +602,19 @@ nsGfxScrollFrame::Paint(nsIPresContext& aPresContext,
aWhichLayer);
}
NS_IMETHODIMP
nsGfxScrollFrame::GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
const nsPoint& aPoint,
nsIContent ** aNewContent,
PRInt32& aContentOffset,
PRInt32& aContentOffsetEnd,
PRBool& aBeginFrameContent)
{
if (! mInner)
return NS_ERROR_NULL_POINTER;
return mInner->mScrollAreaFrame->GetContentAndOffsetsFromPoint(aCX, aPoint, aNewContent, aContentOffset, aContentOffsetEnd, aBeginFrameContent);
}
PRIntn
nsGfxScrollFrame::GetSkipSides() const

Просмотреть файл

@ -93,6 +93,13 @@ public:
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer);
NS_IMETHOD GetContentAndOffsetsFromPoint(nsIPresContext& aCX,
const nsPoint& aPoint,
nsIContent ** aNewContent,
PRInt32& aContentOffset,
PRInt32& aContentOffsetEnd,
PRBool& aBeginFrameContent);
// nsIAnonymousContentCreator
NS_IMETHOD CreateAnonymousContent(nsISupportsArray& aAnonymousItems);

Просмотреть файл

@ -1230,9 +1230,23 @@ NS_IMETHODIMP nsScrollPortView::ComputeScrollOffsets(PRBool aAdjustWidgets)
NS_IMETHODIMP nsScrollPortView::GetContainerSize(nscoord *aWidth, nscoord *aHeight) const
{
GetDimensions(aWidth, aHeight);
if (!aWidth || !aHeight)
return NS_ERROR_NULL_POINTER;
return NS_OK;
*aWidth = 0;
*aHeight = 0;
nsIView *scrolledView = 0;
nsresult result = GetScrolledView(scrolledView);
if (NS_FAILED(result))
return result;
if (!scrolledView)
return NS_ERROR_FAILURE;
return scrolledView->GetDimensions(aWidth, aHeight);
}
NS_IMETHODIMP nsScrollPortView::ShowQuality(PRBool aShow)