Bug 115199 - CSS2 @page rule rendering support. r=roc
☠☠ backed out by 0a7efaf3ac7a ☠ ☠
authorBrendan Dahl <bdahl@mozilla.com>
Fri, 31 Aug 2012 14:20:56 -0700
changeset 117801 30776e402787760e09265f5bf4d651ab5292aa28
parent 117800 8ef6c38c0f2c25f3a91cafe76e2f7d0355490a5c
child 117802 0b6b3d2856a44d7ab71f6cfe1783fe77fd76141f
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs115199
milestone19.0a1
Bug 115199 - CSS2 @page rule rendering support. r=roc
layout/generic/nsPageContentFrame.cpp
layout/generic/nsPageContentFrame.h
layout/generic/nsPageFrame.cpp
layout/generic/nsSimplePageSequence.cpp
layout/generic/nsSimplePageSequence.h
layout/reftests/printing/115199-1-ref.html
layout/reftests/printing/115199-1.html
layout/reftests/printing/115199-2-ref.html
layout/reftests/printing/115199-2a.html
layout/reftests/printing/115199-2b.html
layout/reftests/printing/reftest.list
--- a/layout/generic/nsPageContentFrame.cpp
+++ b/layout/generic/nsPageContentFrame.cpp
@@ -19,29 +19,16 @@
 nsIFrame*
 NS_NewPageContentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsPageContentFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsPageContentFrame)
 
-/* virtual */ nsSize
-nsPageContentFrame::ComputeSize(nsRenderingContext *aRenderingContext,
-                                nsSize aCBSize, nscoord aAvailableWidth,
-                                nsSize aMargin, nsSize aBorder, nsSize aPadding,
-                                uint32_t aFlags)
-{
-  NS_ASSERTION(mPD, "Pages are supposed to have page data");
-  nscoord height = (!mPD || mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE)
-                   ? NS_UNCONSTRAINEDSIZE
-                   : (mPD->mReflowSize.height - mPD->mReflowMargin.TopBottom());
-  return nsSize(aAvailableWidth, height);
-}
-
 NS_IMETHODIMP
 nsPageContentFrame::Reflow(nsPresContext*           aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
@@ -52,28 +39,29 @@ nsPageContentFrame::Reflow(nsPresContext
     nsresult rv = aPresContext->PresShell()->FrameConstructor()
                     ->ReplicateFixedFrames(this);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set our size up front, since some parts of reflow depend on it
   // being already set.  Note that the computed height may be
   // unconstrained; that's ok.  Consumers should watch out for that.
-  SetSize(nsSize(aReflowState.availableWidth, aReflowState.availableHeight));
+  nsSize  maxSize(aReflowState.ComputedWidth(),
+                  aReflowState.ComputedHeight());
+  SetSize(maxSize);
  
   // A PageContentFrame must always have one child: the canvas frame.
   // Resize our frame allowing it only to be as big as we are
   // XXX Pay attention to the page's border and padding...
   if (mFrames.NotEmpty()) {
     nsIFrame* frame = mFrames.FirstChild();
-    nsSize  maxSize(aReflowState.availableWidth, aReflowState.availableHeight);
     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize);
-    kidReflowState.SetComputedHeight(aReflowState.availableHeight);
+    kidReflowState.SetComputedHeight(maxSize.height);
 
-    mPD->mPageContentSize  = aReflowState.availableWidth;
+    mPD->mPageContentSize = maxSize.width;
 
     // Reflow the page content area
     rv = ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // The document element's background should cover the entire canvas, so
     // take into account the combined area and any space taken up by
     // absolutely positioned elements
@@ -107,19 +95,19 @@ nsPageContentFrame::Reflow(nsPresContext
   }
 
   // Reflow our fixed frames
   nsReflowStatus fixedStatus = NS_FRAME_COMPLETE;
   ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, fixedStatus);
   NS_ASSERTION(NS_FRAME_IS_COMPLETE(fixedStatus), "fixed frames can be truncated, but not incomplete");
 
   // Return our desired size
-  aDesiredSize.width = aReflowState.availableWidth;
-  if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
-    aDesiredSize.height = aReflowState.availableHeight;
+  aDesiredSize.width = aReflowState.ComputedWidth();
+  if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
+    aDesiredSize.height = aReflowState.ComputedHeight();
   }
 
   FinishAndStoreOverflow(&aDesiredSize);
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return NS_OK;
 }
 
