Bug 810470. Part 1: Cache nsStyleBackground pointer in nsDisplayBackground. r=mattwoodrow,a=akeybl
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 07 Nov 2012 23:08:40 -0500
changeset 117004 be8a3c6fb70b1aa5122a47b09b9fffa5195064af
parent 117003 5c4e46b4958d7832cc3246d6288a87984c59e9b0
child 117005 6152c4a60d6fb3591e1008453e8afd04a4718911
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, akeybl
bugs810470
milestone18.0a2
Bug 810470. Part 1: Cache nsStyleBackground pointer in nsDisplayBackground. r=mattwoodrow,a=akeybl
layout/base/nsCSSRendering.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsCanvasFrame.cpp
layout/generic/nsCanvasFrame.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1797,37 +1797,37 @@ nsCSSRendering::DetermineBackgroundColor
   aDrawBackgroundImage = true;
   aDrawBackgroundColor = true;
 
   if (aFrame->HonorPrintBackgroundSettings()) {
     aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
     aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
   }
 
+  const nsStyleBackground *bg = aStyleContext->GetStyleBackground();
   nscolor bgColor;
   if (aDrawBackgroundColor) {
     bgColor =
       aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);
-    if (NS_GET_A(bgColor) == 0)
+    if (NS_GET_A(bgColor) == 0) {
       aDrawBackgroundColor = false;
+    }
   } else {
     // If GetBackgroundColorDraw() is false, we are still expected to
     // draw color in the background of any frame that's not completely
     // transparent, but we are expected to use white instead of whatever
     // color was specified.
     bgColor = NS_RGB(255, 255, 255);
-    if (aDrawBackgroundImage ||
-        !aStyleContext->GetStyleBackground()->IsTransparent())
+    if (aDrawBackgroundImage || !bg->IsTransparent()) {
       aDrawBackgroundColor = true;
-    else
+    } else {
       bgColor = NS_RGBA(0,0,0,0);
+    }
   }
 
-  const nsStyleBackground *bg = aStyleContext->GetStyleBackground();
-
   // We can skip painting the background color if a background image is opaque.
   if (aDrawBackgroundColor &&
       bg->BottomLayer().mRepeat.mXRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
       bg->BottomLayer().mRepeat.mYRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
       bg->BottomLayer().mImage.IsOpaque()) {
     aDrawBackgroundColor = false;
   }
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1454,77 +1454,76 @@ RegisterThemeGeometry(nsDisplayListBuild
   nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize());
   aBuilder->RegisterThemeGeometry(aFrame->GetStyleDisplay()->mAppearance,
       borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel()));
 }
 
 nsDisplayBackground::nsDisplayBackground(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame,
                                          uint32_t aLayer,
+                                         bool aIsThemed,
+                                         const nsStyleBackground* aBackgroundStyle,
                                          bool aSkipFixedItemBoundsCheck)
   : nsDisplayItem(aBuilder, aFrame)
+  , mBackgroundStyle(aBackgroundStyle)
+  , mLayer(aLayer)
+  , mIsThemed(aIsThemed)
   , mIsFixed(false)
   , mIsBottommostLayer(true)
