Bug 1098654 - Improve layerization of display items when dealing with async scrollable layers. r=tnikkel
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 30 Dec 2014 09:39:39 -0500
changeset 247395 c1dc4e9356f83ca31ff4ffb410fc57d0d54f6018
parent 247394 fff0439d7dc0f2787441292818d8df60f053056c
child 247396 f971784902fe040c4c13b4ba72ff64d495e886ac
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1098654
milestone37.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 1098654 - Improve layerization of display items when dealing with async scrollable layers. r=tnikkel The function IsSubjectToAsyncTransforms() was not correctly reporting when a layer was actually subject to async transforms. As a result, sometimes display items from above such a layer would end up in a layer below. This could manifest as page elements being improperly covered by async scrolling elements.
layout/base/FrameLayerBuilder.cpp
layout/reftests/bugs/reftest.list
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -249,16 +249,17 @@ static inline MaskLayerImageCache* GetMa
  * PaintedLayer 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 PaintedLayerData {
 public:
   PaintedLayerData() :
     mAnimatedGeometryRoot(nullptr),
+    mIsAsyncScrollable(false),
     mFixedPosFrameForLayerData(nullptr),
     mReferenceFrame(nullptr),
     mLayer(nullptr),
     mIsSolidColorInVisibleRegion(false),
     mFontSmoothingBackgroundColor(NS_RGBA(0,0,0,0)),
     mSingleItemFixedToViewport(false),
     mNeedComponentAlpha(false),
     mForceTransparentSurface(false),
@@ -337,17 +338,26 @@ public:
     if (!mAllDrawingAbove) {
       mVisibleAboveRegion.Or(mVisibleAboveRegion, aAbove);
       mVisibleAboveRegion.SimplifyOutward(8);
     }
   }
 
   void CopyAboveRegion(PaintedLayerData* aOther)
   {
-    if (aOther->mAllDrawingAbove || mAllDrawingAbove) {
+    // If aOther has a draw region and is subject to async transforms then the
+    // layer can potentially be moved arbitrarily on the compositor. So we
+    // should avoid moving display items from on top of the layer to below the
+    // layer, which we do by calling SetAllDrawingAbove. Note that if the draw
+    // region is empty (such as when aOther has only event-regions items) then
+    // we don't need to do this.
+    bool aOtherCanDrawAnywhere = aOther->IsSubjectToAsyncTransforms()
+                              && !aOther->mDrawRegion.IsEmpty();
+
+    if (aOther->mAllDrawingAbove || mAllDrawingAbove || aOtherCanDrawAnywhere) {
       SetAllDrawingAbove();
     } else {
       mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleAboveRegion);
       mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleRegion);
       mVisibleAboveRegion.SimplifyOutward(8);
       mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawAboveRegion);
       mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawRegion);
       mDrawAboveRegion.SimplifyOutward(8);