--- a/layout/generic/nsPageContentFrame.h
+++ b/layout/generic/nsPageContentFrame.h
@@ -28,25 +28,16 @@ public:
   virtual bool IsFrameOfType(uint32_t aFlags) const
   {
     return ViewportFrame::IsFrameOfType(aFlags &
              ~(nsIFrame::eCanContainOverflowContainers));
   }
 
   virtual void SetSharedPageData(nsSharedPageData* aPD) { mPD = aPD; }
 
-  /**
-   *  Computes page size based on shared page data; SetSharedPageData must be
-   *  given valid data first.
-   */
-  virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext,
-                             nsSize aCBSize, nscoord aAvailableWidth,
-                             nsSize aMargin, nsSize aBorder, nsSize aPadding,
-                             uint32_t aFlags) MOZ_OVERRIDE;
-
   virtual bool HasTransformGetter() const MOZ_OVERRIDE { return true; }
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::pageContentFrame
    */
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -67,20 +67,19 @@ NS_IMETHODIMP nsPageFrame::Reflow(nsPres
     nsIFrame* frame = mFrames.FirstChild();
     // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing
     // a single page to print selection. So this means we want to use
     // NS_UNCONSTRAINEDSIZE without altering it
     nscoord avHeight;
     if (mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE) {
       avHeight = NS_UNCONSTRAINEDSIZE;
     } else {
-      avHeight = mPD->mReflowSize.height - mPD->mReflowMargin.TopBottom();
+      avHeight = mPD->mReflowSize.height;
     }
-    nsSize  maxSize(mPD->mReflowSize.width - mPD->mReflowMargin.LeftRight(),
-                    avHeight);
+    nsSize  maxSize(mPD->mReflowSize.width, avHeight);
     float scale = aPresContext->GetPageScale();
     maxSize.width = NSToCoordCeil(maxSize.width / scale);
     if (maxSize.height != NS_UNCONSTRAINEDSIZE) {
       maxSize.height = NSToCoordCeil(maxSize.height / scale);
     }
     // Get the number of Twips per pixel from the PresContext
     nscoord onePixelInTwips = nsPresContext::CSSPixelsToAppUnits(1);
     // insurance against infinite reflow, when reflowing less than a pixel
@@ -92,19 +91,56 @@ NS_IMETHODIMP nsPageFrame::Reflow(nsPres
       NS_WARNING("Reflow aborted; no space for content");
       return NS_OK;
     }
 
     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize);
     kidReflowState.mFlags.mIsTopOfPage = true;
     kidReflowState.mFlags.mTableIsSplittable = true;
 
+    // Use the margins given in the @page rule.
+    // If a margin is 'auto', use the margin from the print settings for that side.
+    nsMargin pageContentMargin;
+    const nsStyleSides& marginStyle = kidReflowState.mStyleMargin->mMargin;
+    NS_FOR_CSS_SIDES(side) {
+      if (marginStyle.GetUnit(side) == eStyleUnit_Auto) {
+        pageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
+      } else {
+        pageContentMargin.Side(side) = kidReflowState.mComputedMargin.Side(side);
+      }
+    }
+
+
+    nscoord maxWidth = maxSize.width - pageContentMargin.LeftRight() / scale;
+    nscoord maxHeight;
+    if (maxSize.height == NS_UNCONSTRAINEDSIZE) {
+      maxHeight = NS_UNCONSTRAINEDSIZE;
+    } else {
+      maxHeight = maxSize.height - pageContentMargin.TopBottom() / scale;
+    }
+
+    // Check the width and height, if they're too small we reset the margins
+    // back to the default.
+    if (maxWidth < onePixelInTwips ||
+       (maxHeight != NS_UNCONSTRAINEDSIZE && maxHeight < onePixelInTwips)) {
+      NS_FOR_CSS_SIDES(side) {
+        pageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
+      }
+      maxWidth = maxSize.width - pageContentMargin.LeftRight() / scale;
+      if (maxHeight != NS_UNCONSTRAINEDSIZE) {
+        maxHeight = maxSize.height - pageContentMargin.TopBottom() / scale;
+      }
+    }
+
+    kidReflowState.SetComputedWidth(maxWidth);
+    kidReflowState.SetComputedHeight(maxHeight);
+
     // calc location of frame
-    nscoord xc = mPD->mReflowMargin.left + mPD->mExtraMargin.left;
-    nscoord yc = mPD->mReflowMargin.top + mPD->mExtraMargin.top;
+    nscoord xc = pageContentMargin.left;
+    nscoord yc = pageContentMargin.top;
 
     // Get the child's desired size
     ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, xc, yc, 0, aStatus);
 
     // Place and size the child
     FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, xc, yc, 0);
 
     NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
