diff --git a/layout/html/base/src/nsScrollFrame.cpp b/layout/html/base/src/nsScrollFrame.cpp
new file mode 100644
index 000000000000..cade60df608f
--- /dev/null
+++ b/layout/html/base/src/nsScrollFrame.cpp
@@ -0,0 +1,516 @@
+/* -*- 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 "nsScrollFrame.h"
+#include "nsIPresContext.h"
+#include "nsIContentDelegate.h"
+#include "nsIStyleContext.h"
+#include "nsReflowCommand.h"
+#include "nsIDeviceContext.h"
+#include "nsPageFrame.h"
+#include "nsViewsCID.h"
+#include "nsIView.h"
+#include "nsIViewManager.h"
+#include "nsBodyFrame.h"
+#include "nsHTMLFrame.h"
+
+#include "nsBodyFrame.h"
+
+class nsScrollBodyFrame : public nsContainerFrame {
+public:
+ nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent);
+
+ NS_IMETHOD Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& 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);
+};
+
+nsScrollBodyFrame::nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent)
+ : nsContainerFrame(aContent, aParent)
+{
+ nsHTMLFrame::CreateViewForFrame(nsnull, this, nsnull, PR_TRUE);
+}
+
+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 */
+ mChildCount = 1;
+ mLastContentOffset = mFirstContentOffset;
+ } 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);
+ mChildCount = 1;
+ mLastContentOffset = mFirstContentOffset;
+#endif
+ }
+ }
+}
+
+// XXX Hack
+#define PAGE_SPACING 100
+
+NS_IMETHODIMP
+nsScrollBodyFrame::Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& aReflowState,
+ nsReflowStatus& aStatus)
+{
+ NS_FRAME_TRACE_MSG(("enter nsScrollBodyFrame::Reflow: maxSize=%d,%d",
+ aReflowState.maxSize.width,
+ aReflowState.maxSize.height));
+
+#ifdef NS_DEBUG
+ PreReflowCheck();
+#endif
+ aStatus = NS_FRAME_COMPLETE;
+
+ // 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
+ NS_ASSERTION(aReflowState.reflowCommand->GetTarget() != this,
+ "root content frame is reflow command target");
+
+ // Verify the next frame in the reflow chain is our child frame
+ nsIFrame* next = aReflowState.reflowCommand->GetNext();
+ NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
+
+ nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
+ nsReflowState kidReflowState(next, aReflowState, maxSize);
+
+ // Dispatch the reflow command to our child frame. Allow it to be as high
+ // as it wants
+ mFirstChild->WillReflow(*aPresContext);
+ aStatus = ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState);
+
+ // 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);
+ mFirstChild->DidReflow(*aPresContext, NS_FRAME_REFLOW_FINISHED);
+
+ } else {
+ // Do we have any children?
+ if (nsnull == mFirstChild) {
+ // No, create the first child frame
+ CreateFirstChild(aPresContext);
+ }
+
+ // Resize our frames
+ if (nsnull != mFirstChild) {
+ if (aPresContext->IsPaginated()) {
+ nscoord y = PAGE_SPACING;
+ nsReflowMetrics kidSize(aDesiredSize.maxElementSize);
+ nsSize pageSize(aPresContext->GetPageWidth(),
+ aPresContext->GetPageHeight());
+
+ // Tile the pages vertically
+ for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
+ // Reflow the page
+ nsReflowState kidReflowState(kidFrame, aReflowState, pageSize);
+ nsReflowStatus status;
+
+ // Place and size the page. If the page is narrower than our
+ // max width then center it horizontally
+ nsIDeviceContext *dx = aPresContext->GetDeviceContext();
+ PRInt32 extra = aReflowState.maxSize.width - kidSize.width -
+ NS_TO_INT_ROUND(dx->GetScrollBarWidth());
+ NS_RELEASE(dx);
+ nscoord x = extra > 0 ? extra / 2 : 0;
+
+ kidFrame->WillReflow(*aPresContext);
+ kidFrame->MoveTo(x, y);
+ status = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState);
+
+ kidFrame->SetRect(nsRect(x, y, kidSize.width, kidSize.height));
+ kidFrame->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);
+ mChildCount++;
+ }
+
+ // 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);
+ nsReflowState kidReflowState(mFirstChild, aReflowState, maxSize);
+
+ // Get the child's desired size. Our child's desired height is our
+ // desired size
+ mFirstChild->WillReflow(*aPresContext);
+ aStatus = ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState);
+ 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);
+ mFirstChild->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;
+ }
+ }
+ }
+
+#ifdef NS_DEBUG
+ PostReflowCheck(aStatus);
+#endif
+
+ NS_FRAME_TRACE_MSG(
+ ("exit nsScrollBodyFrame::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)
+{
+ // 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;
+}
+
+NS_IMETHODIMP
+nsScrollBodyFrame::ListTag(FILE* out) const
+{
+ fputs("*scrollbodyframe<", out);
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ PRInt32 contentIndex;
+ GetContentIndex(contentIndex);
+ fprintf(out, ">(%d)@%p", contentIndex, this);
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+class nsScrollInnerFrame : public nsContainerFrame {
+public:
+ nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent);
+
+ NS_IMETHOD Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& aReflowState,
+ nsReflowStatus& aStatus);
+
+ NS_IMETHOD ListTag(FILE* out = stdout) const;
+
+protected:
+};
+
+nsScrollInnerFrame::nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent)
+ : nsContainerFrame(aContent, aParent)
+{
+}
+
+NS_IMETHODIMP
+nsScrollInnerFrame::Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& aReflowState,
+ nsReflowStatus& aStatus)
+{
+ NS_FRAME_TRACE_MSG(("enter nsScrollInnerFrame::Reflow: maxSize=%d,%d",
+ aReflowState.maxSize.width,
+ aReflowState.maxSize.height));
+
+ nsIView* view;
+ GetView(view);
+ if (nsnull == view) {
+ static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID);
+ static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
+
+ // Get parent view
+ nsIFrame* parent;
+ GetParentWithView(parent);
+ NS_ASSERTION(parent, "GetParentWithView failed");
+ nsIView* parentView;
+ parent->GetView(parentView);
+ NS_ASSERTION(parentView, "GetParentWithView failed");
+
+ nsIViewManager* viewManager = parentView->GetViewManager();
+
+ nsresult rv = NSRepository::CreateInstance(kScrollingViewCID,
+ nsnull,
+ kIViewIID,
+ (void **)&view);
+ if ((NS_OK != rv) || (NS_OK != view->Init(viewManager,
+ mRect,
+ parentView))) {
+ NS_RELEASE(parentView);
+ 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);
+ }
+ if (nsnull == view) {
+ return NS_OK;
+ }
+
+ if (nsnull == mFirstChild) {
+ mFirstChild = new nsScrollBodyFrame(mContent, this);
+ mChildCount = 1;
+ }
+
+ // 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);
+ nsReflowState kidReflowState(mFirstChild, aReflowState, maxSize);
+
+ // Get the child's desired size. Our child's desired height is our
+ // desired size
+ mFirstChild->WillReflow(*aPresContext);
+ aStatus = ReflowChild(mFirstChild, aPresContext, aDesiredSize,
+ kidReflowState);
+ NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
+
+ // Place and size the child
+ nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
+ mFirstChild->SetRect(rect);
+ mFirstChild->DidReflow(*aPresContext, NS_FRAME_REFLOW_FINISHED);
+
+ NS_RELEASE(view);
+
+ NS_FRAME_TRACE_MSG(
+ ("exit nsScrollInnerFrame::Reflow: status=%d width=%d height=%d",
+ aStatus, aDesiredSize.width, aDesiredSize.height));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScrollInnerFrame::ListTag(FILE* out) const
+{
+ fputs("*scrollinnerframe<", out);
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ PRInt32 contentIndex;
+ GetContentIndex(contentIndex);
+ fprintf(out, ">(%d)@%p", contentIndex, this);
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+class nsScrollOuterFrame : public nsContainerFrame {
+public:
+ nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent);
+
+ NS_IMETHOD Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& aReflowState,
+ nsReflowStatus& aStatus);
+
+ NS_IMETHOD ListTag(FILE* out = stdout) const;
+
+protected:
+};
+
+nsScrollOuterFrame::nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent)
+ : nsContainerFrame(aContent, aParent)
+{
+}
+
+NS_IMETHODIMP
+nsScrollOuterFrame::Reflow(nsIPresContext* aPresContext,
+ nsReflowMetrics& aDesiredSize,
+ const nsReflowState& aReflowState,
+ nsReflowStatus& aStatus)
+{
+ NS_FRAME_TRACE_MSG(("enter nsScrollOuterFrame::Reflow: maxSize=%d,%d",
+ aReflowState.maxSize.width,
+ aReflowState.maxSize.height));
+
+ if (nsnull == mFirstChild) {
+ mFirstChild = new nsScrollInnerFrame(mContent, this);
+ mChildCount = 1;
+ }
+
+ nsStyleSpacing* spacing = (nsStyleSpacing*)
+ mStyleContext->GetData(eStyleStruct_Spacing);
+ nsMargin borderPadding;
+ spacing->CalcBorderPaddingFor(this, borderPadding);
+ nscoord lr = borderPadding.left + borderPadding.right;
+
+ // Allow the frame to be as wide as our max width, and as high
+ // as it wants to be.
+ nsSize maxSize(aReflowState.maxSize.width - lr, NS_UNCONSTRAINEDSIZE);
+ nsReflowState kidReflowState(mFirstChild, aReflowState, maxSize);
+
+ // Get the child's desired size. Our child's desired height is
+ // approximately our desired size
+ mFirstChild->WillReflow(*aPresContext);
+ aStatus = ReflowChild(mFirstChild, aPresContext, aDesiredSize,
+ kidReflowState);
+ 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);
+ mFirstChild->DidReflow(*aPresContext, NS_FRAME_REFLOW_FINISHED);
+
+ aDesiredSize.width += lr;
+ aDesiredSize.height += borderPadding.top + borderPadding.bottom;
+ aDesiredSize.ascent = aDesiredSize.height;
+ aDesiredSize.descent = 0;
+
+ NS_FRAME_TRACE_MSG(
+ ("exit nsScrollOuterFrame::Reflow: status=%d width=%d height=%d",
+ aStatus, aDesiredSize.width, aDesiredSize.height));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScrollOuterFrame::ListTag(FILE* out) const
+{
+ fputs("*scrollouterframe<", out);
+ nsIAtom* atom = mContent->GetTag();
+ if (nsnull != atom) {
+ nsAutoString tmp;
+ atom->ToString(tmp);
+ fputs(tmp, out);
+ }
+ PRInt32 contentIndex;
+ GetContentIndex(contentIndex);
+ fprintf(out, ">(%d)@%p", contentIndex, this);
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+NS_NewScrollFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ nsIFrame* aParent)
+{
+ NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
+ if (nsnull == aInstancePtrResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ nsIFrame* it = new nsScrollOuterFrame(aContent, aParent);
+ if (nsnull == it) {
+ 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
new file mode 100644
index 000000000000..e51aca53504c
--- /dev/null
+++ b/layout/html/base/src/nsScrollFrame.h
@@ -0,0 +1,27 @@
+/* -*- 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 "nsContainerFrame.h"
+
+extern nsresult NS_NewScrollFrame(nsIFrame** aInstancePtrResult,
+ nsIContent* aContent,
+ nsIFrame* aParent);
+
+#endif /* nsScrollFrame_h___ */