-  , mLayer(aLayer)
 {
   MOZ_COUNT_CTOR(nsDisplayBackground);
-  const nsStyleDisplay* disp = mFrame->GetStyleDisplay();
-  mIsThemed = mFrame->IsThemed(disp, &mThemeTransparency);
 
   if (mIsThemed) {
+    const nsStyleDisplay* disp = mFrame->GetStyleDisplay();
+    mFrame->IsThemed(disp, &mThemeTransparency);
     // Perform necessary RegisterThemeGeometry
     if (disp->mAppearance == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
         disp->mAppearance == NS_THEME_TOOLBAR) {
       RegisterThemeGeometry(aBuilder, aFrame);
     } else if (disp->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
                disp->mAppearance == NS_THEME_WIN_GLASS) {
       aBuilder->SetGlassDisplayItem(this);
     }
-  } else {
+  } else if (mBackgroundStyle) {
     // Set HasFixedItems if we construct a background-attachment:fixed item
-    nsPresContext* presContext = mFrame->PresContext();
-    nsStyleContext* bgSC;
-    bool hasBG = nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
-    if (hasBG) {
-      const nsStyleBackground* bg = bgSC->GetStyleBackground();
-      if (mLayer != bg->mImageCount - 1) {
-        mIsBottommostLayer = false;
-      }
-
-      // Check if this background layer is attachment-fixed
-      if (!bg->mLayers[mLayer].mImage.IsEmpty() &&
-          bg->mLayers[mLayer].mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED) {
-        aBuilder->SetHasFixedItems();
-
-        // Check whether we should fix to viewport scrolling
-        if (bg->mLayers[mLayer].mClip == NS_STYLE_BG_CLIP_BORDER &&
-            !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius)) {
-          if (aSkipFixedItemBoundsCheck) {
-            mIsFixed = true;
-          } else {
-            nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
-            if (rootScrollFrame) {
-              bool snap;
-              nsRect bounds = GetBounds(aBuilder, &snap);
-
-              // This bounds check prevents an item fixing to the viewport unless it
-              // it encompasses the scroll-port. If a fixed background doesn't
-              // encompass the scroll-port, it usually means that scrolling will
-              // expose a new area of the fixed background and cause a lot of
-              // invalidation. This performs badly, and looks especially bad when
-              // async scrolling is being used.
-              // XXX A better check would be to see if the underlying frame is fixed to
-              //     the viewport/is the viewport.
-              nsIScrollableFrame* scrollable = do_QueryFrame(rootScrollFrame);
-              nsRect scrollport(scrollable->GetScrollPortRect().TopLeft() +
-                                aBuilder->ToReferenceFrame(rootScrollFrame),
-                                scrollable->GetScrollPositionClampingScrollPortSize());
-
-              mIsFixed = bounds.Contains(scrollport);
-            }
+    if (mLayer != mBackgroundStyle->mImageCount - 1) {
+      mIsBottommostLayer = false;
+    }
+
+    // Check if this background layer is attachment-fixed
+    if (!mBackgroundStyle->mLayers[mLayer].mImage.IsEmpty() &&
+        mBackgroundStyle->mLayers[mLayer].mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED) {
+      aBuilder->SetHasFixedItems();
+
+      // Check whether we should fix to viewport scrolling
+      if (mBackgroundStyle->mLayers[mLayer].mClip == NS_STYLE_BG_CLIP_BORDER &&
+          !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius)) {
+        if (aSkipFixedItemBoundsCheck) {
+          mIsFixed = true;
+        } else {
+          nsPresContext* presContext = mFrame->PresContext();
+          nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
+          if (rootScrollFrame) {
+            bool snap;
+            nsRect bounds = GetBounds(aBuilder, &snap);
+
+            // This bounds check prevents an item fixing to the viewport unless it
+            // it encompasses the scroll-port. If a fixed background doesn't
+            // encompass the scroll-port, it usually means that scrolling will
+            // expose a new area of the fixed background and cause a lot of
+            // invalidation. This performs badly, and looks especially bad when
+            // async scrolling is being used.
+            // XXX A better check would be to see if the underlying frame is fixed to
+            //     the viewport/is the viewport.
+            nsIScrollableFrame* scrollable = do_QueryFrame(rootScrollFrame);
+            nsRect scrollport(scrollable->GetScrollPortRect().TopLeft() +
+                              aBuilder->ToReferenceFrame(rootScrollFrame),
+                              scrollable->GetScrollPositionClampingScrollPortSize());
+
+            mIsFixed = bounds.Contains(scrollport);
           }
         }
       }
     }
   }
 }
 
 nsDisplayBackground::~nsDisplayBackground()
@@ -1538,42 +1537,43 @@ nsDisplayBackground::~nsDisplayBackgroun
 nsDisplayBackground::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
                                                 nsIFrame* aFrame,
                                                 nsDisplayList* aList,
                                                 nsDisplayBackground** aBackground)
 {
   nsStyleContext* bgSC = nullptr;
   const nsStyleBackground* bg = nullptr;
   nsPresContext* presContext = aFrame->PresContext();
-  if (!aFrame->IsThemed() &&
+  bool isThemed = aFrame->IsThemed();
+  if (!isThemed &&
       nsCSSRendering::FindBackground(presContext, aFrame, &bgSC)) {
     bg = bgSC->GetStyleBackground();
   }
 
   bool drawBackgroundColor = false;
   nscolor color;
   if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
     bool drawBackgroundImage;
     color =
       nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame,
                                                drawBackgroundImage, drawBackgroundColor);
   }
 
   // Even if we don't actually have a background color to paint, we still need
   // to create the item because it's used for hit testing.
   aList->AppendNewToTop(
-      new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame,
+      new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg,
                                               drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0)));
  
   // Passing bg == nullptr in this macro will result in one iteration with
   // i = 0.
   bool backgroundSet = !aBackground;
   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
     nsDisplayBackground* bgItem =
