diff --git a/content/html/style/src/nsHTMLStyleSheet.cpp b/content/html/style/src/nsHTMLStyleSheet.cpp index 897aa7d071a4..4b7623917aa7 100644 --- a/content/html/style/src/nsHTMLStyleSheet.cpp +++ b/content/html/style/src/nsHTMLStyleSheet.cpp @@ -325,6 +325,8 @@ protected: nsIFrame* aParentFrame, nsIFrame*& aFrame); + PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay); + protected: PRUint32 mInHeap : 1; PRUint32 mRefCnt : 31; @@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext, return rv; } +PRBool +HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame, + const nsStyleDisplay* aDisplay) +{ + // If the overflow property is scroll then it's scrollable regardless + // of whether the content overflows the block. + // XXX This isn't correct. Only do this if the height is not allowed to + // grow to accomodate its child frames... + if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) { + return PR_TRUE; + } + +#if 0 + if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) || + // If the element has a fixed height (it isn't auto) and an overflow + // property of scroll or auto, then it's potentially scrollable. + // XXX Deal with width considerations, too + (NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) { +#endif + return PR_FALSE; +} + NS_IMETHODIMP HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, nsIContent* aContent, @@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, } -#if 0 - // If the frame is a block-level frame and it has a fixed height and overflow - // property of scroll, then wrap it in a scroll frame. - // XXX Deal with replaced elements and overflow of auto and width, too - nsIFrame* kidFrame = nsnull; - nsresult rv; - if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) || - (NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) { - rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidSC); + // If the frame is a block-level frame and is scrollable then wrap it + // in a scroll frame. + // XXX Applies to replaced elements, too, but how to tell if the element + // is replaced? + if (nsnull != aFrameSubTree) { + if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) { + nsIFrame* scrollFrame; + + if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) { + // The scroll frame gets the original style context, and the scrolled + // frame gets a pseudo style context. + // XXX Is this the best way to do this? + scrollFrame->SetStyleContext(aPresContext, styleContext); + + nsIStyleContext* pseudoStyle; + pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo, + scrollFrame); + aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle); + NS_RELEASE(pseudoStyle); + + // Initialize the scroll frame + scrollFrame->Init(*aPresContext, aFrameSubTree); + aFrameSubTree = scrollFrame; + } } } -#endif } } NS_RELEASE(styleContext); diff --git a/layout/generic/nsHTMLContainerFrame.cpp b/layout/generic/nsHTMLContainerFrame.cpp index a6e5c441a73e..f42c5ba15b9b 100644 --- a/layout/generic/nsHTMLContainerFrame.cpp +++ b/layout/generic/nsHTMLContainerFrame.cpp @@ -35,7 +35,6 @@ #include "nsPlaceholderFrame.h" #include "nsIHTMLContent.h" #include "nsHTMLParts.h" -#include "nsScrollFrame.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsViewsCID.h" diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index fdcbde9af902..c41b9f00bc8c 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -303,6 +303,10 @@ nsresult NS_NewWBRFrame(nsIContent* aContent, nsIFrame* aParentFrame, nsIFrame*& aResult); +extern nsresult +NS_NewScrollFrame(nsIContent* aContent, nsIFrame* aParentFrame, + nsIFrame*& aResult); + // forms extern nsresult NS_NewFormFrame(nsIContent* aContent, nsIFrame* aParentFrame, diff --git a/layout/html/base/src/nsHTMLContainerFrame.cpp b/layout/html/base/src/nsHTMLContainerFrame.cpp index a6e5c441a73e..f42c5ba15b9b 100644 --- a/layout/html/base/src/nsHTMLContainerFrame.cpp +++ b/layout/html/base/src/nsHTMLContainerFrame.cpp @@ -35,7 +35,6 @@ #include "nsPlaceholderFrame.h" #include "nsIHTMLContent.h" #include "nsHTMLParts.h" -#include "nsScrollFrame.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsViewsCID.h" diff --git a/layout/html/base/src/nsHTMLParts.h b/layout/html/base/src/nsHTMLParts.h index fdcbde9af902..c41b9f00bc8c 100644 --- a/layout/html/base/src/nsHTMLParts.h +++ b/layout/html/base/src/nsHTMLParts.h @@ -303,6 +303,10 @@ nsresult NS_NewWBRFrame(nsIContent* aContent, nsIFrame* aParentFrame, nsIFrame*& aResult); +extern nsresult +NS_NewScrollFrame(nsIContent* aContent, nsIFrame* aParentFrame, + nsIFrame*& aResult); + // forms extern nsresult NS_NewFormFrame(nsIContent* aContent, nsIFrame* aParentFrame, diff --git a/layout/html/base/src/nsScrollFrame.cpp b/layout/html/base/src/nsScrollFrame.cpp index 85d13189e97f..3012faa1a14a 100644 --- a/layout/html/base/src/nsScrollFrame.cpp +++ b/layout/html/base/src/nsScrollFrame.cpp @@ -15,7 +15,6 @@ * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ -#include "nsScrollFrame.h" #include "nsIPresContext.h" #include "nsIStyleContext.h" #include "nsIReflowCommand.h" @@ -27,264 +26,123 @@ #include "nsBodyFrame.h" #include "nsHTMLContainerFrame.h" #include "nsHTMLIIDs.h" +#include "nsCSSRendering.h" +#include "nsIScrollableView.h" +#include "nsWidgetsCID.h" -#include "nsBodyFrame.h" +static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); +static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); -class nsScrollBodyFrame : public nsContainerFrame { +//---------------------------------------------------------------------- + +class nsScrollViewFrame : public nsHTMLContainerFrame { public: - nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent); + nsScrollViewFrame(nsIContent* aContent, nsIFrame* aParent); + NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); + NS_IMETHOD Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); - NS_IMETHOD Paint(nsIPresContext& aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aDirtyRect); - NS_IMETHOD ListTag(FILE* out = stdout) const; protected: - void CreateFirstChild(nsIPresContext* aPresContext); + virtual PRIntn GetSkipSides() const; }; -nsScrollBodyFrame::nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent) - : nsContainerFrame(aContent, aParent) +nsScrollViewFrame::nsScrollViewFrame(nsIContent* aContent, nsIFrame* aParent) + : nsHTMLContainerFrame(aContent, aParent) { } -void -nsScrollBodyFrame::CreateFirstChild(nsIPresContext* aPresContext) -{ - // Are we paginated? - if (aPresContext->IsPaginated()) { - // Yes. Create the first page frame - mFirstChild = new nsPageFrame(mContent, this);/* XXX mContent won't work here */ - } else { - if (nsnull != mContent) { -#if 0 - // Create a frame for the child we are wrapping up - nsIContentDelegate* cd = mContent->GetDelegate(aPresContext); - if (nsnull != cd) { - nsIStyleContext* kidStyleContext = - aPresContext->ResolveStyleContextFor(mContent, this); - nsresult rv = cd->CreateFrame(aPresContext, mContent, this, - kidStyleContext, mFirstChild); - NS_RELEASE(kidStyleContext); - if (NS_OK == rv) { - mChildCount = 1; - mLastContentOffset = mFirstContentOffset; - } - NS_RELEASE(cd); - } -#else - nsBodyFrame::NewFrame(&mFirstChild, mContent, this); -#endif - } - } -} - -// XXX Hack -#define PAGE_SPACING 100 - NS_IMETHODIMP -nsScrollBodyFrame::Reflow(nsIPresContext& aPresContext, +nsScrollViewFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) +{ + mFirstChild = aChildList; + + // XXX Unless it's already a body frame, child frames that are containers + // need to be wrapped in a body frame + return NS_OK; +} + +NS_IMETHODIMP +nsScrollViewFrame::Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { - nsHTMLContainerFrame::CreateViewForFrame(aPresContext, this, - nsnull, PR_TRUE); - NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("enter nsScrollBodyFrame::Reflow: maxSize=%d,%d", + ("enter nsScrollViewFrame::Reflow: maxSize=%d,%d", aReflowState.maxSize.width, aReflowState.maxSize.height)); - aStatus = NS_FRAME_COMPLETE; + // Create a view + if (eReflowReason_Initial == aReflowState.reason) { + // XXX It would be nice if we could do this sort of thing in our Init() + // member function instead of here... + nsHTMLContainerFrame::CreateViewForFrame(aPresContext, this, + nsnull, PR_TRUE); + } - // XXX Incremental reflow code doesn't handle page mode at all... - if (eReflowReason_Incremental == aReflowState.reason) { - // We don't expect the target of the reflow command to be the root - // content frame -#ifdef NS_DEBUG - nsIFrame* target; - aReflowState.reflowCommand->GetTarget(target); - NS_ASSERTION(target != this, "root content frame is reflow command target"); -#endif - - // Verify the next frame in the reflow chain is our child frame - nsIFrame* next; - aReflowState.reflowCommand->GetNext(next); - NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame"); + // Scroll frame handles the border, and we handle the padding and background + const nsStyleSpacing* spacing = (const nsStyleSpacing*) + mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin padding; + spacing->CalcPaddingFor(this, padding); - nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE); - nsHTMLReflowState kidReflowState(aPresContext, next, aReflowState, maxSize); - nsIHTMLReflow* htmlReflow; - - if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { - // Dispatch the reflow command to our child frame. Allow it to be as high - // as it wants - ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus); - - // Place and size the child. Make sure the child is at least as - // tall as our max size (the containing window) - if (aDesiredSize.height < aReflowState.maxSize.height) { - aDesiredSize.height = aReflowState.maxSize.height; - } - - nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height); - mFirstChild->SetRect(rect); - // XXX Why are we sending a DidReflow() notification here? - htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - } + // Allow the child frame to be as wide as our max width (minus scrollbar + // width and padding), and as high as it wants to be. + nsSize maxSize; + nsIDeviceContext* dc = aPresContext.GetDeviceContext(); + float sbWidth, sbHeight; - } else { - // Do we have any children? - if (nsnull == mFirstChild) { - // No, create the first child frame - CreateFirstChild(&aPresContext); - } + dc->GetScrollBarDimensions(sbWidth, sbHeight); + maxSize.width = aReflowState.maxSize.width - NSToCoordRound(sbWidth) - + (padding.left + padding.right); + NS_RELEASE(dc); + maxSize.height = NS_UNCONSTRAINEDSIZE; - // Resize our frames - if (nsnull != mFirstChild) { - if (aPresContext.IsPaginated()) { - nscoord y = PAGE_SPACING; - nsHTMLReflowMetrics kidSize(aDesiredSize.maxElementSize); - nsSize pageSize(aPresContext.GetPageWidth(), - aPresContext.GetPageHeight()); - - // Tile the pages vertically - for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { - // Reflow the page - nsHTMLReflowState kidReflowState(aPresContext, kidFrame, aReflowState, pageSize); - nsReflowStatus status; + // Reflow the child + nsHTMLReflowMetrics kidMetrics(aDesiredSize.maxElementSize); + nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize); - // Place and size the page. If the page is narrower than our - // max width then center it horizontally - nsIDeviceContext *dx = aPresContext.GetDeviceContext(); - float sbWidth, sbHeight; - dx->GetScrollBarDimensions(sbWidth, sbHeight); - nscoord extra = aReflowState.maxSize.width - kidSize.width - - NSToCoordRound(sbWidth); - NS_RELEASE(dx); - nscoord x = extra > 0 ? extra / 2 : 0; - nsIHTMLReflow* htmlReflow; + ReflowChild(mFirstChild, aPresContext, kidMetrics, kidReflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + + // Place and size the child + nsRect rect(padding.left, padding.top, kidMetrics.width, kidMetrics.height); + mFirstChild->SetRect(rect); - if (NS_OK == kidFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { - htmlReflow->WillReflow(aPresContext); - ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, status); - - kidFrame->SetRect(nsRect(x, y, kidSize.width, kidSize.height)); - htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - y += kidSize.height; - - // Leave a slight gap between the pages - y += PAGE_SPACING; - - // Is the page complete? - nsIFrame* kidNextInFlow; - - kidFrame->GetNextInFlow(kidNextInFlow); - if (NS_FRAME_IS_COMPLETE(status)) { - NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list"); - } else if (nsnull == kidNextInFlow) { - // The page isn't complete and it doesn't have a next-in-flow so - // create a continuing page - nsIStyleContext* kidSC; - kidFrame->GetStyleContext(&aPresContext, kidSC); - nsIFrame* continuingPage; - nsresult rv = kidFrame->CreateContinuingFrame(aPresContext, this, - kidSC, continuingPage); - NS_RELEASE(kidSC); - - // Add it to our child list - #ifdef NS_DEBUG - nsIFrame* kidNextSibling; - - kidFrame->GetNextSibling(kidNextSibling); - NS_ASSERTION(nsnull == kidNextSibling, "unexpected sibling"); - #endif - kidFrame->SetNextSibling(continuingPage); - } - } - - // Get the next page - kidFrame->GetNextSibling(kidFrame); - } - - // Return our desired size - aDesiredSize.width = aReflowState.maxSize.width; - aDesiredSize.height = y; - - } else { - // Allow the frame to be as wide as our max width, and as high - // as it wants to be. - nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE); - nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize); - nsIHTMLReflow* htmlReflow; - - if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { - // Get the child's desired size. Our child's desired height is our - // desired size - htmlReflow->WillReflow(aPresContext); - ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus); - NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); - -#if 0 - // Place and size the child. Make sure the child is at least as - // tall as our max size (the containing window) - if (aDesiredSize.height < aReflowState.maxSize.height) { - aDesiredSize.height = aReflowState.maxSize.height; - } -#endif - - // Place and size the child - nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height); - mFirstChild->SetRect(rect); - htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - } - } - } - else { - aDesiredSize.width = aReflowState.maxSize.width; - aDesiredSize.height = 1; - aDesiredSize.ascent = aDesiredSize.height; - aDesiredSize.descent = 0; - if (nsnull != aDesiredSize.maxElementSize) { - aDesiredSize.maxElementSize->width = 0; - aDesiredSize.maxElementSize->height = 0; - } - } + // Determine our size. Our width is our maxWidth and our height is the max + // of our child's height plus padding and our maxHeight (if our maxHeight is + // constrained). + aDesiredSize.width = aReflowState.maxSize.width; + if (NS_UNCONSTRAINEDSIZE == aReflowState.maxSize.height) { + aDesiredSize.height = kidMetrics.height + padding.top + padding.bottom; + } + else { + aDesiredSize.height = aReflowState.maxSize.height; } NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("exit nsScrollBodyFrame::Reflow: status=%d width=%d height=%d", - aStatus, aDesiredSize.width, aDesiredSize.height)); + ("exit nsScrollViewFrame::Reflow: status=%d width=%d height=%d", + aStatus, aDesiredSize.width, aDesiredSize.height)); return NS_OK; } -NS_IMETHODIMP -nsScrollBodyFrame::Paint(nsIPresContext& aPresContext, - nsIRenderingContext& aRenderingContext, - const nsRect& aDirtyRect) +PRIntn +nsScrollViewFrame::GetSkipSides() const { - // If we're paginated then fill the dirty rect with white - if (aPresContext.IsPaginated()) { - // Cross hatching would be nicer... - aRenderingContext.SetColor(NS_RGB(255,255,255)); - aRenderingContext.FillRect(aDirtyRect); - } - - nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect); - return NS_OK; + // Scroll frame handles the border... + return 0xF; } NS_IMETHODIMP -nsScrollBodyFrame::ListTag(FILE* out) const +nsScrollViewFrame::ListTag(FILE* out) const { - fputs("*scrollbodyframe<", out); + fputs("*scrollviewframe<", out); nsIAtom* atom; mContent->GetTag(atom); if (nsnull != atom) { @@ -299,36 +157,70 @@ nsScrollBodyFrame::ListTag(FILE* out) const //---------------------------------------------------------------------- -class nsScrollInnerFrame : public nsContainerFrame { +/** + * The scrolling view frame creates and manages the scrolling view. + * It creates a nsScrollViewFrame which handles padding and rendering + * of the background. + */ +class nsScrollingViewFrame : public nsHTMLContainerFrame { public: - nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent); + nsScrollingViewFrame(nsIContent* aContent, nsIFrame* aParent); + + NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); NS_IMETHOD Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD ListTag(FILE* out = stdout) const; protected: + virtual PRIntn GetSkipSides() const; }; -nsScrollInnerFrame::nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent) - : nsContainerFrame(aContent, aParent) +nsScrollingViewFrame::nsScrollingViewFrame(nsIContent* aContent, nsIFrame* aParent) + : nsHTMLContainerFrame(aContent, aParent) { } NS_IMETHODIMP -nsScrollInnerFrame::Reflow(nsIPresContext& aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) +nsScrollingViewFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) +{ + // Create a scroll view frame + mFirstChild = new nsScrollViewFrame(mContent, this); + if (nsnull == mFirstChild) { + return NS_ERROR_OUT_OF_MEMORY; + } + // Have it use our style context + mFirstChild->SetStyleContext(&aPresContext, mStyleContext); + + // Reset the child frame's geometric and content parent to be + // the scroll view frame + aChildList->SetGeometricParent(mFirstChild); + aChildList->SetContentParent(mFirstChild); + + // Init the scroll view frame passing it the child list + return mFirstChild->Init(aPresContext, aChildList); +} + +//XXX incremental reflow pass through +NS_IMETHODIMP +nsScrollingViewFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) { NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("enter nsScrollInnerFrame::Reflow: maxSize=%d,%d", + ("enter nsScrollingViewFrame::Reflow: maxSize=%d,%d", aReflowState.maxSize.width, aReflowState.maxSize.height)); + // Make sure we have a scrolling view nsIView* view; GetView(view); if (nsnull == view) { @@ -350,77 +242,75 @@ nsScrollInnerFrame::Reflow(nsIPresContext& aPresContext, nsnull, kIViewIID, (void **)&view); + // XXX We want the scrolling view to have a widget to clip any child + // widgets that aren't visible, e.g. form elements, but there's currently + // a bug which is why it's commented out if ((NS_OK != rv) || (NS_OK != view->Init(viewManager, mRect, - parentView))) { + parentView, + nsnull))) { + // &kWidgetCID))) { NS_RELEASE(viewManager); return rv; } // Insert new view as a child of the parent view viewManager->InsertChild(parentView, view, 0); - NS_RELEASE(viewManager); - - // NS_RELEASE(parentView); SetView(view); + NS_RELEASE(viewManager); } if (nsnull == view) { return NS_OK; } - // NS_RELEASE(view); - if (nsnull == mFirstChild) { - mFirstChild = new nsScrollBodyFrame(mContent, this); - } + // Reflow the child and get its desired size. Let the child's height be + // whatever it wants + nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, + nsSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE)); - // Allow the child frame to be as wide as our max width (minus a - // scroll bar width), and as high as it wants to be. - nsSize maxSize; - nsIDeviceContext* dc = aPresContext.GetDeviceContext(); - float sbWidth, sbHeight; - dc->GetScrollBarDimensions(sbWidth, sbHeight); - maxSize.width = aReflowState.maxSize.width - NSToCoordRound(sbWidth); - NS_RELEASE(dc); - maxSize.height = NS_UNCONSTRAINEDSIZE; + ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); - // Reflow the child - nsHTMLReflowMetrics kidMetrics(aDesiredSize.maxElementSize); + // Place and size the child + nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height); + mFirstChild->SetRect(rect); - nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize); - nsIHTMLReflow* htmlReflow; - - if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { - htmlReflow->WillReflow(aPresContext); - ReflowChild(mFirstChild, aPresContext, kidMetrics, kidReflowState, aStatus); - NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); - - // Place and size the child - nsRect rect(0, 0, kidMetrics.width, kidMetrics.height); - mFirstChild->SetRect(rect); - htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); + // The scroll view frame either shrink wraps around it's single + // child OR uses the style width/height. + if (aReflowState.HaveConstrainedWidth()) { + aDesiredSize.width = aReflowState.minWidth; } - - // Determine our size. Our width is our maxWidth and our height is - // either our child's height or our maxHeight if our maxHeight is - // constrained. - aDesiredSize.width = aReflowState.maxSize.width; - if (NS_UNCONSTRAINEDSIZE == aReflowState.maxSize.height) { - aDesiredSize.height = kidMetrics.height; - } - else { - aDesiredSize.height = aReflowState.maxSize.height; + if (aReflowState.HaveConstrainedHeight()) { + aDesiredSize.height = aReflowState.minHeight; } + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("exit nsScrollInnerFrame::Reflow: status=%d width=%d height=%d", + ("exit nsScrollingViewFrame::Reflow: status=%d width=%d height=%d", aStatus, aDesiredSize.width, aDesiredSize.height)); return NS_OK; } NS_IMETHODIMP -nsScrollInnerFrame::ListTag(FILE* out) const +nsScrollingViewFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) { - fputs("*scrollinnerframe<", out); + // Paint our children + return nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect); +} + +PRIntn +nsScrollingViewFrame::GetSkipSides() const +{ + return 0; +} + +NS_IMETHODIMP +nsScrollingViewFrame::ListTag(FILE* out) const +{ + fputs("*scrollingviewframe<", out); nsIAtom* atom; mContent->GetTag(atom); if (nsnull != atom) { @@ -435,108 +325,160 @@ nsScrollInnerFrame::ListTag(FILE* out) const //---------------------------------------------------------------------- -class nsScrollOuterFrame : public nsHTMLContainerFrame { +/** + * The scroll frame basically acts as a border frame. It leaves room for and + * renders the border. It creates a nsScrollingViewFrame which creates and + * manages the scrollable view. + */ +class nsScrollFrame : public nsHTMLContainerFrame { public: - nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent); + nsScrollFrame(nsIContent* aContent, nsIFrame* aParent); + + NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList); NS_IMETHOD Reflow(nsIPresContext& aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD ListTag(FILE* out = stdout) const; protected: virtual PRIntn GetSkipSides() const; }; -nsScrollOuterFrame::nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent) +nsScrollFrame::nsScrollFrame(nsIContent* aContent, nsIFrame* aParent) : nsHTMLContainerFrame(aContent, aParent) { } +NS_IMETHODIMP +nsScrollFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList) +{ + NS_PRECONDITION(nsnull != aChildList, "no child frame"); + + // Create a scrolling view frame + mFirstChild = new nsScrollingViewFrame(mContent, this); + if (nsnull == mFirstChild) { + return NS_ERROR_OUT_OF_MEMORY; + } + // Have it use our style context + mFirstChild->SetStyleContext(&aPresContext, mStyleContext); + + // Reset the child frame's geometric and content parent to be + // the scrolling view frame +#ifdef NS_DEBUG + // Verify that there's only one child frame + nsIFrame* nextSibling; + aChildList->GetNextSibling(nextSibling); + NS_ASSERTION(nsnull == nextSibling, "expected only one child"); +#endif + aChildList->SetGeometricParent(mFirstChild); + aChildList->SetContentParent(mFirstChild); + + // Init the scrollable view frame passing it the child list + return mFirstChild->Init(aPresContext, aChildList); +} + //XXX incremental reflow pass through NS_IMETHODIMP -nsScrollOuterFrame::Reflow(nsIPresContext& aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) +nsScrollFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) { NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("enter nsScrollOuterFrame::Reflow: maxSize=%d,%d", + ("enter nsScrollFrame::Reflow: maxSize=%d,%d", aReflowState.maxSize.width, aReflowState.maxSize.height)); - if (nsnull == mFirstChild) { - mFirstChild = new nsScrollInnerFrame(mContent, this); - } - + // We handle the border only const nsStyleSpacing* spacing = (const nsStyleSpacing*) mStyleContext->GetStyleData(eStyleStruct_Spacing); - nsMargin borderPadding; - spacing->CalcBorderPaddingFor(this, borderPadding); - nscoord lr = borderPadding.left + borderPadding.right; - nscoord tb = borderPadding.top + borderPadding.bottom; + nsMargin border; + spacing->CalcBorderFor(this, border); + nscoord lr = border.left + border.right; + nscoord tb = border.top + border.bottom; - // Get style size and determine how much area is available for the - // child (the scroll inner frame) to layout into. - nsSize maxSize; + // Compute the scrolling view frame's max size taking into account our + // borders + nsSize kidMaxSize; if (aReflowState.HaveConstrainedWidth()) { - maxSize.width = aReflowState.minWidth - lr; + // This value reflects space for the content area only, so don't + // subtract for borders... + kidMaxSize.width = aReflowState.minWidth; } else { - maxSize.width = aReflowState.maxSize.width; + kidMaxSize.width = aReflowState.maxSize.width; + if (NS_UNCONSTRAINEDSIZE != kidMaxSize.width) { + kidMaxSize.width -= lr; + } } if (aReflowState.HaveConstrainedHeight()) { - maxSize.height = aReflowState.minHeight - tb; + // This value reflects space for the content area only, so don't + // subtract for borders... + kidMaxSize.height = aReflowState.minHeight; } else { - maxSize.height = NS_UNCONSTRAINEDSIZE; + kidMaxSize.height = NS_UNCONSTRAINEDSIZE; + if (NS_UNCONSTRAINEDSIZE != kidMaxSize.height) { + kidMaxSize.height -= tb; + } } - + // Reflow the child and get its desired size - nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize); - nsIHTMLReflow* htmlReflow; + nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, kidMaxSize); - if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { - htmlReflow->WillReflow(aPresContext); - ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus); - NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); - // Place and size the child - nsRect rect(borderPadding.left, borderPadding.top, - aDesiredSize.width, aDesiredSize.height); - mFirstChild->SetRect(rect); - htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED); - } + // Place and size the child + nsRect rect(border.left, border.top, aDesiredSize.width, aDesiredSize.height); + mFirstChild->SetRect(rect); - // The scroll outer frame either shrink wraps around it's single - // child OR uses the style width/height. - if (aReflowState.HaveConstrainedWidth()) { - aDesiredSize.width = aReflowState.minWidth; - } - else { - aDesiredSize.width += lr; - } - if (aReflowState.HaveConstrainedHeight()) { - aDesiredSize.height = aReflowState.minHeight; - } - else { - aDesiredSize.height += tb; - } + // Compute our desired size by adding in space for the borders + aDesiredSize.width += lr; + aDesiredSize.height += tb; aDesiredSize.ascent = aDesiredSize.height; aDesiredSize.descent = 0; NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, - ("exit nsScrollOuterFrame::Reflow: status=%d width=%d height=%d", + ("exit nsScrollFrame::Reflow: status=%d width=%d height=%d", aStatus, aDesiredSize.width, aDesiredSize.height)); return NS_OK; } NS_IMETHODIMP -nsScrollOuterFrame::ListTag(FILE* out) const +nsScrollFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) { - fputs("*scrollouterframe<", out); + // Paint our border only (no background) + const nsStyleSpacing* spacing = + (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + + nsRect rect(0, 0, mRect.width, mRect.height); + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *spacing, 0); + + // Paint our children + return nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect); +} + +PRIntn +nsScrollFrame::GetSkipSides() const +{ + return 0; +} + +NS_IMETHODIMP +nsScrollFrame::ListTag(FILE* out) const +{ + fputs("*scrollframe<", out); nsIAtom* atom; mContent->GetTag(atom); if (nsnull != atom) { @@ -549,27 +491,16 @@ nsScrollOuterFrame::ListTag(FILE* out) const return NS_OK; } -PRIntn -nsScrollOuterFrame::GetSkipSides() const -{ - return 0; -} - //---------------------------------------------------------------------- nsresult -NS_NewScrollFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - nsIFrame* aParent) +NS_NewScrollFrame(nsIContent* aContent, + nsIFrame* aParentFrame, + nsIFrame*& aResult) { - NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); - if (nsnull == aInstancePtrResult) { - return NS_ERROR_NULL_POINTER; - } - nsIFrame* it = new nsScrollOuterFrame(aContent, aParent); - if (nsnull == it) { + aResult = new nsScrollFrame(aContent, aParentFrame); + if (nsnull == aResult) { return NS_ERROR_OUT_OF_MEMORY; } - *aInstancePtrResult = it; return NS_OK; } diff --git a/layout/html/base/src/nsScrollFrame.h b/layout/html/base/src/nsScrollFrame.h deleted file mode 100644 index b866f4a85df3..000000000000 --- a/layout/html/base/src/nsScrollFrame.h +++ /dev/null @@ -1,27 +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. - */ -#ifndef nsScrollFrame_h___ -#define nsScrollFrame_h___ - -#include "nsHTMLContainerFrame.h" - -extern nsresult NS_NewScrollFrame(nsIFrame** aInstancePtrResult, - nsIContent* aContent, - nsIFrame* aParent); - -#endif /* nsScrollFrame_h___ */ diff --git a/layout/html/style/src/nsHTMLStyleSheet.cpp b/layout/html/style/src/nsHTMLStyleSheet.cpp index 897aa7d071a4..4b7623917aa7 100644 --- a/layout/html/style/src/nsHTMLStyleSheet.cpp +++ b/layout/html/style/src/nsHTMLStyleSheet.cpp @@ -325,6 +325,8 @@ protected: nsIFrame* aParentFrame, nsIFrame*& aFrame); + PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay); + protected: PRUint32 mInHeap : 1; PRUint32 mRefCnt : 31; @@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext, return rv; } +PRBool +HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame, + const nsStyleDisplay* aDisplay) +{ + // If the overflow property is scroll then it's scrollable regardless + // of whether the content overflows the block. + // XXX This isn't correct. Only do this if the height is not allowed to + // grow to accomodate its child frames... + if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) { + return PR_TRUE; + } + +#if 0 + if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) || + // If the element has a fixed height (it isn't auto) and an overflow + // property of scroll or auto, then it's potentially scrollable. + // XXX Deal with width considerations, too + (NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) { +#endif + return PR_FALSE; +} + NS_IMETHODIMP HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, nsIContent* aContent, @@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, } -#if 0 - // If the frame is a block-level frame and it has a fixed height and overflow - // property of scroll, then wrap it in a scroll frame. - // XXX Deal with replaced elements and overflow of auto and width, too - nsIFrame* kidFrame = nsnull; - nsresult rv; - if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) || - (NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) { - rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidSC); + // If the frame is a block-level frame and is scrollable then wrap it + // in a scroll frame. + // XXX Applies to replaced elements, too, but how to tell if the element + // is replaced? + if (nsnull != aFrameSubTree) { + if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) { + nsIFrame* scrollFrame; + + if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) { + // The scroll frame gets the original style context, and the scrolled + // frame gets a pseudo style context. + // XXX Is this the best way to do this? + scrollFrame->SetStyleContext(aPresContext, styleContext); + + nsIStyleContext* pseudoStyle; + pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo, + scrollFrame); + aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle); + NS_RELEASE(pseudoStyle); + + // Initialize the scroll frame + scrollFrame->Init(*aPresContext, aFrameSubTree); + aFrameSubTree = scrollFrame; + } } } -#endif } } NS_RELEASE(styleContext); diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index 897aa7d071a4..4b7623917aa7 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -325,6 +325,8 @@ protected: nsIFrame* aParentFrame, nsIFrame*& aFrame); + PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay); + protected: PRUint32 mInHeap : 1; PRUint32 mRefCnt : 31; @@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext, return rv; } +PRBool +HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame, + const nsStyleDisplay* aDisplay) +{ + // If the overflow property is scroll then it's scrollable regardless + // of whether the content overflows the block. + // XXX This isn't correct. Only do this if the height is not allowed to + // grow to accomodate its child frames... + if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) { + return PR_TRUE; + } + +#if 0 + if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) || + // If the element has a fixed height (it isn't auto) and an overflow + // property of scroll or auto, then it's potentially scrollable. + // XXX Deal with width considerations, too + (NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) { +#endif + return PR_FALSE; +} + NS_IMETHODIMP HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, nsIContent* aContent, @@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext, } -#if 0 - // If the frame is a block-level frame and it has a fixed height and overflow - // property of scroll, then wrap it in a scroll frame. - // XXX Deal with replaced elements and overflow of auto and width, too - nsIFrame* kidFrame = nsnull; - nsresult rv; - if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) || - (NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) { - rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame); - if (NS_OK == rv) { - kidFrame->SetStyleContext(aPresContext, kidSC); + // If the frame is a block-level frame and is scrollable then wrap it + // in a scroll frame. + // XXX Applies to replaced elements, too, but how to tell if the element + // is replaced? + if (nsnull != aFrameSubTree) { + if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) { + nsIFrame* scrollFrame; + + if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) { + // The scroll frame gets the original style context, and the scrolled + // frame gets a pseudo style context. + // XXX Is this the best way to do this? + scrollFrame->SetStyleContext(aPresContext, styleContext); + + nsIStyleContext* pseudoStyle; + pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo, + scrollFrame); + aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle); + NS_RELEASE(pseudoStyle); + + // Initialize the scroll frame + scrollFrame->Init(*aPresContext, aFrameSubTree); + aFrameSubTree = scrollFrame; + } } } -#endif } } NS_RELEASE(styleContext);