Bug 171334. r=kmcclusk,sr=kin,a=asa. Fix views crasher by eliminating content-parenting nesting assumptions.

This commit is contained in:
roc+%cs.cmu.edu 2002-10-11 01:23:13 +00:00
parent ff5152a1e2
commit ca03aa9d86
4 changed files with 134 additions and 100 deletions

View File

@ -90,10 +90,13 @@ nsView::nsView()
MOZ_COUNT_CTOR(nsView);
mVis = nsViewVisibility_kShow;
mVFlags = 0;
// Views should be transparent by default. Not being transparent is
// a promise that the view will paint all its pixels opaquely. Views
// should make this promise explicitly by calling
// SetViewContentTransparency.
mVFlags = NS_VIEW_FLAG_TRANSPARENT;
mOpacity = 1.0f;
mViewManager = nsnull;
mCompositorFlags = 0;
mChildRemoved = PR_FALSE;
}
@ -944,19 +947,6 @@ nsresult nsView::GetDirtyRegion(nsIRegion*& aRegion)
return NS_OK;
}
NS_IMETHODIMP nsView::SetCompositorFlags(PRUint32 aFlags)
{
mCompositorFlags = aFlags;
return NS_OK;
}
NS_IMETHODIMP nsView::GetCompositorFlags(PRUint32 *aFlags)
{
NS_ASSERTION((aFlags != nsnull), "no flags");
*aFlags = mCompositorFlags;
return NS_OK;
}
PRBool nsView::IsRoot()
{
NS_ASSERTION(mViewManager != nsnull," View manager is null in nsView::IsRoot()");
@ -985,7 +975,7 @@ PRBool nsView::PointIsInside(nsView& aView, nscoord x, nscoord y) const
NS_IMETHODIMP nsView::GetClippedRect(nsRect& aClippedRect, PRBool& aIsClipped, PRBool& aEmpty) const
{
// Keep track of the view's offset from its ancestor.
// Keep track of this view's offset from its ancestor.
// This is the origin of this view's parent view in the
// coordinate space of 'parentView' below.
nscoord ancestorX = 0;
@ -1005,21 +995,28 @@ NS_IMETHODIMP nsView::GetClippedRect(nsRect& aClippedRect, PRBool& aIsClipped, P
const nsView* zParent = view->GetZParent();
const nsView* parentView = view->GetParent();
if (zParent) {
// This view was reparented. We need to move back down the view tree
// to where it should be to collect whatever might be clipping it there.
// This view was reparented. We need to continue collecting clip
// rects from the zParent.
// parentView is an ancestor of zParent ... this is guaranteed by the way these
// reparented views are set up; a reparented view is always reparented to one of its
// own ancestors
// we need to get ancestorX and ancestorY into the right coordinate system.
// They are the offset of this view within parentView
// First we need to get ancestorX and ancestorY into the right
// coordinate system. They are the offset of this view within
// parentView
const nsView* zParentChain;
for (zParentChain = zParent; zParentChain != parentView;
for (zParentChain = zParent; zParentChain != parentView && zParentChain != nsnull;
zParentChain = zParentChain->GetParent()) {
NS_ASSERTION(zParentChain != nsnull, "Error in view reparenting logic");
zParentChain->ConvertFromParentCoords(&ancestorX, &ancestorY);
}
if (zParentChain == nsnull) {
// normally we'll hit parentView somewhere, but sometimes we
// don't because of odd containing block hierarchies. In this
// case we walked from zParentChain all the way up to the
// root, subtracting from ancestorX/Y. So now we walk from
// parentView up to the root, adding to ancestorX/Y.
while (parentView != nsnull) {
parentView->ConvertToParentCoords(&ancestorX, &ancestorY);
parentView = parentView->GetParent();
}
}
parentView = zParent;
// Now start again at zParent to collect all its clip information
}

View File

@ -66,27 +66,37 @@ public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_ICLIPVIEW_IID)
};
//Flag to determine whether the view will check if events can be handled
//by its children or just handle the events itself
#define NS_VIEW_FLAG_DONT_CHECK_CHILDREN 0x0001
// indicates that the view is or contains a placeholder view
#define NS_VIEW_FLAG_CONTAINS_PLACEHOLDER 0x0002
//the view is transparent
#define NS_VIEW_FLAG_TRANSPARENT 0x0004
//indicates that the view should not be bitblt'd when moved
//or scrolled and instead must be repainted
#define NS_VIEW_FLAG_DONT_BITBLT 0x0010
// indicates that the view is using auto z-indexing
#define NS_VIEW_FLAG_AUTO_ZINDEX 0x0020
// indicates that the view is a floating view.
#define NS_VIEW_FLAG_FLOATING 0x0040
// set if our widget resized.
#define NS_VIEW_FLAG_WIDGET_RESIZED 0x0080
// set if our widget moved.
#define NS_VIEW_FLAG_WIDGET_MOVED 0x0100
#define NS_VIEW_FLAG_CLIPCHILDREN 0x0200
// if set it indicates that this view should be
// displayed above z-index:auto views if this view
// is z-index:auto also
#define NS_VIEW_FLAG_TOPMOST 0x0400
//indicates that the view should not be bitblt'd when moved
//or scrolled and instead must be repainted
#define NS_VIEW_FLAG_DONT_BITBLT 0x0010
//Flag to determine whether the view will check if events can be handled
//by its children or just handle the events itself
#define NS_VIEW_FLAG_DONT_CHECK_CHILDREN 0x0001
class nsView : public nsIView
{
@ -180,6 +190,12 @@ public:
void GetDimensions(nsRect &aRect) const { aRect = mDimBounds; aRect.x -= mPosX; aRect.y -= mPosY; }
void GetDimensions(nsSize &aSize) const { aSize.width = mDimBounds.width; aSize.height = mDimBounds.height; }
/**
* This checks whether the view is a placeholder for some view that has
* been reparented to a different geometric parent.
*/
virtual PRBool IsZPlaceholderView() { return PR_FALSE; }
/**
* Called to set the clip of the children of this view.
* The clip is relative to the origin of the view.
@ -251,12 +267,6 @@ public:
* @return error status
*/
NS_IMETHOD SetWidget(nsIWidget *aWidget);
/**
* Used by the compositor for temporary marking of a view during
* compositing. This will eventually replace GetScratchPoint above.
*/
NS_IMETHOD SetCompositorFlags(PRUint32 aFlags);
NS_IMETHOD GetCompositorFlags(PRUint32 *aFlags);
/**
* Return a rectangle containing the view's bounds adjusted for it's ancestors clipping
* @param aClippedRect views bounds adjusted for ancestors clipping. If aEmpty is TRUE it
@ -338,7 +348,6 @@ protected:
float mOpacity;
PRUint32 mVFlags;
nsIRegion* mDirtyRegion;
PRUint32 mCompositorFlags;
// Bug #19416
PRPackedBool mShouldIgnoreSetPosition;
PRPackedBool mChildRemoved;

View File

@ -1962,6 +1962,71 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
return NS_OK;
}
void nsViewManager::ReparentViews(DisplayZTreeNode* aNode) {
if (aNode == nsnull) {
return;
}
DisplayZTreeNode* child;
DisplayZTreeNode** prev = &aNode->mZChild;
for (child = aNode->mZChild; nsnull != child; child = *prev) {
ReparentViews(child);
nsZPlaceholderView *zParent = nsnull;
if (nsnull != child->mView) {
zParent = child->mView->GetZParent();
}
if (nsnull != zParent) {
nsVoidKey key(zParent);
DisplayZTreeNode* placeholder = (DisplayZTreeNode *)mMapPlaceholderViewToZTreeNode.Get(&key);
if (placeholder == child) {
// don't do anything if we already reparented this node;
// just advance to the next child
prev = &child->mZSibling;
} else {
// unlink the child from the tree
*prev = child->mZSibling;
child->mZSibling = nsnull;
if (nsnull != placeholder) {
NS_ASSERTION((placeholder->mDisplayElement == nsnull), "placeholder already has elements?");
NS_ASSERTION((placeholder->mZChild == nsnull), "placeholder already has Z-children?");
placeholder->mDisplayElement = child->mDisplayElement;
placeholder->mView = child->mView;
placeholder->mZChild = child->mZChild;
delete child;
} else {
// the placeholder was not added to the display list
// we don't need the real view then, either
DestroyZTreeNode(child);
}
}
} else {
prev = &child->mZSibling;
}
}
}
static PRBool ComputePlaceholderContainment(nsView* aView) {
PRBool containsPlaceholder = aView->IsZPlaceholderView();
nsView* child;
for (child = aView->GetFirstChild(); child != nsnull; child = child->GetNextSibling()) {
if (ComputePlaceholderContainment(child)) {
containsPlaceholder = PR_TRUE;
}
}
if (containsPlaceholder) {
aView->SetViewFlags(aView->GetViewFlags() | NS_VIEW_FLAG_CONTAINS_PLACEHOLDER);
} else {
aView->SetViewFlags(aView->GetViewFlags() & ~NS_VIEW_FLAG_CONTAINS_PLACEHOLDER);
}
return containsPlaceholder;
}
/*
Fills mDisplayList with DisplayListElement2* pointers. The caller is responsible
for freeing these structs. The display list elements are ordered by z-order so
@ -2006,6 +2071,9 @@ void nsViewManager::BuildDisplayList(nsView* aView, const nsRect& aRect, PRBool
nsPoint displayRootOrigin(0, 0);
ComputeViewOffset(displayRoot, &displayRootOrigin);
// Determine, for each view, whether it is or contains a ZPlaceholderView
ComputePlaceholderContainment(displayRoot);
// Create the Z-ordered view tree
PRBool paintFloaters;
if (aEventProcessing) {
@ -2015,6 +2083,9 @@ void nsViewManager::BuildDisplayList(nsView* aView, const nsRect& aRect, PRBool
}
CreateDisplayList(displayRoot, PR_FALSE, zTree, PR_FALSE, origin.x, origin.y,
aView, &aRect, nsnull, displayRootOrigin.x, displayRootOrigin.y, paintFloaters, aEventProcessing);
// Reparent any views that need reparenting in the Z-order tree
ReparentViews(zTree);
mMapPlaceholderViewToZTreeNode.Reset();
if (nsnull != zTree) {
@ -3353,7 +3424,21 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView, PRBool aReparentedViewsPr
bounds.x += aOriginX;
bounds.y += aOriginY;
if (!overlap && isClipView) {
// if there's no overlap between the dirty area and the bounds of
// the view, we should be able to ignore the view and all its
// children. Unfortunately there is the possibility of reparenting:
// some other view in the tree might want to be reparented to one of
// our children, and that view might lie outside our
// bounds. Typically we need to go ahead and add this view and its
// children to the ZTree in case someone wants to reparent into it.
//
// We can avoid this in two cases: if we clip our children to our
// bounds, then even if a view is reparented under us, none of its
// visible area can lie outside our bounds, so it can't intersect
// the dirty area. Also, if we don't contain any placeholder views,
// then there is no way for anyone to reparent below us.
if (!overlap && (isClipView ||
(aView->GetViewFlags() & NS_VIEW_FLAG_CONTAINS_PLACEHOLDER) == 0)) {
return PR_FALSE;
}
@ -3369,21 +3454,6 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView, PRBool aReparentedViewsPr
}
}
if (!aReparentedViewsPresent) {
for (nsView* childView = aView->GetFirstChild(); nsnull != childView;
childView = childView->GetNextSibling()) {
nsZPlaceholderView *zParent = childView->GetZParent();
if (nsnull != zParent) {
aReparentedViewsPresent = PR_TRUE;
break;
}
}
if (!overlap && !aReparentedViewsPresent) {
return PR_FALSE;
}
}
PRInt32 childCount = aView->GetChildCount();
nsView *childView = nsnull;
@ -3458,10 +3528,7 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView, PRBool aReparentedViewsPr
bounds.x += aOriginX;
bounds.y += aOriginY;
} else {
PRUint32 compositorFlags = 0;
aView->GetCompositorFlags(&compositorFlags);
if (0 != (compositorFlags & IS_Z_PLACEHOLDER_VIEW)) {
if (aView->IsZPlaceholderView()) {
EnsureZTreeNodeCreated(aView, aResult);
mMapPlaceholderViewToZTreeNode.Put(new nsVoidKey(aView), aResult);
}
@ -3497,41 +3564,6 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView, PRBool aReparentedViewsPr
}
}
// Reparent any views that need reparenting in the Z-order tree
if (nsnull != aResult) {
DisplayZTreeNode* child;
DisplayZTreeNode** prev = &aResult->mZChild;
for (child = aResult->mZChild; nsnull != child; child = *prev) {
nsZPlaceholderView *zParent = nsnull;
if (nsnull != child->mView) {
zParent = child->mView->GetZParent();
}
if (nsnull != zParent) {
// unlink the child from the tree
*prev = child->mZSibling;
child->mZSibling = nsnull;
nsVoidKey key(zParent);
DisplayZTreeNode* placeholder = (DisplayZTreeNode *)mMapPlaceholderViewToZTreeNode.Remove(&key);
if (nsnull != placeholder) {
NS_ASSERTION((placeholder->mDisplayElement == nsnull), "placeholder already has elements?");
NS_ASSERTION((placeholder->mZChild == nsnull), "placeholder already has Z-children?");
placeholder->mDisplayElement = child->mDisplayElement;
placeholder->mView = child->mView;
placeholder->mZChild = child->mZChild;
delete child;
} else {
// the placeholder was never added to the display list ...
// we don't need to display this then
DestroyZTreeNode(child);
}
} else {
prev = &child->mZSibling;
}
}
}
return retval;
}

View File

@ -70,10 +70,6 @@ struct DisplayZTreeNode;
#include "nsTimer.h"
#endif
// compositor per-view flags
#define IS_PARENT_OF_REFRESHED_VIEW 0x00000001
#define IS_Z_PLACEHOLDER_VIEW 0x80000000
/**
FIXED-POSITION FRAMES AND Z-ORDERING
@ -105,8 +101,7 @@ public:
void SetReparentedView(nsView* aView) { mReparentedView = aView; }
nsView* GetReparentedView() { return mReparentedView; }
NS_IMETHOD GetCompositorFlags(PRUint32 *aFlags)
{ nsView::GetCompositorFlags(aFlags); *aFlags |= IS_Z_PLACEHOLDER_VIEW; return NS_OK; }
virtual PRBool IsZPlaceholderView() { return PR_TRUE; }
protected:
virtual ~nsZPlaceholderView() {
@ -271,6 +266,7 @@ private:
nsresult CreateBlendingBuffers(nsIRenderingContext &aRC);
void ReparentViews(DisplayZTreeNode* aNode);
void BuildDisplayList(nsView* aView, const nsRect& aRect, PRBool aEventProcessing, PRBool aCaptured);
void BuildEventTargetList(nsAutoVoidArray &aTargets, nsView* aView, nsGUIEvent* aEvent, PRBool aCaptured);