Bug 1422057 - Extend the clipchain of a display item to the ancestor's clipchain if it is a strict superset. r?mstange draft
authorKartikaya Gupta <kgupta@mozilla.com>
Sat, 06 Jan 2018 09:52:58 -0500
changeset 716816 a287abe5ba1afd123c8ace60c6d6b7d08ff809b6
parent 716815 b082d005d5e04d547750561281120d9f5ee51464
child 716817 2c7e515618faefdfcfa88f472960b99cb82e6aa1
push id94505
push userkgupta@mozilla.com
push dateSat, 06 Jan 2018 16:27:17 +0000
reviewersmstange
bugs1422057
milestone59.0a1
Bug 1422057 - Extend the clipchain of a display item to the ancestor's clipchain if it is a strict superset. r?mstange See the documentation in the added function for details. MozReview-Commit-ID: 4R0WaSy0tm2
gfx/layers/wr/ScrollingLayersHelper.cpp
gfx/layers/wr/ScrollingLayersHelper.h
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ b/gfx/layers/wr/ScrollingLayersHelper.cpp
@@ -74,16 +74,18 @@ ScrollingLayersHelper::EndList(const Sta
 
 void
 ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
                                  const StackingContextHelper& aStackingContext)
 {
   SLH_LOG("processing item %p\n", aItem);
 
   const DisplayItemClipChain* clip = aItem->GetClipChain();
+  clip = ExtendChain(clip);
+
   ItemClips clips(aItem->GetActiveScrolledRoot(), clip);
   MOZ_ASSERT(!mItemClipStack.empty());
   if (clips.HasSameInputs(mItemClipStack.back())) {
     // Early-exit because if the clips are the same then we don't need to do
     // do the work of popping the old stuff and then pushing it right back on
     // for the new item.
     SLH_LOG("early-exit for %p\n", aItem);
     return;
@@ -471,16 +473,81 @@ ScrollingLayersHelper::RecurseAndDefineA
   mBuilder->DefineScrollLayer(scrollId, ancestorIds.first, ancestorIds.second,
       aSc.ToRelativeLayoutRect(contentRect),
       aSc.ToRelativeLayoutRect(clipBounds));
 
   ids.first = Some(scrollId);
   return ids;
 }
 
+const DisplayItemClipChain*
+ScrollingLayersHelper::ExtendChain(const DisplayItemClipChain* aClip)
+{
+  // The intent of this function is to handle Gecko display list scenarios
+  // like so:
+  // nsDisplayFixedPosition with clip chain A -> B -> nullptr
+  //   nsDisplayBackgroundColor with clip chain B -> nullptr
+  //
+  // The specific types are not relevant, but the important part is that there
+  // is a display item whose clip chain is a subchain of the enclosing display
+  // item.
+  //
+  // The semantics of the gecko display items means that the two clip chains
+  // should be intersected for the child display item; because one clip chain
+  // is a subset of the other the intersection comes out to be clip chain from
+  // the parent.
+  // However, WebRender doesn't let us (yet) intersect clip chains, so one of
+  // the jobs of ScrollingLayersHelper is to generate as-good-as-possible clip
+  // chains by merging the necessary clips into a new clip chain. In the example
+  // above, we really want the nsDisplayBackgroundColor to use the clip chain
+  // from A rather than from B in order to get the right clips, and this
+  // function "extends" an input of |B| and returns |A|.
+
+  if (!aClip) {
+    return aClip;
+  }
+  // mItemClipStack has the clips that we pushed for ancestor display items.
+  size_t clipDepth = mItemClipStack.size();
+  MOZ_ASSERT(clipDepth > 0);
+  while (--clipDepth > 0) {
+    const DisplayItemClipChain* enclosingClip = mItemClipStack[clipDepth - 1].mChain;
+    if (!enclosingClip) {
+      // This is a special case; if an item has a nullptr clipchain it basically
+      // inherits the clipchain from its ancestor, so let's skip to that.
+      continue;
+    }
+    if (aClip == enclosingClip) {
+      // The ancestor clip chain is the same as our item's clip chain, so
+      // we're done. Note that because this function will have run on the
+      // ancestor as well, we can be assured via induction that there is no
+      // ancestor beyond this one that has a longer superset-clipchain.
+      return aClip;
+    }
+    const ClipIdMap& cache = mCacheStack.back();
+    if (cache.find(enclosingClip) == cache.end()) {
+      // The ancestor clip chain isn't in our clip cache, which means there
+      // must be a reference frame between the ancestor item and this item.
+      // Therefore we cannot use the enclosing clip, so let's abort
+      return aClip;
+    }
+    for (const DisplayItemClipChain* i = enclosingClip->mParent; i; i = i->mParent) {
+      if (i == aClip) {
+        // aClip is contained inside the enclosingClip clipchain. Since the
+        // enclosingClip also applies to the item we're currently processing,
+        // we should use that as it is a better approximation to the real clip
+        // set that applies to the item.
+        SLH_LOG("extending clip %p to %p\n", aClip, enclosingClip);
+        return enclosingClip;
+      }
+    }
+    break;
+  }
+  return aClip;
+}
+
 Maybe<ScrollingLayersHelper::ClipAndScroll>
 ScrollingLayersHelper::EnclosingClipAndScroll() const
 {
   for (auto it = mItemClipStack.rbegin(); it != mItemClipStack.rend(); it++) {
     if (it->mClipAndScroll) {
       return it->mClipAndScroll;
     }
     // If an entry in the stack pushed a single clip or scroll without pushing
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ b/gfx/layers/wr/ScrollingLayersHelper.h
@@ -65,16 +65,17 @@ private:
 
   std::pair<Maybe<FrameMetrics::ViewID>, Maybe<wr::WrClipId>>
   RecurseAndDefineAsr(nsDisplayItem* aItem,
                       const ActiveScrolledRoot* aAsr,
                       const DisplayItemClipChain* aChain,
                       int32_t aAppUnitsPerDevPixel,
                       const StackingContextHelper& aSc);
 
+  const DisplayItemClipChain* ExtendChain(const DisplayItemClipChain* aClip);
   Maybe<ClipAndScroll> EnclosingClipAndScroll() const;
 
   // Note: two DisplayItemClipChain* A and B might actually be "equal" (as per
   // DisplayItemClipChain::Equal(A, B)) even though they are not the same pointer
   // (A != B). In this hopefully-rare case, they will get separate entries
   // in this map when in fact we could collapse them. However, to collapse
   // them involves writing a custom hash function for the pointer type such that
   // A and B hash to the same things whenever DisplayItemClipChain::Equal(A, B)