Bug 411585. Traverse all pages after the current page when painting a page, to locate placeholders that we need to traverse in order to paint out-of-flows on the current page. r+sr=dbaron
authorroc+@cs.cmu.edu
Tue, 08 Apr 2008 06:06:12 -0700
changeset 14043 67abd0270ecb19ca7c85ff574c19c5e3872a9757
parent 14042 98c9179a875fbb4d68c948529eafd882c6fa82d0
child 14044 e45bb665d9b7fe7df12af3622df88e6b1b6935c1
push idunknown
push userunknown
push dateunknown
bugs411585
milestone1.9pre
Bug 411585. Traverse all pages after the current page when painting a page, to locate placeholders that we need to traverse in order to paint out-of-flows on the current page. r+sr=dbaron
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/generic/nsPageContentFrame.cpp
layout/generic/nsPageContentFrame.h
layout/generic/nsPageFrame.cpp
layout/reftests/bugs/411585-1-ref.html
layout/reftests/bugs/411585-1.html
layout/reftests/bugs/411585-2-ref.html
layout/reftests/bugs/411585-2.html
layout/reftests/bugs/411585-3-ref.html
layout/reftests/bugs/411585-3.html
layout/reftests/bugs/reftest.list
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1185,16 +1185,17 @@ public:
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
      const nsRect& aDirtyRect);
   virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder,
                                     nsRegion* aVisibleRegion);
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("Clip")
   
   nsRect GetClipRect() { return mClip; }
+  void SetClipRect(const nsRect& aRect) { mClip = aRect; }
 
   virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                            nsDisplayItem* aItem);
 
 private:
   nsRect mClip;
 };
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -819,31 +819,144 @@ private:
 
 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
      nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
 {
   aCtx->SetColor(mColor);
   aCtx->FillRect(aDirtyRect);
 }
 