@@ -381,17 +391,18 @@ public:
     if (visibleAboveIntersection.IsEmpty()) {
       return false;
     }
     return true;
   }
 
   bool IsSubjectToAsyncTransforms()
   {
-    return mFixedPosFrameForLayerData != nullptr;
+    return mFixedPosFrameForLayerData != nullptr
+        || mIsAsyncScrollable;
   }
 
   /**
    * 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;
@@ -421,16 +432,22 @@ public:
   nsRegion  mDispatchToContentHitRegion;
   /**
    * The "active scrolled root" for all content in the layer. Must
    * be non-null; all content in a PaintedLayer must have the same
    * active scrolled root.
    */
   const nsIFrame* mAnimatedGeometryRoot;
   /**
+   * Whether or not this layer is async scrollable. If it is, that means display
+   * items above this layer should not end up in a layer below this one, as they
+   * might be obscured when they shouldn't be.
+   */
+  bool mIsAsyncScrollable;
+  /**
    * 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;
   PaintedLayer* mLayer;
   /**
@@ -811,16 +828,23 @@ protected:
 
   /**
    * Indicate that we are done adding items to the PaintedLayer at the top of
    * mPaintedLayerDataStack. Set the final visible region and opaque-content
    * flag, and pop it off the stack.
    */
   void PopPaintedLayerData();
   /**
+   * Check if any of the animated geometry roots from aAnimatedGeometryRoot up
+   * to and including mContainerAnimatedGeometryRoot are async scrollable. If
+   * so, return true. This is used to flag a particular PaintedLayer as being
+   * subject to async transforms.
+   */
+  bool HasAsyncScrollableGeometryInContainer(const nsIFrame* aAnimatedGeometryRoot);
+  /**
    * Find the PaintedLayer to which we should assign the next display item.
    * We scan the PaintedLayerData stack to find the topmost PaintedLayer
    * that is compatible with the display item (i.e., has the same
    * active scrolled root), and that has no content from other layers above
    * it and intersecting the aVisibleRect.
    * Returns the layer, and also updates the PaintedLayerData. Will
    * push a new PaintedLayerData onto the stack if no suitable existing
    * layer is found. If we choose a PaintedLayer that's already on the
@@ -2477,16 +2501,38 @@ PaintedLayerData::Accumulate(ContainerSt
         } else {
           aItem->DisableComponentAlpha();
         }
       }
     }
   }
 }
 
+bool
+ContainerState::HasAsyncScrollableGeometryInContainer(const nsIFrame* aAnimatedGeometryRoot)
+{
+  const nsIFrame* f = aAnimatedGeometryRoot;
+  while (f) {
+    if (nsLayoutUtils::GetScrollableFrameFor(f) &&
+        nsLayoutUtils::GetDisplayPort(f->GetContent(), nullptr)) {
+      return true;
+    }
+    if (f == mContainerAnimatedGeometryRoot) {
+      break;
+    }
+    nsIFrame* fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
+    if (!fParent) {
+      break;
+    }
+    f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(
+          this->mBuilder, fParent, mContainerAnimatedGeometryRoot);
+  }
+  return false;
+}
+
 PaintedLayerData*
 ContainerState::FindPaintedLayerFor(nsDisplayItem* aItem,
                                    const nsIntRect& aVisibleRect,
                                    const nsIFrame* aAnimatedGeometryRoot,
                                    const nsPoint& aTopLeft,
                                    bool aShouldFixToViewport)
 {
   int32_t i;
@@ -2544,16 +2590,18 @@ ContainerState::FindPaintedLayerFor(nsDi
       CreateOrRecyclePaintedLayer(aAnimatedGeometryRoot, aItem->ReferenceFrame(), aTopLeft);
 
     paintedLayerData = new PaintedLayerData();
     mPaintedLayerDataStack.AppendElement(paintedLayerData);
     paintedLayerData->mLayer = layer;
     paintedLayerData->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
     paintedLayerData->mFixedPosFrameForLayerData =
       FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport);
+    paintedLayerData->mIsAsyncScrollable =
+      HasAsyncScrollableGeometryInContainer(aAnimatedGeometryRoot);
     paintedLayerData->mReferenceFrame = aItem->ReferenceFrame();
     paintedLayerData->mSingleItemFixedToViewport = aShouldFixToViewport;
 
     NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
                  "Layer already in list???");
     paintedLayerData->mNewChildLayersIndex = mNewChildLayers.Length();
     NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
     newLayerEntry->mLayer = layer.forget();
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1797,17 +1797,17 @@ fuzzy-if(OSX==10.6,2,30) skip-if(B2G&&br
 fails == 942017.html 942017-ref.html # bug 942017
 fuzzy-if(B2G,1,7) == 942672-1.html 942672-1-ref.html
 == 953334-win32-clipping.html 953334-win32-clipping-ref.html
 == 956513-1.svg 956513-1-ref.svg
 == 944291-1.html 944291-1-ref.html
 == 950436-1.html 950436-1-ref.html
 == 957770-1.svg 957770-1-ref.svg
 == 960277-1.html 960277-1-ref.html
-pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,31) == 966992-1.html 966992-1-ref.html
+pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,145) == 966992-1.html 966992-1-ref.html
 skip-if(Android) == 966510-1.html 966510-1-ref.html # scrollable elements other than the root probably won't work well on android until bug 776030 is fixed
 skip-if(Android) == 966510-2.html 966510-2-ref.html # same as above
 == 978911-1.svg 978911-1-ref.svg
 == 983084-1.html 983084-1-ref.html
 == 983084-2.html 983084-2-ref.html
 == 983084-3.html 983084-1-ref.html
 == 983691-1.html 983691-ref.html
 HTTP(..) == 983985-1.html 983985-1-ref.html