-      new (aBuilder) nsDisplayBackground(aBuilder, aFrame, i);
+      new (aBuilder) nsDisplayBackground(aBuilder, aFrame, i, isThemed, bg);
     nsresult rv = aList->AppendNewToTop(bgItem);
     if (rv != NS_OK) {
       return rv;
     }
     if (!backgroundSet) {
       *aBackground = bgItem;
       backgroundSet = true;
     }
@@ -1674,23 +1674,22 @@ static bool RoundedRectContainsRect(cons
                                       const nsRect& aContainedRect) {
   nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect);
   return rgn.Contains(aContainedRect);
 }
 
 bool
 nsDisplayBackground::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, const nsRect& aClipRect)
 {
-  if (mIsThemed)
+  if (mIsThemed || !mBackgroundStyle)
     return false;
 
   nsPresContext* presContext = mFrame->PresContext();
   nsStyleContext* bgSC;
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC))
-    return false;
+  nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
 
   bool drawBackgroundImage;
   bool drawBackgroundColor;
   nsCSSRendering::DetermineBackgroundColor(presContext,
                                            bgSC,
                                            mFrame,
                                            drawBackgroundImage,
                                            drawBackgroundColor);
@@ -1733,23 +1732,22 @@ nsDisplayBackground::IsSingleFixedPositi
   mDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
 
   return true;
 }
 
 bool
 nsDisplayBackground::TryOptimizeToImageLayer(nsDisplayListBuilder* aBuilder)
 {
-  if (mIsThemed)
+  if (mIsThemed || !mBackgroundStyle)
     return false;
 
   nsPresContext* presContext = mFrame->PresContext();
   nsStyleContext* bgSC;
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC))
-    return false;
+  nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
 
   bool drawBackgroundImage;
   bool drawBackgroundColor;
   nsCSSRendering::DetermineBackgroundColor(presContext,
                                            bgSC,
                                            mFrame,
                                            drawBackgroundImage,
                                            drawBackgroundColor);
@@ -1893,19 +1891,17 @@ nsDisplayBackground::ComputeVisibility(n
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
                                         aAllowVisibleRegionExpansion)) {
     return false;
   }
 
   // Return false if the background was propagated away from this
   // frame. We don't want this display item to show up and confuse
   // anything.
