Bug 476557: update view manager cache of page background color in PresShell::Paint, not nsCSSRendering::PaintBackground; permits a small API cleanup; r+sr=roc
authorZack Weinberg <zweinberg@mozilla.com>
Wed, 25 Feb 2009 09:35:27 -0800
changeset 23441 a6844aa97aceeddc9a81b434d6e68a132412c5e0
parent 23440 95813ca07013f642a9be2442a8e405309340af8b
child 23442 191401e9d02ab97943225a532ac2b21fcfba49f2
push id778
push userrocallahan@mozilla.com
push dateThu, 26 Feb 2009 09:56:42 +0000
bugs476557
milestone1.9.1b3pre
Bug 476557: update view manager cache of page background color in PresShell::Paint, not nsCSSRendering::PaintBackground; permits a small API cleanup; r+sr=roc
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsPresShell.cpp
layout/generic/nsContainerFrame.cpp
layout/tables/nsTableFrame.cpp
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9684,19 +9684,17 @@ ApplyRenderingChangeToTree(nsPresContext
     if (!aChange) {
       return;
     }
   }
 
   // If the frame's background is propagated to an ancestor, walk up to
   // that ancestor.
   const nsStyleBackground *bg;
-  PRBool isCanvas;
-  while (!nsCSSRendering::FindBackground(aPresContext, aFrame,
-                                         &bg, &isCanvas)) {
+  while (!nsCSSRendering::FindBackground(aPresContext, aFrame, &bg)) {
     aFrame = aFrame->GetParent();
     NS_ASSERTION(aFrame, "root frame must paint");
   }
 
   nsIViewManager* viewManager = aPresContext->GetViewManager();
 
   // Trigger rendering updates by damaging this frame and any
   // continuations of this frame.
@@ -9751,20 +9749,18 @@ InvalidateCanvasIfNeeded(nsIFrame* aFram
   }
 
   // At this point the node has no parent or it's an HTML <body> child of the
   // root.  We might not need to invalidate in this case (eg we might be in
   // XHTML or something), but chances are we want to.  Play it safe.  Find the
   // frame to invalidate and do it.
   nsIFrame *ancestor = aFrame;
   const nsStyleBackground *bg;
-  PRBool isCanvas;
   nsPresContext* presContext = aFrame->PresContext();
-  while (!nsCSSRendering::FindBackground(presContext, ancestor,
-                                         &bg, &isCanvas)) {
+  while (!nsCSSRendering::FindBackground(presContext, ancestor, &bg)) {
     ancestor = ancestor->GetParent();
     NS_ASSERTION(ancestor, "canvas must paint");
   }
 
   if (ancestor->GetType() == nsGkAtoms::canvasFrame) {
     // The canvas frame's dimensions are not meaningful; invalidate the
     // viewport instead.
     ancestor = ancestor->GetParent();
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -898,71 +898,75 @@ nsCSSRendering::FindNonTransparentBackgr
  * whether the frame is the canvas frame, because PaintBackground must
  * propagate that frame's background color to the view manager.
  */
 
 // Returns true if aFrame is a canvas frame.
 // We need to treat the viewport as canvas because, even though
 // it does not actually paint a background, we need to get the right
 // background style so we correctly detect transparent documents.
-inline PRBool
-IsCanvasFrame(nsIFrame *aFrame)
+PRBool
+nsCSSRendering::IsCanvasFrame(nsIFrame *aFrame)
 {
   nsIAtom* frameType = aFrame->GetType();
   return frameType == nsGkAtoms::canvasFrame ||
          frameType == nsGkAtoms::rootFrame ||
          frameType == nsGkAtoms::pageFrame ||
          frameType == nsGkAtoms::pageContentFrame ||
          frameType == nsGkAtoms::viewportFrame;
 }
 
-inline PRBool
+const nsStyleBackground*
+nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
+{
+  const nsStyleBackground* result = aForFrame->GetStyleBackground();
+
+  // Check if we need to do propagation from BODY rather than HTML.
+  if (result->IsTransparent()) {
+    nsIContent* content = aForFrame->GetContent();
+    // The root element content can't be null. We wouldn't know what
+    // frame to create for aForFrame.
+    // Use |GetOwnerDoc| so it works during destruction.
+    nsIDocument* document = content->GetOwnerDoc();
+    nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
+    if (htmlDoc) {
+      nsIContent* bodyContent = htmlDoc->GetBodyContentExternal();
+      // We need to null check the body node (bug 118829) since
+      // there are cases, thanks to the fix for bug 5569, where we
+      // will reflow a document with no body.  In particular, if a
+      // SCRIPT element in the head blocks the parser and then has a
+      // SCRIPT that does "document.location.href = 'foo'", then
+      // nsParser::Terminate will call |DidBuildModel| methods
+      // through to the content sink, which will call |StartLayout|
+      // and thus |InitialReflow| on the pres shell.  See bug 119351
+      // for the ugly details.
+      if (bodyContent) {
+        nsIFrame *bodyFrame = aForFrame->PresContext()->GetPresShell()->
+          GetPrimaryFrameFor(bodyContent);
+        if (bodyFrame)
+          result = bodyFrame->GetStyleBackground();
+      }
+    }
+  }
+
+  return result;
+}
+
+inline void
 FindCanvasBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
                      const nsStyleBackground** aBackground)
 {
   if (aRootElementFrame) {
-    const nsStyleBackground* result = aRootElementFrame->GetStyleBackground();
-
-    // Check if we need to do propagation from BODY rather than HTML.
-    if (result->IsTransparent()) {
-      nsIContent* content = aRootElementFrame->GetContent();
-      // The root element content can't be null. We wouldn't know what
-      // frame to create for aRootElementFrame.
-      // Use |GetOwnerDoc| so it works during destruction.
-      nsIDocument* document = content->GetOwnerDoc();
-      nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
-      if (htmlDoc) {
-        nsIContent* bodyContent = htmlDoc->GetBodyContentExternal();
-        // We need to null check the body node (bug 118829) since
-        // there are cases, thanks to the fix for bug 5569, where we
-        // will reflow a document with no body.  In particular, if a
-        // SCRIPT element in the head blocks the parser and then has a
-        // SCRIPT that does "document.location.href = 'foo'", then
-        // nsParser::Terminate will call |DidBuildModel| methods
-        // through to the content sink, which will call |StartLayout|
-        // and thus |InitialReflow| on the pres shell.  See bug 119351
-        // for the ugly details.
-        if (bodyContent) {
-          nsIFrame *bodyFrame = aForFrame->PresContext()->GetPresShell()->
-            GetPrimaryFrameFor(bodyContent);
-          if (bodyFrame)
-            result = bodyFrame->GetStyleBackground();
-        }
-      }
-    }
-
-    *aBackground = result;
+    *aBackground = nsCSSRendering::FindRootFrameBackground(aRootElementFrame);
   } else {
     // This should always give transparent, so we'll fill it in with the
     // default color if needed.  This seems to happen a bit while a page is
     // being loaded.
     *aBackground = aForFrame->GetStyleBackground();
   }
-
-  return PR_TRUE;
 }
 
 inline PRBool
 FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
                       const nsStyleBackground** aBackground)
 {
   if (aForFrame == aRootElementFrame) {
     // We must have propagated our background to the viewport or canvas. Abort.
@@ -1001,26 +1005,26 @@ FindElementBackground(nsIFrame* aForFram
 
   const nsStyleBackground* htmlBG = aRootElementFrame->GetStyleBackground();
   return !htmlBG->IsTransparent();
 }
 
 PRBool
 nsCSSRendering::FindBackground(nsPresContext* aPresContext,
                                nsIFrame* aForFrame,
-                               const nsStyleBackground** aBackground,
-                               PRBool* aIsCanvas)
+                               const nsStyleBackground** aBackground)
 {
   nsIFrame* rootElementFrame =
     aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
-  PRBool isCanvasFrame = IsCanvasFrame(aForFrame);
-  *aIsCanvas = isCanvasFrame;
-  return isCanvasFrame
-      ? FindCanvasBackground(aForFrame, rootElementFrame, aBackground)
-      : FindElementBackground(aForFrame, rootElementFrame, aBackground);
+  if (IsCanvasFrame(aForFrame)) {
+    FindCanvasBackground(aForFrame, rootElementFrame, aBackground);
+    return PR_TRUE;
+  } else {
+    return FindElementBackground(aForFrame, rootElementFrame, aBackground);
+  }
 }
 
 void
 nsCSSRendering::DidPaint()
 {
   gInlineBGData->Reset();
 }
 
@@ -1157,20 +1161,18 @@ nsCSSRendering::PaintBackground(nsPresCo
                                 const nsRect& aDirtyRect,
                                 const nsRect& aBorderArea,
                                 PRBool aUsePrintSettings,
                                 nsRect* aBGClipRect)
 {
   NS_PRECONDITION(aForFrame,
                   "Frame is expected to be provided to PaintBackground");
 
-  PRBool isCanvas;
   const nsStyleBackground *color;
-
-  if (!FindBackground(aPresContext, aForFrame, &color, &isCanvas)) {
+  if (!FindBackground(aPresContext, aForFrame, &color)) {
     // we don't want to bail out of moz-appearance is set on a root
     // node. If it has a parent content node, bail because it's not
     // a root, other wise keep going in order to let the theme stuff
     // draw the background. The canvas really should be drawing the
     // bg, but there's no way to hook that up via css.
     if (!aForFrame->GetStyleDisplay()->mAppearance) {
       return;
     }
@@ -1178,23 +1180,16 @@ nsCSSRendering::PaintBackground(nsPresCo
     nsIContent* content = aForFrame->GetContent();
     if (!content || content->GetParent()) {
       return;
     }
         
     color = aForFrame->GetStyleBackground();
   }
 
-  if (isCanvas) {
-    nsIViewManager* vm = aPresContext->GetViewManager();
-    vm->SetDefaultBackgroundColor(
-      NS_ComposeColors(aPresContext->DefaultBackgroundColor(),
-                       color->mBackgroundColor));
-  }
-
   PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
                         aDirtyRect, aBorderArea, *color,
                         *aForFrame->GetStyleBorder(),
                         aUsePrintSettings, aBGClipRect);
 }
 
 static PRBool
 IsSolidBorderEdge(const nsStyleBorder& aBorder, PRUint32 aSide)
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -108,27 +108,37 @@ struct nsCSSRendering {
    * Not used for controls, because the native theme may differ.
    */
   static void PaintFocus(nsPresContext* aPresContext,
                          nsIRenderingContext& aRenderingContext,
                          const nsRect& aFocusRect,
                          nscolor aColor);
 
   /**
-   * Fill in an nsStyleBackground to be used to paint the background for
-   * an element.  The nsStyleBackground should first be initialized
-   * using the pres context.  This applies the rules for propagating
+   * @return PR_TRUE if |aForFrame| is a canvas frame, in the CSS sense.
+   */
+  static PRBool IsCanvasFrame(nsIFrame* aFrame);
+
+  /**
+   * Fill in an nsStyleBackground to be used to paint the background
+   * for an element.  This applies the rules for propagating
    * backgrounds between BODY, the root element, and the canvas.
    * @return PR_TRUE if there is some meaningful background.
    */
   static PRBool FindBackground(nsPresContext* aPresContext,
                                nsIFrame* aForFrame,
-                               const nsStyleBackground** aBackground,
-                               PRBool* aIsCanvas);
-                               
+                               const nsStyleBackground** aBackground);
+
+  /**
+   * As FindBackground, but the passed-in frame is known to be a root frame
+   * (returned from nsCSSFrameConstructor::GetRootElementStyleFrame())
+   * and there is always some meaningful background returned.
+   */
+  static const nsStyleBackground* FindRootFrameBackground(nsIFrame* aForFrame);
+
   /**
    * Find a non-transparent background, for various table-related and
    * HR-related backwards-compatibility hacks.  Be very hesitant if
    * you're considering calling this function -- it's usually not what
    * you want.
    */
   static const nsStyleBackground*
   FindNonTransparentBackground(nsStyleContext* aContext,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -488,57 +488,53 @@ void nsDisplayList::Sort(nsDisplayListBu
 
 PRBool
 nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder) {
   // theme background overrides any other background
   if (mIsThemed)
     return PR_FALSE;
 
   const nsStyleBackground* bg;
-  PRBool isCanvas; // not used
   PRBool hasBG =
-    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame,
-                                   &bg, &isCanvas);
+    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg);
 
   return (hasBG && NS_GET_A(bg->mBackgroundColor) == 255 &&
           bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER &&
           !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->
                                          mBorderRadius));
 }
 
 PRBool
 nsDisplayBackground::IsUniform(nsDisplayListBuilder* aBuilder) {
   // theme background overrides any other background
   if (mIsThemed)
     return PR_FALSE;
 
-  PRBool isCanvas;
   const nsStyleBackground* bg;
   PRBool hasBG =
-    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg, &isCanvas);
+    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg);
   if (!hasBG)
     return PR_TRUE;
   if ((bg->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) &&
       !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius) &&
       bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER)
     return PR_TRUE;
   return PR_FALSE;
 }
 
 PRBool
 nsDisplayBackground::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder)
 {
   NS_ASSERTION(aBuilder->IsMovingFrame(mFrame),
               "IsVaryingRelativeToMovingFrame called on non-moving frame!");
 
   nsPresContext* presContext = mFrame->PresContext();
-  PRBool isCanvas;
   const nsStyleBackground* bg;
   PRBool hasBG =
-    nsCSSRendering::FindBackground(presContext, mFrame, &bg, &isCanvas);
+    nsCSSRendering::FindBackground(presContext, mFrame, &bg);
   if (!hasBG)
     return PR_FALSE;
   if (!bg->HasFixedBackground())
     return PR_FALSE;
 
   nsIFrame* movingFrame = aBuilder->GetRootMovingFrame();
   // movingFrame is the frame that is going to be moved. It must be equal
   // to mFrame or some ancestor of mFrame, see assertion above.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3067,19 +3067,18 @@ nsLayoutUtils::GetFrameTransparency(nsIF
   // We need an uninitialized window to be treated as opaque because
   // doing otherwise breaks window display effects on some platforms,
   // specifically Vista. (bug 450322)
   if (aFrame->GetType() == nsGkAtoms::viewportFrame &&
       !aFrame->GetFirstChild(nsnull)) {
     return eTransparencyOpaque;
   }
 
-  PRBool isCanvas;
   const nsStyleBackground* bg;
-  if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg, &isCanvas))
+  if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg))
     return eTransparencyTransparent;
   if (NS_GET_A(bg->mBackgroundColor) < 255)
     return eTransparencyTransparent;
   if (bg->mBackgroundClip != NS_STYLE_BG_CLIP_BORDER)
     return eTransparencyTransparent;
   return eTransparencyOpaque;
 }
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5385,44 +5385,72 @@ PresShell::Paint(nsIView*             aV
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
 
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aView, "null view");
 
   // Compute the backstop color for the view.  This color must be
   // totally transparent if the view is within a glass or transparent
