Bug 810470. Part 1: Cache nsStyleBackground pointer in nsDisplayBackground. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 07 Nov 2012 23:08:40 -0500
changeset 113427 a3ea6aa33de6c18651aca7633221dcf7373eb18b
parent 113426 5444aff38dfb15f91b2c343208418cdb7849fdb0
child 113428 76e6feedd0d6ab359ba04ccc94b8356ef646d22a
push id18151
push userrocallahan@mozilla.com
push dateFri, 16 Nov 2012 00:03:18 +0000
treeherdermozilla-inbound@e8c599817e97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs810470
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 810470. Part 1: Cache nsStyleBackground pointer in nsDisplayBackground. r=mattwoodrow
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
@@ -1805,37 +1805,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
@@ -1463,77 +1463,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()
@@ -1547,42 +1546,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;
     }
@@ -1683,23 +1683,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);
@@ -1742,23 +1741,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);
@@ -1902,19 +1900,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)
 {
@@ -1962,37 +1958,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) {
@@ -2002,47 +1997,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));
@@ -2056,25 +2042,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)
@@ -2143,26 +2124,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();
 }
@@ -2187,41 +2166,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
@@ -1785,18 +1785,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,
@@ -1850,36 +1855,42 @@ 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;
@@ -1889,16 +1900,18 @@ public:
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
   {
     *aSnap = true;
     return nsRect(ToReferenceFrame(), GetUnderlyingFrame()->GetSize());
   }
 
   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
@@ -286,24 +286,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
   {