-  nsStyleContext* bgSC;
-  return mIsThemed ||
-    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC);
+  return mIsThemed || mBackgroundStyle;
 }
 
 /* static */ nsRegion
 nsDisplayBackground::GetInsideClipRegion(nsDisplayItem* aItem,
                                          nsPresContext* aPresContext,
                                          uint8_t aClip, const nsRect& aRect,
                                          bool* aSnap)
 {
@@ -1953,37 +1949,36 @@ nsDisplayBackground::GetOpaqueRegion(nsD
   // theme background overrides any other background
   if (mIsThemed) {
     if (mThemeTransparency == nsITheme::eOpaque) {
       result = nsRect(ToReferenceFrame(), mFrame->GetSize());
     }
     return result;
   }
 
-  nsStyleContext* bgSC;
-  nsPresContext* presContext = mFrame->PresContext();
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC))
+  if (!mBackgroundStyle)
     return result;
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
+
 
   *aSnap = true;
 
   // For policies other than EACH_BOX, don't try to optimize here, since
   // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
   // which expects frames to be sent to it in content order, not reverse
   // content order which we'll produce here.
   // Of course, if there's only one frame in the flow, it doesn't matter.
-  if (bg->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX ||
+  if (mBackgroundStyle->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX ||
       (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
-    const nsStyleBackground::Layer& layer = bg->mLayers[mLayer];
+    const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
     if (layer.mImage.IsOpaque()) {
       nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
+      nsPresContext* presContext = mFrame->PresContext();
       nsRect r = nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
-          borderBox, *bg, layer);
-      result.Or(result, GetInsideClipRegion(this, presContext, layer.mClip, r, aSnap));
+          borderBox, *mBackgroundStyle, layer);
+      result = GetInsideClipRegion(this, presContext, layer.mClip, r, aSnap);
     }
   }
 
   return result;
 }
 
 bool
 nsDisplayBackground::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
@@ -1993,47 +1988,38 @@ nsDisplayBackground::IsUniform(nsDisplay
     if (disp->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS ||
         disp->mAppearance == NS_THEME_WIN_GLASS) {
       *aColor = NS_RGBA(0,0,0,0);
       return true;
     }
     return false;
   }
 
-  nsStyleContext *bgSC;
-  bool hasBG =
-    nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC);
-  if (!hasBG) {
+  if (!mBackgroundStyle) {
     *aColor = NS_RGBA(0,0,0,0);
     return true;
   }
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
-  if (bg->mLayers[mLayer].mImage.IsEmpty()) {
+  if (mBackgroundStyle->mLayers[mLayer].mImage.IsEmpty()) {
     *aColor = NS_RGBA(0,0,0,0);
     return true;
   }
   return false;
 }
 
 bool
 nsDisplayBackground::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
                                                     nsIFrame* aFrame)
 {
   // theme background overrides any other background and is never fixed
   if (mIsThemed)
     return false;
 
-  nsPresContext* presContext = mFrame->PresContext();
-  nsStyleContext *bgSC;
-  bool hasBG =
-    nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
-  if (!hasBG)
+  if (!mBackgroundStyle)
     return false;
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
-  if (!bg->HasFixedBackground())
+  if (!mBackgroundStyle->HasFixedBackground())
     return false;
 
   // If aFrame is mFrame or an ancestor in this document, and aFrame is
   // not the viewport frame, then moving aFrame will move mFrame
   // relative to the viewport, so our fixed-pos background will change.
   return aFrame->GetParent() &&
     (aFrame == mFrame ||
      nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame));
@@ -2047,25 +2033,20 @@ nsDisplayBackground::RenderingMightDepen
     return true;
   
   // We could be smarter with rounded corners and only invalidate the new area + the piece that was previously
   // clipped out.
   nscoord radii[8];
   if (mFrame->GetBorderRadii(radii))
     return true;
 
-  nsPresContext* presContext = mFrame->PresContext();
-  nsStyleContext *bgSC;
-  bool hasBG =
-    nsCSSRendering::FindBackground(presContext, mFrame, &bgSC);
-  if (!hasBG)
+  if (!mBackgroundStyle)
     return false;
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
-
-  const nsStyleBackground::Layer &layer = bg->mLayers[mLayer];
+
+  const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
   if (layer.RenderingMightDependOnFrameSize()) {
     return true;
   }
   return false;
 }
 
 bool
 nsDisplayBackground::ShouldFixToViewport(nsDisplayListBuilder* aBuilder)
