Bug 485275. Display fallback background color for non-root documents as necessary. r+sr=roc
authortn <tnikkel@gmail.com>
Sat, 25 Apr 2009 20:19:23 +1200
changeset 27834 444eff22b67500fa1ab675509f288e72e4551637
parent 27833 4113fe30352df6f757974b1db8043b211723bfc7
child 27835 bcfd2899cc379446a50f21b789666fef2c579afe
push id6736
push userrocallahan@mozilla.com
push dateSat, 25 Apr 2009 09:01:35 +0000
treeherderautoland@dddb7146a7d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs485275
milestone1.9.2a1pre
Bug 485275. Display fallback background color for non-root documents as necessary. r+sr=roc
layout/base/nsCSSRendering.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsPresShell.cpp
layout/generic/nsFrameFrame.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -972,19 +972,17 @@ nsCSSRendering::FindRootFrame(nsIFrame* 
  *  + we paint the correct background on the |nsCanvasFrame|,
  *    |nsRootBoxFrame|, or |nsPageFrame|,
  *  + we don't paint the background on the root element, and
  *  + we don't paint the background on the BODY element in *some* cases,
  *    and for SGML-based HTML documents only.
  *
  * |FindBackground| returns true if a background should be painted, and
  * the resulting style context to use for the background information
- * will be filled in to |aBackground|.  It fills in a boolean indicating
- * whether the frame is the canvas frame, because PaintBackground must
- * propagate that frame's background color to the view manager.
+ * will be filled in to |aBackground|.
  */
 const nsStyleBackground*
 nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
 {
   return FindRootFrame(aForFrame)->GetStyleBackground();
 }
 
 inline void
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -166,16 +166,18 @@ nsDisplayListBuilder::EnterPresShell(nsI
                                      const nsRect& aDirtyRect) {
   PresShellState* state = mPresShellStates.AppendElement();
   if (!state)
     return;
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nsnull;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
 
+  state->mPresShell->UpdateCanvasBackground();
+
   if (!mBuildCaret)
     return;
 
   nsRefPtr<nsCaret> caret;
   state->mPresShell->GetCaret(getter_AddRefs(caret));
   state->mCaretFrame = caret->GetCaretFrame();
 
   if (state->mCaretFrame) {
@@ -187,18 +189,17 @@ nsDisplayListBuilder::EnterPresShell(nsI
       mFramesMarkedForDisplay.AppendElement(state->mCaretFrame);
       MarkFrameForDisplay(state->mCaretFrame, nsnull);
     }
   }
 }
 
 void
 nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
-                                     const nsRect& aDirtyRect)
-{
+                                     const nsRect& aDirtyRect) {
   if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) {
     // Must have not allocated a state for this presshell, presumably due
     // to OOM.
     return;
   }
 
   // Unmark and pop off the frames marked for display in this pres shell.
   PRUint32 firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
@@ -249,17 +250,17 @@ nsDisplayItem::OptimizeVisibility(nsDisp
   NS_ASSERTION(f, "GetUnderlyingFrame() must return non-null for leaf items");
   PRBool isMoving = aBuilder->IsMovingFrame(f);
 
   if (IsOpaque(aBuilder)) {
     nsRect opaqueArea = bounds;
     if (isMoving) {
       // The display list should include items for both the before and after
       // states (see nsLayoutUtils::ComputeRepaintRegionForCopy. So the
-      // only area we want to cover is the the area that was opaque in the
+      // only area we want to cover is the area that was opaque in the
       // before state and in the after state.
       opaqueArea.IntersectRect(bounds - aBuilder->GetMoveDelta(), bounds);
     }
     aVisibleRegion->SimpleSubtract(opaqueArea);
   }
 
   return PR_TRUE;
 }
@@ -486,16 +487,40 @@ void nsDisplayList::SortByContentOrder(n
 }
 
 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
                          SortLEQ aCmp, void* aClosure) {
   ExplodeAnonymousChildLists(aBuilder);
   ::Sort(this, Count(), aCmp, aClosure);
 }
 
