diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 79ca90330315..ca541582ec26 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -576,7 +576,8 @@ public: const nsIntRect& aVisibleRect, const DisplayItemClip& aClip, LayerState aLayerState, - nsDisplayList *aList); + nsDisplayList *aList, + DisplayItemEntryType aType); AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; } /** @@ -772,7 +773,11 @@ public: * These items get added by Accumulate(). */ nsTArray mAssignedDisplayItems; - + /** + * Tracks the active opacity markers by holding the indices to PUSH_OPACITY + * items in |mAssignedDisplayItems|. + */ + nsTArray mOpacityIndices; }; struct NewLayerEntry { @@ -2736,6 +2741,13 @@ ContainerState::FindOpaqueBackgroundColorInLayer(const PaintedLayerData* aData, appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale); for (auto& assignedItem : Reversed(aData->mAssignedDisplayItems)) { + if (assignedItem.mType != DisplayItemEntryType::ITEM || + assignedItem.mHasOpacity) { + // |assignedItem| is either an effect marker, or within a flatten opacity + // group. In both cases, there is no opaque area. + continue; + } + nsDisplayItem* item = assignedItem.mItem; bool snap; nsRect bounds = item->GetBounds(mBuilder, &snap); @@ -3305,6 +3317,8 @@ void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueB { PaintedLayerData* data = &aData; + MOZ_ASSERT(data->mOpacityIndices.IsEmpty()); + if (!data->mLayer) { // No layer was recycled, so we create a new one. RefPtr paintedLayer = CreatePaintedLayer(data); @@ -3373,6 +3387,11 @@ void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueB MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS); MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO); + if (item.mType == DisplayItemEntryType::POP_OPACITY) { + // Do not invalidate for end markers. + continue; + } + InvalidateForLayerChange(item.mItem, data->mLayer, item.mDisplayItemData); mLayerBuilder->AddPaintedDisplayItem(data, item, *this, layer); item.mDisplayItemData = nullptr; @@ -3580,32 +3599,68 @@ IsItemAreaInWindowOpaqueRegion(nsDisplayListBuilder* aBuilder, void PaintedLayerData::Accumulate(ContainerState* aState, - nsDisplayItem* aItem, - const nsIntRect& aVisibleRect, - const DisplayItemClip& aClip, - LayerState aLayerState, - nsDisplayList* aList) + nsDisplayItem* aItem, + const nsIntRect& aVisibleRect, + const DisplayItemClip& aClip, + LayerState aLayerState, + nsDisplayList* aList, + DisplayItemEntryType aType) { FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this); + const bool hasOpacity = mOpacityIndices.Length() > 0; + + if (aType == DisplayItemEntryType::POP_OPACITY) { + MOZ_ASSERT(!mOpacityIndices.IsEmpty()); + mOpacityIndices.RemoveLastElement(); + + AssignedDisplayItem item(aItem, aClip, aLayerState, + nullptr, aType, hasOpacity); + mAssignedDisplayItems.AppendElement(Move(item)); + return; + } + if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) { mForceTransparentSurface = true; } + + nsRect componentAlphaBounds; if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { // Disable component alpha. - // Note that the transform (if any) on the PaintedLayer is always an integer translation so - // we don't have to factor that in here. + // Note that the transform (if any) on the PaintedLayer is always an integer + // translation so we don't have to factor that in here. aItem->DisableComponentAlpha(); + } else { + componentAlphaBounds = aItem->GetComponentAlphaBounds(aState->mBuilder); + + if (!componentAlphaBounds.IsEmpty()) { + // This display item needs background copy when pushing opacity group. + for (size_t i : mOpacityIndices) { + AssignedDisplayItem& item = mAssignedDisplayItems[i]; + MOZ_ASSERT(item.mType == DisplayItemEntryType::PUSH_OPACITY || + item.mType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG); + item.mType = DisplayItemEntryType::PUSH_OPACITY_WITH_BG; + } + } } bool clipMatches = mItemClip == aClip; mItemClip = aClip; + DisplayItemData* currentData = + aItem->HasMergedFrames() ? nullptr : aItem->GetDisplayItemData(); + DisplayItemData* oldData = - aState->mLayerBuilder->GetOldLayerForFrame(aItem->Frame(), - aItem->GetPerFrameKey(), - aItem->HasMergedFrames() ? nullptr : aItem->GetDisplayItemData()); - mAssignedDisplayItems.AppendElement(AssignedDisplayItem(aItem, aClip, aLayerState, oldData)); + aState->mLayerBuilder->GetOldLayerForFrame(aItem->Frame(), + aItem->GetPerFrameKey(), + currentData); + AssignedDisplayItem item(aItem, aClip, aLayerState, + oldData, aType, hasOpacity); + mAssignedDisplayItems.AppendElement(Move(item)); + + if (aType == DisplayItemEntryType::PUSH_OPACITY) { + mOpacityIndices.AppendElement(mAssignedDisplayItems.Length() - 1); + } if (aItem->MustPaintOnContentSide()) { mShouldPaintOnContentSide = true; @@ -3624,10 +3679,15 @@ PaintedLayerData::Accumulate(ContainerState* aState, return; } - nsIntRegion opaquePixels = aState->ComputeOpaqueRect(aItem, - mAnimatedGeometryRoot, mASR, aClip, aList, - &mHideAllLayersBelow, &mOpaqueForAnimatedGeometryRootParent); - opaquePixels.AndWith(aVisibleRect); + nsIntRegion opaquePixels; + + // Active opacity means no opaque pixels. + if (!hasOpacity) { + opaquePixels = aState->ComputeOpaqueRect(aItem, mAnimatedGeometryRoot, mASR, + aClip, aList, &mHideAllLayersBelow, + &mOpaqueForAnimatedGeometryRootParent); + opaquePixels.AndWith(aVisibleRect); + } /* Mark as available for conversion to image layer if this is a nsDisplayImage and * it's the only thing visible in this layer. @@ -3644,7 +3704,10 @@ PaintedLayerData::Accumulate(ContainerState* aState, bool isFirstVisibleItem = mVisibleRegion.IsEmpty(); - Maybe uniformColor = aItem->IsUniform(aState->mBuilder); + Maybe uniformColor; + if (!hasOpacity) { + uniformColor = aItem->IsUniform(aState->mBuilder); + } // Some display items have to exist (so they can set forceTransparentSurface // below) but don't draw anything. They'll return true for isUniform but @@ -3702,18 +3765,17 @@ PaintedLayerData::Accumulate(ContainerState* aState, } } - if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { - nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder); - if (!componentAlpha.IsEmpty()) { - nsIntRect componentAlphaRect = - aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect); - if (!mOpaqueRegion.Contains(componentAlphaRect)) { - if (IsItemAreaInWindowOpaqueRegion(aState->mBuilder, aItem, - componentAlpha.Intersect(aItem->GetVisibleRect()))) { - mNeedComponentAlpha = true; - } else { - aItem->DisableComponentAlpha(); - } + if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants && + !componentAlphaBounds.IsEmpty()) { + nsIntRect componentAlphaRect = + aState->ScaleToOutsidePixels(componentAlphaBounds, false).Intersect(aVisibleRect); + + if (!mOpaqueRegion.Contains(componentAlphaRect)) { + if (IsItemAreaInWindowOpaqueRegion(aState->mBuilder, aItem, + componentAlphaBounds.Intersect(aItem->GetVisibleRect()))) { + mNeedComponentAlpha = true; + } else { + aItem->DisableComponentAlpha(); } } } @@ -3723,8 +3785,7 @@ PaintedLayerData::Accumulate(ContainerState* aState, // not support subpixel positioning of text that animated transforms can // generate. bug 633097 if (aState->mParameters.mInActiveTransformedSubtree && - (mNeedComponentAlpha || - !aItem->GetComponentAlphaBounds(aState->mBuilder).IsEmpty())) { + (mNeedComponentAlpha || !componentAlphaBounds.IsEmpty())) { mDisableFlattening = true; } } @@ -4221,11 +4282,20 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) AnimatedGeometryRoot* lastAnimatedGeometryRoot = nullptr; nsPoint lastTopLeft; - FlattenedDisplayItemIterator iter(mBuilder, aList); - while (nsDisplayItem* i = iter.GetNext()) { + + // Tracks the PaintedLayerData that the item will be accumulated in, if it is + // non-null. Currently only used with PUSH_OPACITY and POP_OPACITY markers. + PaintedLayerData* selectedPLD = nullptr; + + FLBDisplayItemIterator iter(mBuilder, aList, this); + while (iter.HasNext()) { + DisplayItemEntry e = iter.GetNextEntry(); + nsDisplayItem* i = e.mItem; + DisplayItemEntryType marker = e.mType; + + nsDisplayItem* item = i; MOZ_ASSERT(item); - DisplayItemType itemType = item->GetType(); // If the item is a event regions item, but is empty (has no regions in it) @@ -4257,20 +4327,23 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) // Only allow either LayerEventRegions or CompositorHitTestInfo items. MOZ_ASSERT(!(hadLayerEventRegions && hadCompositorHitTestInfo)); - // Peek ahead to the next item and see if it can be merged with the current // item. We create a list of consecutive items that can be merged together. AutoTArray mergedItems; - mergedItems.AppendElement(item); - while (nsDisplayItem* peek = iter.PeekNext()) { - if (!item->CanMerge(peek)) { - break; + + if (marker == DisplayItemEntryType::ITEM) { + mergedItems.AppendElement(item); + + while (nsDisplayItem* peek = iter.PeekNext()) { + if (!item->CanMerge(peek)) { + break; + } + + mergedItems.AppendElement(peek); + + // Move the iterator forward since we will merge this item. + i = iter.GetNext(); } - - mergedItems.AppendElement(peek); - - // Move the iterator forward since we will merge this item. - i = iter.GetNext(); } if (mergedItems.Length() > 1) { @@ -4296,10 +4369,14 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) continue; } - LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); - if (layerState == LAYER_INACTIVE && - nsDisplayItem::ForceActiveLayers()) { - layerState = LAYER_ACTIVE; + LayerState layerState = LAYER_NONE; + + if (marker == DisplayItemEntryType::ITEM) { + layerState = item->GetLayerState(mBuilder, mManager, mParameters); + + if (layerState == LAYER_INACTIVE && nsDisplayItem::ForceActiveLayers()) { + layerState = LAYER_ACTIVE; + } } bool forceInactive = false; @@ -4402,6 +4479,11 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) layerCount++; + // Currently we do not support flattening effects within nested inactive + // layer trees. + MOZ_ASSERT(selectedPLD == nullptr); + MOZ_ASSERT(marker == DisplayItemEntryType::ITEM); + // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata. // We should never see an empty layer with any visible content! NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY || @@ -4721,14 +4803,21 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) const bool backfaceHidden = item->In3DContextAndBackfaceIsHidden(); const nsIFrame* referenceFrame = item->ReferenceFrame(); - PaintedLayerData* paintedLayerData = - mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, itemASR, layerClipChain, - itemVisibleRect, backfaceHidden, - [&](PaintedLayerData* aData) { - NewPaintedLayerData(aData, animatedGeometryRoot, itemASR, - layerClipChain, scrollMetadataASR, topLeft, - referenceFrame, backfaceHidden); + PaintedLayerData* paintedLayerData = selectedPLD; + + if (!selectedPLD) { + MOZ_ASSERT(marker != DisplayItemEntryType::POP_OPACITY); + + paintedLayerData = + mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, itemASR, layerClipChain, + itemVisibleRect, backfaceHidden, + [&](PaintedLayerData* aData) { + NewPaintedLayerData(aData, animatedGeometryRoot, itemASR, + layerClipChain, scrollMetadataASR, topLeft, + referenceFrame, backfaceHidden); }); + } + MOZ_ASSERT(paintedLayerData); if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { nsDisplayLayerEventRegions* eventRegions = @@ -4739,7 +4828,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) static_cast(item); paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo); } else { - paintedLayerData->Accumulate(this, item, itemVisibleRect, itemClip, layerState, aList); + paintedLayerData->Accumulate(this, item, itemVisibleRect, itemClip, + layerState, aList, marker); if (!paintedLayerData->mLayer) { // Try to recycle the old layer of this display item. @@ -4758,6 +4848,18 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) } } } + + if (marker == DisplayItemEntryType::PUSH_OPACITY) { + selectedPLD = paintedLayerData; + } + + if (marker == DisplayItemEntryType::POP_OPACITY ) { + MOZ_ASSERT(selectedPLD); + + if (selectedPLD->mOpacityIndices.IsEmpty()) { + selectedPLD = nullptr; + } + } } nsDisplayList* childItems = item->GetSameCoordinateSystemChildren(); @@ -4765,6 +4867,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) aList->SetNeedsTransparentSurface(); } } + + MOZ_ASSERT(selectedPLD == nullptr); } void @@ -5097,13 +5201,17 @@ FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame, AssignedDisplayItem::AssignedDisplayItem(nsDisplayItem* aItem, const DisplayItemClip& aClip, LayerState aLayerState, - DisplayItemData* aData) + DisplayItemData* aData, + DisplayItemEntryType aType, + const bool aHasOpacity) : mItem(aItem) , mClip(aClip) , mLayerState(aLayerState) , mDisplayItemData(aData) , mReused(aItem->IsReused()) , mMerged(aItem->HasMergedFrames()) + , mType(aType) + , mHasOpacity(aHasOpacity) {} AssignedDisplayItem::~AssignedDisplayItem() @@ -6044,6 +6152,16 @@ FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray& aI if (!cdi->mItem) { continue; } + + if (cdi->mType == DisplayItemEntryType::POP_OPACITY || + (cdi->mType == DisplayItemEntryType::ITEM && cdi->mHasOpacity)) { + // The visibility calculations are skipped when the item is an effect end + // marker, or when the display item is within a flattened opacity group. + // This is because RecomputeVisibility has already been called for the + // group item, and all the children. + continue; + } + const DisplayItemClip& clip = cdi->mItem->GetClip(); NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == aAppUnitsPerDevPixel, @@ -6078,6 +6196,70 @@ FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray& aI } } +/** + * Sets the clip chain and starts a new opacity group. + */ +static void +PushOpacity(gfxContext* aContext, + const nsRect& aPaintRect, + AssignedDisplayItem& aItem, + const int32_t aAUPDP) +{ + MOZ_ASSERT(aItem.mType == DisplayItemEntryType::PUSH_OPACITY || + aItem.mType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG); + MOZ_ASSERT(aItem.mItem->GetType() == DisplayItemType::TYPE_OPACITY); + + aContext->Save(); + + DisplayItemClip clip; + clip.SetTo(aPaintRect); + clip.IntersectWith(aItem.mItem->GetClip()); + clip.ApplyTo(aContext, aAUPDP); + + nsDisplayOpacity* opacityItem = static_cast(aItem.mItem); + const float opacity = opacityItem->GetOpacity(); + + if (aItem.mType == DisplayItemEntryType::PUSH_OPACITY_WITH_BG) { + aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, opacity); + } else { + aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); + } +} + +/** + * Tracks item clips per opacity nesting level. + */ +struct ClipTracker { + explicit ClipTracker(gfxContext* aContext) + : mContext(aContext) + {} + + bool HasClip(int aOpacityNesting) const + { + return !mClips.IsEmpty() && + mClips.LastElement() == aOpacityNesting; + } + + void PopClipIfNeeded(int aOpacityNesting) + { + if (!HasClip(aOpacityNesting)) { + return; + } + + mContext->Restore(); + mClips.RemoveLastElement(); + }; + + void SaveClip(int aOpacityNesting) + { + mContext->Save(); + mClips.AppendElement(aOpacityNesting); + }; + + AutoTArray mClips; + gfxContext* mContext; +}; + void FrameLayerBuilder::PaintItems(nsTArray& aItems, const nsIntRect& aRect, @@ -6095,73 +6277,102 @@ FrameLayerBuilder::PaintItems(nsTArray& aItems, NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel)); boundRect.ScaleInverseRoundOut(aXScale, aYScale); - DisplayItemClip currentClip; - bool currentClipIsSetInContext = false; - DisplayItemClip tmpClip; + DisplayItemClip currentClip, tmpClip; + + int opacityNesting = 0; + ClipTracker clipTracker(aContext); for (uint32_t i = 0; i < aItems.Length(); ++i) { - AssignedDisplayItem* cdi = &aItems[i]; - if (!cdi->mItem) { + AssignedDisplayItem& cdi = aItems[i]; + nsDisplayItem* item = cdi.mItem; + + if (!item) { + MOZ_ASSERT(cdi.mType == DisplayItemEntryType::ITEM); continue; } - nsRect paintRect = cdi->mItem->GetVisibleRect().Intersect(boundRect); - if (paintRect.IsEmpty()) + const nsRect& visibleRect = item->GetVisibleRect(); + + nsRect paintRect = visibleRect.Intersect(boundRect); + if (paintRect.IsEmpty()) { continue; + } #ifdef MOZ_DUMP_PAINTING AUTO_PROFILER_LABEL_DYNAMIC_CSTR("FrameLayerBuilder::PaintItems", GRAPHICS, - cdi->mItem->Name()); + item->Name()); #else AUTO_PROFILER_LABEL("FrameLayerBuilder::PaintItems", GRAPHICS); #endif + 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) { + continue; + } + // If the new desired clip state is different from the current state, // update the clip. - const DisplayItemClip* clip = &cdi->mItem->GetClip(); + const DisplayItemClip* clip = &item->GetClip(); if (clip->GetRoundedRectCount() > 0 && - !clip->IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) { + !clip->IsRectClippedByRoundedCorner(visibleRect)) { tmpClip = *clip; tmpClip.RemoveRoundedCorners(); clip = &tmpClip; } - if (currentClipIsSetInContext != clip->HasClip() || + if (clipTracker.HasClip(opacityNesting) != clip->HasClip() || (clip->HasClip() && *clip != currentClip)) { - if (currentClipIsSetInContext) { - aContext->Restore(); - } - currentClipIsSetInContext = clip->HasClip(); - if (currentClipIsSetInContext) { + clipTracker.PopClipIfNeeded(opacityNesting); + + if (clip->HasClip()) { currentClip = *clip; - aContext->Save(); - currentClip.ApplyTo(aContext, aPresContext->AppUnitsPerDevPixel()); + clipTracker.SaveClip(opacityNesting); + currentClip.ApplyTo(aContext, appUnitsPerDevPixel); aContext->NewPath(); } } - if (cdi->mInactiveLayerManager) { + if (cdi.mInactiveLayerManager) { bool saved = aDrawTarget.GetPermitSubpixelAA(); - PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aContext); + PaintInactiveLayer(aBuilder, cdi.mInactiveLayerManager, + item, aContext, aContext); aDrawTarget.SetPermitSubpixelAA(saved); } else { - nsIFrame* frame = cdi->mItem->Frame(); + nsIFrame* frame = item->Frame(); if (aBuilder->IsPaintingToWindow()) { frame->AddStateBits(NS_FRAME_PAINTED_THEBES); } #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpPaintItems()) { - DebugPaintItem(aDrawTarget, aPresContext, cdi->mItem, aBuilder); + DebugPaintItem(aDrawTarget, aPresContext, item, aBuilder); } else #endif { - cdi->mItem->Paint(aBuilder, aContext); + item->Paint(aBuilder, aContext); } } } - if (currentClipIsSetInContext) { - aContext->Restore(); - } + clipTracker.PopClipIfNeeded(opacityNesting); + MOZ_ASSERT(opacityNesting == 0); } /** diff --git a/layout/painting/FrameLayerBuilder.h b/layout/painting/FrameLayerBuilder.h index f1350542685a..ce0b078a1208 100644 --- a/layout/painting/FrameLayerBuilder.h +++ b/layout/painting/FrameLayerBuilder.h @@ -48,6 +48,7 @@ class PaintedDisplayItemLayerUserData; enum class DisplayItemEntryType { ITEM, PUSH_OPACITY, + PUSH_OPACITY_WITH_BG, POP_OPACITY }; @@ -221,7 +222,9 @@ struct AssignedDisplayItem AssignedDisplayItem(nsDisplayItem* aItem, const DisplayItemClip& aClip, LayerState aLayerState, - DisplayItemData* aData); + DisplayItemData* aData, + DisplayItemEntryType aType, + const bool aHasOpacity); ~AssignedDisplayItem(); nsDisplayItem* mItem; @@ -238,6 +241,9 @@ struct AssignedDisplayItem bool mReused; bool mMerged; + + DisplayItemEntryType mType; + bool mHasOpacity; }; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 3e4c679702bd..dff25bfa2af0 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -6612,7 +6612,10 @@ nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) return false; } - return ApplyOpacityToChildren(aBuilder); + // Return true if we successfully applied opacity to child items, or if + // WebRender is not in use. In the latter case, the opacity gets flattened and + // applied during layer building. + return ApplyOpacityToChildren(aBuilder) || !gfxVars::UseWebRender(); } nsDisplayItem::LayerState