@@ -215,25 +251,25 @@ nscoord nsPageFrame::GetXPosition(nsRend
                                   const nsString&      aStr)
 {
   nscoord width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
                                                 aStr.get(), aStr.Length());
 
   nscoord x = aRect.x;
   switch (aJust) {
     case nsIPrintSettings::kJustLeft:
-      x += mPD->mExtraMargin.left + mPD->mEdgePaperMargin.left;
+      x += mPD->mEdgePaperMargin.left;
       break;
 
     case nsIPrintSettings::kJustCenter:
       x += (aRect.width - width) / 2;
       break;
 
     case nsIPrintSettings::kJustRight:
-      x += aRect.width - width - mPD->mExtraMargin.right - mPD->mEdgePaperMargin.right;
+      x += aRect.width - width - mPD->mEdgePaperMargin.right;
       break;
   } // switch
 
   return x;
 }
 
 // Draw a header or footer
 // @param aRenderingContext - rendering content ot draw into
@@ -340,19 +376,19 @@ nsPageFrame::DrawHeaderFooter(nsRenderin
     if (HasRTLChars(str)) {
       PresContext()->SetBidiEnabled();
     }
 
     // cacl the x and y positions of the text
     nscoord x = GetXPosition(aRenderingContext, aRect, aJust, str);
     nscoord y;
     if (aHeaderFooter == eHeader) {
-      y = aRect.y + mPD->mExtraMargin.top + mPD->mEdgePaperMargin.top;
+      y = aRect.y + mPD->mEdgePaperMargin.top;
     } else {
-      y = aRect.YMost() - aHeight - mPD->mExtraMargin.bottom - mPD->mEdgePaperMargin.bottom;
+      y = aRect.YMost() - aHeight - mPD->mEdgePaperMargin.bottom;
     }
 
     // set up new clip and draw the text
     aRenderingContext.PushState();
     aRenderingContext.SetColor(NS_RGB(0,0,0));
     aRenderingContext.IntersectClip(aRect);
     nsLayoutUtils::DrawString(this, &aRenderingContext, str.get(), str.Length(), nsPoint(x, y + aAscent));
     aRenderingContext.PopState();
@@ -506,18 +542,17 @@ nsPageFrame::BuildDisplayList(nsDisplayL
       break;
     y += page->GetSize().height;
   }
 
   float scale = PresContext()->GetPageScale();
   nsRect clipRect(nsPoint(0, 0), child->GetSize());
   // Note: this computation matches how we compute maxSize.height
   // in nsPageFrame::Reflow
-  nscoord expectedPageContentHeight = 
-    NSToCoordCeil((GetSize().height - mPD->mReflowMargin.TopBottom()) / scale);
+  nscoord expectedPageContentHeight = NSToCoordCeil(GetSize().height / scale);
   if (clipRect.height > expectedPageContentHeight) {
     // We're doing print-selection, with one long page-content frame.
     // Clip to the appropriate page-content slice for the current page.
     NS_ASSERTION(mPageNum > 0, "page num should be positive");
     // Note: The pageContentFrame's y-position has been set such that a zero
     // y-value matches the top edge of the current page.  So, to clip to the
     // current page's content (in coordinates *relative* to the page content
     // frame), we just negate its y-position and add the top margin.
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -60,17 +60,16 @@ nsSharedPageData::nsSharedPageData() :
   mDateTimeStr(nullptr),
   mHeadFootFont(nullptr),
   mPageNumFormat(nullptr),
   mPageNumAndTotalsFormat(nullptr),
   mDocTitle(nullptr),
   mDocURL(nullptr),
   mReflowSize(0,0),
   mReflowMargin(0,0,0,0),
-  mExtraMargin(0,0,0,0),
   mEdgePaperMargin(0,0,0,0),
   mPageContentXMost(0),
   mPageContentSize(0)
 {
 }
 
 nsSharedPageData::~nsSharedPageData()
 {
@@ -215,51 +214,32 @@ nsSimplePageSequenceFrame::Reflow(nsPres
   // If we're printing a selection, we need to reflow with
   // unconstrained height, to make sure we'll get to the selection
   // even if it's beyond the first page of content.
   if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
     mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE;
   }
   mPageData->mReflowMargin = mMargin;
 
-  // Compute the size of each page and the x coordinate that each page will
-  // be placed at
-  nscoord extraThreshold = NS_MAX(pageSize.width, pageSize.height)/10;
-  int32_t gapInTwips = Preferences::GetInt("print.print_extra_margin");
-  gapInTwips = NS_MAX(0, gapInTwips);
-
-  nscoord extraGap = aPresContext->CSSTwipsToAppUnits(gapInTwips);
-  extraGap = NS_MIN(extraGap, extraThreshold); // clamp to 1/10 of the largest dim of the page
-
-  nsMargin extraMargin(0,0,0,0);
-  if (aPresContext->IsScreen()) {
-    extraMargin.SizeTo(extraGap, extraGap, extraGap, extraGap);
-  }
-
-  mPageData->mExtraMargin = extraMargin;
-
   // We use the CSS "margin" property on the -moz-page pseudoelement
   // to determine the space between each page in print preview.
   // Keep a running y-offset for each page.
   nscoord y = 0;
   nscoord maxXMost = 0;
 
-  nsSize availSize(pageSize.width + extraMargin.LeftRight(),
-                   pageSize.height + extraMargin.TopBottom());
-
   // Tile the pages vertically
   nsHTMLReflowMetrics kidSize;
   for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) {
     // Set the shared data into the page frame before reflow
     nsPageFrame * pf = static_cast<nsPageFrame*>(kidFrame);
     pf->SetSharedPageData(mPageData);
 
     // Reflow the page
     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame,
-                                     availSize);
+                                     pageSize);
     nsReflowStatus  status;
 
     kidReflowState.SetComputedWidth(kidReflowState.availableWidth);
     //kidReflowState.SetComputedHeight(kidReflowState.availableHeight);
     PR_PL(("AV W: %d   H: %d\n", kidReflowState.availableWidth, kidReflowState.availableHeight));
 
     nsMargin pageCSSMargin = kidReflowState.mComputedMargin;
     y += pageCSSMargin.top;
--- a/layout/generic/nsSimplePageSequence.h
+++ b/layout/generic/nsSimplePageSequence.h
@@ -26,19 +26,16 @@ public:
   nsFont *    mHeadFootFont;
   PRUnichar * mPageNumFormat;
   PRUnichar * mPageNumAndTotalsFormat;
   PRUnichar * mDocTitle;
   PRUnichar * mDocURL;
 
   nsSize      mReflowSize;
   nsMargin    mReflowMargin;
-  // Extra Margin between the device area and the edge of the page;
-  // approximates unprintable area
-  nsMargin    mExtraMargin;
   // Margin for headers and footers; it defaults to 4/100 of an inch on UNIX 
   // and 0 elsewhere; I think it has to do with some inconsistency in page size
   // computations
   nsMargin    mEdgePaperMargin;
 
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   nsCOMPtr<nsIPrintOptions> mPrintOptions;
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/115199-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+    }
+    body {
+      margin: 0.1in 0.1in;
+    }
+    div {
+      border: 0.25in solid green;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/115199-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+  <style>
+    @page {
+      /*
+       * Default page margins are .5in and the reference file adds .1inch margin
+       * to the body hence we do .6in to match this.
+       */
+      margin: 0.6in;
+    }
+    * {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      width: 100%;
+      border: 0.25in solid green;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/115199-2-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      width: 100%;
+      border: 0.25in solid green;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/115199-2a.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+  <style>
+    @page {
+      /*
+       * Test a margin that causes the page to have zero width.
+       */
+      margin: 0 2.5in;
+    }
+    * {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      width: 100%;
+      border: 0.25in solid green;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/115199-2b.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+  <style>
+    @page {
+      /*
+       * Test huge margins, they should be reset back the default margin size.
+       */
+      margin: 10in;
+    }
+    * {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      width: 100%;
+      border: 0.25in solid green;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
--- a/layout/reftests/printing/reftest.list
+++ b/layout/reftests/printing/reftest.list
@@ -15,10 +15,13 @@
 == 577450-1.html 577450-1-ref.html
 == 626395-1a.html 626395-1-ref.html
 == 626395-1b.html 626395-1-ref.html
 == 626395-2a.html 626395-2-ref.html
 == 626395-2b.html 626395-2-ref.html
 == 626395-2c.html 626395-2-ref.html
 == 626395-2d.html 626395-2-ref.html
 == 652178-1.html 652178-1-ref.html
+== 115199-1.html 115199-1-ref.html
+== 115199-2a.html 115199-2-ref.html
+== 115199-2b.html 115199-2-ref.html
 == 652178-1.html 652178-1-ref2.html
 == 745025-1.html 745025-1-ref.html