diff --git a/layout/base/src/nsPresShell.cpp b/layout/base/nsPresShell.cpp similarity index 100% rename from layout/base/src/nsPresShell.cpp rename to layout/base/nsPresShell.cpp diff --git a/layout/base/src/makefile.win b/layout/base/src/makefile.win index b24596e3c25c..ddde4d1bae5f 100644 --- a/layout/base/src/makefile.win +++ b/layout/base/src/makefile.win @@ -21,20 +21,16 @@ LIBRARY_NAME=raptorlayout_s DEFINES=-D_IMPL_NS_LAYOUT -DWIN32_LEAN_AND_MEAN CPPSRCS = \ - nsContainerFrame.cpp \ nsContentList.cpp \ nsDocument.cpp \ - nsFrame.cpp \ nsFrameImageLoader.cpp \ nsGalleyContext.cpp \ nsPresContext.cpp \ - nsPresShell.cpp \ nsPrintPreviewContext.cpp \ nsSelection.cpp \ nsSelectionPoint.cpp \ nsSelectionRange.cpp \ nsSpaceManager.cpp \ - nsSplittableFrame.cpp \ nsStyleContext.cpp \ nsStyleCoord.cpp \ nsStyleSet.cpp \ @@ -46,20 +42,16 @@ REQUIRES=xpcom raptor dom EXPORTS=nsSelectionRange.h nsSelectionPoint.h CPP_OBJS= \ - .\$(OBJDIR)\nsContainerFrame.obj \ .\$(OBJDIR)\nsContentList.obj \ .\$(OBJDIR)\nsDocument.obj \ - .\$(OBJDIR)\nsFrame.obj \ .\$(OBJDIR)\nsFrameImageLoader.obj \ .\$(OBJDIR)\nsGalleyContext.obj \ .\$(OBJDIR)\nsPresContext.obj \ - .\$(OBJDIR)\nsPresShell.obj \ .\$(OBJDIR)\nsPrintPreviewContext.obj \ .\$(OBJDIR)\nsSelection.obj \ .\$(OBJDIR)\nsSelectionPoint.obj \ .\$(OBJDIR)\nsSelectionRange.obj \ .\$(OBJDIR)\nsSpaceManager.obj \ - .\$(OBJDIR)\nsSplittableFrame.obj \ .\$(OBJDIR)\nsStyleContext.obj \ .\$(OBJDIR)\nsStyleCoord.obj \ .\$(OBJDIR)\nsStyleSet.obj \ diff --git a/layout/base/src/nsContainerFrame.cpp b/layout/base/src/nsContainerFrame.cpp deleted file mode 100644 index f89b29ce1fdc..000000000000 --- a/layout/base/src/nsContainerFrame.cpp +++ /dev/null @@ -1,700 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * The contents of this file are subject to the Netscape Public License - * Version 1.0 (the "NPL"); you may not use this file except in - * compliance with the NPL. You may obtain a copy of the NPL at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the NPL is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL - * for the specific language governing rights and limitations under the - * NPL. - * - * The Initial Developer of this code under the NPL is Netscape - * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All Rights - * Reserved. - */ -#include "nsContainerFrame.h" -#include "nsIContent.h" -#include "nsIPresContext.h" -#include "nsIRenderingContext.h" -#include "nsIRunaround.h" -#include "nsISpaceManager.h" -#include "nsIStyleContext.h" -#include "nsRect.h" -#include "nsPoint.h" -#include "nsGUIEvent.h" -#include "nsStyleConsts.h" -#include "nsIView.h" -#include "nsVoidArray.h" -#include "nsISizeOfHandler.h" - -#ifdef NS_DEBUG -#undef NOISY -#else -#undef NOISY -#endif - -nsContainerFrame::nsContainerFrame(nsIContent* aContent, nsIFrame* aParent) - : nsSplittableFrame(aContent, aParent) -{ -} - -nsContainerFrame::~nsContainerFrame() -{ -} - -NS_IMETHODIMP -nsContainerFrame::SizeOf(nsISizeOfHandler* aHandler) const -{ - aHandler->Add(sizeof(*this)); - nsContainerFrame::SizeOfWithoutThis(aHandler); - return NS_OK; -} - -NS_IMETHODIMP -nsContainerFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) -{ - NS_PRECONDITION(nsnull == mFirstChild, "already initialized"); - - mFirstChild = aChildList; - return NS_OK; -} - -NS_IMETHODIMP -nsContainerFrame::DeleteFrame(nsIPresContext& aPresContext) -{ - // Delete our child frames before doing anything else. In particular - // we do all of this before our base class releases it's hold on the - // view. - for (nsIFrame* child = mFirstChild; child; ) { - mFirstChild = nsnull; // XXX hack until HandleEvent is not called until after destruction - nsIFrame* nextChild; - - child->GetNextSibling(nextChild); - child->DeleteFrame(aPresContext); - child = nextChild; - } - - nsFrame::DeleteFrame(aPresContext); - - return NS_OK; -} - -void -nsContainerFrame::SizeOfWithoutThis(nsISizeOfHandler* aHandler) const -{ - nsSplittableFrame::SizeOfWithoutThis(aHandler); - for (nsIFrame* child = mFirstChild; child; ) { - child->SizeOf(aHandler); - child->GetNextSibling(child); - } -} - -void -nsContainerFrame::PrepareContinuingFrame(nsIPresContext& aPresContext, - nsIFrame* aParent, - nsIStyleContext* aStyleContext, - nsContainerFrame* aContFrame) -{ - // Append the continuing frame to the flow - aContFrame->AppendToFlow(this); - aContFrame->SetStyleContext(&aPresContext, aStyleContext); -} - -NS_METHOD -nsContainerFrame::DidReflow(nsIPresContext& aPresContext, - nsDidReflowStatus aStatus) -{ - NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("enter nsContainerFrame::DidReflow: status=%d", - aStatus)); - if (NS_FRAME_REFLOW_FINISHED == aStatus) { - nsIFrame* kid; - FirstChild(kid); - while (nsnull != kid) { - kid->DidReflow(aPresContext, aStatus); - kid->GetNextSibling(kid); - } - } - - NS_FRAME_TRACE_OUT("nsContainerFrame::DidReflow"); - - // Let nsFrame position and size our view (if we have one), and clear - // the NS_FRAME_IN_REFLOW bit - return nsFrame::DidReflow(aPresContext, aStatus); -} - -///////////////////////////////////////////////////////////////////////////// -// Child frame enumeration - -NS_METHOD nsContainerFrame::FirstChild(nsIFrame*& aFirstChild) const -{ - aFirstChild = mFirstChild; - return NS_OK; -} - -///////////////////////////////////////////////////////////////////////////// -// Painting - -NS_METHOD nsContainerFrame::Paint(nsIPresContext& aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aDirtyRect) -{ - PaintChildren(aPresContext, aRenderingContext, aDirtyRect); - return NS_OK; -} - -// aDirtyRect is in our coordinate system -// child rect's are also in our coordinate system -void nsContainerFrame::PaintChildren(nsIPresContext& aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aDirtyRect) -{ - // Set clip rect so that children don't leak out of us - const nsStyleDisplay* disp = - (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); - PRBool hidden = PR_FALSE; - if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) { - aRenderingContext.PushState(); - aRenderingContext.SetClipRect(nsRect(0, 0, mRect.width, mRect.height), - nsClipCombine_kIntersect); - hidden = PR_TRUE; - } - - // XXX reminder: use the coordinates in the dirty rect to figure out - // which set of children are impacted and only do the intersection - // work for them. In addition, stop when we no longer overlap. - - nsIFrame* kid = mFirstChild; - while (nsnull != kid) { - nsIView *pView; - - kid->GetView(pView); - if (nsnull == pView) { - nsRect kidRect; - kid->GetRect(kidRect); - nsRect damageArea; -#ifdef NS_DEBUG - PRBool overlap = PR_FALSE; - if (nsIFrame::GetShowFrameBorders() && - ((kidRect.width == 0) || (kidRect.height == 0))) { - nscoord xmost = aDirtyRect.XMost(); - nscoord ymost = aDirtyRect.YMost(); - if ((aDirtyRect.x <= kidRect.x) && (kidRect.x < xmost) && - (aDirtyRect.y <= kidRect.y) && (kidRect.y < ymost)) { - overlap = PR_TRUE; - } - } - else { - overlap = damageArea.IntersectRect(aDirtyRect, kidRect); - } -#else - PRBool overlap = damageArea.IntersectRect(aDirtyRect, kidRect); -#endif - if (overlap) { - // Translate damage area into kid's coordinate system - nsRect kidDamageArea(damageArea.x - kidRect.x, - damageArea.y - kidRect.y, - damageArea.width, damageArea.height); - aRenderingContext.PushState(); - aRenderingContext.Translate(kidRect.x, kidRect.y); - kid->Paint(aPresContext, aRenderingContext, kidDamageArea); -#ifdef NS_DEBUG - if (nsIFrame::GetShowFrameBorders() && - (0 != kidRect.width) && (0 != kidRect.height)) { - nsIView* view; - GetView(view); - if (nsnull != view) { - aRenderingContext.SetColor(NS_RGB(0,0,255)); - } - else { - aRenderingContext.SetColor(NS_RGB(255,0,0)); - } - aRenderingContext.DrawRect(0, 0, kidRect.width, kidRect.height); - } -#endif - aRenderingContext.PopState(); - } - } - kid->GetNextSibling(kid); - } - - if (hidden) { - aRenderingContext.PopState(); - } -} - -///////////////////////////////////////////////////////////////////////////// -// Events - -NS_METHOD nsContainerFrame::HandleEvent(nsIPresContext& aPresContext, - nsGUIEvent* aEvent, - nsEventStatus& aEventStatus) -{ - aEventStatus = nsEventStatus_eIgnore; - - nsIFrame* kid; - FirstChild(kid); - while (nsnull != kid) { - nsRect kidRect; - kid->GetRect(kidRect); - if (kidRect.Contains(aEvent->point)) { - aEvent->point.MoveBy(-kidRect.x, -kidRect.y); - kid->HandleEvent(aPresContext, aEvent, aEventStatus); - aEvent->point.MoveBy(kidRect.x, kidRect.y); - break; - } - kid->GetNextSibling(kid); - } - - return NS_OK; -} - -NS_METHOD nsContainerFrame::GetCursorAndContentAt(nsIPresContext& aPresContext, - const nsPoint& aPoint, - nsIFrame** aFrame, - nsIContent** aContent, - PRInt32& aCursor) -{ - aCursor = NS_STYLE_CURSOR_INHERIT; - *aContent = mContent; - - nsIFrame* kid; - FirstChild(kid); - nsPoint tmp; - while (nsnull != kid) { - nsRect kidRect; - kid->GetRect(kidRect); - if (kidRect.Contains(aPoint)) { - tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y); - kid->GetCursorAndContentAt(aPresContext, tmp, aFrame, aContent, aCursor); - break; - } - kid->GetNextSibling(kid); - } - return NS_OK; -} - -///////////////////////////////////////////////////////////////////////////// -// Helper member functions - -/** - * Reflow a child frame and return the status of the reflow. If the - * child is complete and it has next-in-flows (it was a splittable child) - * then delete the next-in-flows. - */ -nsReflowStatus nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, - nsIPresContext* aPresContext, - nsReflowMetrics& aDesiredSize, - const nsReflowState& aReflowState) -{ - nsReflowStatus status; - - NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); -#ifdef NS_DEBUG - nsFrameState kidFrameState; - - aKidFrame->GetFrameState(kidFrameState); - NS_ASSERTION(kidFrameState & NS_FRAME_IN_REFLOW, "kid frame is not in reflow"); -#endif - aKidFrame->Reflow(*aPresContext, aDesiredSize, aReflowState, status); - - if (NS_FRAME_IS_COMPLETE(status)) { - nsIFrame* kidNextInFlow; - - aKidFrame->GetNextInFlow(kidNextInFlow); - if (nsnull != kidNextInFlow) { - // Remove all of the childs next-in-flows. Make sure that we ask - // the right parent to do the removal (it's possible that the - // parent is not this because we are executing pullup code) - nsIFrame* parent; - aKidFrame->GetGeometricParent(parent); - ((nsContainerFrame*)parent)->DeleteChildsNextInFlow(*aPresContext, aKidFrame); - } - } - return status; -} - -/** - * Remove and delete aChild's next-in-flow(s). Updates the sibling and flow - * pointers - * - * Updates the child count and content offsets of all containers that are - * affected - * - * @param aChild child this child's next-in-flow - * @return PR_TRUE if successful and PR_FALSE otherwise - */ -PRBool -nsContainerFrame::DeleteChildsNextInFlow(nsIPresContext& aPresContext, nsIFrame* aChild) -{ - NS_PRECONDITION(IsChild(aChild), "bad geometric parent"); - - nsIFrame* nextInFlow; - nsContainerFrame* parent; - - aChild->GetNextInFlow(nextInFlow); - NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow"); - nextInFlow->GetGeometricParent((nsIFrame*&)parent); - - // If the next-in-flow has a next-in-flow then delete it, too (and - // delete it first). - nsIFrame* nextNextInFlow; - - nextInFlow->GetNextInFlow(nextNextInFlow); - if (nsnull != nextNextInFlow) { - parent->DeleteChildsNextInFlow(aPresContext, nextInFlow); - } - -#ifdef NS_DEBUG - PRInt32 childCount; - nsIFrame* firstChild; - - nextInFlow->FirstChild(firstChild); - childCount = LengthOf(firstChild); - - if ((0 != childCount) || (nsnull != firstChild)) { - nsIFrame* top = nextInFlow; - for (;;) { - nsIFrame* parent; - top->GetGeometricParent(parent); - if (nsnull == parent) { - break; - } - top = parent; - } - top->List(); - } - NS_ASSERTION((0 == childCount) && (nsnull == firstChild), - "deleting !empty next-in-flow"); -#endif - - // Disconnect the next-in-flow from the flow list - nextInFlow->BreakFromPrevFlow(); - - // Take the next-in-flow out of the parent's child list - if (parent->mFirstChild == nextInFlow) { - nextInFlow->GetNextSibling(parent->mFirstChild); - - } else { - nsIFrame* nextSibling; - - // Because the next-in-flow is not the first child of the parent - // we know that it shares a parent with aChild. Therefore, we need - // to capture the next-in-flow's next sibling (in case the - // next-in-flow is the last next-in-flow for aChild AND the - // next-in-flow is not the last child in parent) - NS_ASSERTION(((nsContainerFrame*)parent)->IsChild(aChild), "screwy flow"); - aChild->GetNextSibling(nextSibling); - NS_ASSERTION(nextSibling == nextInFlow, "unexpected sibling"); - - nextInFlow->GetNextSibling(nextSibling); - aChild->SetNextSibling(nextSibling); - } - - // Delete the next-in-flow frame and adjust its parents child count - WillDeleteNextInFlowFrame(nextInFlow); - nextInFlow->DeleteFrame(aPresContext); - -#ifdef NS_DEBUG - aChild->GetNextInFlow(nextInFlow); - NS_POSTCONDITION(nsnull == nextInFlow, "non null next-in-flow"); -#endif - return PR_TRUE; -} - -void nsContainerFrame::WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow) -{ -} - -/** - * Push aFromChild and its next siblings to the next-in-flow. Change the - * geometric parent of each frame that's pushed. If there is no next-in-flow - * the frames are placed on the overflow list (and the geometric parent is - * left unchanged). - * - * Updates the next-in-flow's child count. Does not update the - * pusher's child count. - * - * @param aFromChild the first child frame to push. It is disconnected from - * aPrevSibling - * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's - * an error to push a parent's first child frame - */ -void nsContainerFrame::PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling) -{ - NS_PRECONDITION(nsnull != aFromChild, "null pointer"); - NS_PRECONDITION(nsnull != aPrevSibling, "pushing first child"); -#ifdef NS_DEBUG - nsIFrame* prevNextSibling; - - aPrevSibling->GetNextSibling(prevNextSibling); - NS_PRECONDITION(prevNextSibling == aFromChild, "bad prev sibling"); -#endif - - // Disconnect aFromChild from its previous sibling - aPrevSibling->SetNextSibling(nsnull); - - // Do we have a next-in-flow? - nsContainerFrame* nextInFlow = (nsContainerFrame*)mNextInFlow; - if (nsnull != nextInFlow) { - PRInt32 numChildren = 0; - nsIFrame* lastChild = nsnull; - - // Compute the number of children being pushed, and for each child change - // its geometric parent. Remember the last child - for (nsIFrame* f = aFromChild; nsnull != f; f->GetNextSibling(f)) { - numChildren++; -#ifdef NOISY - printf(" "); - ((nsFrame*)f)->ListTag(stdout); - printf("\n"); -#endif - lastChild = f; - f->SetGeometricParent(nextInFlow); - - nsIFrame* contentParent; - - f->GetContentParent(contentParent); - if (this == contentParent) { - f->SetContentParent(nextInFlow); - } - } - NS_ASSERTION(numChildren > 0, "no children to push"); - - // Prepend the frames to our next-in-flow's child list - lastChild->SetNextSibling(nextInFlow->mFirstChild); - nextInFlow->mFirstChild = aFromChild; - - } else { - // Add the frames to our overflow list - NS_ASSERTION(nsnull == mOverflowList, "bad overflow list"); -#ifdef NOISY - ListTag(stdout); - printf(": pushing kids to my overflow list\n"); -#endif - mOverflowList = aFromChild; - } -} - -/** - * Moves any frames on the overflwo lists (the prev-in-flow's overflow list and - * the receiver's overflow list) to the child list. - * - * Updates this frame's child count and content mapping. - * - * @return PR_TRUE if any frames were moved and PR_FALSE otherwise - */ -PRBool nsContainerFrame::MoveOverflowToChildList() -{ - PRBool result = PR_FALSE; - - // Check for an overflow list with our prev-in-flow - nsContainerFrame* prevInFlow = (nsContainerFrame*)mPrevInFlow; - if (nsnull != prevInFlow) { - if (nsnull != prevInFlow->mOverflowList) { - NS_ASSERTION(nsnull == mFirstChild, "bad overflow list"); - AppendChildren(prevInFlow->mOverflowList); - prevInFlow->mOverflowList = nsnull; - result = PR_TRUE; - } - } - - // It's also possible that we have an overflow list for ourselves - if (nsnull != mOverflowList) { - NS_ASSERTION(nsnull != mFirstChild, "overflow list but no mapped children"); - AppendChildren(mOverflowList, PR_FALSE); - mOverflowList = nsnull; - result = PR_TRUE; - } - - return result; -} - -/** - * Append child list starting at aChild to this frame's child list. Used for - * processing of the overflow list. - * - * Updates this frame's child count and content mapping. - * - * @param aChild the beginning of the child list - * @param aSetParent if true each child's geometric (and content parent if - * they're the same) parent is set to this frame. - */ -void nsContainerFrame::AppendChildren(nsIFrame* aChild, PRBool aSetParent) -{ - // Link the frames into our child frame list - if (nsnull == mFirstChild) { - // We have no children so aChild becomes the first child - mFirstChild = aChild; - } else { - nsIFrame* lastChild = LastFrame(mFirstChild); - lastChild->SetNextSibling(aChild); - } - - // Update our child count and last content offset - nsIFrame* lastChild; - for (nsIFrame* f = aChild; nsnull != f; f->GetNextSibling(f)) { - lastChild = f; - - // Reset the geometric parent if requested - if (aSetParent) { - nsIFrame* geometricParent; - nsIFrame* contentParent; - - f->GetGeometricParent(geometricParent); - f->GetContentParent(contentParent); - if (contentParent == geometricParent) { - f->SetContentParent(this); - } - f->SetGeometricParent(this); - } - } -} - -///////////////////////////////////////////////////////////////////////////// -// Debugging - -NS_METHOD nsContainerFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const -{ - // if a filter is present, only output this frame if the filter says we should - nsIAtom* tag; - nsAutoString tagString; - mContent->GetTag(tag); - if (tag != nsnull) - tag->ToString(tagString); - PRBool outputMe = (PRBool)((nsnull==aFilter) || (PR_TRUE==aFilter->OutputTag(&tagString))); - if (PR_TRUE==outputMe) - { - // Indent - for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - - // Output the tag - ListTag(out); - nsIView* view; - GetView(view); - if (nsnull != view) { - fprintf(out, " [view=%p]", view); - } - - if (nsnull != mPrevInFlow) { - fprintf(out, "prev-in-flow=%p ", mPrevInFlow); - } - if (nsnull != mNextInFlow) { - fprintf(out, "next-in-flow=%p ", mNextInFlow); - } - - // Output the rect - out << mRect; - } - - // Output the children - if (nsnull != mFirstChild) { - if (PR_TRUE==outputMe) - { - if (0 != mState) { - fprintf(out, " [state=%08x]", mState); - } - fputs("<\n", out); - } - for (nsIFrame* child = mFirstChild; child; child->GetNextSibling(child)) { - child->List(out, aIndent + 1, aFilter); - } - if (PR_TRUE==outputMe) - { - for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); - fputs(">\n", out); - } - } else { - if (PR_TRUE==outputMe) - { - if (0 != mState) { - fprintf(out, " [state=%08x]", mState); - } - fputs("<>\n", out); - } - } - - return NS_OK; -} - -#define VERIFY_ASSERT(_expr, _msg) \ - if (!(_expr)) { \ - DumpTree(); \ - } \ - NS_ASSERTION(_expr, _msg) - -NS_METHOD nsContainerFrame::VerifyTree() const -{ -#ifdef NS_DEBUG - NS_ASSERTION(0 == (mState & NS_FRAME_IN_REFLOW), "frame is in reflow"); - VERIFY_ASSERT(nsnull == mOverflowList, "bad overflow list"); -#endif - return NS_OK; -} - -PRInt32 nsContainerFrame::LengthOf(nsIFrame* aFrame) -{ - PRInt32 result = 0; - - while (nsnull != aFrame) { - result++; - aFrame->GetNextSibling(aFrame); - } - - return result; -} - -nsIFrame* nsContainerFrame::LastFrame(nsIFrame* aFrame) -{ - nsIFrame* lastChild = nsnull; - - while (nsnull != aFrame) { - lastChild = aFrame; - aFrame->GetNextSibling(aFrame); - } - - return lastChild; -} - -nsIFrame* nsContainerFrame::FrameAt(nsIFrame* aFrame, PRInt32 aIndex) -{ - while ((aIndex-- > 0) && (aFrame != nsnull)) { - aFrame->GetNextSibling(aFrame); - } - return aFrame; -} - -///////////////////////////////////////////////////////////////////////////// - -#ifdef NS_DEBUG - -PRBool nsContainerFrame::IsChild(const nsIFrame* aChild) const -{ - // Check the geometric parent - nsIFrame* parent; - - aChild->GetGeometricParent(parent); - if (parent != (nsIFrame*)this) { - return PR_FALSE; - } - - return PR_TRUE; -} - -void nsContainerFrame::DumpTree() const -{ - nsIFrame* root = (nsIFrame*)this; - nsIFrame* parent = mGeometricParent; - - while (nsnull != parent) { - root = parent; - parent->GetGeometricParent(parent); - } - - root->List(); -} - -#endif diff --git a/layout/base/src/nsContainerFrame.h b/layout/generic/nsContainerFrame.h similarity index 100% rename from layout/base/src/nsContainerFrame.h rename to layout/generic/nsContainerFrame.h diff --git a/layout/base/src/nsFrame.cpp b/layout/generic/nsFrame.cpp similarity index 100% rename from layout/base/src/nsFrame.cpp rename to layout/generic/nsFrame.cpp diff --git a/layout/base/src/nsFrame.h b/layout/generic/nsFrame.h similarity index 100% rename from layout/base/src/nsFrame.h rename to layout/generic/nsFrame.h diff --git a/layout/base/src/nsSplittableFrame.cpp b/layout/generic/nsSplittableFrame.cpp similarity index 100% rename from layout/base/src/nsSplittableFrame.cpp rename to layout/generic/nsSplittableFrame.cpp diff --git a/layout/base/src/nsSplittableFrame.h b/layout/generic/nsSplittableFrame.h similarity index 100% rename from layout/base/src/nsSplittableFrame.h rename to layout/generic/nsSplittableFrame.h diff --git a/layout/html/base/src/makefile.win b/layout/html/base/src/makefile.win index 2b6f37f0139b..077355338dca 100644 --- a/layout/html/base/src/makefile.win +++ b/layout/html/base/src/makefile.win @@ -27,6 +27,8 @@ CPPSRCS= \ nsBRFrame.cpp \ nsBlockFrame.cpp \ nsBodyFrame.cpp \ + nsContainerFrame.cpp \ + nsFrame.cpp \ nsFrameReflowState.cpp \ nsGlobalVariables.cpp \ nsHRFrame.cpp \ @@ -44,8 +46,10 @@ CPPSRCS= \ nsObjectFrame.cpp \ nsPageFrame.cpp \ nsPlaceholderFrame.cpp \ + nsPresShell.cpp \ nsScrollFrame.cpp \ nsSpacerFrame.cpp \ + nsSplittableFrame.cpp \ nsTextFrame.cpp \ nsWBRFrame.cpp \ $(NULL) @@ -55,6 +59,8 @@ CPP_OBJS= \ .\$(OBJDIR)\nsBRFrame.obj \ .\$(OBJDIR)\nsBlockFrame.obj \ .\$(OBJDIR)\nsBodyFrame.obj \ + .\$(OBJDIR)\nsContainerFrame.obj \ + .\$(OBJDIR)\nsFrame.obj \ .\$(OBJDIR)\nsFrameReflowState.obj \ .\$(OBJDIR)\nsGlobalVariables.obj \ .\$(OBJDIR)\nsHRFrame.obj \ @@ -72,8 +78,10 @@ CPP_OBJS= \ .\$(OBJDIR)\nsObjectFrame.obj \ .\$(OBJDIR)\nsPageFrame.obj \ .\$(OBJDIR)\nsPlaceholderFrame.obj \ + .\$(OBJDIR)\nsPresShell.obj \ .\$(OBJDIR)\nsScrollFrame.obj \ .\$(OBJDIR)\nsSpacerFrame.obj \ + .\$(OBJDIR)\nsSplittableFrame.obj \ .\$(OBJDIR)\nsTextFrame.obj \ .\$(OBJDIR)\nsWBRFrame.obj \ $(NULL) @@ -85,6 +93,7 @@ LINCS=-I$(PUBLIC)\xpcom -I$(PUBLIC)\raptor -I$(PUBLIC)\js \ -I..\..\style\src \ -I..\..\content\src \ -I..\..\..\base\src -I$(PUBLIC)\plugin -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ -I$(PUBLIC)\oji LCFLAGS = \ diff --git a/layout/html/base/src/nsContainerFrame.h b/layout/html/base/src/nsContainerFrame.h new file mode 100644 index 000000000000..9b32bc284057 --- /dev/null +++ b/layout/html/base/src/nsContainerFrame.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef nsContainerFrame_h___ +#define nsContainerFrame_h___ + +#include "nsSplittableFrame.h" + +/** + * Implementation of a container frame. + */ +class nsContainerFrame : public nsSplittableFrame +{ +public: + NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const; + + NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); + + NS_IMETHOD DidReflow(nsIPresContext& aPresContext, + nsDidReflowStatus aStatus); + + // Painting + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + + /** + * Pass through the event to the correct child frame. + * Return PR_TRUE if the event is consumed. + */ + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD GetCursorAndContentAt(nsIPresContext& aPresContext, + const nsPoint& aPoint, + nsIFrame** aFrame, + nsIContent** aContent, + PRInt32& aCursor); + + // Child frame enumeration. + // ChildAt() retruns null if the index is not in the range 0 .. ChildCount() - 1. + // IndexOf() returns -1 if the frame is not in the child list. + NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; + + // Debugging + NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0, nsIListFilter *aFilter = nsnull) const; + NS_IMETHOD VerifyTree() const; + + /** + * Return the number of children in the sibling list, starting at aChild. + * Returns zero if aChild is nsnull. + */ + static PRInt32 LengthOf(nsIFrame* aChild); + + /** + * Return the last frame in the sibling list. + * Returns nsnullif aChild is nsnull. + */ + static nsIFrame* LastFrame(nsIFrame* aChild); + + /** + * Returns the frame at the specified index relative to aFrame + */ + static nsIFrame* FrameAt(nsIFrame* aFrame, PRInt32 aIndex); + + // XXX needs to be virtual so that nsBlockFrame can override it + virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext, + nsIFrame* aChild); + +protected: + // Constructor. Takes as arguments the content object, the index in parent, + // and the Frame for the content parent + nsContainerFrame(nsIContent* aContent, nsIFrame* aParent); + + virtual ~nsContainerFrame(); + + void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const; + + /** + * Prepare a continuation frame of this frame for reflow. Appends + * it to the flow, sets its content offsets, mLastContentIsComplete, + * and style context. Subclasses should invoke this method after + * construction of a continuing frame. + */ + void PrepareContinuingFrame(nsIPresContext& aPresContext, + nsIFrame* aParent, + nsIStyleContext* aStyleContext, + nsContainerFrame* aContFrame); + + + virtual void PaintChildren(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + + /** + * Reflow a child frame and return the status of the reflow. If the child + * is complete and it has next-in-flows, then delete the next-in-flows. + */ + nsReflowStatus ReflowChild(nsIFrame* aKidFrame, + nsIPresContext* aPresContext, + nsReflowMetrics& aDesiredSize, + const nsReflowState& aReflowState); + + /** + * Moves any frames on both the prev-in-flow's overflow list and the receiver's + * overflow to the receiver's child list. + * + * Resets the overlist pointers to nsnull, and updates the receiver's child + * count and content mapping. + * + * @return PR_TRUE if any frames were moved and PR_FALSE otherwise + */ + PRBool MoveOverflowToChildList(); + + /** + * Push aFromChild and its next siblings to the next-in-flow. Change the + * geometric parent of each frame that's pushed. If there is no next-in-flow + * the frames are placed on the overflow list (and the geometric parent is + * left unchanged). + * + * Updates the next-in-flow's child count. Does not update the + * pusher's child count. + * + * @param aFromChild the first child frame to push. It is disconnected from + * aPrevSibling + * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's + * an error to push a parent's first child frame + */ + void PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling); + + /** + * Append child list starting at aChild to this frame's child list. Used for + * processing of the overflow list. + * + * Updates this frame's child count and content mapping. + * + * @param aChild the beginning of the child list + * @param aSetParent if true each child's geometric (and content parent if + * they're the same) parent is set to this frame. + */ + void AppendChildren(nsIFrame* aChild, PRBool aSetParent = PR_TRUE); + + virtual void WillDeleteNextInFlowFrame(nsIFrame* aNextInFlow); + +#ifdef NS_DEBUG + /** + * Returns PR_TRUE if aChild is a child of this frame. + */ + PRBool IsChild(const nsIFrame* aChild) const; + + void DumpTree() const; +#endif + + nsIFrame* mFirstChild; + nsIFrame* mOverflowList; +}; + +#endif /* nsContainerFrame_h___ */ diff --git a/layout/html/base/src/nsFrame.cpp b/layout/html/base/src/nsFrame.cpp new file mode 100644 index 000000000000..36b0705e8c2c --- /dev/null +++ b/layout/html/base/src/nsFrame.cpp @@ -0,0 +1,1880 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "nsFrame.h" +#include "nsIContent.h" +#include "nsIAtom.h" +#include "nsString.h" +#include "nsIStyleContext.h" +#include "nsIView.h" +#include "nsIViewManager.h" +#include "nsIPresContext.h" +#include "nsCRT.h" +#include "nsGUIEvent.h" +#include "nsDOMEvent.h" +#include "nsStyleConsts.h" +#include "nsIPresShell.h" +#include "prlog.h" +#include "prprf.h" +#include +#include "nsIPtr.h" +#include "nsISizeOfHandler.h" + +#include "nsIDOMText.h" +#include "nsSelectionRange.h" +#include "nsDocument.h" +#include "nsIDeviceContext.h" +#include "nsIPresShell.h" + +// Some Misc #defines +#define SELECTION_DEBUG 0 +#define FORCE_SELECTION_UPDATE 1 +#define TRACKER_DEBUG 0 +#define CALC_DEBUG 0 + +// Tracker Data +#define kInsertInRemoveList 0 +#define kInsertInAddList 1 + +// Kludged Content stuff +nsIFrame * fFrameArray[1024]; +nsIContent * fContentArray[1024]; +PRInt32 fMax = -1; + +nsIContent * fTrackerContentArrayRemoveList[1024]; +PRInt32 fTrackerRemoveListMax = 0; +nsIContent * fTrackerContentArrayAddList[1024]; +PRInt32 fTrackerAddListMax = 0; + +nsIDocument *gDoc = nsnull; + +// [HACK] Foward Declarations +void RefreshContentFrames(nsIPresContext& aPresContext, nsIContent * aStartContent, nsIContent * aEndContent); +void ForceDrawFrame(nsFrame * aFrame); +void resetContentTrackers(); +void RefreshFromContentTrackers(nsIPresContext& aPresContext); +void addRangeToSelectionTrackers(nsIContent * aStartContent, nsIContent * aEndContent, PRUint32 aType); + + + + +// Initialize Global Selection Data +nsIFrame * nsFrame::mCurrentFrame = nsnull; +PRBool nsFrame::mDoingSelection = PR_FALSE; +PRBool nsFrame::mDidDrag = PR_FALSE; +PRInt32 nsFrame::mStartPos = 0; + +// Selection data is valid only from the Mouse Press to the Mouse Release +nsSelectionRange * nsFrame::mSelectionRange = nsnull; +nsISelection * nsFrame::mSelection = nsnull; + +nsSelectionPoint * nsFrame::mStartSelectionPoint = nsnull; +nsSelectionPoint * nsFrame::mEndSelectionPoint = nsnull; + +//---------------------------------------------------------------------- + +static PRBool gShowFrameBorders = PR_FALSE; + +NS_LAYOUT void nsIFrame::ShowFrameBorders(PRBool aEnable) +{ + gShowFrameBorders = aEnable; +} + +NS_LAYOUT PRBool nsIFrame::GetShowFrameBorders() +{ + return gShowFrameBorders; +} + +//////////////////////////////////////////////// +// Debug Listing Helper Class +//////////////////////////////////////////////// +class TableListFilter : public nsIListFilter +{ +private: + static char* kTagTable[]; + +public: + + TableListFilter() + {}; + + virtual ~TableListFilter() + {}; + + virtual PRBool OutputTag(nsAutoString *aTag) const + { + PRBool result = PR_FALSE; + if (nsnull!=aTag && 0!=aTag->Length()) + { + for (PRInt32 i=0; ; i++) + { + const char *tableTag = kTagTable[i]; + if (nsnull==tableTag) + break; + if (aTag->EqualsIgnoreCase(tableTag)) + { + result = PR_TRUE; + break; + } + } + } + return result; + }; + +}; + +char* TableListFilter::kTagTable[] = { + "table", + "tbody", "thead", "tfoot", + "tr", "td", "th", + "colgroup", "col", + //"caption", captions are left out because there's no caption frame + // to hang a decent output method on. + "" +}; + +NS_LAYOUT nsIListFilter * nsIFrame::GetFilter(nsString *aFilterName) +{ + nsIListFilter *result = nsnull; + if (nsnull!=aFilterName) + { + if (aFilterName->EqualsIgnoreCase("table")) + { + static nsIListFilter * tableListFilter; + if (nsnull==tableListFilter) + tableListFilter = new TableListFilter(); + result = tableListFilter; + } + } + return result; +} + +/** + * Note: the log module is created during library initialization which + * means that you cannot perform logging before then. + */ +PRLogModuleInfo* nsIFrame::gLogModule = PR_NewLogModule("frame"); + +static PRLogModuleInfo* gFrameVerifyTreeLogModuleInfo; + +static PRBool gFrameVerifyTreeEnable = PRBool(0x55); + +NS_LAYOUT PRBool +nsIFrame::GetVerifyTreeEnable() +{ +#ifdef NS_DEBUG + if (gFrameVerifyTreeEnable == PRBool(0x55)) { + if (nsnull == gFrameVerifyTreeLogModuleInfo) { + gFrameVerifyTreeLogModuleInfo = PR_NewLogModule("frameverifytree"); + gFrameVerifyTreeEnable = 0 != gFrameVerifyTreeLogModuleInfo->level; + printf("Note: frameverifytree is %sabled\n", + gFrameVerifyTreeEnable ? "en" : "dis"); + } + } +#endif + return gFrameVerifyTreeEnable; +} + +NS_LAYOUT void +nsIFrame::SetVerifyTreeEnable(PRBool aEnabled) +{ + gFrameVerifyTreeEnable = aEnabled; +} + +NS_LAYOUT PRLogModuleInfo* +nsIFrame::GetLogModuleInfo() +{ + return gLogModule; +} + +//---------------------------------------------------------------------- + +static NS_DEFINE_IID(kIFrameIID, NS_IFRAME_IID); + +nsresult +nsFrame::NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + nsIFrame* aParent) +{ + NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); + if (nsnull == aInstancePtrResult) { + return NS_ERROR_NULL_POINTER; + } + nsIFrame* it = new nsFrame(aContent, aParent); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + *aInstancePtrResult = it; + return NS_OK; +} + +void* nsFrame::operator new(size_t size) +{ + void* result = new char[size]; + + nsCRT::zero(result, size); + return result; +} + +nsFrame::nsFrame(nsIContent* aContent, nsIFrame* aParent) + : mContent(aContent), mContentParent(aParent), mGeometricParent(aParent) +{ + NS_ADDREF(mContent); + mState = NS_FRAME_FIRST_REFLOW; +} + +nsFrame::~nsFrame() +{ + NS_IF_RELEASE(mContent); + NS_IF_RELEASE(mStyleContext); + if (nsnull != mView) { + // Break association between view and frame + mView->Destroy(); + mView = nsnull; + } + + if (mStartSelectionPoint != nsnull) { + delete mStartSelectionPoint; + mStartSelectionPoint = nsnull; + } + if (mEndSelectionPoint != nsnull) { + delete mEndSelectionPoint; + mEndSelectionPoint = nsnull; + } +} + +///////////////////////////////////////////////////////////////////////////// +// nsISupports + +nsresult nsFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) +{ + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + static NS_DEFINE_IID(kClassIID, kIFrameIID); + if (aIID.Equals(kClassIID) || (aIID.Equals(kISupportsIID))) { + *aInstancePtr = (void*)this; + return NS_OK; + } + return NS_NOINTERFACE; +} + +nsrefcnt nsFrame::AddRef(void) +{ + NS_WARNING("not supported for frames"); + return 1; +} + +nsrefcnt nsFrame::Release(void) +{ + NS_WARNING("not supported for frames"); + return 1; +} + +///////////////////////////////////////////////////////////////////////////// +// nsIFrame + +NS_IMETHODIMP nsFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) +{ + if (nsnull != aChildList) { + NS_ERROR("not a container"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_METHOD nsFrame::DeleteFrame(nsIPresContext& aPresContext) +{ + //XXX Why is this done in nsFrame instead of some frame class + // that actually loads images? + aPresContext.StopLoadImage(this); + + //Set to prevent event dispatch during destruct + if (nsnull != mView) { + mView->SetClientData(nsnull); + } + + delete this; + return NS_OK; +} + +NS_IMETHODIMP +nsFrame::SizeOf(nsISizeOfHandler* aHandler) const +{ + aHandler->Add(sizeof(*this)); + SizeOfWithoutThis(aHandler); + return NS_OK; +} + +void +nsFrame::SizeOfWithoutThis(nsISizeOfHandler* aHandler) const +{ + // Note: style context's are accounted for via the style system's + // sizeof support + + // Note: content is accounted for via the content system's sizeof + // support +} + +NS_METHOD nsFrame::GetContent(nsIContent*& aContent) const +{ + if (nsnull != mContent) { + NS_ADDREF(mContent); + } + aContent = mContent; + return NS_OK; +} + +NS_METHOD nsFrame::GetContentIndex(PRInt32& aIndexInParent) const +{ + nsIContent* parent; + mContent->GetParent(parent); + if (nsnull != parent) { + parent->IndexOf(mContent, aIndexInParent); + NS_RELEASE(parent); + } + else { + aIndexInParent = 0; + } + return NS_OK; +} + +NS_METHOD nsFrame::GetStyleContext(nsIPresContext* aPresContext, + nsIStyleContext*& aStyleContext) +{ + if ((nsnull == mStyleContext) && (nsnull != aPresContext)) { + mStyleContext = aPresContext->ResolveStyleContextFor(mContent, mGeometricParent); // XXX should be content parent??? + if (nsnull != mStyleContext) { + DidSetStyleContext(aPresContext); + } + } + NS_IF_ADDREF(mStyleContext); + aStyleContext = mStyleContext; + return NS_OK; +} + +NS_METHOD nsFrame::SetStyleContext(nsIPresContext* aPresContext,nsIStyleContext* aContext) +{ + NS_PRECONDITION(nsnull != aContext, "null ptr"); + if (aContext != mStyleContext) { + NS_IF_RELEASE(mStyleContext); + if (nsnull != aContext) { + mStyleContext = aContext; + NS_ADDREF(aContext); + DidSetStyleContext(aPresContext); + } + } + + return NS_OK; +} + +// Subclass hook for style post processing +NS_METHOD nsFrame::DidSetStyleContext(nsIPresContext* aPresContext) +{ + return NS_OK; +} + +NS_METHOD nsFrame::GetStyleData(nsStyleStructID aSID, const nsStyleStruct*& aStyleStruct) const +{ + NS_ASSERTION(mStyleContext!=nsnull,"null style context"); + if (mStyleContext) { + aStyleStruct = mStyleContext->GetStyleData(aSID); + } else { + aStyleStruct = nsnull; + } + return NS_OK; +} + +// Geometric and content parent member functions + +NS_METHOD nsFrame::GetContentParent(nsIFrame*& aParent) const +{ + aParent = mContentParent; + return NS_OK; +} + +NS_METHOD nsFrame::SetContentParent(const nsIFrame* aParent) +{ + mContentParent = (nsIFrame*)aParent; + return NS_OK; +} + +NS_METHOD nsFrame::GetGeometricParent(nsIFrame*& aParent) const +{ + aParent = mGeometricParent; + return NS_OK; +} + +NS_METHOD nsFrame::SetGeometricParent(const nsIFrame* aParent) +{ + mGeometricParent = (nsIFrame*)aParent; + return NS_OK; +} + +// Bounding rect member functions + +NS_METHOD nsFrame::GetRect(nsRect& aRect) const +{ + aRect = mRect; + return NS_OK; +} + +NS_METHOD nsFrame::GetOrigin(nsPoint& aPoint) const +{ + aPoint.x = mRect.x; + aPoint.y = mRect.y; + return NS_OK; +} + +NS_METHOD nsFrame::GetSize(nsSize& aSize) const +{ + aSize.width = mRect.width; + aSize.height = mRect.height; + return NS_OK; +} + +NS_METHOD nsFrame::SetRect(const nsRect& aRect) +{ + MoveTo(aRect.x, aRect.y); + SizeTo(aRect.width, aRect.height); + return NS_OK; +} + +NS_METHOD nsFrame::MoveTo(nscoord aX, nscoord aY) +{ + mRect.x = aX; + mRect.y = aY; + + // Let the view know + if ((nsnull != mView) && (0 == (mState & NS_FRAME_IN_REFLOW))) { + // Position view relative to its parent, not relative to our + // parent frame (our parent frame may not have a view). + nsIView* parentWithView; + nsPoint origin; + GetOffsetFromView(origin, parentWithView); + nsIViewManager *vm; + mView->GetViewManager(vm); + vm->MoveViewTo(mView, origin.x, origin.y); + NS_RELEASE(vm); + } + + return NS_OK; +} + +NS_METHOD nsFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + mRect.width = aWidth; + mRect.height = aHeight; + + // Let the view know + if ((nsnull != mView) && (0 == (mState & NS_FRAME_IN_REFLOW))) { + nsIViewManager *vm; + mView->GetViewManager(vm); + vm->ResizeView(mView, aWidth, aHeight); + NS_RELEASE(vm); + } + return NS_OK; +} + +// Child frame enumeration + +NS_METHOD nsFrame::FirstChild(nsIFrame*& aFirstChild) const +{ + aFirstChild = nsnull; + return NS_OK; +} + +PRBool nsFrame::DisplaySelection(nsIPresContext& aPresContext, PRBool isOkToTurnOn) +{ + PRBool result = PR_FALSE; + + nsIPresShell *shell = aPresContext.GetShell(); + if (nsnull != shell) { + nsIDocument *doc = shell->GetDocument(); + if (nsnull != doc) { + result = doc->GetDisplaySelection(); + if (isOkToTurnOn && !result) { + doc->SetDisplaySelection(PR_TRUE); + result = PR_TRUE; + } + NS_RELEASE(doc); + } + NS_RELEASE(shell); + } + + return result; +} + +NS_METHOD nsFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + if (DisplaySelection(aPresContext) == PR_FALSE) + return NS_OK; + + PRBool clearAfterPaint = PR_FALSE; + + // Get Content + nsIContent * content; + GetContent(content); + PRInt32 n; + content->ChildCount(n); + if (n > 0) { + NS_RELEASE(content); + return NS_OK; + } + + nsIPresShell * shell = aPresContext.GetShell(); + nsIDocument * doc = shell->GetDocument(); + if (mSelectionRange == nsnull) { // Get Selection Object + nsISelection * selection; + doc->GetSelection(selection); + + mSelectionRange = selection->GetRange(); + + clearAfterPaint = PR_TRUE; + NS_RELEASE(selection); + } + + nsIContent * selStartContent = mSelectionRange->GetStartContent(); // ref counted + nsIContent * selEndContent = mSelectionRange->GetEndContent(); // ref counted + + if (doc->IsInRange(selStartContent, selEndContent, content)) { + nsRect rect; + GetRect(rect); + rect.width--; + rect.height--; + aRenderingContext.SetColor(NS_RGB(0,0,255)); + aRenderingContext.DrawRect(rect); + aRenderingContext.DrawLine(rect.x, rect.y, rect.x+rect.width, rect.y+rect.height); + aRenderingContext.DrawLine(rect.x, rect.y+rect.height, rect.x+rect.width, rect.y); + } + + if (clearAfterPaint) { + mSelectionRange = nsnull; + } + NS_IF_RELEASE(selStartContent); + NS_IF_RELEASE(selEndContent); + NS_RELEASE(content); + NS_RELEASE(doc); + NS_RELEASE(shell); + + return NS_OK; +} + +/** + * + */ +NS_METHOD nsFrame::HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + aEventStatus = nsEventStatus_eIgnore; + + if (nsnull != mContent) { + mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus); + } + + if (DisplaySelection(aPresContext) == PR_FALSE) { + if (aEvent->message != NS_MOUSE_LEFT_BUTTON_DOWN) { + return NS_OK; + } + } + + if(nsEventStatus_eConsumeNoDefault != aEventStatus) { + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + } else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection || + aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + // no-op + } else { + return NS_OK; + } + if (SELECTION_DEBUG) printf("Message: %d-------------------------------------------------------------\n",aEvent->message); + + + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + if (mDoingSelection) { + HandleRelease(aPresContext, aEvent, aEventStatus); + } + } else if (aEvent->message == NS_MOUSE_MOVE) { + mDidDrag = PR_TRUE; + HandleDrag(aPresContext, aEvent, aEventStatus); + if (SELECTION_DEBUG) printf("HandleEvent(Drag)::mSelectionRange %s\n", mSelectionRange->ToString()); + + } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + HandlePress(aPresContext, aEvent, aEventStatus); + } + } + + return NS_OK; +} + +/** + * Handles the Mouse Press Event for the frame + */ +NS_METHOD nsFrame::HandlePress(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (DisplaySelection(aPresContext, PR_TRUE) == PR_FALSE) + { + aEventStatus = nsEventStatus_eIgnore; + return NS_OK; + } + + nsFrame * currentFrame = this; + nsIPresShell * shell = aPresContext.GetShell(); + nsMouseEvent * mouseEvent = (nsMouseEvent *)aEvent; + + gDoc = shell->GetDocument(); + + nsISelection * selection; + gDoc->GetSelection(selection); + + + mSelectionRange = selection->GetRange(); + + PRUint32 actualOffset = 0; + + mDoingSelection = PR_TRUE; + mDidDrag = PR_FALSE; + mCurrentFrame = currentFrame; + + mStartPos = GetPosition(aPresContext, aEvent, currentFrame, actualOffset); + + // Click count is 1 + nsIContent * newContent; + currentFrame->GetContent(newContent); + + mCurrentFrame = currentFrame; + + if (mStartSelectionPoint == nsnull) { + mStartSelectionPoint = new nsSelectionPoint(nsnull, -1, PR_TRUE); + } + if (mEndSelectionPoint == nsnull) { + mEndSelectionPoint = new nsSelectionPoint(nsnull, -1, PR_TRUE); + } + + mSelectionRange->GetStartPoint(mStartSelectionPoint); + mSelectionRange->GetEndPoint(mEndSelectionPoint); + + resetContentTrackers(); + + // Get the Current Start and End Content Pointers + nsIContent * selStartContent = mSelectionRange->GetStartContent(); // ref counted + nsIContent * selEndContent = mSelectionRange->GetEndContent(); // ref counted + + if (SELECTION_DEBUG) printf("****** Shift[%s]\n", (mouseEvent->isShift?"Down":"Up")); + + PRBool inRange = gDoc->IsInRange(selStartContent, selEndContent, newContent); + + if (inRange) { + //resetContentTrackers(); + if (TRACKER_DEBUG) printf("Adding split range to removed selection. Shift[%s]\n", (mouseEvent->isShift?"Down":"Up")); + + if (mouseEvent->isShift) { + if (newContent == selStartContent && newContent == selEndContent) { + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); + } else { + if (selStartContent == newContent) { + // Trackers just do painting so add all the content nodes in the remove list + // even though you might think you shouldn't put the start content node there + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); + } else if (selEndContent == newContent) { + // just repaint the end content node + addRangeToSelectionTrackers(selEndContent, selEndContent, kInsertInAddList); + } else { + if (gDoc->IsBefore(newContent, selEndContent)) { + addRangeToSelectionTrackers(newContent, selEndContent, kInsertInRemoveList); + } else { + addRangeToSelectionTrackers(selEndContent, newContent, kInsertInRemoveList); + } + } + } + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); + } else { + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection + + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection + } + + } else if (gDoc->IsBefore(newContent, selStartContent)) { + if (mouseEvent->isShift) { + if (mStartSelectionPoint->IsAnchor()) { + if (SELECTION_DEBUG) printf("New Content is before, Start will now be end\n"); + + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection + + mEndSelectionPoint->SetPoint(selStartContent, mStartSelectionPoint->GetOffset(), PR_TRUE); + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); + + // End Point has changed + nsIContent * endcontent = mEndSelectionPoint->GetContent(); // ref counted + addRangeToSelectionTrackers(newContent, endcontent, kInsertInAddList); // add to selection + NS_RELEASE(endcontent); + } else { + if (SELECTION_DEBUG) printf("New Content is before, Appending to Beginning\n"); + addRangeToSelectionTrackers(newContent, selEndContent, kInsertInAddList); // add to selection + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); + } + } else { + if (SELECTION_DEBUG) printf("Adding full range to removed selection. (insert selection)\n"); + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection + + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection + } + } else { // Content is After End + + if (mouseEvent->isShift) { + if (selStartContent == nsnull && selEndContent == nsnull) { + // Shift Click without first clicking in the window + // is interpreted the same as just clicking in a window + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection + + } else if (mStartSelectionPoint->IsAnchor()) { + if (SELECTION_DEBUG) printf("New Content is after, Append new content\n"); + addRangeToSelectionTrackers(selEndContent, newContent, kInsertInAddList); // add to selection + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); + + } else { + if (SELECTION_DEBUG) printf("New Content is after, End will now be Start\n"); + + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection + mStartSelectionPoint->SetPoint(selEndContent, mEndSelectionPoint->GetOffset(), PR_TRUE); + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_FALSE); + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection + } + + } else { + if (TRACKER_DEBUG) printf("Adding full range to removed selection.\n"); + addRangeToSelectionTrackers(selStartContent, selEndContent, kInsertInRemoveList); // removed from selection + + mEndSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + + mStartSelectionPoint->SetPoint(newContent, mStartPos, PR_TRUE); + + addRangeToSelectionTrackers(newContent, newContent, kInsertInAddList); // add to selection + } + } + + + mSelectionRange->SetStartPoint(mStartSelectionPoint); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + + //if (selStartContent) printf("selStartContent count %d\n", selStartContent->Release()); + //if (selEndContent) printf("selEndContent count %d\n", selEndContent->Release()); + //if (newContent) printf("newContent count %d\n", newContent->Release()); + + NS_IF_RELEASE(selStartContent); + NS_IF_RELEASE(selEndContent); + NS_IF_RELEASE(newContent); + + RefreshFromContentTrackers(aPresContext); + + + if (SELECTION_DEBUG) printf("HandleEvent::mSelectionRange %s\n", mSelectionRange->ToString()); + + // Force Update + ForceDrawFrame(this); + + NS_RELEASE(shell); + NS_RELEASE(selection); + + aEventStatus = nsEventStatus_eIgnore; + return NS_OK; + +} + +NS_METHOD nsFrame::HandleDrag(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (DisplaySelection(aPresContext) == PR_FALSE) + { + aEventStatus = nsEventStatus_eIgnore; + return NS_OK; + } + + // Keep old start and end + nsIContent * startContent = mSelectionRange->GetStartContent(); // ref counted + nsIContent * endContent = mSelectionRange->GetEndContent(); // ref counted + + mDidDrag = PR_TRUE; + + //if (aFrame != nsnull) { + //printf("nsFrame::HandleDrag\n"); + + // Check to see if we have changed frame + if (this != mCurrentFrame) { + // We are in a new Frame! + if (SELECTION_DEBUG) printf("HandleDrag::Different Frame in selection!\n"); + + // Get Content for current Frame + nsIContent * currentContent; + mCurrentFrame->GetContent(currentContent); + + // Get Content for New Frame + nsIContent * newContent; + this->GetContent(newContent); + + // Check to see if we are still in the same Content + if (currentContent == newContent) { + if (SELECTION_DEBUG) printf("HandleDrag::New Frame, same content.\n"); + + AdjustPointsInSameContent(aPresContext, aEvent); + addRangeToSelectionTrackers(currentContent, currentContent, kInsertInAddList); + + } else if (gDoc->IsBefore(newContent, currentContent)) { + if (SELECTION_DEBUG) printf("HandleDrag::New Frame, is Before.\n"); + + resetContentTrackers(); + NewContentIsBefore(aPresContext, aEvent, newContent, currentContent, this); + + } else { // Content is AFTER + if (SELECTION_DEBUG) printf("HandleDrag::New Frame, is After.\n"); + + resetContentTrackers(); + NewContentIsAfter(aPresContext, aEvent, newContent, currentContent, this); + } + mCurrentFrame = this; + + NS_RELEASE(currentContent); + NS_RELEASE(newContent); + } else if ((nsnull != mStartSelectionPoint) && (nsnull != mEndSelectionPoint)) { + if (SELECTION_DEBUG) printf("HandleDrag::Same Frame.\n"); + + // Same Frame as before + //if (SELECTION_DEBUG) printf("\nSame Start: %s\n", mStartSelectionPoint->ToString()); + //if (SELECTION_DEBUG) printf("Same End: %s\n", mEndSelectionPoint->ToString()); + // [TODO] Uncomment these soon + + nsIContent * selStartContent = mStartSelectionPoint->GetContent(); + nsIContent * selEndContent = mEndSelectionPoint->GetContent(); + + if (selStartContent == selEndContent) { + if (SELECTION_DEBUG) printf("Start & End Frame are the same: \n"); + AdjustPointsInSameContent(aPresContext, aEvent); + } else { + if (SELECTION_DEBUG) printf("Start & End Frame are different: \n"); + + // Get Content for New Frame + nsIContent * newContent; + this->GetContent(newContent); + PRInt32 newPos = -1; + PRUint32 actualOffset = 0; + + newPos = GetPosition(aPresContext, aEvent, this, actualOffset); + + if (newContent == selStartContent) { + if (SELECTION_DEBUG) printf("New Content equals Start Content\n"); + mStartSelectionPoint->SetOffset(newPos); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } else if (newContent == selEndContent) { + if (SELECTION_DEBUG) printf("New Content equals End Content\n"); + mEndSelectionPoint->SetOffset(newPos); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } else { + if (SELECTION_DEBUG) printf("=====\n=====\n=====\n=====\n=====\n=====\n Should NOT be here.\n"); + } + + //if (SELECTION_DEBUG) printf("*Same Start: "+mStartSelectionPoint->GetOffset()+ + // " "+mStartSelectionPoint->IsAnchor()+ + // " End: "+mEndSelectionPoint->GetOffset() + + // " "+mEndSelectionPoint->IsAnchor()); + NS_RELEASE(newContent); + } + NS_IF_RELEASE(selStartContent); + NS_IF_RELEASE(selEndContent); + } + //} + + + NS_IF_RELEASE(startContent); + NS_IF_RELEASE(endContent); + + // Force Update + ForceDrawFrame(this); + //RefreshContentFrames(aPresContext, startContent, endContent); + RefreshFromContentTrackers(aPresContext); + + aEventStatus = nsEventStatus_eIgnore; + return NS_OK; +} + +NS_METHOD nsFrame::HandleRelease(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + mDoingSelection = PR_FALSE; + aEventStatus = nsEventStatus_eIgnore; + NS_IF_RELEASE(gDoc); + return NS_OK; +} + +//-------------------------------------------------------------------------- +//-- GetPosition +//-------------------------------------------------------------------------- +PRInt32 nsFrame::GetPosition(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIFrame * aNewFrame, + PRUint32& aAcutalContentOffset) { + + //PRInt32 offset; + //PRInt32 width; + //CalcCursorPosition(aPresContext, aEvent, aNewFrame, offset, width); + //offset += aNewFrame->GetContentOffset(); + + //return offset; + aAcutalContentOffset = 0; + return -1; +} + +/******************************************************** +* Adjusts the Starting and Ending TextPoint for a Range +*********************************************************/ +void nsFrame::AdjustPointsInNewContent(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIFrame * aNewFrame) { + PRUint32 actualOffset = 0; + + // Get new Cursor Poition in the new content + PRInt32 newPos = GetPosition(aPresContext, aEvent, aNewFrame, actualOffset); + + if (mStartSelectionPoint->IsAnchor()) { + if (newPos == mStartSelectionPoint->GetOffset()) { + mEndSelectionPoint->SetOffset(newPos); + mEndSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } else if (newPos < mStartSelectionPoint->GetOffset()) { + mEndSelectionPoint->SetOffset(mStartSelectionPoint->GetOffset()); + mEndSelectionPoint->SetAnchor(PR_TRUE); + mStartSelectionPoint->SetOffset(newPos); + mStartSelectionPoint->SetAnchor(PR_FALSE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + } else { + mEndSelectionPoint->SetOffset(newPos); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } + } else if (mEndSelectionPoint->IsAnchor()) { + int endPos = mEndSelectionPoint->GetOffset(); + if (newPos == mEndSelectionPoint->GetOffset()) { + mStartSelectionPoint->SetOffset(newPos); + mStartSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } else if (newPos > mEndSelectionPoint->GetOffset()) { + mEndSelectionPoint->SetOffset(newPos); + mEndSelectionPoint->SetAnchor(PR_FALSE); + mStartSelectionPoint->SetOffset(endPos); + mStartSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + } else { + mStartSelectionPoint->SetOffset(newPos); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } + } else { + // [TODO] Should get here + // throw exception + if (SELECTION_DEBUG) printf("--\n--\n--\n--\n--\n--\n--\n Should be here. #102\n"); + //return; + } +} + +/******************************************************** +* Adjusts the Starting and Ending TextPoint for a Range +*********************************************************/ +void nsFrame::AdjustPointsInSameContent(nsIPresContext& aPresContext, + nsGUIEvent * aEvent) { + PRUint32 actualOffset = 0; + + // Get new Cursor Poition in the same content + PRInt32 newPos = GetPosition(aPresContext, aEvent, mCurrentFrame, actualOffset); + //newPos += actualOffset; + if (SELECTION_DEBUG) printf("AdjustTextPointsInSameContent newPos: %d\n", newPos); + + if (mStartSelectionPoint->IsAnchor()) { + if (newPos == mStartSelectionPoint->GetOffset()) { + mEndSelectionPoint->SetOffset(newPos); + mEndSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } else if (newPos < mStartSelectionPoint->GetOffset()) { + mStartSelectionPoint->SetOffset(newPos); + mStartSelectionPoint->SetAnchor(PR_FALSE); + mEndSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + } else { + mEndSelectionPoint->SetAnchor(PR_FALSE); + mEndSelectionPoint->SetOffset(newPos); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } + } else if (mEndSelectionPoint->IsAnchor()) { + if (newPos == mEndSelectionPoint->GetOffset()) { + mStartSelectionPoint->SetOffset(newPos); + mStartSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } else if (newPos > mEndSelectionPoint->GetOffset()) { + mEndSelectionPoint->SetOffset(newPos); + mEndSelectionPoint->SetAnchor(PR_FALSE); + mStartSelectionPoint->SetAnchor(PR_TRUE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + } else { + mStartSelectionPoint->SetAnchor(PR_FALSE); + mStartSelectionPoint->SetOffset(newPos); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } + } else { + // [TODO] Should get here + // throw exception + if (SELECTION_DEBUG) printf("--\n--\n--\n--\n--\n--\n--\n Should be here. #101\n"); + //return; + } + + if (SELECTION_DEBUG) printf("Start %s End %s\n", mStartSelectionPoint->ToString(), mEndSelectionPoint->ToString()); + //} +} + +NS_METHOD nsFrame::GetCursorAndContentAt(nsIPresContext& aPresContext, + const nsPoint& aPoint, + nsIFrame** aFrame, + nsIContent** aContent, + PRInt32& aCursor) +{ + *aContent = mContent; + aCursor = NS_STYLE_CURSOR_INHERIT; + return NS_OK; +} + +// Resize and incremental reflow +NS_METHOD +nsFrame::GetFrameState(nsFrameState& aResult) +{ + aResult = mState; + return NS_OK; +} + +NS_METHOD +nsFrame::SetFrameState(nsFrameState aNewState) +{ + mState = aNewState; + return NS_OK; +} + +// Resize reflow methods + +NS_METHOD +nsFrame::WillReflow(nsIPresContext& aPresContext) +{ + NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, + ("WillReflow: oldState=%x", mState)); + mState |= NS_FRAME_IN_REFLOW; + return NS_OK; +} + +NS_METHOD +nsFrame::DidReflow(nsIPresContext& aPresContext, + nsDidReflowStatus aStatus) +{ + NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, + ("nsFrame::DidReflow: aStatus=%d", aStatus)); + if (NS_FRAME_REFLOW_FINISHED == aStatus) { + mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW); + + if (nsnull != mView) { + // Position and size view relative to its parent, not relative to our + // parent frame (our parent frame may not have a view). + nsIView* parentWithView; + nsPoint origin; + GetOffsetFromView(origin, parentWithView); + nsIViewManager *vm; + mView->GetViewManager(vm); + vm->ResizeView(mView, mRect.width, mRect.height); + vm->MoveViewTo(mView, origin.x, origin.y); + NS_RELEASE(vm); + } + } + return NS_OK; +} + +NS_METHOD nsFrame::Reflow(nsIPresContext& aPresContext, + nsReflowMetrics& aDesiredSize, + const nsReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + aDesiredSize.width = 0; + aDesiredSize.height = 0; + aDesiredSize.ascent = 0; + aDesiredSize.descent = 0; + if (nsnull != aDesiredSize.maxElementSize) { + aDesiredSize.maxElementSize->width = 0; + aDesiredSize.maxElementSize->height = 0; + } + aStatus = NS_FRAME_COMPLETE; + + if (eReflowReason_Incremental == aReflowState.reason) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + return NS_OK; +} + +NS_METHOD nsFrame::ContentChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsISupports* aSubContent) +{ + return NS_OK; +} + +NS_IMETHODIMP nsFrame::AttributeChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsIAtom* aAttribute, + PRInt32 aHint) +{ + return NS_OK; +} + +NS_METHOD nsFrame::GetReflowMetrics(nsIPresContext& aPresContext, + nsReflowMetrics& aMetrics) +{ + aMetrics.width = mRect.width; + aMetrics.height = mRect.height; + aMetrics.ascent = mRect.height; + aMetrics.descent = 0; + return NS_OK; +} + +// Flow member functions + +NS_METHOD nsFrame::IsSplittable(nsSplittableType& aIsSplittable) const +{ + aIsSplittable = NS_FRAME_NOT_SPLITTABLE; + return NS_OK; +} + +NS_METHOD nsFrame::CreateContinuingFrame(nsIPresContext& aPresContext, + nsIFrame* aParent, + nsIStyleContext* aStyleContext, + nsIFrame*& aContinuingFrame) +{ + NS_ERROR("not splittable"); + aContinuingFrame = nsnull; + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::GetPrevInFlow(nsIFrame*& aPrevInFlow) const +{ + aPrevInFlow = nsnull; + return NS_OK; +} + +NS_METHOD nsFrame::SetPrevInFlow(nsIFrame*) +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::GetNextInFlow(nsIFrame*& aNextInFlow) const +{ + aNextInFlow = nsnull; + return NS_OK; +} + +NS_METHOD nsFrame::SetNextInFlow(nsIFrame*) +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::AppendToFlow(nsIFrame* aAfterFrame) +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::PrependToFlow(nsIFrame* aBeforeFrame) +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::RemoveFromFlow() +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::BreakFromPrevFlow() +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_METHOD nsFrame::BreakFromNextFlow() +{ + NS_ERROR("not splittable"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// Associated view object +NS_METHOD nsFrame::GetView(nsIView*& aView) const +{ + aView = mView; + return NS_OK; +} + +NS_METHOD nsFrame::SetView(nsIView* aView) +{ + nsresult rv; + + if (nsnull != aView) { + mView = aView; + aView->SetClientData(this); + rv = NS_OK; + } + else + rv = NS_OK; + + return rv; +} + +// Find the first geometric parent that has a view +NS_METHOD nsFrame::GetParentWithView(nsIFrame*& aParent) const +{ + aParent = mGeometricParent; + + while (nsnull != aParent) { + nsIView* parView; + + aParent->GetView(parView); + if (nsnull != parView) { + break; + } + aParent->GetGeometricParent(aParent); + } + + return NS_OK; +} + +// Returns the offset from this frame to the closest geometric parent that +// has a view. Also returns the containing view or null in case of error +NS_METHOD nsFrame::GetOffsetFromView(nsPoint& aOffset, nsIView*& aView) const +{ + nsIFrame* frame = (nsIFrame*)this; + + aView = nsnull; + aOffset.MoveTo(0, 0); + do { + nsPoint origin; + + frame->GetOrigin(origin); + aOffset += origin; + frame->GetGeometricParent(frame); + if (nsnull != frame) { + frame->GetView(aView); + } + } while ((nsnull != frame) && (nsnull == aView)); + return NS_OK; +} + +NS_METHOD nsFrame::GetWindow(nsIWidget*& aWindow) const +{ + nsIFrame* frame = (nsIFrame*)this; + + aWindow = nsnull; + while (nsnull != frame) { + nsIView* view; + + frame->GetView(view); + if (nsnull != view) { + view->GetWidget(aWindow); + if (nsnull != aWindow) { + break; + } + } + frame->GetParentWithView(frame); + } + NS_POSTCONDITION(nsnull != aWindow, "no window in frame tree"); + return NS_OK; +} + +void +nsFrame::Invalidate(const nsRect& aDamageRect, + PRBool aImmediate) const +{ + nsIViewManager* viewManager = nsnull; + + PRUint32 flags = aImmediate ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC; + if (nsnull != mView) { + mView->GetViewManager(viewManager); + viewManager->UpdateView(mView, aDamageRect, flags); + + } else { + nsRect rect(aDamageRect); + nsPoint offset; + nsIView* view; + + GetOffsetFromView(offset, view); + NS_ASSERTION(nsnull != view, "no view"); + rect += offset; + view->GetViewManager(viewManager); + viewManager->UpdateView(view, rect, flags); + } + + NS_IF_RELEASE(viewManager); +} + +// Style sizing methods +NS_METHOD nsFrame::IsPercentageBase(PRBool& aBase) const +{ + const nsStylePosition* position; + GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)position); + if (position->mPosition != NS_STYLE_POSITION_NORMAL) { + aBase = PR_TRUE; + } + else { + const nsStyleDisplay* display; + GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)display); + if ((display->mDisplay == NS_STYLE_DISPLAY_BLOCK) || + (display->mDisplay == NS_STYLE_DISPLAY_LIST_ITEM)) { + aBase = PR_TRUE; + } + else { + aBase = PR_FALSE; + } + } + return NS_OK; +} + +NS_METHOD nsFrame::GetAutoMarginSize(PRUint8 aSide, nscoord& aSize) const +{ + aSize = 0; // XXX probably not right, subclass override? + return NS_OK; +} + + +// Sibling pointer used to link together frames + +NS_METHOD nsFrame::GetNextSibling(nsIFrame*& aNextSibling) const +{ + aNextSibling = mNextSibling; + return NS_OK; +} + +NS_METHOD nsFrame::SetNextSibling(nsIFrame* aNextSibling) +{ + mNextSibling = aNextSibling; + return NS_OK; +} + +// Transparency query +NS_METHOD nsFrame::IsTransparent(PRBool& aTransparent) const +{ + //XXX this needs to be overridden in just about every leaf class? MMP + aTransparent = PR_TRUE; + return NS_OK; +} + +NS_METHOD nsFrame::Scrolled(nsIView *aView) +{ + return NS_OK; +} + +// Debugging +NS_METHOD nsFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const +{ + // if a filter is present, only output this frame if the filter says we should + nsIAtom* tag; + nsAutoString tagString; + mContent->GetTag(tag); + if (tag != nsnull) + { + tag->ToString(tagString); + NS_RELEASE(tag); + } + if ((nsnull==aFilter) || (PR_TRUE==aFilter->OutputTag(&tagString))) + { + // Indent + for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out); + + // Output the tag and rect + ListTag(out); + if (nsnull != mView) { + fprintf(out, " [view=%p]", mView); + } + fputs(" ", out); + out << mRect; + if (0 != mState) { + fprintf(out, " [state=%08x]", mState); + } + fputs("<>\n", out); + } + return NS_OK; +} + +// Output the frame's tag +NS_METHOD nsFrame::ListTag(FILE* out) const +{ + nsIAtom* tag; + mContent->GetTag(tag); + if (tag != nsnull) { + nsAutoString buf; + tag->ToString(buf); + fputs(buf, out); + NS_RELEASE(tag); + } + PRInt32 contentIndex; + + GetContentIndex(contentIndex); + fprintf(out, "(%d)@%p", contentIndex, this); + return NS_OK; +} + +NS_METHOD nsFrame::VerifyTree() const +{ + NS_ASSERTION(0 == (mState & NS_FRAME_IN_REFLOW), "frame is in reflow"); + return NS_OK; +} + +//----------------------------------------------------------------------------------- + +/******************************************************** +* Handles a when the cursor enters new content that is before +* the content that the cursor is currently in +*********************************************************/ +void nsFrame::NewContentIsBefore(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIContent * aNewContent, + nsIContent * aCurrentContent, + nsIFrame * aNewFrame) +{ + if (SELECTION_DEBUG) printf("New Frame, New content is before.\n"); + // Dragging mouse to the left or backward in content + // + // 1) The cursor is being dragged backward in the content + // and the mouse is "after" the anchor in the content + // and the current piece of content is being removed from the selection + // + // This section cover two cases: + // 2) The cursor is being dragged backward in the content + // and the mouse is "before" the anchor in the content + // and each new piece of content is being added to the selection + + if ((nsnull == mStartSelectionPoint) || (nsnull == mEndSelectionPoint)) { + return; + } + nsIPresShell * shell = aPresContext.GetShell(); + nsIDocument * doc = shell->GetDocument(); + nsIContent * selStartContent = mStartSelectionPoint->GetContent(); + nsIContent * selEndContent = mEndSelectionPoint->GetContent(); + + PRBool inRange = doc->IsInRange(selStartContent, selEndContent, aNewContent); + + // Check to see if the new content is in the selection + if (inRange) { + + // Case #1 - Remove Current Content from Selection (at End) + if (SELECTION_DEBUG) printf("Case #1 - (Before) New Content is in selected Range.\n"); + + + if (aNewContent == selStartContent) { + // [TODO] This is where we might have to delete from end to new content + + // Returns the new End Point, if Start and End are on the + // same content then End Point's Cursor is set to Start's + mEndSelectionPoint->SetContent(selStartContent); + AdjustPointsInNewContent(aPresContext, aEvent, aNewFrame); + + } else { + PRUint32 actualOffset = 0; + PRInt32 newPos = GetPosition(aPresContext, aEvent, aNewFrame, actualOffset); + mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + } + + // The current content is being removed from Selection + addRangeToSelectionTrackers(aNewContent, aCurrentContent, kInsertInRemoveList); + } else { + // Case #2 - Add new content to selection (At Start) + if (SELECTION_DEBUG) printf("Case #2 - (Before) New Content is NOT in selected Range. Moving Start Backward.\n"); + + PRUint32 actualOffset = 0; + PRInt32 newPos = GetPosition(aPresContext, aEvent, aNewFrame, actualOffset); + + // Create new TextPoint and move Start Point backward + mStartSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); // position is set correctly in adjustTextPoints + mSelectionRange->SetStartPoint(mStartSelectionPoint); + + // The New Content is added to Tracker + addRangeToSelectionTrackers(aNewContent, aCurrentContent, kInsertInAddList); + } + NS_RELEASE(selStartContent); + NS_RELEASE(selEndContent); + NS_RELEASE(doc); + NS_RELEASE(shell); +} + + /******************************************************** +* Refreshes each content's frame +*********************************************************/ +void RefreshAllContentFrames(nsIFrame * aFrame, nsIContent * aContent) +{ + nsIContent* frameContent; + aFrame->GetContent(frameContent); + if (frameContent == aContent) { + ForceDrawFrame((nsFrame *)aFrame); + } + NS_RELEASE(frameContent); + + aFrame->FirstChild(aFrame); + while (aFrame) { + RefreshAllContentFrames(aFrame, aContent); + aFrame->GetNextSibling(aFrame); + } +} + +/******************************************************** +* Refreshes each content's frame +*********************************************************/ +void RefreshContentFrames(nsIPresContext& aPresContext, + nsIContent * aStartContent, + nsIContent * aEndContent) +{ + //------------------------------------- + // Undraw all the current selected frames + // XXX Kludge for now + nsIPresShell * shell = aPresContext.GetShell(); + nsIFrame * rootFrame = shell->GetRootFrame(); + + PRBool foundStart = PR_FALSE; + for (PRInt32 i=0;iFindFrameWithContent(node)); + RefreshAllContentFrames(rootFrame, node); + if (aStartContent == aEndContent) { + break; + } + } else if (foundStart) { + //ForceDrawFrame((nsFrame *)shell->FindFrameWithContent(node)); + RefreshAllContentFrames(rootFrame, node); + } else if (aEndContent == node) { + //ForceDrawFrame((nsFrame *)shell->FindFrameWithContent(node)); + RefreshAllContentFrames(rootFrame, node); + break; + } + } + //NS_RELEASE(rootFrame); + NS_RELEASE(shell); + //------------------------------------- +} + +/******************************************************** +* Handles a when the cursor enters new content that is After +* the content that the cursor is currently in +*********************************************************/ +void nsFrame::NewContentIsAfter(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIContent * aNewContent, + nsIContent * aCurrentContent, + nsIFrame * aNewFrame) +{ + if (SELECTION_DEBUG) printf("New Frame, New content is after.\n"); + + + // Dragging Mouse to the Right + // + // 3) The cursor is being dragged foward in the content + // and the mouse is "before" the anchor in the content + // and the current piece of content is being removed from the selection + // + // This section cover two cases: + // 4) The cursor is being dragged foward in the content + // and the mouse is "after" the anchor in the content + // and each new piece of content is being added to the selection + // + + // Check to see if the new content is in the selection + nsIPresShell * shell = aPresContext.GetShell(); + nsIDocument * doc = shell->GetDocument(); + nsIContent * selStartContent = mStartSelectionPoint->GetContent(); + nsIContent * selEndContent = mEndSelectionPoint->GetContent(); + + PRBool inRange = doc->IsInRange(selStartContent, selEndContent, aNewContent); + + if (inRange) { + // Case #3 - Remove Content (from Start) + if (SELECTION_DEBUG) printf("Case #3 - (After) New Content is in selected Range.\n"); + + // Remove Current Content in Tracker, but leave New Content in Selection + addRangeToSelectionTrackers(mStartSelectionPoint->GetContent(), aNewContent, kInsertInRemoveList); + + PRUint32 actualOffset = 0; + // [TODO] Always get nearest Text content + PRInt32 newPos = GetPosition(aPresContext, aEvent, aNewFrame, actualOffset); + + // Check to see if the new Content is the same as the End Point's + if (aNewContent == selEndContent) { + if (SELECTION_DEBUG) printf("New Content matches End Point\n"); + + mStartSelectionPoint->SetContent(aNewContent); + AdjustPointsInNewContent(aPresContext, aEvent, aNewFrame); + + } else { + if (SELECTION_DEBUG) printf("New Content does NOT matches End Point\n"); + mStartSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); + mSelectionRange->SetStartPoint(mStartSelectionPoint); + } + + } else { + if (SELECTION_DEBUG) + printf("Case #4 - (After) Adding New Content\n"); + + // Case #2 - Adding Content (at End) + PRUint32 actualOffset = 0; + // The new content is not in the selection + PRInt32 newPos = GetPosition(aPresContext, aEvent, aNewFrame, actualOffset); + + // Check to see if we need to create a new SelectionPoint and add it + // or do we simply move the existing start or end point + if (selStartContent == selEndContent) { + if (SELECTION_DEBUG) printf("Case #4 - Start & End Content the Same\n"); + // Move start or end point + // Get new Cursor Poition in the new content + + if (mStartSelectionPoint->IsAnchor()) { + if (SELECTION_DEBUG) printf("Case #4 - Start is Anchor\n"); + // Since the Start is the Anchor just adjust the end + + // XXX Note this includes the current End point (it should be end->nextContent) + addRangeToSelectionTrackers(selEndContent, aNewContent, kInsertInAddList); + + mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); + mSelectionRange->SetEndPoint(mEndSelectionPoint); + + } else { + if (SELECTION_DEBUG) printf("Case #4 - Start is NOT Anchor\n"); + // Because End was the anchor, we need to set the Start Point to + // the End's Offset and set it to be the new anchor + addRangeToSelectionTrackers(selStartContent,selEndContent, kInsertInRemoveList); + + int endPos = mEndSelectionPoint->GetOffset(); + mStartSelectionPoint->SetOffset(endPos); + mStartSelectionPoint->SetAnchor(PR_TRUE); + + // The Start point was being moved so when it jumped to the new frame + // we needed to make it the new End Point + mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + + // The Content values have changed so go get new contents + nsIContent * startContent = mStartSelectionPoint->GetContent(); + nsIContent * endContent = mEndSelectionPoint->GetContent(); + addRangeToSelectionTrackers(startContent, endContent, kInsertInRemoveList); + NS_RELEASE(startContent); + NS_RELEASE(endContent); + } + } else { + if (SELECTION_DEBUG) printf("Case #4 - Start & End Content NOT the Same\n"); + // Adjust the end point + mEndSelectionPoint->SetPoint(aNewContent, newPos, PR_FALSE); + mSelectionRange->SetRange(mStartSelectionPoint, mEndSelectionPoint); + + // Add New Content to Selection Tracker + // The Content values have changed so go get new contents + // NOTE: selEndContent holds the "old" end content pointer + // and endContent hold the "new" content pointer + nsIContent * endContent = mEndSelectionPoint->GetContent(); + addRangeToSelectionTrackers(selEndContent, endContent, kInsertInAddList); + NS_RELEASE(endContent); + } + } + + NS_RELEASE(selStartContent); + NS_RELEASE(selEndContent); + NS_RELEASE(doc); + NS_RELEASE(shell); +} + +/** + * + */ +void ForceDrawFrame(nsFrame * aFrame)//, PRBool) +{ + if (aFrame == nsnull) { + return; + } + nsRect rect; + nsIView * view; + nsPoint pnt; + aFrame->GetOffsetFromView(pnt, view); + aFrame->GetRect(rect); + rect.x = pnt.x; + rect.y = pnt.y; + if (view != nsnull) { + nsIViewManager * viewMgr; + view->GetViewManager(viewMgr); + if (viewMgr != nsnull) { + viewMgr->UpdateView(view, rect, 0); + NS_RELEASE(viewMgr); + } + //viewMgr->UpdateView(view, rect, NS_VMREFRESH_DOUBLE_BUFFER | NS_VMREFRESH_IMMEDIATE); + } + +} + + +///////////////////////////////////////////////// +// Selection Tracker Methods +///////////////////////////////////////////////// + +//---------------------------- +// +//---------------------------- +void resetContentTrackers() { + PRInt32 i; + for (i=0;iGetRootFrame(); + for (i=0;iFindFrameWithContent(fTrackerContentArrayRemoveList[i])); + if (SELECTION_DEBUG) printf("ForceDrawFrame (remove) content 0x%X\n", fTrackerContentArrayRemoveList[i]); + } + for (i=0;iFindFrameWithContent(fTrackerContentArrayAddList[i]); + //ForceDrawFrame((nsFrame *)frame); + RefreshAllContentFrames(rootFrame, fTrackerContentArrayAddList[i]); + if (SELECTION_DEBUG) printf("ForceDrawFrame (add) content 0x%X\n", fTrackerContentArrayAddList[i]); + } + NS_RELEASE(shell); + resetContentTrackers(); +} + +//---------------------------- +// +//---------------------------- +void addRangeToSelectionTrackers(nsIContent * aStartContent, nsIContent * aEndContent, PRUint32 aType) +{ + if (aStartContent == nsnull || aEndContent == nsnull) { + return; + } + nsIContent ** contentList = (aType == kInsertInRemoveList?fTrackerContentArrayRemoveList:fTrackerContentArrayAddList); + int inx = (aType == kInsertInRemoveList?fTrackerRemoveListMax:fTrackerAddListMax); + + NS_ADDREF(aStartContent); + nsIContent * contentPtr = aStartContent; + while (contentPtr != aEndContent) { + contentList[inx++] = contentPtr; + contentPtr = gDoc->GetNextContent(contentPtr); // This does an AddRef + } + contentList[inx++] = aEndContent; + + if (SELECTION_DEBUG) printf("Adding to %s %d\n", (aType == kInsertInRemoveList?"Remove Array":"Add Array"), inx); + + if (aType == kInsertInRemoveList) { + fTrackerRemoveListMax = inx; + } else { // kInsertInAddList + fTrackerAddListMax = inx; + } +} + + +#ifdef NS_DEBUG +static void +GetTagName(nsFrame* aFrame, nsIContent* aContent, PRIntn aResultSize, + char* aResult) +{ + char namebuf[40]; + namebuf[0] = 0; + if (nsnull != aContent) { + nsIAtom* tag; + aContent->GetTag(tag); + if (nsnull != tag) { + nsAutoString tmp; + tag->ToString(tmp); + tmp.ToCString(namebuf, sizeof(namebuf)); + NS_RELEASE(tag); + } + } + PR_snprintf(aResult, aResultSize, "%s@%p", namebuf, aFrame); +} + +void +nsFrame::Trace(const char* aMethod, PRBool aEnter) +{ + if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { + char tagbuf[40]; + GetTagName(this, mContent, sizeof(tagbuf), tagbuf); + PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod); + } +} + +void +nsFrame::Trace(const char* aMethod, PRBool aEnter, nsReflowStatus aStatus) +{ + if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { + char tagbuf[40]; + GetTagName(this, mContent, sizeof(tagbuf), tagbuf); + PR_LogPrint("%s: %s %s, status=%scomplete%s", + tagbuf, aEnter ? "enter" : "exit", aMethod, + NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "", + (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : ""); + } +} + +void +nsFrame::TraceMsg(const char* aFormatString, ...) +{ + if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { + // Format arguments into a buffer + char argbuf[200]; + va_list ap; + va_start(ap, aFormatString); + PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap); + va_end(ap); + + char tagbuf[40]; + GetTagName(this, mContent, sizeof(tagbuf), tagbuf); + PR_LogPrint("%s: %s", tagbuf, argbuf); + } +} +#endif diff --git a/layout/html/base/src/nsFrame.h b/layout/html/base/src/nsFrame.h new file mode 100644 index 000000000000..8e75f3215794 --- /dev/null +++ b/layout/html/base/src/nsFrame.h @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef nsFrame_h___ +#define nsFrame_h___ + +#include "nsIFrame.h" +#include "nsRect.h" +#include "nsISelection.h" +#include "nsSelectionRange.h" +#include "nsSelectionPoint.h" +#include "prlog.h" + +/** + * nsFrame logging constants. We redefine the nspr + * PRLogModuleInfo.level field to be a bitfield. Each bit controls a + * specific type of logging. Each logging operation has associated + * inline methods defined below. + */ +#define NS_FRAME_TRACE_CALLS 0x1 +#define NS_FRAME_TRACE_PUSH_PULL 0x2 +#define NS_FRAME_TRACE_CHILD_REFLOW 0x4 +#define NS_FRAME_TRACE_NEW_FRAMES 0x8 + +#define NS_FRAME_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit)) + +#ifdef NS_DEBUG +#define NS_FRAME_LOG(_bit,_args) \ + PR_BEGIN_MACRO \ + if (NS_FRAME_LOG_TEST(nsIFrame::GetLogModuleInfo(),_bit)) { \ + PR_LogPrint _args; \ + } \ + PR_END_MACRO +#else +#define NS_FRAME_LOG(_bit,_args) +#endif + +// XXX Need to rework this so that logging is free when it's off +#ifdef NS_DEBUG +#define NS_FRAME_TRACE_IN(_method) Trace(_method, PR_TRUE) + +#define NS_FRAME_TRACE_OUT(_method) Trace(_method, PR_FALSE) + +// XXX remove me +#define NS_FRAME_TRACE_MSG(_bit,_args) \ + PR_BEGIN_MACRO \ + if (NS_FRAME_LOG_TEST(nsIFrame::GetLogModuleInfo(),_bit)) { \ + TraceMsg _args; \ + } \ + PR_END_MACRO + +#define NS_FRAME_TRACE(_bit,_args) \ + PR_BEGIN_MACRO \ + if (NS_FRAME_LOG_TEST(nsIFrame::GetLogModuleInfo(),_bit)) { \ + TraceMsg _args; \ + } \ + PR_END_MACRO + +#define NS_FRAME_TRACE_REFLOW_IN(_method) Trace(_method, PR_TRUE) + +#define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) \ + Trace(_method, PR_FALSE, _status) + +#else +#define NS_FRAME_TRACE(_bits,_args) +#define NS_FRAME_TRACE_IN(_method) +#define NS_FRAME_TRACE_OUT(_method) +#define NS_FRAME_TRACE_MSG(_bits,_args) +#define NS_FRAME_TRACE_REFLOW_IN(_method) +#define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) +#endif + +//---------------------------------------------------------------------- + +// Implementation of a simple frame with no children and that isn't splittable +class nsFrame : public nsIFrame +{ +public: + /** + * Create a new "empty" frame that maps a given piece of content into a + * 0,0 area. + */ + static nsresult NewFrame(nsIFrame** aInstancePtrResult, + nsIContent* aContent, + nsIFrame* aParent); + + // Overloaded new operator. Initializes the memory to 0 + void* operator new(size_t size); + + // nsISupports + NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + + // nsIFrame + NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); + NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const; + NS_IMETHOD GetContent(nsIContent*& aContent) const; + NS_IMETHOD GetContentIndex(PRInt32& aIndexInParent) const; + NS_IMETHOD GetStyleContext(nsIPresContext* aContext, + nsIStyleContext*& aStyleContext); + NS_IMETHOD SetStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aContext); + NS_IMETHOD GetStyleData(nsStyleStructID aSID, + const nsStyleStruct*& aStyleStruct) const; + NS_IMETHOD GetContentParent(nsIFrame*& aParent) const; + NS_IMETHOD SetContentParent(const nsIFrame* aParent); + NS_IMETHOD GetGeometricParent(nsIFrame*& aParent) const; + NS_IMETHOD SetGeometricParent(const nsIFrame* aParent); + NS_IMETHOD GetRect(nsRect& aRect) const; + NS_IMETHOD GetOrigin(nsPoint& aPoint) const; + NS_IMETHOD GetSize(nsSize& aSize) const; + NS_IMETHOD SetRect(const nsRect& aRect); + NS_IMETHOD MoveTo(nscoord aX, nscoord aY); + NS_IMETHOD SizeTo(nscoord aWidth, nscoord aHeight); + NS_IMETHOD FirstChild(nsIFrame*& aFirstChild) const; + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + NS_IMETHOD GetCursorAndContentAt(nsIPresContext& aPresContext, + const nsPoint& aPoint, + nsIFrame** aFrame, + nsIContent** aContent, + PRInt32& aCursor); + NS_IMETHOD GetFrameState(nsFrameState& aResult); + NS_IMETHOD SetFrameState(nsFrameState aNewState); + NS_IMETHOD WillReflow(nsIPresContext& aPresContext); + NS_IMETHOD DidReflow(nsIPresContext& aPresContext, + nsDidReflowStatus aStatus); + NS_IMETHOD Reflow(nsIPresContext& aPresContext, + nsReflowMetrics& aDesiredSize, + const nsReflowState& aReflowState, + nsReflowStatus& aStatus); + + NS_IMETHOD ContentChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsISupports* aSubContent); + NS_IMETHOD AttributeChanged(nsIPresContext* aPresContext, + nsIContent* aChild, + nsIAtom* aAttribute, + PRInt32 aHint); + NS_IMETHOD GetReflowMetrics(nsIPresContext& aPresContext, + nsReflowMetrics& aMetrics); + NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; + NS_IMETHOD CreateContinuingFrame(nsIPresContext& aPresContext, + nsIFrame* aParent, + nsIStyleContext* aStyleContext, + nsIFrame*& aContinuingFrame); + NS_IMETHOD GetPrevInFlow(nsIFrame*& aPrevInFlow) const; + NS_IMETHOD SetPrevInFlow(nsIFrame*); + NS_IMETHOD GetNextInFlow(nsIFrame*& aNextInFlow) const; + NS_IMETHOD SetNextInFlow(nsIFrame*); + NS_IMETHOD AppendToFlow(nsIFrame* aAfterFrame); + NS_IMETHOD PrependToFlow(nsIFrame* aAfterFrame); + NS_IMETHOD RemoveFromFlow(); + NS_IMETHOD BreakFromPrevFlow(); + NS_IMETHOD BreakFromNextFlow(); + NS_IMETHOD GetView(nsIView*& aView) const; + NS_IMETHOD SetView(nsIView* aView); + NS_IMETHOD GetParentWithView(nsIFrame*& aParent) const; + NS_IMETHOD GetOffsetFromView(nsPoint& aOffset, nsIView*& aView) const; + NS_IMETHOD GetWindow(nsIWidget*&) const; + NS_IMETHOD IsPercentageBase(PRBool& aBase) const; + NS_IMETHOD GetAutoMarginSize(PRUint8 aSide, nscoord& aSize) const; + NS_IMETHOD GetNextSibling(nsIFrame*& aNextSibling) const; + NS_IMETHOD SetNextSibling(nsIFrame* aNextSibling); + NS_IMETHOD IsTransparent(PRBool& aTransparent) const; + NS_IMETHOD Scrolled(nsIView *aView); + NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0, nsIListFilter *aFilter = nsnull) const; + NS_IMETHOD ListTag(FILE* out = stdout) const; + NS_IMETHOD VerifyTree() const; + + // Selection Methods + NS_IMETHOD HandlePress(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD HandleDrag(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD HandleRelease(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsEventStatus& aEventStatus); + + virtual PRInt32 GetPosition(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsIFrame * aNewFrame, + PRUint32& aAcutalContentOffset); + + //-------------------------------------------------- + // Additional methods + + // Invalidate part of the frame by asking the view manager to repaint. + // aDamageRect is in the frame's local coordinate space + void Invalidate(const nsRect& aDamageRect, + PRBool aImmediate = PR_FALSE) const; + +#ifdef NS_DEBUG + /** + * Tracing method that writes a method enter/exit routine to the + * nspr log using the nsIFrame log module. The tracing is only + * done when the NS_FRAME_TRACE_CALLS bit is set in the log module's + * level field. + */ + void Trace(const char* aMethod, PRBool aEnter); + void Trace(const char* aMethod, PRBool aEnter, nsReflowStatus aStatus); + void TraceMsg(const char* fmt, ...); +#endif + +protected: + virtual void NewContentIsBefore(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIContent * aNewContent, + nsIContent * aCurrentContent, + nsIFrame * aNewFrame); + + virtual void NewContentIsAfter(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIContent * aNewContent, + nsIContent * aCurrentContent, + nsIFrame * aNewFrame); + + virtual void AdjustPointsInNewContent(nsIPresContext& aPresContext, + nsGUIEvent * aEvent, + nsIFrame * aNewFrame); + + virtual void AdjustPointsInSameContent(nsIPresContext& aPresContext, + nsGUIEvent * aEvent); + + PRBool DisplaySelection(nsIPresContext& aPresContext, PRBool isOkToTurnOn = PR_FALSE); + + // Style post processing hook + NS_IMETHOD DidSetStyleContext(nsIPresContext* aPresContext); + + + + // Constructor. Takes as arguments the content object, the index in parent, + // and the Frame for the content parent + nsFrame(nsIContent* aContent, nsIFrame* aParent); + + virtual ~nsFrame(); + + void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const; + + nsRect mRect; + nsIContent* mContent; + nsIStyleContext* mStyleContext; + nsIFrame* mContentParent; + nsIFrame* mGeometricParent; + nsIFrame* mNextSibling; // singly linked list of frames + nsFrameState mState; + + /////////////////////////////////// + // Important Selection Variables + /////////////////////////////////// + static nsIFrame * mCurrentFrame; + static PRBool mDoingSelection; + static PRBool mDidDrag; + static PRInt32 mStartPos; + + // Selection data is valid only from the Mouse Press to the Mouse Release + static nsSelectionRange * mSelectionRange; + static nsISelection * mSelection; + + static nsSelectionPoint * mStartSelectionPoint; + static nsSelectionPoint * mEndSelectionPoint; + /////////////////////////////////// + + +private: + nsIView* mView; // must use accessor member functions + + NS_IMETHOD_(nsrefcnt) AddRef(void); + NS_IMETHOD_(nsrefcnt) Release(void); +}; + +#endif /* nsFrame_h___ */ diff --git a/layout/html/base/src/nsPresShell.cpp b/layout/html/base/src/nsPresShell.cpp new file mode 100644 index 000000000000..12e931335f91 --- /dev/null +++ b/layout/html/base/src/nsPresShell.cpp @@ -0,0 +1,1140 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "nsIPresShell.h" +#include "nsIPresContext.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDocumentObserver.h" +#include "nsIStyleSet.h" +#include "nsIStyleContext.h" +#include "nsFrame.h" +#include "nsIReflowCommand.h" +#include "nsIViewManager.h" +#include "nsCRT.h" +#include "plhash.h" +#include "prlog.h" +#include "nsVoidArray.h" +#include "nsIPref.h" +#include "nsIViewObserver.h" +#include "nsContainerFrame.h" + +static PRBool gsNoisyRefs = PR_FALSE; +#undef NOISY + +#if 0 +static PLHashNumber +HashKey(nsIFrame* key) +{ + return (PLHashNumber) key; +} + +static PRIntn +CompareKeys(nsIFrame* key1, nsIFrame* key2) +{ + return key1 == key2; +} + +class FrameHashTable { +public: + FrameHashTable(); + ~FrameHashTable(); + + void* Get(nsIFrame* aKey); + void* Put(nsIFrame* aKey, void* aValue); + void* Remove(nsIFrame* aKey); + +protected: + PLHashTable* mTable; +}; + +FrameHashTable::FrameHashTable() +{ + mTable = PL_NewHashTable(8, (PLHashFunction) HashKey, + (PLHashComparator) CompareKeys, + (PLHashComparator) nsnull, + nsnull, nsnull); +} + +FrameHashTable::~FrameHashTable() +{ + // XXX if debugging then we should assert that the table is empty + PL_HashTableDestroy(mTable); +} + +/** + * Get the data associated with a frame. + */ +void* +FrameHashTable::Get(nsIFrame* aKey) +{ + PRInt32 hashCode = (PRInt32) aKey; + PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey); + PLHashEntry* he = *hep; + if (nsnull != he) { + return he->value; + } + return nsnull; +} + +/** + * Create an association between a frame and some data. This call + * returns an old association if there was one (or nsnull if there + * wasn't). + */ +void* +FrameHashTable::Put(nsIFrame* aKey, void* aData) +{ + PRInt32 hashCode = (PRInt32) aKey; + PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey); + PLHashEntry* he = *hep; + if (nsnull != he) { + void* oldValue = he->value; + he->value = aData; + return oldValue; + } + PL_HashTableRawAdd(mTable, hep, hashCode, aKey, aData); + return nsnull; +} + +/** + * Remove an association between a frame and it's data. This returns + * the old associated data. + */ +void* +FrameHashTable::Remove(nsIFrame* aKey) +{ + PRInt32 hashCode = (PRInt32) aKey; + PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey); + PLHashEntry* he = *hep; + void* oldValue = nsnull; + if (nsnull != he) { + oldValue = he->value; + PL_HashTableRawRemove(mTable, hep, he); + } + return oldValue; +} +#endif + +//---------------------------------------------------------------------- + +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +static NS_DEFINE_IID(kIPresShellIID, NS_IPRESSHELL_IID); +static NS_DEFINE_IID(kIDocumentObserverIID, NS_IDOCUMENT_OBSERVER_IID); +static NS_DEFINE_IID(kIViewObserverIID, NS_IVIEWOBSERVER_IID); + +class PresShell : public nsIPresShell, public nsIViewObserver, + private nsIDocumentObserver + +{ +public: + PresShell(); + + void* operator new(size_t sz) { + void* rv = new char[sz]; + nsCRT::zero(rv, sz); + return rv; + } + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDocumentObserver + NS_IMETHOD BeginUpdate(nsIDocument *aDocument); + NS_IMETHOD EndUpdate(nsIDocument *aDocument); + NS_IMETHOD BeginLoad(nsIDocument *aDocument); + NS_IMETHOD EndLoad(nsIDocument *aDocument); + NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell); + NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell); + NS_IMETHOD ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent); + NS_IMETHOD AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aHint); + NS_IMETHOD ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer); + NS_IMETHOD ContentInserted(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + NS_IMETHOD ContentReplaced(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer); + NS_IMETHOD ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer); + NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet); + NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); + + // nsIPresShell + NS_IMETHOD Init(nsIDocument* aDocument, + nsIPresContext* aPresContext, + nsIViewManager* aViewManager, + nsIStyleSet* aStyleSet); + virtual nsIDocument* GetDocument(); + virtual nsIPresContext* GetPresContext(); + virtual nsIViewManager* GetViewManager(); + virtual nsIStyleSet* GetStyleSet(); + NS_IMETHOD EnterReflowLock(); + NS_IMETHOD ExitReflowLock(); + virtual void BeginObservingDocument(); + virtual void EndObservingDocument(); + NS_IMETHOD InitialReflow(nscoord aWidth, nscoord aHeight); + NS_IMETHOD ResizeReflow(nscoord aWidth, nscoord aHeight); + virtual nsIFrame* GetRootFrame(); + virtual nsIFrame* FindFrameWithContent(nsIContent* aContent); + virtual void AppendReflowCommand(nsIReflowCommand* aReflowCommand); + virtual void ProcessReflowCommands(); + + //nsIViewObserver + + //nsIViewObserver interface + + NS_IMETHOD Paint(nsIView *aView, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD HandleEvent(nsIView* aView, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + NS_IMETHOD Scrolled(nsIView *aView); + NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight); + +protected: + ~PresShell(); + +#ifdef NS_DEBUG + void VerifyIncrementalReflow(); +#endif + + nsIDocument* mDocument; + nsIPresContext* mPresContext; + nsIStyleSet* mStyleSet; + nsIFrame* mRootFrame; + nsIViewManager* mViewManager; + PRUint32 mUpdateCount; + nsVoidArray mReflowCommands; + PRUint32 mReflowLockCount; +}; + +#ifdef NS_DEBUG +/** + * Note: the log module is created during library initialization which + * means that you cannot perform logging before then. + */ +static PRLogModuleInfo* gLogModule = PR_NewLogModule("verifyreflow"); +#endif + +static PRBool gVerifyReflow = PRBool(0x55); + +NS_LAYOUT PRBool +nsIPresShell::GetVerifyReflowEnable() +{ +#ifdef NS_DEBUG + if (gVerifyReflow == PRBool(0x55)) { + gVerifyReflow = 0 != gLogModule->level; + printf("Note: verifyreflow is %sabled\n", + gVerifyReflow ? "en" : "dis"); + } +#endif + return gVerifyReflow; +} + +NS_LAYOUT void +nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled) +{ + gVerifyReflow = aEnabled; +} + +//---------------------------------------------------------------------- + +NS_LAYOUT nsresult +NS_NewPresShell(nsIPresShell** aInstancePtrResult) +{ + NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); + if (nsnull == aInstancePtrResult) { + return NS_ERROR_NULL_POINTER; + } + PresShell* it = new PresShell(); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + return it->QueryInterface(kIPresShellIID, (void **) aInstancePtrResult); +} + +PresShell::PresShell() +{ +} + +#ifdef NS_DEBUG +// for debugging only +nsrefcnt PresShell::AddRef(void) +{ + if (gsNoisyRefs) printf("PresShell: AddRef: %x, cnt = %d \n",this, mRefCnt+1); + return ++mRefCnt; +} + +// for debugging only +nsrefcnt PresShell::Release(void) +{ + if (gsNoisyRefs==PR_TRUE) printf("PresShell Release: %x, cnt = %d \n",this, mRefCnt-1); + if (--mRefCnt == 0) { + if (gsNoisyRefs==PR_TRUE) printf("PresShell Delete: %x, \n",this); + delete this; + return 0; + } + return mRefCnt; +} +#else +NS_IMPL_ADDREF(PresShell) +NS_IMPL_RELEASE(PresShell) +#endif + +nsresult +PresShell::QueryInterface(const nsIID& aIID, void** aInstancePtr) +{ + if (aIID.Equals(kIPresShellIID)) { + nsIPresShell* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kIDocumentObserverIID)) { + nsIDocumentObserver* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kIViewObserverIID)) { + nsIViewObserver* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kISupportsIID)) { + nsIPresShell* tmp = this; + nsISupports* tmp2 = tmp; + *aInstancePtr = (void*) tmp2; + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; +} + +PresShell::~PresShell() +{ + mRefCnt = 99;/* XXX hack! get around re-entrancy bugs */ + if (nsnull != mRootFrame) { + mRootFrame->DeleteFrame(*mPresContext); + } + NS_IF_RELEASE(mViewManager); + //Release mPresContext after mViewManager + NS_IF_RELEASE(mPresContext); + NS_IF_RELEASE(mStyleSet); + if (nsnull != mDocument) { + mDocument->DeleteShell(this); + NS_RELEASE(mDocument); + } + mRefCnt = 0; +} + +/** + * Initialize the presentation shell. Create view manager and style + * manager. + */ +nsresult +PresShell::Init(nsIDocument* aDocument, + nsIPresContext* aPresContext, + nsIViewManager* aViewManager, + nsIStyleSet* aStyleSet) +{ + NS_PRECONDITION(nsnull != aDocument, "null ptr"); + NS_PRECONDITION(nsnull != aPresContext, "null ptr"); + NS_PRECONDITION(nsnull != aViewManager, "null ptr"); + if ((nsnull == aDocument) || (nsnull == aPresContext) || + (nsnull == aViewManager)) { + return NS_ERROR_NULL_POINTER; + } + if (nsnull != mDocument) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + mDocument = aDocument; + NS_ADDREF(aDocument); + mViewManager = aViewManager; + NS_ADDREF(mViewManager); + + //doesn't add a ref since we own it... MMP + mViewManager->SetViewObserver((nsIViewObserver *)this); + + // Bind the context to the presentation shell. + mPresContext = aPresContext; + NS_ADDREF(aPresContext); + aPresContext->SetShell(this); + + mStyleSet = aStyleSet; + NS_ADDREF(aStyleSet); + + return NS_OK; +} + +NS_METHOD +PresShell::EnterReflowLock() +{ + ++mReflowLockCount; + return NS_OK; +} + +NS_METHOD +PresShell::ExitReflowLock() +{ + PRUint32 newReflowLockCount = mReflowLockCount - 1; + if (newReflowLockCount == 0) { + ProcessReflowCommands(); + } + mReflowLockCount = newReflowLockCount; + return NS_OK; +} + +nsIDocument* +PresShell::GetDocument() +{ + NS_IF_ADDREF(mDocument); + return mDocument; +} + +nsIPresContext* +PresShell::GetPresContext() +{ + NS_IF_ADDREF(mPresContext); + return mPresContext; +} + +nsIViewManager* +PresShell::GetViewManager() +{ + NS_IF_ADDREF(mViewManager); + return mViewManager; +} + +nsIStyleSet* +PresShell::GetStyleSet() +{ + NS_IF_ADDREF(mStyleSet); + return mStyleSet; +} + +// Make shell be a document observer +void +PresShell::BeginObservingDocument() +{ + if (nsnull != mDocument) { + mDocument->AddObserver(this); + } +} + +// Make shell stop being a document observer +void +PresShell::EndObservingDocument() +{ + if (nsnull != mDocument) { + mDocument->RemoveObserver(this); + } +} + +NS_IMETHODIMP +PresShell::InitialReflow(nscoord aWidth, nscoord aHeight) +{ + NS_PRECONDITION(nsnull == mRootFrame, "unexpected root frame"); + + EnterReflowLock(); + + if (nsnull != mPresContext) { + nsRect r(0, 0, aWidth, aHeight); + mPresContext->SetVisibleArea(r); + } + + if (nsnull == mRootFrame) { + if (nsnull != mDocument) { + nsIContent* root = mDocument->GetRootContent(); + if (nsnull != root) { + // Have style sheet processor construct a frame for the + // root content object + mStyleSet->ConstructFrame(mPresContext, root, nsnull, mRootFrame); + NS_RELEASE(root); + } + } + } + + if (nsnull != mRootFrame) { + // Kick off a top-down reflow + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, + ("enter nsPresShell::InitialReflow: %d,%d", aWidth, aHeight)); +#ifdef NS_DEBUG + if (nsIFrame::GetVerifyTreeEnable()) { + mRootFrame->VerifyTree(); + } +#endif + nsRect bounds; + mPresContext->GetVisibleArea(bounds); + nsSize maxSize(bounds.width, bounds.height); + nsReflowMetrics desiredSize(nsnull); + nsReflowStatus status; + nsReflowState reflowState(mRootFrame, eReflowReason_Initial, maxSize); + + mRootFrame->Reflow(*mPresContext, desiredSize, reflowState, status); + mRootFrame->SizeTo(desiredSize.width, desiredSize.height); +#ifdef NS_DEBUG + if (nsIFrame::GetVerifyTreeEnable()) { + mRootFrame->VerifyTree(); + } +#endif + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::InitialReflow")); + } + + ExitReflowLock(); + + return NS_OK; //XXX this needs to be real. MMP +} + +NS_IMETHODIMP +PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) +{ + EnterReflowLock(); + + if (nsnull != mPresContext) { + nsRect r(0, 0, aWidth, aHeight); + mPresContext->SetVisibleArea(r); + } + + // If we don't have a root frame yet, that means we haven't had our initial + // reflow... + if (nsnull != mRootFrame) { + // Kick off a top-down reflow + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, + ("enter nsPresShell::ResizeReflow: %d,%d", aWidth, aHeight)); +#ifdef NS_DEBUG + if (nsIFrame::GetVerifyTreeEnable()) { + mRootFrame->VerifyTree(); + } +#endif + nsRect bounds; + mPresContext->GetVisibleArea(bounds); + nsSize maxSize(bounds.width, bounds.height); + nsReflowMetrics desiredSize(nsnull); + nsReflowStatus status; + nsReflowState reflowState(mRootFrame, eReflowReason_Resize, maxSize); + + mRootFrame->Reflow(*mPresContext, desiredSize, reflowState, status); + mRootFrame->SizeTo(desiredSize.width, desiredSize.height); +#ifdef NS_DEBUG + if (nsIFrame::GetVerifyTreeEnable()) { + mRootFrame->VerifyTree(); + } +#endif + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::ResizeReflow")); + + // XXX if debugging then we should assert that the cache is empty + } else { +#ifdef NOISY + printf("PresShell::ResizeReflow: null root frame\n"); +#endif + } + + ExitReflowLock(); + + return NS_OK; //XXX this needs to be real. MMP +} + +nsIFrame* +PresShell::GetRootFrame() +{ + return mRootFrame; +} + +NS_IMETHODIMP +PresShell::BeginUpdate(nsIDocument *aDocument) +{ + mUpdateCount++; + return NS_OK; +} + +NS_IMETHODIMP +PresShell::EndUpdate(nsIDocument *aDocument) +{ + NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's"); + if (--mUpdateCount == 0) { + // XXX do something here + } + return NS_OK; +} + +NS_IMETHODIMP +PresShell::BeginLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +PresShell::EndLoad(nsIDocument *aDocument) +{ + return NS_OK; +} + +NS_IMETHODIMP +PresShell::BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +NS_IMETHODIMP +PresShell::EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) +{ + return NS_OK; +} + +void +PresShell::AppendReflowCommand(nsIReflowCommand* aReflowCommand) +{ + mReflowCommands.AppendElement(aReflowCommand); + NS_ADDREF(aReflowCommand); +} + +void +PresShell::ProcessReflowCommands() +{ + if (0 != mReflowCommands.Count()) { + nsReflowMetrics desiredSize(nsnull); + + while (0 != mReflowCommands.Count()) { + nsIReflowCommand* rc = (nsIReflowCommand*) mReflowCommands.ElementAt(0); + mReflowCommands.RemoveElementAt(0); + + // Dispatch the reflow command + nsSize maxSize; + mRootFrame->GetSize(maxSize); +#ifdef NS_DEBUG + nsIReflowCommand::ReflowType type; + rc->GetType(type); + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, + ("PresShell::ProcessReflowCommands: begin reflow command type=%d", + type)); +#endif + rc->Dispatch(*mPresContext, desiredSize, maxSize); + NS_RELEASE(rc); + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, + ("PresShell::ProcessReflowCommands: end reflow command")); + } + + // Place and size the root frame + mRootFrame->SizeTo(desiredSize.width, desiredSize.height); +#ifdef NS_DEBUG + if (nsIFrame::GetVerifyTreeEnable()) { + mRootFrame->VerifyTree(); + } + if (GetVerifyReflowEnable()) { + VerifyIncrementalReflow(); + } +#endif + } +} + +#ifdef NS_DEBUG +static char* +ContentTag(nsIContent* aContent, PRIntn aSlot) +{ + static char buf0[100], buf1[100], buf2[100]; + static char* bufs[] = { buf0, buf1, buf2 }; + char* buf = bufs[aSlot]; + nsIAtom* atom; + aContent->GetTag(atom); + if (nsnull != atom) { + nsAutoString tmp; + atom->ToString(tmp); + tmp.ToCString(buf, 100); + } + else { + buf[0] = 0; + } + return buf; +} +#endif + +NS_IMETHODIMP +PresShell::ContentChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsISupports* aSubContent) +{ + NS_PRECONDITION(nsnull != mRootFrame, "null root frame"); + + EnterReflowLock(); + nsresult rv = mStyleSet->ContentChanged(mPresContext, aContent, aSubContent); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::AttributeChanged(nsIDocument *aDocument, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aHint) +{ + NS_PRECONDITION(nsnull != mRootFrame, "null root frame"); + + EnterReflowLock(); + nsresult rv = mStyleSet->AttributeChanged(mPresContext, aContent, aAttribute, aHint); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::ContentAppended(nsIDocument *aDocument, + nsIContent* aContainer, + PRInt32 aNewIndexInContainer) +{ + EnterReflowLock(); + nsresult rv = mStyleSet->ContentAppended(mPresContext, aContainer, aNewIndexInContainer); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::ContentInserted(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + EnterReflowLock(); + nsresult rv = mStyleSet->ContentInserted(mPresContext, aContainer, aChild, aIndexInContainer); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::ContentReplaced(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aOldChild, + nsIContent* aNewChild, + PRInt32 aIndexInContainer) +{ + EnterReflowLock(); + nsresult rv = mStyleSet->ContentReplaced(mPresContext, aContainer, aOldChild, + aNewChild, aIndexInContainer); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::ContentRemoved(nsIDocument *aDocument, + nsIContent* aContainer, + nsIContent* aChild, + PRInt32 aIndexInContainer) +{ + EnterReflowLock(); + nsresult rv = mStyleSet->ContentRemoved(mPresContext, aContainer, + aChild, aIndexInContainer); + ExitReflowLock(); + return rv; +} + +NS_IMETHODIMP +PresShell::StyleSheetAdded(nsIDocument *aDocument, + nsIStyleSheet* aStyleSheet) +{ + return NS_OK; +} + +NS_IMETHODIMP +PresShell::DocumentWillBeDestroyed(nsIDocument *aDocument) +{ + return NS_OK; +} + +static nsIFrame* +FindFrameWithContent(nsIFrame* aFrame, nsIContent* aContent) +{ + nsIContent* frameContent; + + aFrame->GetContent(frameContent); + if (frameContent == aContent) { + NS_RELEASE(frameContent); + return aFrame; + } + NS_RELEASE(frameContent); + + aFrame->FirstChild(aFrame); + while (aFrame) { + nsIFrame* result = FindFrameWithContent(aFrame, aContent); + + if (result) { + return result; + } + + aFrame->GetNextSibling(aFrame); + } + + return nsnull; +} + +nsIFrame* +PresShell::FindFrameWithContent(nsIContent* aContent) +{ + // For the time being do a brute force depth-first search of + // the frame tree + return ::FindFrameWithContent(mRootFrame, aContent); +} + +//nsIViewObserver + +NS_IMETHODIMP PresShell :: Paint(nsIView *aView, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + void* clientData; + nsIFrame* frame; + nsresult rv; + + NS_ASSERTION(!(nsnull == aView), "null view"); + + aView->GetClientData(clientData); + frame = (nsIFrame *)clientData; + + if (nsnull != frame) + rv = frame->Paint(*mPresContext, aRenderingContext, aDirtyRect); + else + rv = NS_OK; + + return rv; +} + +NS_IMETHODIMP PresShell :: HandleEvent(nsIView *aView, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + void* clientData; + nsIFrame* frame; + nsresult rv; + + NS_ASSERTION(!(nsnull == aView), "null view"); + + aView->GetClientData(clientData); + frame = (nsIFrame *)clientData; + + if (nsnull != frame) + rv = frame->HandleEvent(*mPresContext, aEvent, aEventStatus); + else + rv = NS_OK; + + return rv; +} + +NS_IMETHODIMP PresShell :: Scrolled(nsIView *aView) +{ + void* clientData; + nsIFrame* frame; + nsresult rv; + + NS_ASSERTION(!(nsnull == aView), "null view"); + + aView->GetClientData(clientData); + frame = (nsIFrame *)clientData; + + if (nsnull != frame) + rv = frame->Scrolled(aView); + else + rv = NS_OK; + + return rv; +} + +NS_IMETHODIMP PresShell :: ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight) +{ + return ResizeReflow(aWidth, aHeight); +} + +#ifdef NS_DEBUG +#include "nsViewsCID.h" +#include "nsWidgetsCID.h" +#include "nsIScrollableView.h" +#include "nsIDeviceContext.h" +#include "nsIURL.h" +#include "nsICSSParser.h" +#include "nsIStyleSheet.h" + +static NS_DEFINE_IID(kViewManagerCID, NS_VIEW_MANAGER_CID); +static NS_DEFINE_IID(kIViewManagerIID, NS_IVIEWMANAGER_IID); +static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); +static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); +static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); +static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); + +static void +ShowDiffs(nsIFrame* k1, nsIFrame* k2, const nsRect& r1, const nsRect& r2) +{ + printf("verifyreflow: "); + k1->ListTag(stdout); + printf(" "); + stdout << r1; + printf(" != "); + k2->ListTag(stdout); + printf(" "); + stdout << r2; + printf("\n"); +} + +static void +CompareTrees(nsIFrame* aA, nsIFrame* aB) +{ + nsIFrame* k1, *k2; + aA->FirstChild(k1); + aB->FirstChild(k2); + NS_ASSERTION(nsContainerFrame::LengthOf(k1) == nsContainerFrame::LengthOf(k2), + "child counts don't match"); + + nsRect r1, r2; + nsIView* v1, *v2; + nsIWidget* w1, *w2; + for (;;) { + if (nsnull == k1) { + NS_ASSERTION(nsnull == k2, "child lists are different"); + break; + } + NS_ASSERTION(nsnull != k2, "child lists are different"); + + // Verify that the frames are the same size + k1->GetRect(r1); + k2->GetRect(r2); + if (r1 != r2) { + ShowDiffs(k1, k2, r1, r2); + } + else { + // Make sure either both have views or neither have views; if they + // do have views, make sure the views are the same size. If the + // views have widgets, make sure they both do or neither does. If + // they do, make sure the widgets are the same size. + k1->GetView(v1); + k2->GetView(v2); + if (nsnull != v1) { + NS_ASSERTION(nsnull != v2, "child views are not matched"); + v1->GetBounds(r1); + v2->GetBounds(r2); + NS_ASSERTION(r1 == r2, "child views are different sizes"); + + v1->GetWidget(w1); + v2->GetWidget(w2); + if (nsnull != w1) { + NS_ASSERTION(nsnull != w2, "child widgets are not matched"); + w1->GetBounds(r1); + w2->GetBounds(r2); + NS_ASSERTION(r1 == r2, "child widgets are different sizes"); + } + else { + NS_ASSERTION(nsnull == w2, "child widgets are not matched"); + } + } + else { + NS_ASSERTION(nsnull == v2, "child views are not matched"); + } + + // Compare the sub-trees too + CompareTrees(k1, k2); + } + + // Advance to next sibling + k1->GetNextSibling(k1); + k2->GetNextSibling(k2); + } +} + +// XXX: copy of nsWebWidget's ua.css loading code!!! +#define UA_CSS_URL "resource:/res/ua.css" + +static nsIStyleSheet* gUAStyleSheet; + +static nsresult +InitUAStyleSheet() +{ + nsresult rv = NS_OK; + + if (nsnull == gUAStyleSheet) { // snarf one + nsIURL* uaURL; + rv = NS_NewURL(&uaURL, nsnull, UA_CSS_URL); // XXX this bites, fix it + if (NS_OK == rv) { + // Get an input stream from the url + PRInt32 ec; + nsIInputStream* in = uaURL->Open(&ec); + if (nsnull != in) { + // Translate the input using the argument character set id into unicode + nsIUnicharInputStream* uin; + rv = NS_NewConverterStream(&uin, nsnull, in); + if (NS_OK == rv) { + // Create parser and set it up to process the input file + nsICSSParser* css; + rv = NS_NewCSSParser(&css); + if (NS_OK == rv) { + // Parse the input and produce a style set + // XXX note: we are ignoring rv until the error code stuff in the + // input routines is converted to use nsresult's + css->Parse(uin, uaURL, gUAStyleSheet); + NS_RELEASE(css); + } + NS_RELEASE(uin); + } + NS_RELEASE(in); + } + else { +// printf("open of %s failed: error=%x\n", UA_CSS_URL, ec); + rv = NS_ERROR_ILLEGAL_VALUE; // XXX need a better error code here + } + + NS_RELEASE(uaURL); + } + } + return rv; +} + +static nsresult +CreateStyleSet(nsIDocument* aDocument, nsIStyleSet** aStyleSet) +{ + nsresult rv = InitUAStyleSheet(); + if (NS_OK != rv) { + NS_WARNING("unable to load UA style sheet"); + } + + rv = NS_NewStyleSet(aStyleSet); + if (NS_OK == rv) { + PRInt32 index = aDocument->GetNumberOfStyleSheets(); + while (0 < index--) { + // NOTE: turn the order around for the set + nsIStyleSheet* sheet = aDocument->GetStyleSheetAt(index); + (*aStyleSet)->AppendDocStyleSheet(sheet); + NS_RELEASE(sheet); + } + // XXX this is just wrong, the UA style sheet should be owned by the UA + // for that matter, the style set should be created by the UA too + if (nsnull != gUAStyleSheet) { + (*aStyleSet)->AppendBackstopStyleSheet(gUAStyleSheet); + } + } + return rv; +} + +// After an incremental reflow, we verify the correctness by doing a +// full reflow into a fresh frame tree. +void +PresShell::VerifyIncrementalReflow() +{ + // All the stuff we are creating that needs releasing + nsIPresContext* cx; + nsIViewManager* vm; + nsIView* view; + nsIPresShell* sh; + nsIStyleSet* ss; + + // Create a presentation context to view the new frame tree + nsresult rv; + if (mPresContext->IsPaginated()) { + rv = NS_NewPrintPreviewContext(&cx); + } + else { + rv = NS_NewGalleyContext(&cx); + } + NS_ASSERTION(NS_OK == rv, "failed to create presentation context"); + nsIDeviceContext* dc = mPresContext->GetDeviceContext(); + nsIPref* prefs; + mPresContext->GetPrefs(prefs); + cx->Init(dc, prefs); + NS_IF_RELEASE(prefs); + + rv = CreateStyleSet(mDocument, &ss); + NS_ASSERTION(NS_OK == rv, "failed to create style set"); + + // Get our scrolling preference + nsScrollPreference scrolling; + nsIView* rootView; + mViewManager->GetRootView(rootView); + nsIScrollableView* scrollView; + rv = rootView->QueryInterface(kScrollViewIID, (void**)&scrollView); + if (NS_OK == rv) { + scrollView->GetScrollPreference(scrolling); + } + nsIWidget* rootWidget; + rootView->GetWidget(rootWidget); + void* nativeParentWidget = rootWidget->GetNativeData(NS_NATIVE_WIDGET); + + // Create a new view manager. + rv = nsRepository::CreateInstance(kViewManagerCID, nsnull, kIViewManagerIID, + (void**) &vm); + if ((NS_OK != rv) || (NS_OK != vm->Init(dc))) { + NS_ASSERTION(NS_OK == rv, "failed to create view manager"); + } + + NS_RELEASE(dc); + + vm->SetViewObserver((nsIViewObserver *)this); + + // Create a child window of the parent that is our "root view/window" + // Create a view + nsRect tbounds; + mPresContext->GetVisibleArea(tbounds); +// tbounds *= mPresContext->GetPixelsToTwips(); + rv = nsRepository::CreateInstance(kScrollingViewCID, nsnull, kIViewIID, + (void **) &view); + if ((NS_OK != rv) || (NS_OK != view->Init(vm, tbounds, nsnull, &kWidgetCID, + nsnull, nativeParentWidget))) { + NS_ASSERTION(NS_OK == rv, "failed to create scroll view"); + } + rv = view->QueryInterface(kScrollViewIID, (void**)&scrollView); + if (NS_OK == rv) { + scrollView->SetScrollPreference(scrolling); + } + else { + NS_ASSERTION(0, "invalid scrolling view"); + } + + // Setup hierarchical relationship in view manager + vm->SetRootView(view); + + // Make the new presentation context the same size as our + // presentation context. + nsRect r; + mPresContext->GetVisibleArea(r); + cx->SetVisibleArea(r); + + // Create a new presentation shell to view the document + rv = mDocument->CreateShell(cx, vm, ss, &sh); + NS_ASSERTION(NS_OK == rv, "failed to create presentation shell"); + sh->ResizeReflow(r.width, r.height); + + // Now that the document has been reflowed, use its frame tree to + // compare against our frame tree. + CompareTrees(GetRootFrame(), sh->GetRootFrame()); + + NS_RELEASE(vm); + + NS_RELEASE(cx); + NS_RELEASE(sh); + + NS_RELEASE(ss); +} +#endif diff --git a/layout/html/base/src/nsSplittableFrame.cpp b/layout/html/base/src/nsSplittableFrame.cpp new file mode 100644 index 000000000000..2a004b5bab57 --- /dev/null +++ b/layout/html/base/src/nsSplittableFrame.cpp @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "nsSplittableFrame.h" +#include "nsIContent.h" +#include "nsIPresContext.h" +#include "nsIStyleContext.h" +#include "nsISizeOfHandler.h" + +nsSplittableFrame::nsSplittableFrame(nsIContent* aContent, + nsIFrame* aParent) + : nsFrame(aContent, aParent) +{ +} + +nsSplittableFrame::~nsSplittableFrame() +{ +} + +NS_IMETHODIMP +nsSplittableFrame::SizeOf(nsISizeOfHandler* aHandler) const +{ + aHandler->Add(sizeof(*this)); + nsSplittableFrame::SizeOfWithoutThis(aHandler); + return NS_OK; +} + +void +nsSplittableFrame::SizeOfWithoutThis(nsISizeOfHandler* aHandler) const +{ + nsFrame::SizeOfWithoutThis(aHandler); + if (!aHandler->HaveSeen(mPrevInFlow)) { + mPrevInFlow->SizeOf(aHandler); + } + if (!aHandler->HaveSeen(mNextInFlow)) { + mNextInFlow->SizeOf(aHandler); + } +} + +// Flow member functions + +NS_IMETHODIMP +nsSplittableFrame::IsSplittable(nsSplittableType& aIsSplittable) const +{ + aIsSplittable = NS_FRAME_SPLITTABLE; + return NS_OK; +} + +NS_METHOD nsSplittableFrame::GetPrevInFlow(nsIFrame*& aPrevInFlow) const +{ + aPrevInFlow = mPrevInFlow; + return NS_OK; +} + +NS_METHOD nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame) +{ + mPrevInFlow = aFrame; + return NS_OK; +} + +NS_METHOD nsSplittableFrame::GetNextInFlow(nsIFrame*& aNextInFlow) const +{ + aNextInFlow = mNextInFlow; + return NS_OK; +} + +NS_METHOD nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame) +{ + mNextInFlow = aFrame; + return NS_OK; +} + +nsIFrame* nsSplittableFrame::GetFirstInFlow() const +{ + nsSplittableFrame* firstInFlow; + nsSplittableFrame* prevInFlow = (nsSplittableFrame*)this; + while (nsnull!=prevInFlow) { + firstInFlow = prevInFlow; + prevInFlow = (nsSplittableFrame*)firstInFlow->mPrevInFlow; + } + NS_POSTCONDITION(nsnull!=firstInFlow, "illegal state in flow chain."); + return firstInFlow; +} + +nsIFrame* nsSplittableFrame::GetLastInFlow() const +{ + nsSplittableFrame* lastInFlow; + nsSplittableFrame* nextInFlow = (nsSplittableFrame*)this; + while (nsnull!=nextInFlow) { + lastInFlow = nextInFlow; + nextInFlow = (nsSplittableFrame*)lastInFlow->mNextInFlow; + } + NS_POSTCONDITION(nsnull!=lastInFlow, "illegal state in flow chain."); + return lastInFlow; +} + +// Append this frame to flow after aAfterFrame +NS_METHOD nsSplittableFrame::AppendToFlow(nsIFrame* aAfterFrame) +{ + NS_PRECONDITION(aAfterFrame != nsnull, "null pointer"); + + mPrevInFlow = aAfterFrame; + aAfterFrame->GetNextInFlow(mNextInFlow); + mPrevInFlow->SetNextInFlow(this); + if (mNextInFlow) { + mNextInFlow->SetPrevInFlow(this); + } + return NS_OK; +} + +// Prepend this frame to flow before aBeforeFrame +NS_METHOD nsSplittableFrame::PrependToFlow(nsIFrame* aBeforeFrame) +{ + NS_PRECONDITION(aBeforeFrame != nsnull, "null pointer"); + + aBeforeFrame->GetPrevInFlow(mPrevInFlow); + mNextInFlow = aBeforeFrame; + mNextInFlow->SetPrevInFlow(this); + if (mPrevInFlow) { + mPrevInFlow->SetNextInFlow(this); + } + return NS_OK; +} + +// Remove this frame from the flow. Connects prev in flow and next in flow +NS_METHOD nsSplittableFrame::RemoveFromFlow() +{ + if (mPrevInFlow) { + mPrevInFlow->SetNextInFlow(mNextInFlow); + } + + if (mNextInFlow) { + mNextInFlow->SetPrevInFlow(mPrevInFlow); + } + + mPrevInFlow = mNextInFlow = nsnull; + return NS_OK; +} + +// Detach from previous frame in flow +NS_METHOD nsSplittableFrame::BreakFromPrevFlow() +{ + if (mPrevInFlow) { + mPrevInFlow->SetNextInFlow(nsnull); + mPrevInFlow = nsnull; + } + return NS_OK; +} + +// Detach from next frame in flow +NS_METHOD nsSplittableFrame::BreakFromNextFlow() +{ + if (mNextInFlow) { + mNextInFlow->SetPrevInFlow(nsnull); + mNextInFlow = nsnull; + } + return NS_OK; +} + +nsIFrame * nsSplittableFrame::GetPrevInFlow() +{ + return mPrevInFlow; +} + +nsIFrame * nsSplittableFrame::GetNextInFlow() +{ + return mNextInFlow; +} diff --git a/layout/html/base/src/nsSplittableFrame.h b/layout/html/base/src/nsSplittableFrame.h new file mode 100644 index 000000000000..ce9e9f5cd755 --- /dev/null +++ b/layout/html/base/src/nsSplittableFrame.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef nsSplittableFrame_h___ +#define nsSplittableFrame_h___ + +#include "nsFrame.h" + +// Derived class that allows splitting +class nsSplittableFrame : public nsFrame +{ +public: + NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const; + // CreateContinuingFrame() does the default behavior of using the + // content delegate to create a new frame + NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; + + // Flow member functions. + NS_IMETHOD GetPrevInFlow(nsIFrame*& aPrevInFlow) const; + NS_IMETHOD SetPrevInFlow(nsIFrame*); + NS_IMETHOD GetNextInFlow(nsIFrame*& aNextInFlow) const; + NS_IMETHOD SetNextInFlow(nsIFrame*); + + /** + * Return the first frame in our current flow. + */ + nsIFrame* GetFirstInFlow() const; + + /** + * Return the last frame in our current flow. + */ + nsIFrame* GetLastInFlow() const; + + NS_IMETHOD AppendToFlow(nsIFrame* aAfterFrame); + NS_IMETHOD PrependToFlow(nsIFrame* aAfterFrame); + NS_IMETHOD RemoveFromFlow(); + NS_IMETHOD BreakFromPrevFlow(); + NS_IMETHOD BreakFromNextFlow(); + + nsIFrame* GetPrevInFlow(); + nsIFrame* GetNextInFlow(); + + +protected: + // Constructor. Takes as arguments the content object, the index in parent, + // and the Frame for the content parent + nsSplittableFrame(nsIContent* aContent, nsIFrame* aParent); + + virtual ~nsSplittableFrame(); + + void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const; + + nsIFrame* mPrevInFlow; + nsIFrame* mNextInFlow; +}; + +#endif /* nsSplittableFrame_h___ */