+void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
+     nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
+  nsRect dirty;
+  dirty.IntersectRect(GetBounds(aBuilder), aDirtyRect);
+  aCtx->SetColor(mColor);
+  aCtx->FillRect(dirty);
+}
+
+// Even though we aren't supposed to, we need to override this because
+// we have no frame.
+PRBool
+nsDisplaySolidColor::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
+                                        nsRegion* aVisibleRegion) {
+  // Do what nsDisplayItem::OptimizeVisibility would do but without a frame.
+  if (!aVisibleRegion->Intersects(mBounds))
+    return PR_FALSE;
+
+  if (IsOpaque(aBuilder)) {
+    aVisibleRegion->SimpleSubtract(mBounds);
+  }
+
+  return PR_TRUE;
+}
+
 // Returns TRUE if aContainedRect is guaranteed to be contained in
 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
 // handled conservatively by returning FALSE in some situations where
 // a more thorough analysis could return TRUE.
 static PRBool RoundedRectContainsRect(const nsRect& aRoundedRect,
                                       const nscoord aRadii[8],
                                       const nsRect& aContainedRect) {
   // rectFullHeight and rectFullWidth together will approximately contain
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1003,16 +1003,55 @@ public:
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
      const nsRect& aDirtyRect);
   virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion);
   NS_DISPLAY_DECL_NAME("Border")
 };
 
 /**
+ * A simple display item that just renders a solid color across a frame or
+ * specified bounds. Used in cases where we can't draw the frame tree but
+ * we want to draw something to avoid an ugly flash of white when
+ * navigating between pages. Also used as a bottom item to ensure that
+ * something is painted everywhere.
+ */
+class nsDisplaySolidColor : public nsDisplayItem {
+public:
+  nsDisplaySolidColor(const nsRect& aBounds, nscolor aColor)
+    : nsDisplayItem(nsnull), mBounds(aBounds), mColor(aColor) {
+    MOZ_COUNT_CTOR(nsDisplaySolidColor);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplaySolidColor() {
+    MOZ_COUNT_DTOR(nsDisplaySolidColor);
+  }
+#endif
+
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { return mBounds; }
+
+  virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder) {
+    return (NS_GET_A(mColor) == 255);
+  }
+
+  virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder) { return PR_TRUE; }
+
+  virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
+     const nsRect& aDirtyRect);
+
+  virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder,
+                                    nsRegion* aVisibleRegion);
+
+  NS_DISPLAY_DECL_NAME("SolidColor")
+private:
+  nsRect  mBounds;
+  nscolor mColor;
+};
+
+/**
  * The standard display item to paint the CSS background of a frame.
  */
 class nsDisplayBackground : public nsDisplayItem {
 public:
   nsDisplayBackground(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
     mIsThemed = mFrame->IsThemed();
     MOZ_COUNT_CTOR(nsDisplayBackground);
   }
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -96,20 +96,20 @@ class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 class gfxContext;
 class nsPIDOMEventTarget;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-// b8ace28a-d3fa-46d8-a5a0-d7c35c12fd41
+// fa1bf801-9fb6-4d19-8d33-698e9961fc10
 #define NS_IPRESSHELL_IID \
-{ 0xb8ace28a, 0xd3fa, 0x46d8, \
-  { 0xa5, 0xa0, 0xd7, 0xc3, 0x5c, 0x12, 0xfd, 0x41 } }
+{ 0xfa1bf801, 0x9fb6, 0x4d19, \
+  { 0x8d, 0x33, 0x69, 0x8e, 0x99, 0x61, 0xfc, 0x10 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -788,16 +788,21 @@ public:
    * This color is composited on top of the user's default background
    * color whenever we need to provide an "ultimate" background color.
    * See PresShell::Paint, PresShell::PaintDefaultBackground, and
    * nsDocShell::SetupNewViewer; bug 476557 and other bugs mentioned there.
    */
   void SetCanvasBackground(nscolor aColor) { mCanvasBackgroundColor = aColor; }
   nscolor GetCanvasBackground() { return mCanvasBackgroundColor; }
 
+  /* Use the current frame tree (if it exists) to update the background
+   * color of the most recent canvas.
+   */
+  virtual void UpdateCanvasBackground() = 0;
+
   void ObserveNativeAnonMutationsForPrint(PRBool aObserve)
   {
     mObservesMutationsForPrint = aObserve;
   }
   PRBool ObservesNativeAnonMutationsForPrint()
   {
     return mObservesMutationsForPrint;
   }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -936,46 +936,16 @@ nsLayoutUtils::GetFrameForPoint(nsIFrame
   
   nsDisplayItem::HitTestState hitTestState;
   nsIFrame* result = list.HitTest(&builder, aPt, &hitTestState);
   list.DeleteAll();
   return result;
 }
 
 /**
- * A simple display item that just renders a solid color across the entire
- * visible area.
- */
-class nsDisplaySolidColor : public nsDisplayItem {
-public:
-  nsDisplaySolidColor(nsIFrame* aFrame, nscolor aColor)
-    : nsDisplayItem(aFrame), mColor(aColor) {
-    MOZ_COUNT_CTOR(nsDisplaySolidColor);
-  }
-#ifdef NS_BUILD_REFCNT_LOGGING
-  virtual ~nsDisplaySolidColor() {
-    MOZ_COUNT_DTOR(nsDisplaySolidColor);
-  }
-#endif
-
-  virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
-     const nsRect& aDirtyRect);
-  NS_DISPLAY_DECL_NAME("SolidColor")
-private:
-  nscolor   mColor;
-};
-
-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
@@ -1102,17 +1072,19 @@ nsLayoutUtils::PaintFrame(nsIRenderingCo
   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
     // document (at least!) so this will be removed by the optimizer. In some
     // cases we might not have a root frame, so this will prevent garbage
     // from being drawn.
-    rv = list.AppendNewToBottom(new (&builder) nsDisplaySolidColor(aFrame, aBackground));
+    rv = list.AppendNewToBottom(new (&builder) nsDisplaySolidColor(
+           nsRect(builder.ToReferenceFrame(aFrame), aFrame->GetSize()),
+           aBackground));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
 #ifdef DEBUG
   if (gDumpPaintList) {
     fprintf(stderr, "Painting --- before optimization (dirty %d,%d,%d,%d):\n",
             dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
     nsIFrameDebug::PrintDisplayList(&builder, list);
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1012,16 +1012,18 @@ public:
 #endif
 
 #ifdef PR_LOGGING
   static PRLogModuleInfo* gLog;
 #endif
 
   NS_IMETHOD DisableNonTestMouseEvents(PRBool aDisable);
 
+  virtual void UpdateCanvasBackground();
+
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks();
   void CancelPostedReflowCallbacks();
 
   void UnsuppressAndInvalidate();
 
@@ -5518,35 +5520,40 @@ PresShell::RenderSelection(nsISelection*
       return nsnull;
     }
   }
 
   return PaintRangePaintInfo(&rangeItems, aSelection, nsnull, area, aPoint,
                              aScreenRect);
 }
 
+void PresShell::UpdateCanvasBackground()
+{
+  // If we have a frame tree and it has style information that
+  // specifies the background color of the canvas, update our local
+  // cache of that color.
+  nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
+  if (rootFrame) {
+    const nsStyleBackground* bgStyle =
+      nsCSSRendering::FindRootFrameBackground(rootFrame);
+    mCanvasBackgroundColor = bgStyle->mBackgroundColor;
+  }
+}
+
 NS_IMETHODIMP
 PresShell::Paint(nsIView*             aView,
                  nsIRenderingContext* aRenderingContext,
                  const nsRegion&      aDirtyRegion)
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
 
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aView, "null view");
 
-  // If we have a frame tree and it has style information that
-  // specifies the background color of the canvas, update our local
-  // cache of that color.
-  nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
-  if (rootFrame) {
-    const nsStyleBackground* bgStyle =
-      nsCSSRendering::FindRootFrameBackground(rootFrame);
-    mCanvasBackgroundColor = bgStyle->mBackgroundColor;
-  }
+  UpdateCanvasBackground();
 
   // Compute the backstop color for the view.
   nscolor bgcolor;
   nsIWidget* widget = aView->GetNearestWidget(nsnull);
   if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
     // Within a transparent widget, so the backstop color must be
     // totally transparent.
     bgcolor = NS_RGBA(0,0,0,0);
--- a/layout/generic/nsFrameFrame.cpp
+++ b/layout/generic/nsFrameFrame.cpp
@@ -316,17 +316,50 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (!mInnerView)
     return NS_OK;
   nsIView* subdocView = mInnerView->GetFirstChild();
   if (!subdocView)
     return NS_OK;
+
+  // Get the PresShell so we can check if painting is suppressed
+  // on the subdocument. We use this roundabout way in case we
+  // don't have a frame tree.
+  if (!mFrameLoader)
+    return NS_OK;
+  nsCOMPtr<nsIDocShell> docShell;
+  mFrameLoader->GetDocShell(getter_AddRefs(docShell));
+  if (!docShell)
+    return NS_OK;
+  nsCOMPtr<nsIPresShell> presShell;
+  docShell->GetPresShell(getter_AddRefs(presShell));
+  if (!presShell)
+    return NS_OK;
+
+  PRBool suppressed = PR_TRUE;
+  presShell->IsPaintingSuppressed(&suppressed);
+
   nsIFrame* f = static_cast<nsIFrame*>(subdocView->GetClientData());
+
+  if ((!f || suppressed) && !aBuilder->IsForEventDelivery()) {
+    // If we don't have a frame or painting of the PresShell is suppressed,
+    // try to draw the default background color. (Bug 485275)
+
+    // Get the bounds of subdocView relative to the reference frame.
+    nsRect shellBounds = subdocView->GetBounds() +
+                         mInnerView->GetPosition() +
+                         GetOffsetTo(aBuilder->ReferenceFrame());
+    rv = aLists.Content()->AppendNewToBottom(
+             new (aBuilder) nsDisplaySolidColor(
+                  shellBounds,
+                  presShell->GetCanvasBackground()));
+  }
+
   if (!f)
     return NS_OK;
   
   nsRect dirty = aDirtyRect - f->GetOffsetTo(this);
 
   aBuilder->EnterPresShell(f, dirty);
 
   // Clip children to the child root frame's rectangle