diff --git a/layout/generic/nsHTMLContainerFrame.cpp b/layout/generic/nsHTMLContainerFrame.cpp index 7dd99fbf7fab..9f143f537cea 100644 --- a/layout/generic/nsHTMLContainerFrame.cpp +++ b/layout/generic/nsHTMLContainerFrame.cpp @@ -43,6 +43,7 @@ #include "nsIScrollableView.h" #include "nsWidgetsCID.h" #include "nsIStyleSet.h" +#include "nsCOMPtr.h" static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID); @@ -126,17 +127,16 @@ nsHTMLContainerFrame::CreateNextInFlow(nsIPresContext& aPresContext, } static nsresult -ReparentFrameViewTo(nsIFrame* aFrame, - nsIView* aNewParentView, - nsIView* aOldParentView) +ReparentFrameViewTo(nsIFrame* aFrame, + nsIViewManager* aViewManager, + nsIView* aNewParentView, + nsIView* aOldParentView) { nsIView* view; // Does aFrame have a view? aFrame->GetView(&view); if (view) { - nsIViewManager* viewManager; - #ifdef NS_DEBUG // Verify that the current parent view is what we think it is nsIView* parentView; @@ -145,18 +145,13 @@ ReparentFrameViewTo(nsIFrame* aFrame, NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); #endif - // Get the view manager - aNewParentView->GetViewManager(viewManager); - NS_ASSERTION(nsnull != viewManager, "null view manager"); - // Change the parent view. PRInt32 zIndex; view->GetZIndex(zIndex); - viewManager->RemoveChild(aOldParentView, view); + aViewManager->RemoveChild(aOldParentView, view); // XXX We need to insert this view in the correct place within its z-order... - viewManager->InsertChild(aNewParentView, view, zIndex); - NS_RELEASE(viewManager); + aViewManager->InsertChild(aNewParentView, view, zIndex); } else { // Iterate the child frames, and check each child frame to see if it has @@ -165,7 +160,7 @@ ReparentFrameViewTo(nsIFrame* aFrame, aFrame->FirstChild(nsnull, &childFrame); while (childFrame) { - ReparentFrameViewTo(childFrame, aNewParentView, aOldParentView); + ReparentFrameViewTo(childFrame, aViewManager, aNewParentView, aOldParentView); childFrame->GetNextSibling(&childFrame); } } @@ -173,16 +168,21 @@ ReparentFrameViewTo(nsIFrame* aFrame, return NS_OK; } +// Helper function that returns the nearest view to this frame. Checks +// this frame, its parent frame, its parent frame, ... static nsIView* -GetViewFor(nsIFrame* aFrame) +GetClosestViewFor(nsIFrame* aFrame) { + NS_PRECONDITION(aFrame, "null frame pointer"); nsIView* view; - aFrame->GetView(&view); - if (!view) { - nsPoint offset; - aFrame->GetOffsetFromView(offset, &view); - } + do { + aFrame->GetView(&view); + if (view) { + break; + } + aFrame->GetParent(&aFrame); + } while (aFrame); NS_POSTCONDITION(view, "no containing view"); return view; @@ -198,13 +198,84 @@ nsHTMLContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); - // First see if the old parent frame and the new parent frame are in the - // same view sub-hierarchy - nsIView* oldParentView = GetViewFor(aOldParentFrame); - nsIView* newParentView = GetViewFor(aNewParentFrame); + nsIView* childView; + nsIView* oldParentView; + nsIView* newParentView; + nsIFrame* commonParent = nsnull; + + // This code is called often and we need it to be as fast as possible, so + // see if we can trivially detect that no work needs to be done + aChildFrame->GetView(&childView); + if (!childView) { + // Child frame doesn't have a view. See if it has any child frames + nsIFrame* firstChild; + aChildFrame->FirstChild(nsnull, &firstChild); + if (!firstChild) { + return NS_OK; + } + } + // See if either the old parent frame or the new parent frame have a view + aOldParentFrame->GetView(&oldParentView); + aNewParentFrame->GetView(&newParentView); + + if (!oldParentView && !newParentView) { + // Walk up both the old parent frame and the new parent frame nodes + // stopping when we either find a common parent or views for one + // or both of the frames. + // + // This works well in the common case where we push/pull and the old parent + // frame and the new parent frame are part of the same flow. They will + // typically be the same distance (height wise) from the + do { + aOldParentFrame->GetParent(&aOldParentFrame); + aNewParentFrame->GetParent(&aNewParentFrame); + + // We should never walk all the way to the root frame without finding + // a view + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); + + // See if we reached a common parent + if (aOldParentFrame == aNewParentFrame) { + break; + } + + // Get the views + aOldParentFrame->GetView(&oldParentView); + aNewParentFrame->GetView(&newParentView); + } while (!(oldParentView || newParentView)); + } + + + // See if we found a common parent frame + if (aOldParentFrame == aNewParentFrame) { + // We found a common parent and there are no views between the old parent + // and the common parent or the new parent frame and the common parent. + // Because neither the old parent frame nor the new parent frame have views, + // then any child views don't need reparenting + return NS_OK; + } + + // We found views for one or both of the parent frames before we found a + // common parent + NS_ASSERTION(oldParentView || newParentView, "internal error"); + if (!oldParentView) { + oldParentView = GetClosestViewFor(aOldParentFrame); + } + if (!newParentView) { + newParentView = GetClosestViewFor(aNewParentFrame); + } + + // See if the old parent frame and the new parent frame are in the + // same view sub-hierarchy. If they are then we don't have to do + // anything if (oldParentView != newParentView) { - return ReparentFrameViewTo(aChildFrame, newParentView, oldParentView); + nsCOMPtr viewManager; + oldParentView->GetViewManager(*getter_AddRefs(viewManager)); + + // They're not so we need to reparent any child views + return ReparentFrameViewTo(aChildFrame, viewManager, newParentView, + oldParentView); } return NS_OK; diff --git a/layout/html/base/src/nsHTMLContainerFrame.cpp b/layout/html/base/src/nsHTMLContainerFrame.cpp index 7dd99fbf7fab..9f143f537cea 100644 --- a/layout/html/base/src/nsHTMLContainerFrame.cpp +++ b/layout/html/base/src/nsHTMLContainerFrame.cpp @@ -43,6 +43,7 @@ #include "nsIScrollableView.h" #include "nsWidgetsCID.h" #include "nsIStyleSet.h" +#include "nsCOMPtr.h" static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID); @@ -126,17 +127,16 @@ nsHTMLContainerFrame::CreateNextInFlow(nsIPresContext& aPresContext, } static nsresult -ReparentFrameViewTo(nsIFrame* aFrame, - nsIView* aNewParentView, - nsIView* aOldParentView) +ReparentFrameViewTo(nsIFrame* aFrame, + nsIViewManager* aViewManager, + nsIView* aNewParentView, + nsIView* aOldParentView) { nsIView* view; // Does aFrame have a view? aFrame->GetView(&view); if (view) { - nsIViewManager* viewManager; - #ifdef NS_DEBUG // Verify that the current parent view is what we think it is nsIView* parentView; @@ -145,18 +145,13 @@ ReparentFrameViewTo(nsIFrame* aFrame, NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); #endif - // Get the view manager - aNewParentView->GetViewManager(viewManager); - NS_ASSERTION(nsnull != viewManager, "null view manager"); - // Change the parent view. PRInt32 zIndex; view->GetZIndex(zIndex); - viewManager->RemoveChild(aOldParentView, view); + aViewManager->RemoveChild(aOldParentView, view); // XXX We need to insert this view in the correct place within its z-order... - viewManager->InsertChild(aNewParentView, view, zIndex); - NS_RELEASE(viewManager); + aViewManager->InsertChild(aNewParentView, view, zIndex); } else { // Iterate the child frames, and check each child frame to see if it has @@ -165,7 +160,7 @@ ReparentFrameViewTo(nsIFrame* aFrame, aFrame->FirstChild(nsnull, &childFrame); while (childFrame) { - ReparentFrameViewTo(childFrame, aNewParentView, aOldParentView); + ReparentFrameViewTo(childFrame, aViewManager, aNewParentView, aOldParentView); childFrame->GetNextSibling(&childFrame); } } @@ -173,16 +168,21 @@ ReparentFrameViewTo(nsIFrame* aFrame, return NS_OK; } +// Helper function that returns the nearest view to this frame. Checks +// this frame, its parent frame, its parent frame, ... static nsIView* -GetViewFor(nsIFrame* aFrame) +GetClosestViewFor(nsIFrame* aFrame) { + NS_PRECONDITION(aFrame, "null frame pointer"); nsIView* view; - aFrame->GetView(&view); - if (!view) { - nsPoint offset; - aFrame->GetOffsetFromView(offset, &view); - } + do { + aFrame->GetView(&view); + if (view) { + break; + } + aFrame->GetParent(&aFrame); + } while (aFrame); NS_POSTCONDITION(view, "no containing view"); return view; @@ -198,13 +198,84 @@ nsHTMLContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); - // First see if the old parent frame and the new parent frame are in the - // same view sub-hierarchy - nsIView* oldParentView = GetViewFor(aOldParentFrame); - nsIView* newParentView = GetViewFor(aNewParentFrame); + nsIView* childView; + nsIView* oldParentView; + nsIView* newParentView; + nsIFrame* commonParent = nsnull; + + // This code is called often and we need it to be as fast as possible, so + // see if we can trivially detect that no work needs to be done + aChildFrame->GetView(&childView); + if (!childView) { + // Child frame doesn't have a view. See if it has any child frames + nsIFrame* firstChild; + aChildFrame->FirstChild(nsnull, &firstChild); + if (!firstChild) { + return NS_OK; + } + } + // See if either the old parent frame or the new parent frame have a view + aOldParentFrame->GetView(&oldParentView); + aNewParentFrame->GetView(&newParentView); + + if (!oldParentView && !newParentView) { + // Walk up both the old parent frame and the new parent frame nodes + // stopping when we either find a common parent or views for one + // or both of the frames. + // + // This works well in the common case where we push/pull and the old parent + // frame and the new parent frame are part of the same flow. They will + // typically be the same distance (height wise) from the + do { + aOldParentFrame->GetParent(&aOldParentFrame); + aNewParentFrame->GetParent(&aNewParentFrame); + + // We should never walk all the way to the root frame without finding + // a view + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); + + // See if we reached a common parent + if (aOldParentFrame == aNewParentFrame) { + break; + } + + // Get the views + aOldParentFrame->GetView(&oldParentView); + aNewParentFrame->GetView(&newParentView); + } while (!(oldParentView || newParentView)); + } + + + // See if we found a common parent frame + if (aOldParentFrame == aNewParentFrame) { + // We found a common parent and there are no views between the old parent + // and the common parent or the new parent frame and the common parent. + // Because neither the old parent frame nor the new parent frame have views, + // then any child views don't need reparenting + return NS_OK; + } + + // We found views for one or both of the parent frames before we found a + // common parent + NS_ASSERTION(oldParentView || newParentView, "internal error"); + if (!oldParentView) { + oldParentView = GetClosestViewFor(aOldParentFrame); + } + if (!newParentView) { + newParentView = GetClosestViewFor(aNewParentFrame); + } + + // See if the old parent frame and the new parent frame are in the + // same view sub-hierarchy. If they are then we don't have to do + // anything if (oldParentView != newParentView) { - return ReparentFrameViewTo(aChildFrame, newParentView, oldParentView); + nsCOMPtr viewManager; + oldParentView->GetViewManager(*getter_AddRefs(viewManager)); + + // They're not so we need to reparent any child views + return ReparentFrameViewTo(aChildFrame, viewManager, newParentView, + oldParentView); } return NS_OK;