@@ -2134,26 +2115,24 @@ nsDisplayBackground::GetBounds(nsDisplay
 #ifdef XP_MACOSX
     // Bug 748219
     r.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
 #endif
 
     return r + ToReferenceFrame();
   }
 
-  nsStyleContext* bgSC;
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC)) {
+  if (!mBackgroundStyle) {
     return nsRect();
   }
 
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
   nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  const nsStyleBackground::Layer& layer = bg->mLayers[mLayer];
+  const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer];
   return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame,
-                                                borderBox, *bg, layer);
+                                                borderBox, *mBackgroundStyle, layer);
 }
 
 uint32_t
 nsDisplayBackground::GetPerFrameKey()
 {
   return (mLayer << nsDisplayItem::TYPE_BITS) |
     nsDisplayItem::GetPerFrameKey();
 }
@@ -2178,41 +2157,37 @@ nsDisplayBackgroundColor::Paint(nsDispla
 nsRegion
 nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                           bool* aSnap) 
 {
   if (NS_GET_A(mColor) != 255) {
     return nsRegion();
   }
 
-  nsStyleContext* bgSC;
-  nsPresContext* presContext = mFrame->PresContext();
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC))
+  if (!mBackgroundStyle)
     return nsRegion();
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
-  const nsStyleBackground::Layer& bottomLayer = bg->BottomLayer();
 
   *aSnap = true;
 
+  const nsStyleBackground::Layer& bottomLayer = mBackgroundStyle->BottomLayer();
   nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize());
+  nsPresContext* presContext = mFrame->PresContext();
   return nsDisplayBackground::GetInsideClipRegion(this, presContext, bottomLayer.mClip, borderBox, aSnap);
 }
 
 bool
 nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) 
 {
   *aColor = mColor;
-  nsStyleContext* bgSC;
-  nsPresContext* presContext = mFrame->PresContext();
-  if (!nsCSSRendering::FindBackground(presContext, mFrame, &bgSC))
+
+  if (!mBackgroundStyle)
     return true;
 
-  const nsStyleBackground* bg = bgSC->GetStyleBackground();
   return (!nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius) &&
-          bg->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER);
+          mBackgroundStyle->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER);
 }
 
 void
 nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder,
                                   const nsRect& aRect,
                                   HitTestState* aState,
                                   nsTArray<nsIFrame*> *aOutFrames)
 {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1784,18 +1784,23 @@ private:
  * The standard display item to paint the CSS background of a frame.
  */
 class nsDisplayBackground : public nsDisplayItem {
 public:
   // aLayer signifies which background layer this item represents. Normally
   // a background layer will only be marked as fixed if it covers the scroll-
   // port of the root scroll-frame. This check can be skipped using
   // aSkipFixedItemBoundsCheck.
+  // aIsThemed should be the value of aFrame->IsThemed.
+  // aBackgroundStyle should be the result of
+  // nsCSSRendering::FindBackground, or null if FindBackground returned false.
   nsDisplayBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                      uint32_t aLayer, bool aSkipFixedItemBoundsCheck = false);
+                      uint32_t aLayer, bool aIsThemed,
+                      const nsStyleBackground* aBackgroundStyle,
+                      bool aSkipFixedItemBoundsCheck = false);
   virtual ~nsDisplayBackground();
 
   // This will create and append new items for all the layers of the
   // background. If given, aBackground will be set with the address of the
   // bottom-most background item.
   static nsresult AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
                                              nsIFrame* aFrame,
                                              nsDisplayList* aList,
@@ -1849,49 +1854,57 @@ protected:
   typedef class mozilla::layers::ImageContainer ImageContainer;
   typedef class mozilla::layers::ImageLayer ImageLayer;
 
 
   bool TryOptimizeToImageLayer(nsDisplayListBuilder* aBuilder);
   bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, const nsRect& aClipRect);
   void ConfigureLayer(ImageLayer* aLayer);
 
