Bug 1455944 - Do not paint nsDisplayOpacity children when opacity push/pop markers have empty paint rect draft
authorMiko Mynttinen <mikokm@gmail.com>
Wed, 25 Apr 2018 22:54:18 +0200
changeset 788331 eb3c86f61ad6ed97d6c39936ecf1cfee2f5bceae
parent 787462 600e62886f403a60b59cb87c7be4b484605ac5ce
push id107958
push userbmo:mikokm@gmail.com
push dateThu, 26 Apr 2018 09:22:32 +0000
bugs1455944
milestone61.0a1
Bug 1455944 - Do not paint nsDisplayOpacity children when opacity push/pop markers have empty paint rect MozReview-Commit-ID: 5BgHOFjW34H
layout/painting/FrameLayerBuilder.cpp
layout/painting/crashtests/1455944-1.html
layout/painting/crashtests/crashtests.list
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -6254,16 +6254,31 @@ struct ClipTracker {
     mContext->Save();
     mClips.AppendElement(aOpacityNesting);
   };
 
   AutoTArray<int, 2> mClips;
   gfxContext* mContext;
 };
 
+static void
+UpdateOpacityNesting(int& aOpacityNesting, DisplayItemEntryType aType)
+{
+  if (aType == DisplayItemEntryType::PUSH_OPACITY ||
+      aType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG) {
+    aOpacityNesting++;
+  }
+
+  if (aType == DisplayItemEntryType::POP_OPACITY) {
+    aOpacityNesting--;
+  }
+
+  MOZ_ASSERT(aOpacityNesting >= 0);
+}
+
 void
 FrameLayerBuilder::PaintItems(nsTArray<AssignedDisplayItem>& aItems,
                               const nsIntRect& aRect,
                               gfxContext *aContext,
                               nsDisplayListBuilder* aBuilder,
                               nsPresContext* aPresContext,
                               const nsIntPoint& aOffset,
                               float aXScale, float aYScale)
@@ -6273,32 +6288,43 @@ FrameLayerBuilder::PaintItems(nsTArray<A
   int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
   nsRect boundRect = ToAppUnits(aRect, appUnitsPerDevPixel);
   boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel),
                  NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel));
   boundRect.ScaleInverseRoundOut(aXScale, aYScale);
 
   DisplayItemClip currentClip, tmpClip;
 
+  // Tracks opacity nesting level for item level clipping.
   int opacityNesting = 0;
+
+  // Tracks opacity nesting level for skipping items between opacity markers,
+  // when opacity has empty visible rect set.
+  int emptyOpacityNesting = 0;
+
   ClipTracker clipTracker(aContext);
 
   for (uint32_t i = 0; i < aItems.Length(); ++i) {
     AssignedDisplayItem& cdi = aItems[i];
     nsDisplayItem* item = cdi.mItem;
 
     if (!item) {
       MOZ_ASSERT(cdi.mType == DisplayItemEntryType::ITEM);
       continue;
     }
 
     const nsRect& visibleRect = item->GetVisibleRect();
-
-    nsRect paintRect = visibleRect.Intersect(boundRect);
-    if (paintRect.IsEmpty()) {
+    const nsRect paintRect = visibleRect.Intersect(boundRect);
+
+    if (paintRect.IsEmpty() || emptyOpacityNesting > 0) {
+      // In order for this branch to be hit, either this item has an empty paint
+      // rect and nothing would be drawn, or a PUSH_OPACITY marker before this
+      // item had an empty paint rect. In the latter case, the items are skipped
+      // until POP_OPACITY markers bring |emptyOpacityNesting| back to 0.
+      UpdateOpacityNesting(emptyOpacityNesting, cdi.mType);
       continue;
     }
 
 #ifdef MOZ_DUMP_PAINTING
     AUTO_PROFILER_LABEL_DYNAMIC_CSTR("FrameLayerBuilder::PaintItems", GRAPHICS,
                                      item->Name());
 #else
     AUTO_PROFILER_LABEL("FrameLayerBuilder::PaintItems", GRAPHICS);
@@ -6306,30 +6332,29 @@ FrameLayerBuilder::PaintItems(nsTArray<A
 
     MOZ_ASSERT((opacityNesting == 0 && !cdi.mHasOpacity) ||
                (opacityNesting > 0 && cdi.mHasOpacity));
 
     if (cdi.mType == DisplayItemEntryType::PUSH_OPACITY ||
         cdi.mType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG) {
       clipTracker.PopClipIfNeeded(opacityNesting);
       PushOpacity(aContext, paintRect, cdi, appUnitsPerDevPixel);
-      opacityNesting++;
     }
 
     if (cdi.mType == DisplayItemEntryType::POP_OPACITY) {
       MOZ_ASSERT(item->GetType() == DisplayItemType::TYPE_OPACITY);
       MOZ_ASSERT(opacityNesting > 0);
 
       clipTracker.PopClipIfNeeded(opacityNesting);
       aContext->PopGroupAndBlend();
       aContext->Restore();
-      opacityNesting--;
     }
 
     if (cdi.mType != DisplayItemEntryType::ITEM) {
+      UpdateOpacityNesting(opacityNesting, cdi.mType);
       continue;
     }
 
     // If the new desired clip state is different from the current state,
     // update the clip.
     const DisplayItemClip* clip = &item->GetClip();
     if (clip->GetRoundedRectCount() > 0 &&
         !clip->IsRectClippedByRoundedCorner(visibleRect)) {
@@ -6367,16 +6392,17 @@ FrameLayerBuilder::PaintItems(nsTArray<A
       {
         item->Paint(aBuilder, aContext);
       }
     }
   }
 
   clipTracker.PopClipIfNeeded(opacityNesting);
   MOZ_ASSERT(opacityNesting == 0);
+  MOZ_ASSERT(emptyOpacityNesting == 0);
 }
 
 /**
  * Returns true if it is preferred to draw the list of display
  * items separately for each rect in the visible region rather
  * than clipping to a complex region.
  */
 static bool
new file mode 100644
--- /dev/null
+++ b/layout/painting/crashtests/1455944-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="utf-8">
+</head>
+
+<body>
+<div style="opacity: 0.9;">
+    <iframe style="border: none;" src=""></iframe>
+</div>
+
+</body>
+</html>
--- a/layout/painting/crashtests/crashtests.list
+++ b/layout/painting/crashtests/crashtests.list
@@ -5,8 +5,9 @@ load 1413073-2.html
 load 1405881-1.html
 load 1418177-1.html
 load 1418722-1.html
 load 1419917.html
 load 1425271-1.html
 load 1428906-1.html
 skip-if(webrender) load 1430589-1.html # bug 1421825 for webrender
 load 1454105-1.html
+load 1455944-1.html