Bug 976412. Part 1: Fix logic for determining when a display item can be placed in a layer. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 26 Feb 2014 17:17:18 +1300
changeset 170948 7193af53cd1e4b06401aa19cc5889fb4dcca684f
parent 170947 a6a0700bf77546590aab05f9bbedccd837de2bad
child 170970 fd6f16c2f588a1934ef79c1ae126fd7518b867df
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmattwoodrow
bugs976412
milestone30.0a1
Bug 976412. Part 1: Fix logic for determining when a display item can be placed in a layer. r=mattwoodrow
layout/base/FrameLayerBuilder.cpp
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -229,17 +229,19 @@ static inline MaskLayerImageCache* GetMa
  * assign display items to existing ThebesLayers, and to the lowest
  * ThebesLayer in z-order. This reduces the number of layers and
  * makes it more likely a display item will be rendered to an opaque
  * layer, giving us the best chance of getting subpixel AA.
  */
 class ThebesLayerData {
 public:
   ThebesLayerData() :
-    mAnimatedGeometryRoot(nullptr), mReferenceFrame(nullptr),
+    mAnimatedGeometryRoot(nullptr),
+    mFixedPosFrameForLayerData(nullptr),
+    mReferenceFrame(nullptr),
     mLayer(nullptr),
     mIsSolidColorInVisibleRegion(false),
     mSingleItemFixedToViewport(false),
     mNeedComponentAlpha(false),
     mForceTransparentSurface(false),
     mImage(nullptr),
     mCommonClipCount(-1),
     mAllDrawingAbove(false)
@@ -316,34 +318,44 @@ public:
 
   void SetAllDrawingAbove()
   {
     mAllDrawingAbove = true;
     mDrawAboveRegion.SetEmpty();
     mVisibleAboveRegion.SetEmpty();
   }
 
-  bool IsBelow(const nsIntRect& aRect)
+  bool DrawAboveRegionIntersects(const nsIntRect& aRect)
   {
     return mAllDrawingAbove || mDrawAboveRegion.Intersects(aRect);
   }
 
+  bool DrawRegionIntersects(const nsIntRect& aRect)
+  {
+    return IsSubjectToAsyncTransforms() || mDrawRegion.Intersects(aRect);
+  }
+
   bool IntersectsVisibleAboveRegion(const nsIntRegion& aVisibleRegion)
   {
     if (mAllDrawingAbove) {
       return true;
     }
     nsIntRegion visibleAboveIntersection;
     visibleAboveIntersection.And(mVisibleAboveRegion, aVisibleRegion);
     if (visibleAboveIntersection.IsEmpty()) {
       return false;
     }
     return true;
   }
 
+  bool IsSubjectToAsyncTransforms()
+  {
+    return mFixedPosFrameForLayerData != nullptr;
+  }
+
   /**
    * The region of visible content in the layer, relative to the
    * container layer (which is at the snapped top-left of the display
    * list reference frame).
    */
   nsIntRegion  mVisibleRegion;
   /**
    * The region containing the bounds of all display items in the layer,
@@ -370,16 +382,22 @@ public:
    */
   nsIntRegion  mDispatchToContentHitRegion;
   /**
    * The "active scrolled root" for all content in the layer. Must
    * be non-null; all content in a ThebesLayer must have the same
    * active scrolled root.
    */
   const nsIFrame* mAnimatedGeometryRoot;
+  /**
+   * If non-null, the frame from which we'll extract "fixed positioning"
+   * metadata for this layer. This can be a position:fixed frame or a viewport
+   * frame; the latter case is used for background-attachment:fixed content.
+   */
+  const nsIFrame* mFixedPosFrameForLayerData;
   const nsIFrame* mReferenceFrame;
   ThebesLayer* mLayer;
   /**
    * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
    * region.
    */
   nscolor      mSolidColor;
   /**
@@ -615,20 +633,21 @@ protected:
    * aDrawRegion and the displayport, and updates *aIsSolidColorInVisibleRegion
    * (if non-null) to false if the visible region grows.
    * aDisplayItemFixedToViewport is true if the layer contains a single display
    * item which returned true for ShouldFixToViewport.
    * This can return the actual viewport frame for layers whose display items
    * are directly on the viewport (e.g. background-attachment:fixed backgrounds).
    */
   const nsIFrame* FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot,
-                                                bool aDisplayItemFixedToViewport,
-                                                const nsIntRegion& aDrawRegion,
-                                                nsIntRegion* aVisibleRegion,
-                                                bool* aIsSolidColorInVisibleRegion = nullptr);
+                                                bool aDisplayItemFixedToViewport);
+  void AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame,
+                                          const nsIntRegion& aDrawRegion,
+                                          nsIntRegion* aVisibleRegion,
+                                          bool* aIsSolidColorInVisibleRegion = nullptr);
   /**
    * Set fixed-pos layer metadata on aLayer according to the data for aFixedPosFrame.
    */
   void SetFixedPositionLayerData(Layer* aLayer,
                                  const nsIFrame* aFixedPosFrame);
   /**
    * Indicate that we are done adding items to the ThebesLayer at the top of
    * mThebesLayerDataStack. Set the final visible region and opaque-content
@@ -1671,20 +1690,17 @@ ThebesLayerData::CanOptimizeImageLayer(n
     return nullptr;
   }
 
   return mImage->GetContainer(mLayer->Manager(), aBuilder);
 }
 
 const nsIFrame*
 ContainerState::FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot,
-                                              bool aDisplayItemFixedToViewport,
-                                              const nsIntRegion& aDrawRegion,
-                                              nsIntRegion* aVisibleRegion,
-                                              bool* aIsSolidColorInVisibleRegion)
+                                              bool aDisplayItemFixedToViewport)
 {
   if (!mManager->IsWidgetLayerManager()) {
     // Never attach any fixed-pos metadata to inactive layers, it's pointless!
     return nullptr;
   }
 
   nsPresContext* presContext = mContainerFrame->PresContext();
   nsIFrame* viewport = presContext->PresShell()->GetRootFrame();
@@ -1709,30 +1725,47 @@ ContainerState::FindFixedPosFrameForLaye
         // The metadata will go on an ancestor layer if necessary.
         return nullptr;
       }
     }
     if (!result) {
       return nullptr;
     }
   }
-
+  return result;
+}
+
+void
+ContainerState::AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame,
+                                                   const nsIntRegion& aDrawRegion,
+                                                   nsIntRegion* aVisibleRegion,
+                                                   bool* aIsSolidColorInVisibleRegion)
+{
+  if (!aFixedPosFrame) {
+    return;
+  }
+
+  nsRect displayPort;
+  nsPresContext* presContext = aFixedPosFrame->PresContext();
+  DebugOnly<bool> hasDisplayPort =
+    nsLayoutUtils::ViewportHasDisplayPort(presContext, &displayPort);
+  NS_ASSERTION(hasDisplayPort, "No fixed-pos layer data if there's no displayport");
   // Display ports are relative to the viewport, convert it to be relative
   // to our reference frame.
+  nsIFrame* viewport = presContext->PresShell()->GetRootFrame();
   displayPort += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame);
   nsIntRegion newVisibleRegion;
   newVisibleRegion.And(ScaleToOutsidePixels(displayPort, false),
                        aDrawRegion);
   if (!aVisibleRegion->Contains(newVisibleRegion)) {
     if (aIsSolidColorInVisibleRegion) {
       *aIsSolidColorInVisibleRegion = false;
     }
     *aVisibleRegion = newVisibleRegion;
   }
-  return result;
 }
 
 void
 ContainerState::SetFixedPositionLayerData(Layer* aLayer,
                                           const nsIFrame* aFixedPosFrame)
 {
   aLayer->SetIsFixedPosition(aFixedPosFrame != nullptr);
   if (!aFixedPosFrame) {
@@ -1799,22 +1832,20 @@ AddTransformedBoundsToRegion(const nsInt
 void
 ContainerState::PopThebesLayerData()
 {
   NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
 
   int32_t lastIndex = mThebesLayerDataStack.Length() - 1;
   ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
 
-  const nsIFrame* fixedPosFrameForLayerData =
-    FindFixedPosFrameForLayerData(data->mAnimatedGeometryRoot,
-                                  data->mSingleItemFixedToViewport,
-                                  data->mDrawRegion,
-                                  &data->mVisibleRegion,
-                                  &data->mIsSolidColorInVisibleRegion);
+  AdjustLayerDataForFixedPositioning(data->mFixedPosFrameForLayerData,
+                                     data->mDrawRegion,
+                                     &data->mVisibleRegion,
+                                     &data->mIsSolidColorInVisibleRegion);
   nsRefPtr<Layer> layer;
   nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
 
   if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
       (data->mLayer->GetValidRegion().IsEmpty() || mLayerBuilder->CheckInLayerTreeCompressionMode())) {
     NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
                  "Can't be a solid color as well as an image!");
     if (imageContainer) {
@@ -1930,17 +1961,17 @@ ContainerState::PopThebesLayerData()
   }
   if (isOpaque && !data->mForceTransparentSurface) {
     flags |= Layer::CONTENT_OPAQUE;
   } else if (data->mNeedComponentAlpha && !hidpi) {
     flags |= Layer::CONTENT_COMPONENT_ALPHA;
   }
   layer->SetContentFlags(flags);
 
-  SetFixedPositionLayerData(layer, fixedPosFrameForLayerData);
+  SetFixedPositionLayerData(layer, data->mFixedPosFrameForLayerData);
 
   ThebesLayerData* containingThebesLayerData =
      mLayerBuilder->GetContainingThebesLayerData();
   if (containingThebesLayerData) {
     gfx3DMatrix matrix = GetTransformToRoot(layer);
     nsIntPoint translatedDest = GetTranslationForThebesLayer(containingThebesLayerData->mLayer);
     matrix.TranslatePost(-gfxPoint3D(translatedDest.x, translatedDest.y, 0));
     AddTransformedBoundsToRegion(data->mDispatchToContentHitRegion, matrix,
@@ -2133,70 +2164,80 @@ ThebesLayerData::Accumulate(ContainerSta
       }
     }
   }
 }
 
 ThebesLayerData*
 ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
                                    const nsIntRect& aVisibleRect,
-                                   const nsIFrame* aActiveScrolledRoot,
+                                   const nsIFrame* aAnimatedGeometryRoot,
                                    const nsPoint& aTopLeft,
                                    bool aShouldFixToViewport)
 {
   int32_t i;
   int32_t lowestUsableLayerWithScrolledRoot = -1;
   int32_t topmostLayerWithScrolledRoot = -1;
   for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
-    ThebesLayerData* data = mThebesLayerDataStack[i];
-    // ThebesLayers for items returning true from ShouldFixToViewport should
-    // not be added to (or have content moved below them),
-    // since they will receive special treatment for async scrolling.
-    // If the item is going to be fixed to the viewport, don't let it combine
-    // with any existing layer.
-    if (data->IsBelow(aVisibleRect) || data->mSingleItemFixedToViewport ||
-        aShouldFixToViewport) {
+    // Don't let should-fix-to-viewport items share a layer with any other items.
+    if (aShouldFixToViewport) {
       ++i;
       break;
     }
-    if (data->mAnimatedGeometryRoot == aActiveScrolledRoot) {
+    ThebesLayerData* data = mThebesLayerDataStack[i];
+    // Give up if there is content drawn above (in z-order) this layer that
+    // intersects aItem's visible region; aItem must be placed in a
+    // layer this layer.
+    if (data->DrawAboveRegionIntersects(aVisibleRect)) {
+      ++i;
+      break;
+    }
+    // If the animated scrolled roots are the same and we can share this layer
+    // with the item, note this as a usable layer.
+    if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot &&
+        !data->mSingleItemFixedToViewport) {
       lowestUsableLayerWithScrolledRoot = i;
       if (topmostLayerWithScrolledRoot < 0) {
         topmostLayerWithScrolledRoot = i;
       }
     }
-    if (data->mDrawRegion.Intersects(aVisibleRect))
+    // If the layer's drawn region intersects the item, stop now since no
+    // lower layer will be usable. Do the same if the layer is subject to
+    // async transforms, since we don't know where it will really be drawn.
+    if (data->DrawRegionIntersects(aVisibleRect))
       break;
   }
   if (topmostLayerWithScrolledRoot < 0) {
     --i;
     for (; i >= 0; --i) {
       ThebesLayerData* data = mThebesLayerDataStack[i];
-      if (data->mAnimatedGeometryRoot == aActiveScrolledRoot) {
+      if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot) {
         topmostLayerWithScrolledRoot = i;
         break;
       }
     }
   }
 
   if (topmostLayerWithScrolledRoot >= 0) {
     while (uint32_t(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) {
       PopThebesLayerData();
     }
   }
 
   ThebesLayerData* thebesLayerData = nullptr;
   if (lowestUsableLayerWithScrolledRoot < 0) {
     nsRefPtr<ThebesLayer> layer =
-      CreateOrRecycleThebesLayer(aActiveScrolledRoot, aItem->ReferenceFrame(), aTopLeft);
+      CreateOrRecycleThebesLayer(aAnimatedGeometryRoot, aItem->ReferenceFrame(), aTopLeft);
 
     thebesLayerData = new ThebesLayerData();
     mThebesLayerDataStack.AppendElement(thebesLayerData);
     thebesLayerData->mLayer = layer;
-    thebesLayerData->mAnimatedGeometryRoot = aActiveScrolledRoot;
+    thebesLayerData->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
+    thebesLayerData->mFixedPosFrameForLayerData =
+      FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport);
     thebesLayerData->mReferenceFrame = aItem->ReferenceFrame();
     thebesLayerData->mSingleItemFixedToViewport = aShouldFixToViewport;
 
     NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
     *mNewChildLayers.AppendElement() = layer.forget();
   } else {
     thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
   }
@@ -2423,21 +2464,22 @@ ContainerState::ProcessDisplayItems(cons
       nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
       if (!ownLayer) {
         continue;
       }
 
       NS_ASSERTION(!ownLayer->AsThebesLayer(),
                    "Should never have created a dedicated Thebes layer!");
 
-      nsIntRegion visibleRegion(itemVisibleRect);
       const nsIFrame* fixedPosFrame =
-        FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport,
-                                      nsIntRegion(itemDrawRect), &visibleRegion);
+        FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport);
       if (fixedPosFrame) {
+        nsIntRegion visibleRegion(itemVisibleRect);
+        AdjustLayerDataForFixedPositioning(fixedPosFrame,
+                                           nsIntRegion(itemDrawRect), &visibleRegion);
         itemVisibleRect = visibleRegion.GetBounds();
       }
       SetFixedPositionLayerData(ownLayer, fixedPosFrame);
 
       nsRect invalid;
       if (item->IsInvalid(invalid)) {
         ownLayer->SetInvalidRectToVisibleRegion();
       }