+/**
+ * Remove all leaf display items that are not for descendants of
+ * aBuilder->GetReferenceFrame() from aList, and move all nsDisplayClip
+ * wrappers to their correct locations.
+ * @param aExtraPage the page we constructed aList for
+ * @param aY the Y-coordinate where aPage would be positioned relative
+ * to the main page (aBuilder->GetReferenceFrame()), considering only
+ * the content and ignoring page margins and dead space
+ * @param aList the list that is modified in-place
+ */
+static void
+PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
+        nsIFrame* aExtraPage, nscoord aY, nsDisplayList* aList)
+{
+  nsDisplayList newList;
+  // The page which we're really constructing a display list for
+  nsIFrame* mainPage = aBuilder->ReferenceFrame();
+
+  while (PR_TRUE) {
+    nsDisplayItem* i = aList->RemoveBottom();
+    if (!i)
+      break;
+    nsDisplayList* subList = i->GetList();
+    if (subList) {
+      PruneDisplayListForExtraPage(aBuilder, aExtraPage, aY, subList);
+      if (i->GetType() == nsDisplayItem::TYPE_CLIP) {
+        // This might clip an element which should appear on the first
+        // page, and that element might be visible if this uses a 'clip'
+        // property with a negative top.
+        // The clip area needs to be moved because the frame geometry doesn't
+        // put page content frames for adjacent pages vertically adjacent,
+        // there are page margins and dead space between them in print 
+        // preview, and in printing all pages are at (0,0)...
+        // XXX we have no way to test this right now that I know of;
+        // the 'clip' property requires an abs-pos element and we never
+        // paint abs-pos elements that start after the main page
+        // (bug 426909).
+        nsDisplayClip* clip = static_cast<nsDisplayClip*>(i);
+        clip->SetClipRect(clip->GetClipRect() + nsPoint(0, aY) -
+                aExtraPage->GetOffsetTo(mainPage));
+      }
+      newList.AppendToTop(i);
+    } else {
+      nsIFrame* f = i->GetUnderlyingFrame();
+      if (f && nsLayoutUtils::IsProperAncestorFrameCrossDoc(mainPage, f)) {
+        // This one is in the page we care about, keep it
+        newList.AppendToTop(i);
+      } else {
+        // We're throwing this away so call its destructor now. The memory
+        // is owned by aBuilder which destroys all items at once.
+        i->nsDisplayItem::~nsDisplayItem();
+      }
+    }
+  }
+  aList->AppendToTop(&newList);
+}
+
+static nsresult
+BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
+        nsIFrame* aPage, nscoord aY, nsDisplayList* aList)
+{
+  nsDisplayList list;
+  // Pass an empty dirty rect since we're only interested in finding
+  // placeholders whose out-of-flows are in the page
+  // aBuilder->GetReferenceFrame(), and the paths to those placeholders
+  // have already been marked as NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO.
+  // Note that we should still do a prune step since we don't want to
+  // rely on dirty-rect checking for correctness.
+  nsresult rv = aPage->BuildDisplayListForStackingContext(aBuilder, nsRect(), &list);
+  if (NS_FAILED(rv))
+    return rv;
+  PruneDisplayListForExtraPage(aBuilder, aPage, aY, &list);
+  aList->AppendToTop(&list);
+  return NS_OK;
+}
+
+static nsIFrame*
+GetNextPage(nsIFrame* aPageContentFrame)
+{
+  // XXX ugh
+  nsIFrame* pageFrame = aPageContentFrame->GetParent();
+  NS_ASSERTION(pageFrame->GetType() == nsGkAtoms::pageFrame,
+               "pageContentFrame has unexpected parent");
+  nsIFrame* nextPageFrame = pageFrame->GetNextSibling();
+  if (!nextPageFrame)
+    return nsnull;
+  NS_ASSERTION(nextPageFrame->GetType() == nsGkAtoms::pageFrame,
+               "pageFrame's sibling is not a page frame...");
+  nsIFrame* f = nextPageFrame->GetFirstChild(nsnull);
+  NS_ASSERTION(f, "pageFrame has no page content frame!");
+  NS_ASSERTION(f->GetType() == nsGkAtoms::pageContentFrame,
+               "pageFrame's child is not page content!");
+  return f;
+}
+
 nsresult
 nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
                           const nsRegion& aDirtyRegion, nscolor aBackground)
 {
   nsDisplayListBuilder builder(aFrame, PR_FALSE, PR_TRUE);
   nsDisplayList list;
   nsRect dirtyRect = aDirtyRegion.GetBounds();
 
   builder.EnterPresShell(aFrame, dirtyRect);
 
   nsresult rv;
   {
     nsAutoDisableGetUsedXAssertions disableAssert;
     rv =
       aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
+    
+    if (NS_SUCCEEDED(rv) && aFrame->GetType() == nsGkAtoms::pageContentFrame) {
+      // We may need to paint out-of-flow frames whose placeholders are
+      // on other pages. Add those pages to our display list. Note that
+      // out-of-flow frames can't be placed after their placeholders so
+      // we don't have to process earlier pages. The display lists for
+      // these extra pages are pruned so that only display items for the
+      // page we currently care about (which we would have reached by
+      // following placeholders to their out-of-flows) end up on the list.
+      nsIFrame* page = aFrame;
+      nscoord y = aFrame->GetSize().height;
+      while ((page = GetNextPage(page)) != nsnull) {
+        rv = BuildDisplayListForExtraPage(&builder, page, y, &list);
+        if (NS_FAILED(rv))
+          break;
+        y += page->GetSize().height;
+      }
+    }
   }
 
   builder.LeavePresShell(aFrame, dirtyRect);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (NS_GET_A(aBackground) > 0) {
     // Fill the visible area with a background color. In the common case,
     // the visible area is entirely covered by the background of the root
--- a/layout/generic/nsPageContentFrame.cpp
+++ b/layout/generic/nsPageContentFrame.cpp
@@ -247,23 +247,8 @@ nsPageContentFrame::GetFrameName(nsAStri
 }
 #endif
 
 /* virtual */ PRBool
 nsPageContentFrame::IsContainingBlock() const
 {
   return PR_TRUE;
 }
-
-
-//------------------------------------------------------------------------------
-NS_IMETHODIMP
-nsPageContentFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                     const nsRect&           aDirtyRect,
-                                     const nsDisplayListSet& aLists)
-{
-  nsDisplayListCollection set;
-  nsresult rv = ViewportFrame::BuildDisplayList(aBuilder, aDirtyRect, set);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return Clip(aBuilder, set, aLists,
-              nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
-}
--- a/layout/generic/nsPageContentFrame.h
+++ b/layout/generic/nsPageContentFrame.h
@@ -49,37 +49,33 @@ public:
   friend class nsPageFrame;
 
   // nsIFrame
   NS_IMETHOD  Reflow(nsPresContext*      aPresContext,
                      nsHTMLReflowMetrics& aDesiredSize,
                      const nsHTMLReflowState& aMaxSize,
                      nsReflowStatus&      aStatus);
 
-  NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                              const nsRect&           aDirtyRect,
-                              const nsDisplayListSet& aLists);
-
   virtual PRBool IsContainingBlock() const;
 
   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(nsIRenderingContext *aRenderingContext,
                              nsSize aCBSize, nscoord aAvailableWidth,
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
                              PRBool aShrinkWrap);
 
   /**
    * Get the "type" of the frame
    *
-   * @see nsGkAtoms::pageFrame
+   * @see nsGkAtoms::pageContentFrame
    */
   virtual nsIAtom* GetType() const;
   
 #ifdef NS_DEBUG
   // Debugging
   NS_IMETHOD  GetFrameName(nsAString& aResult) const;
 #endif
 
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -532,28 +532,27 @@ nsPageFrame::PaintHeaderFooter(nsIRender
 void
 nsPageFrame::PaintPageContent(nsIRenderingContext& aRenderingContext,
                               const nsRect&        aDirtyRect,
                               nsPoint              aPt) {
   nsIFrame* pageContentFrame  = mFrames.FirstChild();
   nsRect rect = aDirtyRect;
   float scale = PresContext()->GetPageScale();
   aRenderingContext.PushState();
-  // Make sure we don't draw where we aren't supposed to draw, especially
-  // when printing selection
-  nsRect clipRect(nsPoint(0, 0), GetSize());
-  clipRect.Deflate(mPD->mReflowMargin);
-  aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
+  nsPoint framePos = aPt + pageContentFrame->GetOffsetTo(this);
+  aRenderingContext.Translate(framePos.x, framePos.y);
   // aPt translates to coords relative to this, then margins translate to
   // pageContentFrame's coords
-  nsPoint framePos = aPt + pageContentFrame->GetOffsetTo(this);
-  aRenderingContext.Translate(framePos.x, framePos.y);
   rect -= framePos;
   aRenderingContext.Scale(scale, scale);
   rect.ScaleRoundOut(1.0f / scale);
+  // Make sure we don't draw where we aren't supposed to draw, especially
+  // when printing selection
+  nsRect clipRect(nsPoint(0, 0), pageContentFrame->GetSize());
+  aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
 
   const nsStyleBorder* border = GetStyleBorder();
   const nsStylePadding* padding = GetStylePadding();
   nsRect backgroundRect = nsRect(nsPoint(0, 0), pageContentFrame->GetSize());
   nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
                                   rect, backgroundRect, *border, *padding,
                                   PR_TRUE);
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div style="position:absolute; top:0; left:0;">This text should be visible in print preview</div>
+<div style="height:800px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div style="height:800px;"></div>
+<div style="position:absolute; top:0; left:0;">This text should be visible in print preview</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-2-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div style="position:absolute; top:0; left:0; width:100px; height:100px; background:black; opacity:0.5;">Hi</div>
+<div style="height:800px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div style="height:800px;"></div>
+<div style="opacity:0.5;">
+  <div style="position:absolute; top:0; left:0; width:100px; height:100px; background:black;">Hi</div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-3-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body style="margin:0">
+<div style="position:absolute; top:0; left:0; width:100px; height:100px; background:black;">Hi</div>
+<div style="height:800px;"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/411585-3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body style="margin:0">
+<div style="height:800px;"></div>
+<div style="position:absolute; clip:rect(-800px 200px 100px 0px);">
+  <div style="position:absolute; top:-800px; left:0; width:100px; height:100px; background:black;">Hi</div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -722,16 +722,19 @@ random == 403134-1.html 403134-1-ref.htm
 == 408782-1b.html 408782-1-ref.html
 == 408782-2a.html 408782-2-ref.html
 == 408782-2b.html 408782-2-ref.html
 == 409659-1a.html 409659-1-ref.html
 != 409659-1b.html 409659-1-ref.html
 != 409659-1c.html 409659-1-ref.html
 == 409659-1d.html 409659-1-ref.html
 == 411334-1.xml 411334-1-ref.xml
+== 411585-1.html 411585-1-ref.html
+== 411585-2.html 411585-2-ref.html
+fails == 411585-3.html 411585-3-ref.html # bug 426909
 == 411792-1.html 411792-1-ref.html
 == 412093-1.html 412093-1-ref.html
 == 412607-1a.html 412607-1-ref.html
 == 412607-1b.html 412607-1-ref.html
 == 412679-1.html 412679-1-ref.html
 == 412679-2.html 412679-2-ref.html
 fails == 413027-1.html 413027-1-ref.html
 fails == 413027-2.html 413027-2-ref.html