+  // Cache the result of nsCSSRendering::FindBackground. Always null if
+  // mIsThemed is true or if FindBackground returned false.
+  const nsStyleBackground* mBackgroundStyle;
+  /* If this background can be a simple image layer, we store the format here. */
+  nsRefPtr<ImageContainer> mImageContainer;
+  gfxRect mDestRect;
+  uint32_t mLayer;
+
+  nsITheme::Transparency mThemeTransparency;
   /* Used to cache mFrame->IsThemed() since it isn't a cheap call */
   bool mIsThemed;
   /* true if this item represents a background-attachment:fixed layer and
    * should fix to the viewport. */
   bool mIsFixed;
   /* true if this item represents the bottom-most background layer */
   bool mIsBottommostLayer;
-  nsITheme::Transparency mThemeTransparency;
-
-  /* If this background can be a simple image layer, we store the format here. */
-  nsRefPtr<ImageContainer> mImageContainer;
-  gfxRect mDestRect;
-  uint32_t mLayer;
 };
 
 class nsDisplayBackgroundColor : public nsDisplayItem
 {
 public:
-  nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nscolor aColor)
+  nsDisplayBackgroundColor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                           const nsStyleBackground* aBackgroundStyle,
+                           nscolor aColor)
     : nsDisplayItem(aBuilder, aFrame)
+    , mBackgroundStyle(aBackgroundStyle)
     , mColor(aColor)
   { }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
   
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) MOZ_OVERRIDE;
   virtual bool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) MOZ_OVERRIDE;
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
 
   NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR)
 
+protected:
+  const nsStyleBackground* mBackgroundStyle;
   nscolor mColor;
 };
 
 /**
  * The standard display item to paint the outer CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowOuter : public nsDisplayItem {
 public:
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -287,24 +287,26 @@ nsCanvasFrame::BuildDisplayList(nsDispla
   // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us
   // a background.
   // We don't have any border or outline, and our background draws over
   // the overflow area, so just add nsDisplayCanvasBackground instead of
   // calling DisplayBorderBackgroundOutline.
   if (IsVisibleForPainting(aBuilder)) {
     nsStyleContext* bgSC;
     const nsStyleBackground* bg = nullptr;
-    if (!IsThemed() &&
+    bool isThemed = IsThemed();
+    if (!isThemed &&
         nsCSSRendering::FindBackground(PresContext(), this, &bgSC)) {
       bg = bgSC->GetStyleBackground();
     }
     // Create separate items for each background layer.
     NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
       rv = aLists.BorderBackground()->AppendNewToTop(
-          new (aBuilder) nsDisplayCanvasBackground(aBuilder, this, i));
+          new (aBuilder) nsDisplayCanvasBackground(aBuilder, this, i,
+                                                   isThemed, bg));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   nsIFrame* kid;
   for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
     // Put our child into its own pseudo-stack.
     rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -141,18 +141,21 @@ public:
 /**
  * Override nsDisplayBackground methods so that we pass aBGClipRect to
  * PaintBackground, covering the whole overflow area.
  * We can also paint an "extra background color" behind the normal
  * background.
  */
 class nsDisplayCanvasBackground : public nsDisplayBackground {
 public:
-  nsDisplayCanvasBackground(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, uint32_t aLayer)
-    : nsDisplayBackground(aBuilder, aFrame, aLayer, true)
+  nsDisplayCanvasBackground(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
+                            uint32_t aLayer, bool aIsThemed,
+                            const nsStyleBackground* aBackgroundStyle)
+    : nsDisplayBackground(aBuilder, aFrame, aLayer,
+                          aIsThemed, aBackgroundStyle, true)
   {
     mExtraBackgroundColor = NS_RGBA(0,0,0,0);
   }
 
   virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                  nsRegion* aVisibleRegion,
                                  const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE
   {