Bug 115199 - CSS2 @page rule rendering support. r=roc
authorBrendan Dahl <bdahl@mozilla.com>
Fri, 31 Aug 2012 14:20:56 -0700
changeset 113331 31312fa1abdee6f8089c549f426602800ac73329
parent 113330 638678d971c1bdb9573c77ecdc9279eee8e4243b
child 113332 2ea5d36b35a15f440c86bf825b1c3737234f4e59
push id23869
push useremorley@mozilla.com
push dateThu, 15 Nov 2012 16:18:16 +0000
treeherdermozilla-central@a37525d304d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs115199
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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