-  // widget; otherwise, use the default in the prescontext, which will
+  // widget; otherwise it must be totally opaque.  The user's default
+  // background color as recorded in the prescontext is guaranteed to
   // be opaque.
 
-  PRBool needTransparency = PR_FALSE;
-
+  nscolor backgroundColor = mPresContext->DefaultBackgroundColor();
   for (nsIView *view = aView; view; view = view->GetParent()) {
     if (view->HasWidget() &&
         view->GetWidget()->GetTransparencyMode() != eTransparencyOpaque) {
-      needTransparency = PR_TRUE;
+      backgroundColor = NS_RGBA(0,0,0,0);
       break;
     }
   }
 
-  nscolor backgroundColor;
-  if (needTransparency)
-    backgroundColor = NS_RGBA(0,0,0,0);
-  else
-    backgroundColor = mPresContext->DefaultBackgroundColor();
-
+  // Check whether the view manager knows the background color of the
+  // canvas.  We set this below, and the docshell propagates it across
+  // page loads; using it in preference to the user's default color
+  // avoids screen flashing in between pages that use the same
+  // non-default background.
+  //
+  // If we're called at some weird moment when there is no view
+  // manager, default to transparent.
+  nscolor viewDefaultColor = NS_RGBA(0,0,0,0);
+  if (mViewManager)
+    mViewManager->GetDefaultBackgroundColor(&viewDefaultColor);
+
+  // If we don't have a frame tree yet, all we can do is paint the
+  // backstop colors.
   nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
-  if (frame) {
-    nsLayoutUtils::PaintFrame(aRenderingContext, frame, aDirtyRegion,
-                              backgroundColor);
-  } else if (NS_GET_A(backgroundColor) > 0) {
+  if (!frame) {
+    backgroundColor = NS_ComposeColors(backgroundColor, viewDefaultColor);
     aRenderingContext->SetColor(backgroundColor);
     aRenderingContext->FillRect(aDirtyRegion.GetBounds());
-  }
-
+    return NS_OK;
+  }
+
+  // If we do have a frame tree, check whether it specifies a canvas
+  // background color yet.  If it does, use that instead of whatever
+  // color the view manager reported, and update the view manager
+  // accordingly.
+  nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
+  if (rootFrame) {
+    const nsStyleBackground* bgStyle =
+      nsCSSRendering::FindRootFrameBackground(rootFrame);
+    // XXX ideally we would set the view manager default to
+    // bgStyle->mBackgroundColor, and nsViewManager::DefaultRefresh would
+    // be able to cope with partial transparency.  But it can't so we can't.
+    // -- zwol 2009-02-11
+    backgroundColor = NS_ComposeColors(backgroundColor,
+                                       bgStyle->mBackgroundColor);
+    mViewManager->SetDefaultBackgroundColor(backgroundColor);
+  } else {
+    backgroundColor = NS_ComposeColors(backgroundColor, viewDefaultColor);
+  }
+
+  nsLayoutUtils::PaintFrame(aRenderingContext, frame, aDirtyRegion,
+                            backgroundColor);
   return NS_OK;
 }
 
 nsIFrame*
 PresShell::GetCurrentEventFrame()
 {
   if (NS_UNLIKELY(mIsDestroying)) {
     return nsnull;
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -477,37 +477,34 @@ IsTopLevelWidget(nsPresContext* aPresCon
   nsWindowType windowType;
   mainWidget->GetWindowType(windowType);
   return windowType == eWindowType_toplevel ||
          windowType == eWindowType_dialog;
   // popups aren't toplevel so they're not handled here
 }
 
 static void
-SyncFrameViewGeometryDependentProperties(nsPresContext*  aPresContext,
+SyncFrameViewGeometryDependentProperties(nsPresContext*   aPresContext,
                                          nsIFrame*        aFrame,
                                          nsStyleContext*  aStyleContext,
                                          nsIView*         aView,
                                          PRUint32         aFlags)
 {
 #ifdef MOZ_XUL
-  nsIViewManager* vm = aView->GetViewManager();
-
-  PRBool isCanvas;
-  const nsStyleBackground* bg;
-  nsCSSRendering::FindBackground(aPresContext, aFrame, &bg, &isCanvas);
-
-  if (!isCanvas)
+  if (!nsCSSRendering::IsCanvasFrame(aFrame))
     return;
 
+  if (!aView->HasWidget() || !IsTopLevelWidget(aPresContext))
+    return;
+
+  nsIViewManager* vm = aView->GetViewManager();
   nsIView* rootView;
   vm->GetRootView(rootView);
 
-  if (!aView->HasWidget() || aView != rootView ||
-      !IsTopLevelWidget(aPresContext))
+  if (aView != rootView)
     return;
 
   nsIContent* rootContent = aPresContext->Document()->GetRootContent();
   if (!rootContent || !rootContent->IsNodeOfType(nsINode::eXUL)) {
     // Scrollframes use native widgets which don't work well with
     // translucent windows, at least in Windows XP. So if the document
     // has a root scrollrame it's useless to try to make it transparent,
     // we'll just get something broken.
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1279,21 +1279,18 @@ nsDisplayTableItem::IsVaryingRelativeToM
   nsIFrame* rootMover = aBuilder->GetRootMovingFrame();
   return mFrame == rootMover ||
     nsLayoutUtils::IsProperAncestorFrame(rootMover, mFrame);
 }
 
 /* static */ void
 nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
 {
-  PRBool isCanvas;
   const nsStyleBackground* bg;
-  PRBool hasBG =
-    nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg, &isCanvas);
-  if (!hasBG)
+  if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg))
     return;
   if (!bg->HasFixedBackground())
     return;
 
   mPartHasFixedBackground = PR_TRUE;
 }
 
 class nsDisplayTableBorderBackground : public nsDisplayTableItem {