| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/paint/PaintLayerPainter.h" |
| |
| #include "core/frame/LocalFrame.h" |
| #include "core/layout/LayoutInline.h" |
| #include "core/layout/LayoutView.h" |
| #include "core/paint/ClipPathClipper.h" |
| #include "core/paint/FilterPainter.h" |
| #include "core/paint/LayerClipRecorder.h" |
| #include "core/paint/ObjectPaintProperties.h" |
| #include "core/paint/PaintInfo.h" |
| #include "core/paint/PaintLayer.h" |
| #include "core/paint/ScrollRecorder.h" |
| #include "core/paint/ScrollableAreaPainter.h" |
| #include "core/paint/Transform3DRecorder.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/geometry/FloatPoint3D.h" |
| #include "platform/graphics/GraphicsLayer.h" |
| #include "platform/graphics/paint/CompositingRecorder.h" |
| #include "platform/graphics/paint/DisplayItemCacheSkipper.h" |
| #include "platform/graphics/paint/PaintChunkProperties.h" |
| #include "platform/graphics/paint/ScopedPaintChunkProperties.h" |
| #include "platform/graphics/paint/SubsequenceRecorder.h" |
| #include "platform/graphics/paint/Transform3DDisplayItem.h" |
| #include "wtf/Optional.h" |
| |
| namespace blink { |
| |
| static inline bool shouldSuppressPaintingLayer(const PaintLayer& layer) |
| { |
| // Avoid painting descendants of the root layer when stylesheets haven't loaded. This avoids some FOUC. |
| // It's ok not to draw, because later on, when all the stylesheets do load, Document::styleResolverMayHaveChanged() |
| // will invalidate all painted output via a call to LayoutView::invalidatePaintForViewAndCompositedLayers(). |
| // We also avoid caching subsequences in this mode; see shouldCreateSubsequence(). |
| if (layer.layoutObject()->document().didLayoutWithPendingStylesheets() && !layer.isRootLayer() && !layer.layoutObject()->isDocumentElement()) |
| return true; |
| |
| return false; |
| } |
| |
| void PaintLayerPainter::paint(GraphicsContext& context, const LayoutRect& damageRect, const GlobalPaintFlags globalPaintFlags, PaintLayerFlags paintFlags) |
| { |
| PaintLayerPaintingInfo paintingInfo(&m_paintLayer, LayoutRect(enclosingIntRect(damageRect)), globalPaintFlags, LayoutSize()); |
| if (shouldPaintLayerInSoftwareMode(globalPaintFlags, paintFlags)) |
| paintLayer(context, paintingInfo, paintFlags); |
| } |
| |
| static ShouldRespectOverflowClipType shouldRespectOverflowClip(PaintLayerFlags paintFlags, const LayoutObject* layoutObject) |
| { |
| return (paintFlags & PaintLayerPaintingOverflowContents || (paintFlags & PaintLayerPaintingChildClippingMaskPhase && layoutObject->hasClipPath())) ? IgnoreOverflowClip : RespectOverflowClip; |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintLayer(GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) |
| { |
| // https://code.google.com/p/chromium/issues/detail?id=343772 |
| DisableCompositingQueryAsserts disabler; |
| |
| if (m_paintLayer.compositingState() != NotComposited) { |
| if (paintingInfo.getGlobalPaintFlags() & GlobalPaintFlattenCompositingLayers) { |
| // FIXME: ok, but what about GlobalPaintFlattenCompositingLayers? That's for printing and drag-image. |
| // FIXME: why isn't the code here global, as opposed to being set on each paintLayer() call? |
| paintFlags |= PaintLayerUncachedClipRects; |
| } |
| } |
| |
| // Non self-painting layers without self-painting descendants don't need to be painted as their |
| // layoutObject() should properly paint itself. |
| if (!m_paintLayer.isSelfPaintingLayer() && !m_paintLayer.hasSelfPaintingLayerDescendant()) |
| return FullyPainted; |
| |
| if (shouldSuppressPaintingLayer(m_paintLayer)) |
| return FullyPainted; |
| |
| if (m_paintLayer.layoutObject()->view()->frame() && m_paintLayer.layoutObject()->view()->frame()->shouldThrottleRendering()) |
| return FullyPainted; |
| |
| // If this layer is totally invisible then there is nothing to paint. |
| if (!m_paintLayer.layoutObject()->opacity() && !m_paintLayer.layoutObject()->hasBackdropFilter()) |
| return FullyPainted; |
| |
| if (m_paintLayer.paintsWithTransparency(paintingInfo.getGlobalPaintFlags())) |
| paintFlags |= PaintLayerHaveTransparency; |
| |
| // Transforms will be applied by property nodes directly for SPv2. |
| // PaintLayerAppliedTransform is used in LayoutReplica, to avoid applying the transform twice. |
| if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && m_paintLayer.paintsWithTransform(paintingInfo.getGlobalPaintFlags()) && !(paintFlags & PaintLayerAppliedTransform)) |
| return paintLayerWithTransform(context, paintingInfo, paintFlags); |
| |
| return paintLayerContentsAndReflection(context, paintingInfo, paintFlags); |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerContentsAndReflection(GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, FragmentPolicy fragmentPolicy) |
| { |
| ASSERT(m_paintLayer.isSelfPaintingLayer() || m_paintLayer.hasSelfPaintingLayerDescendant()); |
| |
| PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform); |
| |
| PaintResult result = FullyPainted; |
| |
| // Paint the reflection first if we have one. |
| if (m_paintLayer.reflectionInfo() && !RuntimeEnabledFeatures::cssBoxReflectFilterEnabled()) { |
| DisplayItemCacheSkipper skipper(context); |
| if (m_paintLayer.reflectionInfo()->paint(context, paintingInfo, localPaintFlags) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| |
| localPaintFlags |= PaintLayerPaintingCompositingAllPhases; |
| if (paintLayerContents(context, paintingInfo, localPaintFlags, fragmentPolicy) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| |
| return result; |
| } |
| |
| static bool shouldCreateSubsequence(const PaintLayer& paintLayer, GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) |
| { |
| // Caching is not needed during printing. |
| if (context.printing()) |
| return false; |
| |
| // Don't create subsequence for a composited layer because if it can be cached, |
| // we can skip the whole painting in GraphicsLayer::paint() with CachedDisplayItemList. |
| // This also avoids conflict of PaintLayer::previousXXX() when paintLayer is composited |
| // scrolling and is painted twice for GraphicsLayers of container and scrolling contents. |
| if (paintLayer.compositingState() == PaintsIntoOwnBacking) |
| return false; |
| |
| // Don't create subsequence during special painting to avoid cache conflict with normal painting. |
| if (paintingInfo.getGlobalPaintFlags() & GlobalPaintFlattenCompositingLayers) |
| return false; |
| if (paintFlags & (PaintLayerPaintingReflection | PaintLayerPaintingRootBackgroundOnly | PaintLayerPaintingOverlayScrollbars | PaintLayerUncachedClipRects)) |
| return false; |
| |
| // Create subsequence for only stacking contexts whose painting are atomic. |
| if (!paintLayer.stackingNode()->isStackingContext()) |
| return false; |
| |
| // The layer doesn't have children. Subsequence caching is not worth because normally the actual painting will be cheap. |
| if (!PaintLayerStackingNodeIterator(*paintLayer.stackingNode(), AllChildren).next()) |
| return false; |
| |
| // When in FOUC-avoidance mode, don't cache any subsequences, to avoid having |
| // to invalidate all of them when leaving this mode. There is an early-out in BlockPainter::paintContents that may result |
| // in nothing getting painted in thos mode, in addition to early-out logic in PaintLayerPainter. |
| if (paintLayer.layoutObject()->document().didLayoutWithPendingStylesheets()) |
| return false; |
| |
| return true; |
| } |
| |
| static bool shouldRepaintSubsequence(PaintLayer& paintLayer, const PaintLayerPaintingInfo& paintingInfo, ShouldRespectOverflowClipType respectOverflowClip, const LayoutSize& subpixelAccumulation, bool& shouldClearEmptyPaintPhaseFlags) |
| { |
| bool needsRepaint = false; |
| |
| // We should set shouldResetEmptyPaintPhaseFlags if some previously unpainted objects may begin |
| // to be painted, causing a previously empty paint phase to become non-empty. |
| |
| // Repaint subsequence if the layer is marked for needing repaint. |
| // We don't set needsResetEmptyPaintPhase here, but clear the empty paint phase flags |
| // in PaintLayer::setNeedsPaintPhaseXXX(), to ensure that we won't clear |
| // previousPaintPhaseXXXEmpty flags when unrelated things changed which won't |
| // cause the paint phases to become non-empty. |
| if (paintLayer.needsRepaint()) |
| needsRepaint = true; |
| |
| // Repaint if layer's clip changes. |
| ClipRects& clipRects = paintLayer.clipper().paintingClipRects(paintingInfo.rootLayer, respectOverflowClip, subpixelAccumulation); |
| ClipRects* previousClipRects = paintLayer.previousPaintingClipRects(); |
| if (&clipRects != previousClipRects && (!previousClipRects || clipRects != *previousClipRects)) { |
| needsRepaint = true; |
| shouldClearEmptyPaintPhaseFlags = true; |
| } |
| paintLayer.setPreviousPaintingClipRects(clipRects); |
| |
| // Repaint if previously the layer might be clipped by paintDirtyRect and paintDirtyRect changes. |
| if (paintLayer.previousPaintResult() == PaintLayerPainter::MayBeClippedByPaintDirtyRect && paintLayer.previousPaintDirtyRect() != paintingInfo.paintDirtyRect) { |
| needsRepaint = true; |
| shouldClearEmptyPaintPhaseFlags = true; |
| } |
| paintLayer.setPreviousPaintDirtyRect(paintingInfo.paintDirtyRect); |
| |
| // Repaint if scroll offset accumulation changes. |
| if (paintingInfo.scrollOffsetAccumulation != paintLayer.previousScrollOffsetAccumulationForPainting()) { |
| needsRepaint = true; |
| shouldClearEmptyPaintPhaseFlags = true; |
| } |
| paintLayer.setPreviousScrollOffsetAccumulationForPainting(paintingInfo.scrollOffsetAccumulation); |
| |
| return needsRepaint; |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerContents(GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfoArg, PaintLayerFlags paintFlags, FragmentPolicy fragmentPolicy) |
| { |
| ASSERT(m_paintLayer.isSelfPaintingLayer() || m_paintLayer.hasSelfPaintingLayerDescendant()); |
| ASSERT(!(paintFlags & PaintLayerAppliedTransform)); |
| |
| bool isSelfPaintingLayer = m_paintLayer.isSelfPaintingLayer(); |
| bool isPaintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars; |
| bool isPaintingScrollingContent = paintFlags & PaintLayerPaintingCompositingScrollingPhase; |
| bool isPaintingCompositedForeground = paintFlags & PaintLayerPaintingCompositingForegroundPhase; |
| bool isPaintingCompositedBackground = paintFlags & PaintLayerPaintingCompositingBackgroundPhase; |
| bool isPaintingOverflowContents = paintFlags & PaintLayerPaintingOverflowContents; |
| // Outline always needs to be painted even if we have no visible content. Also, |
| // the outline is painted in the background phase during composited scrolling. |
| // If it were painted in the foreground phase, it would move with the scrolled |
| // content. When not composited scrolling, the outline is painted in the |
| // foreground phase. Since scrolled contents are moved by paint invalidation in this |
| // case, the outline won't get 'dragged along'. |
| bool shouldPaintSelfOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars |
| && ((isPaintingScrollingContent && isPaintingCompositedBackground) |
| || (!isPaintingScrollingContent && isPaintingCompositedForeground)) |
| && m_paintLayer.layoutObject()->styleRef().hasOutline(); |
| bool shouldPaintContent = m_paintLayer.hasVisibleContent() && isSelfPaintingLayer && !isPaintingOverlayScrollbars; |
| |
| PaintResult result = FullyPainted; |
| |
| if (paintFlags & PaintLayerPaintingRootBackgroundOnly && !m_paintLayer.layoutObject()->isLayoutView()) |
| return result; |
| |
| if (m_paintLayer.layoutObject()->view()->frame() && m_paintLayer.layoutObject()->view()->frame()->shouldThrottleRendering()) |
| return result; |
| |
| // Ensure our lists are up to date. |
| m_paintLayer.stackingNode()->updateLayerListsIfNeeded(); |
| |
| LayoutSize subpixelAccumulation = m_paintLayer.compositingState() == PaintsIntoOwnBacking ? m_paintLayer.subpixelAccumulation() : paintingInfoArg.subPixelAccumulation; |
| ShouldRespectOverflowClipType respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); |
| |
| Optional<SubsequenceRecorder> subsequenceRecorder; |
| bool shouldClearEmptyPaintPhaseFlags = false; |
| if (shouldCreateSubsequence(m_paintLayer, context, paintingInfoArg, paintFlags)) { |
| if (!shouldRepaintSubsequence(m_paintLayer, paintingInfoArg, respectOverflowClip, subpixelAccumulation, shouldClearEmptyPaintPhaseFlags) |
| && SubsequenceRecorder::useCachedSubsequenceIfPossible(context, m_paintLayer)) |
| return result; |
| subsequenceRecorder.emplace(context, m_paintLayer); |
| } else { |
| shouldClearEmptyPaintPhaseFlags = true; |
| } |
| |
| if (shouldClearEmptyPaintPhaseFlags) { |
| m_paintLayer.setPreviousPaintPhaseDescendantOutlinesEmpty(false); |
| m_paintLayer.setPreviousPaintPhaseFloatEmpty(false); |
| m_paintLayer.setPreviousPaintPhaseDescendantBlockBackgroundsEmpty(false); |
| } |
| |
| PaintLayerPaintingInfo paintingInfo = paintingInfoArg; |
| |
| LayoutPoint offsetFromRoot; |
| m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); |
| offsetFromRoot.move(subpixelAccumulation); |
| |
| LayoutRect bounds = m_paintLayer.physicalBoundingBox(offsetFromRoot); |
| if (!paintingInfo.paintDirtyRect.contains(bounds)) |
| result = MayBeClippedByPaintDirtyRect; |
| |
| if (paintingInfo.ancestorHasClipPathClipping && m_paintLayer.layoutObject()->isPositioned()) |
| UseCounter::count(m_paintLayer.layoutObject()->document(), UseCounter::ClipPathOfPositionedElement); |
| |
| // These helpers output clip and compositing operations using a RAII pattern. Stack-allocated-varibles are destructed in the reverse order of construction, |
| // so they are nested properly. |
| Optional<ClipPathClipper> clipPathClipper; |
| // Clip-path, like border radius, must not be applied to the contents of a composited-scrolling container. |
| // It must, however, still be applied to the mask layer, so that the compositor can properly mask the |
| // scrolling contents and scrollbars. |
| if (m_paintLayer.layoutObject()->hasClipPath() && (!m_paintLayer.needsCompositedScrolling() || (paintFlags & PaintLayerPaintingChildClippingMaskPhase))) { |
| paintingInfo.ancestorHasClipPathClipping = true; |
| |
| LayoutRect referenceBox(m_paintLayer.boxForClipPath()); |
| // Note that this isn't going to work correctly if crossing a column boundary. The reference box should be |
| // determined per-fragment, and hence this ought to be performed after fragmentation. |
| if (m_paintLayer.enclosingPaginationLayer()) |
| m_paintLayer.convertFromFlowThreadToVisualBoundingBoxInAncestor(paintingInfo.rootLayer, referenceBox); |
| else |
| referenceBox.moveBy(offsetFromRoot); |
| clipPathClipper.emplace( |
| context, *m_paintLayer.layoutObject(), FloatRect(referenceBox), FloatPoint(referenceBox.location())); |
| } |
| |
| Optional<CompositingRecorder> compositingRecorder; |
| // Blending operations must be performed only with the nearest ancestor stacking context. |
| // Note that there is no need to composite if we're painting the root. |
| // FIXME: this should be unified further into PaintLayer::paintsWithTransparency(). |
| bool shouldCompositeForBlendMode = (!m_paintLayer.layoutObject()->isDocumentElement() || m_paintLayer.layoutObject()->isSVGRoot()) && m_paintLayer.stackingNode()->isStackingContext() && m_paintLayer.hasNonIsolatedDescendantWithBlendMode(); |
| if (shouldCompositeForBlendMode || m_paintLayer.paintsWithTransparency(paintingInfo.getGlobalPaintFlags())) { |
| FloatRect compositingBounds = FloatRect(m_paintLayer.paintingExtent(paintingInfo.rootLayer, paintingInfo.subPixelAccumulation, paintingInfo.getGlobalPaintFlags())); |
| compositingRecorder.emplace(context, *m_paintLayer.layoutObject(), |
| WebCoreCompositeToSkiaComposite(CompositeSourceOver, m_paintLayer.layoutObject()->style()->blendMode()), |
| m_paintLayer.layoutObject()->opacity(), &compositingBounds); |
| } |
| |
| PaintLayerPaintingInfo localPaintingInfo(paintingInfo); |
| localPaintingInfo.subPixelAccumulation = subpixelAccumulation; |
| |
| PaintLayerFragments layerFragments; |
| if (shouldPaintContent || shouldPaintSelfOutline || isPaintingOverlayScrollbars) { |
| // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment. |
| ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; |
| // TODO(trchen): We haven't decided how to handle visual fragmentation with SPv2. |
| // Related thread https://groups.google.com/a/chromium.org/forum/#!topic/graphics-dev/81XuWFf-mxM |
| if (fragmentPolicy == ForceSingleFragment || RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| m_paintLayer.appendSingleFragmentIgnoringPagination(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot, localPaintingInfo.subPixelAccumulation); |
| else |
| m_paintLayer.collectFragments(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot, localPaintingInfo.subPixelAccumulation); |
| |
| // TODO(trchen): Needs to adjust cull rect between transform spaces. https://crbug.com/593596 |
| // Disables layer culling for SPv2 for now because the space of the cull rect doesn't match |
| // the space we paint in. Clipping will still be done by clip nodes, so this won't cause |
| // rendering issues, only performance. |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| layerFragments[0].backgroundRect = LayoutRect(LayoutRect::infiniteIntRect()); |
| layerFragments[0].foregroundRect = LayoutRect(LayoutRect::infiniteIntRect()); |
| } |
| |
| if (shouldPaintContent) { |
| // TODO(wangxianzhu): This is for old slow scrolling. Implement similar optimization for slimming paint v2. |
| shouldPaintContent = atLeastOneFragmentIntersectsDamageRect(layerFragments, localPaintingInfo, paintFlags, offsetFromRoot); |
| if (!shouldPaintContent) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| } |
| |
| bool selectionOnly = localPaintingInfo.getGlobalPaintFlags() & GlobalPaintSelectionOnly; |
| |
| { // Begin block for the lifetime of any filter. |
| FilterPainter filterPainter(m_paintLayer, context, offsetFromRoot, layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect, localPaintingInfo, paintFlags); |
| |
| Optional<ScopedPaintChunkProperties> scopedPaintChunkProperties; |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| const ObjectPaintProperties* objectPaintProperties = m_paintLayer.layoutObject()->objectPaintProperties(); |
| ASSERT(objectPaintProperties && objectPaintProperties->localBorderBoxProperties()); |
| PaintChunkProperties properties(context.getPaintController().currentPaintChunkProperties()); |
| auto& localBorderBoxProperties = *objectPaintProperties->localBorderBoxProperties(); |
| properties.transform = localBorderBoxProperties.propertyTreeState.transform; |
| properties.clip = localBorderBoxProperties.propertyTreeState.clip; |
| properties.effect = localBorderBoxProperties.propertyTreeState.effect; |
| properties.backfaceHidden = m_paintLayer.layoutObject()->hasHiddenBackface(); |
| scopedPaintChunkProperties.emplace(context.getPaintController(), m_paintLayer, properties); |
| } |
| |
| bool isPaintingRootLayer = (&m_paintLayer) == paintingInfo.rootLayer; |
| bool shouldPaintBackground = shouldPaintContent && !selectionOnly |
| && (isPaintingCompositedBackground || (isPaintingRootLayer && !(paintFlags & PaintLayerPaintingSkipRootBackground))); |
| bool shouldPaintNegZOrderList = (isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground); |
| bool shouldPaintOwnContents = isPaintingCompositedForeground && shouldPaintContent; |
| bool shouldPaintNormalFlowAndPosZOrderLists = isPaintingCompositedForeground; |
| bool shouldPaintOverlayScrollbars = isPaintingOverlayScrollbars; |
| |
| if (shouldPaintBackground) { |
| paintBackgroundForFragments(layerFragments, context, paintingInfo.paintDirtyRect, |
| localPaintingInfo, paintFlags); |
| } |
| |
| if (shouldPaintNegZOrderList) { |
| if (paintChildren(NegativeZOrderChildren, context, paintingInfo, paintFlags) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| |
| if (shouldPaintOwnContents) { |
| paintForegroundForFragments(layerFragments, context, paintingInfo.paintDirtyRect, |
| localPaintingInfo, selectionOnly, paintFlags); |
| } |
| |
| if (shouldPaintSelfOutline) |
| paintSelfOutlineForFragments(layerFragments, context, localPaintingInfo, paintFlags); |
| |
| if (shouldPaintNormalFlowAndPosZOrderLists) { |
| if (paintChildren(NormalFlowChildren | PositiveZOrderChildren, context, paintingInfo, paintFlags) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| |
| if (shouldPaintOverlayScrollbars) |
| paintOverflowControlsForFragments(layerFragments, context, localPaintingInfo, paintFlags); |
| } // FilterPainter block |
| |
| bool shouldPaintMask = (paintFlags & PaintLayerPaintingCompositingMaskPhase) && shouldPaintContent && m_paintLayer.layoutObject()->hasMask() && !selectionOnly; |
| bool shouldPaintClippingMask = (paintFlags & PaintLayerPaintingChildClippingMaskPhase) && shouldPaintContent && !selectionOnly; |
| |
| if (shouldPaintMask) |
| paintMaskForFragments(layerFragments, context, localPaintingInfo, paintFlags); |
| if (shouldPaintClippingMask) { |
| // Paint the border radius mask for the fragments. |
| paintChildClippingMaskForFragments(layerFragments, context, localPaintingInfo, paintFlags); |
| } |
| |
| if (subsequenceRecorder) |
| m_paintLayer.setPreviousPaintResult(result); |
| return result; |
| } |
| |
| bool PaintLayerPainter::needsToClip(const PaintLayerPaintingInfo& localPaintingInfo, const ClipRect& clipRect) |
| { |
| // Clipping will be applied by property nodes directly for SPv2. |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| return false; |
| |
| return clipRect.rect() != localPaintingInfo.paintDirtyRect || clipRect.hasRadius(); |
| } |
| |
| bool PaintLayerPainter::atLeastOneFragmentIntersectsDamageRect(PaintLayerFragments& fragments, const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags localPaintFlags, const LayoutPoint& offsetFromRoot) |
| { |
| if (m_paintLayer.enclosingPaginationLayer()) |
| return true; // The fragments created have already been found to intersect with the damage rect. |
| |
| if (&m_paintLayer == localPaintingInfo.rootLayer && (localPaintFlags & PaintLayerPaintingOverflowContents)) |
| return true; |
| |
| for (PaintLayerFragment& fragment: fragments) { |
| LayoutPoint newOffsetFromRoot = offsetFromRoot + fragment.paginationOffset; |
| // Note that this really only works reliably on the first fragment. If the layer has visible |
| // overflow and a subsequent fragment doesn't intersect with the border box of the layer |
| // (i.e. only contains an overflow portion of the layer), intersection will fail. The reason |
| // for this is that fragment.layerBounds is set to the border box, not the bounding box, of |
| // the layer. |
| if (m_paintLayer.intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), newOffsetFromRoot)) |
| return true; |
| } |
| return false; |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerWithTransform(GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) |
| { |
| // Transforms will be applied by property nodes directly for SPv2. |
| ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| |
| TransformationMatrix layerTransform = m_paintLayer.renderableTransform(paintingInfo.getGlobalPaintFlags()); |
| // If the transform can't be inverted, then don't paint anything. |
| if (!layerTransform.isInvertible()) |
| return FullyPainted; |
| |
| // FIXME: We should make sure that we don't walk past paintingInfo.rootLayer here. |
| // m_paintLayer may be the "root", and then we should avoid looking at its parent. |
| PaintLayer* parentLayer = m_paintLayer.parent(); |
| |
| ClipRect ancestorBackgroundClipRect; |
| if (parentLayer) { |
| // Calculate the clip rectangle that the ancestors establish. |
| ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize); |
| if (shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()) == IgnoreOverflowClip) |
| clipRectsContext.setIgnoreOverflowClip(); |
| ancestorBackgroundClipRect = m_paintLayer.clipper().backgroundClipRect(clipRectsContext); |
| } |
| |
| LayoutObject* object = m_paintLayer.layoutObject(); |
| LayoutView* view = object->view(); |
| bool isFixedPosObjectInPagedMedia = object->style()->position() == FixedPosition && object->container() == view && view->pageLogicalHeight(); |
| PaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer(); |
| PaintLayerFragments fragments; |
| // TODO(crbug.com/619094): Figure out the correct behaviour for fixed position objects |
| // in paged media with vertical writing modes. |
| if (isFixedPosObjectInPagedMedia && view->isHorizontalWritingMode()) { |
| // "For paged media, boxes with fixed positions are repeated on every page." |
| // - https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#fixed-positioning |
| unsigned pages = ceilf(view->documentRect().height() / view->pageLogicalHeight()); |
| LayoutPoint paginationOffset; |
| for (unsigned i = 0; i < pages; i++) { |
| PaintLayerFragment fragment; |
| fragment.backgroundRect = paintingInfo.paintDirtyRect; |
| fragment.paginationOffset = paginationOffset; |
| fragments.append(fragment); |
| paginationOffset += LayoutPoint(LayoutUnit(), view->pageLogicalHeight()); |
| } |
| } else if (paginationLayer) { |
| // FIXME: This is a mess. Look closely at this code and the code in Layer and fix any |
| // issues in it & refactor to make it obvious from code structure what it does and that it's |
| // correct. |
| ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; |
| ShouldRespectOverflowClipType respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); |
| // Calculate the transformed bounding box in the current coordinate space, to figure out |
| // which fragmentainers (e.g. columns) we need to visit. |
| LayoutRect transformedExtent = PaintLayer::transparencyClipBox(&m_paintLayer, paginationLayer, PaintLayer::PaintingTransparencyClipBox, PaintLayer::RootOfTransparencyClipBox, paintingInfo.subPixelAccumulation, paintingInfo.getGlobalPaintFlags()); |
| // FIXME: we don't check if paginationLayer is within paintingInfo.rootLayer here. |
| paginationLayer->collectFragments(fragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0, paintingInfo.subPixelAccumulation, &transformedExtent); |
| } else { |
| // We don't need to collect any fragments in the regular way here. We have already |
| // calculated a clip rectangle for the ancestry if it was needed, and clipping this |
| // layer is something that can be done further down the path, when the transform has |
| // been applied. |
| PaintLayerFragment fragment; |
| fragment.backgroundRect = paintingInfo.paintDirtyRect; |
| fragments.append(fragment); |
| } |
| |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (fragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| PaintResult result = FullyPainted; |
| for (const auto& fragment : fragments) { |
| Optional<LayerClipRecorder> clipRecorder; |
| if (parentLayer) { |
| ClipRect clipRectForFragment(ancestorBackgroundClipRect); |
| // A fixed-position object is repeated on every page, but if it is clipped by an ancestor layer then |
| // the repetitions are clipped out. |
| if (!isFixedPosObjectInPagedMedia) |
| clipRectForFragment.moveBy(fragment.paginationOffset); |
| clipRectForFragment.intersect(fragment.backgroundRect); |
| if (clipRectForFragment.isEmpty()) |
| continue; |
| if (needsToClip(paintingInfo, clipRectForFragment)) { |
| if (m_paintLayer.layoutObject()->isPositioned() && clipRectForFragment.isClippedByClipCss()) |
| UseCounter::count(m_paintLayer.layoutObject()->document(), UseCounter::ClipCssOfPositionedElement); |
| clipRecorder.emplace(context, *parentLayer->layoutObject(), DisplayItem::kClipLayerParent, clipRectForFragment, &paintingInfo, fragment.paginationOffset, paintFlags); |
| } |
| } |
| if (paintFragmentByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| return result; |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintFragmentByApplyingTransform(GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& fragmentTranslation) |
| { |
| // Transforms will be applied by property nodes directly for SPv2. |
| ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| |
| // This involves subtracting out the position of the layer in our current coordinate space, but preserving |
| // the accumulated error for sub-pixel layout. |
| LayoutPoint delta; |
| m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, delta); |
| delta.moveBy(fragmentTranslation); |
| TransformationMatrix transform(m_paintLayer.renderableTransform(paintingInfo.getGlobalPaintFlags())); |
| IntPoint roundedDelta = roundedIntPoint(delta); |
| transform.translateRight(roundedDelta.x(), roundedDelta.y()); |
| LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta); |
| |
| // TODO(jbroman): Put the real transform origin here, instead of using a |
| // matrix with the origin baked in. |
| FloatPoint3D transformOrigin; |
| Transform3DRecorder transform3DRecorder(context, *m_paintLayer.layoutObject(), DisplayItem::kTransform3DElementTransform, transform, transformOrigin); |
| |
| // Now do a paint with the root layer shifted to be us. |
| PaintLayerPaintingInfo transformedPaintingInfo(&m_paintLayer, LayoutRect(enclosingIntRect(transform.inverse().mapRect(paintingInfo.paintDirtyRect))), paintingInfo.getGlobalPaintFlags(), |
| adjustedSubPixelAccumulation); |
| transformedPaintingInfo.ancestorHasClipPathClipping = paintingInfo.ancestorHasClipPathClipping; |
| |
| // Remove skip root background flag when we're painting with a new root. |
| if (&m_paintLayer != paintingInfo.rootLayer) |
| paintFlags &= ~PaintLayerPaintingSkipRootBackground; |
| |
| return paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags, ForceSingleFragment); |
| } |
| |
| PaintLayerPainter::PaintResult PaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) |
| { |
| PaintResult result = FullyPainted; |
| if (!m_paintLayer.hasSelfPaintingLayerDescendant()) |
| return result; |
| |
| #if ENABLE(ASSERT) |
| LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); |
| #endif |
| |
| PaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); |
| PaintLayerStackingNode* child = iterator.next(); |
| if (!child) |
| return result; |
| |
| IntSize scrollOffsetAccumulationForChildren = paintingInfo.scrollOffsetAccumulation; |
| if (m_paintLayer.layoutObject()->hasOverflowClip()) |
| scrollOffsetAccumulationForChildren += m_paintLayer.layoutBox()->scrolledContentOffset(); |
| |
| for (; child; child = iterator.next()) { |
| PaintLayerPainter childPainter(*child->layer()); |
| // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedLayerMapping::paintContents() |
| // and CompositedLayerMapping::doPaintTask(). |
| if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.getGlobalPaintFlags(), paintFlags)) |
| continue; |
| |
| PaintLayerPaintingInfo childPaintingInfo = paintingInfo; |
| childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulationForChildren; |
| // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. |
| for (PaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { |
| if (parentLayer->layoutObject()->hasOverflowClip()) |
| childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); |
| } |
| |
| if (childPainter.paintLayer(context, childPaintingInfo, paintFlags) == MayBeClippedByPaintDirtyRect) |
| result = MayBeClippedByPaintDirtyRect; |
| } |
| |
| return result; |
| } |
| |
| bool PaintLayerPainter::shouldPaintLayerInSoftwareMode(const GlobalPaintFlags globalPaintFlags, PaintLayerFlags paintFlags) |
| { |
| DisableCompositingQueryAsserts disabler; |
| |
| return m_paintLayer.compositingState() == NotComposited |
| || (globalPaintFlags & GlobalPaintFlattenCompositingLayers) |
| || ((paintFlags & PaintLayerPaintingReflection) && !m_paintLayer.has3DTransform()); |
| } |
| |
| void PaintLayerPainter::paintOverflowControlsForFragments(const PaintLayerFragments& layerFragments, GraphicsContext& context, const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) |
| { |
| PaintLayerScrollableArea* scrollableArea = m_paintLayer.getScrollableArea(); |
| if (!scrollableArea) |
| return; |
| |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment : layerFragments) { |
| // We need to apply the same clips and transforms that |
| // paintFragmentWithPhase would have. |
| LayoutRect cullRect = fragment.backgroundRect.rect(); |
| |
| Optional<LayerClipRecorder> clipRecorder; |
| if (needsToClip(localPaintingInfo, fragment.backgroundRect)) |
| clipRecorder.emplace(context, *m_paintLayer.layoutObject(), DisplayItem::kClipLayerOverflowControls, fragment.backgroundRect, &localPaintingInfo, fragment.paginationOffset, paintFlags); |
| |
| Optional<ScrollRecorder> scrollRecorder; |
| if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && !localPaintingInfo.scrollOffsetAccumulation.isZero()) { |
| cullRect.move(localPaintingInfo.scrollOffsetAccumulation); |
| scrollRecorder.emplace(context, *m_paintLayer.layoutObject(), DisplayItem::kScrollOverflowControls, localPaintingInfo.scrollOffsetAccumulation); |
| } |
| |
| // We pass IntPoint() as the paint offset here, because |
| // ScrollableArea::paintOverflowControls just ignores it and uses the |
| // offset found in a previous pass. |
| CullRect snappedCullRect(pixelSnappedIntRect(cullRect)); |
| ScrollableAreaPainter(*scrollableArea).paintOverflowControls(context, IntPoint(), snappedCullRect, true); |
| } |
| } |
| |
| void PaintLayerPainter::paintFragmentWithPhase(PaintPhase phase, const PaintLayerFragment& fragment, GraphicsContext& context, const ClipRect& clipRect, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, ClipState clipState) |
| { |
| ASSERT(m_paintLayer.isSelfPaintingLayer()); |
| |
| Optional<LayerClipRecorder> clipRecorder; |
| if (clipState != HasClipped && paintingInfo.clipToDirtyRect && needsToClip(paintingInfo, clipRect)) { |
| DisplayItem::Type clipType = DisplayItem::paintPhaseToClipLayerFragmentType(phase); |
| LayerClipRecorder::BorderRadiusClippingRule clippingRule; |
| switch (phase) { |
| case PaintPhaseSelfBlockBackgroundOnly: // Background painting will handle clipping to self. |
| case PaintPhaseSelfOutlineOnly: |
| case PaintPhaseMask: // Mask painting will handle clipping to self. |
| clippingRule = LayerClipRecorder::DoNotIncludeSelfForBorderRadius; |
| break; |
| default: |
| clippingRule = LayerClipRecorder::IncludeSelfForBorderRadius; |
| break; |
| } |
| |
| clipRecorder.emplace(context, *m_paintLayer.layoutObject(), clipType, clipRect, &paintingInfo, fragment.paginationOffset, paintFlags, clippingRule); |
| } |
| |
| LayoutRect newCullRect(clipRect.rect()); |
| Optional<ScrollRecorder> scrollRecorder; |
| LayoutPoint paintOffset = -m_paintLayer.layoutBoxLocation(); |
| if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| const ObjectPaintProperties* objectPaintProperties = m_paintLayer.layoutObject()->objectPaintProperties(); |
| ASSERT(objectPaintProperties && objectPaintProperties->localBorderBoxProperties()); |
| paintOffset += toSize(objectPaintProperties->localBorderBoxProperties()->paintOffset); |
| } else { |
| paintOffset += toSize(fragment.layerBounds.location()); |
| if (!paintingInfo.scrollOffsetAccumulation.isZero()) { |
| // As a descendant of the root layer, m_paintLayer's painting is not controlled by the ScrollRecorders |
| // created by BlockPainter of the ancestor layers up to the root layer, so we need to issue ScrollRecorder |
| // for this layer seperately, with the scroll offset accumulated from the root layer to the parent of this |
| // layer, to get the same result as ScrollRecorder in BlockPainter. |
| paintOffset += paintingInfo.scrollOffsetAccumulation; |
| |
| newCullRect.move(paintingInfo.scrollOffsetAccumulation); |
| scrollRecorder.emplace(context, *m_paintLayer.layoutObject(), phase, paintingInfo.scrollOffsetAccumulation); |
| } |
| } |
| PaintInfo paintInfo(context, pixelSnappedIntRect(newCullRect), phase, |
| paintingInfo.getGlobalPaintFlags(), paintFlags, paintingInfo.rootLayer->layoutObject()); |
| |
| m_paintLayer.layoutObject()->paint(paintInfo, paintOffset); |
| } |
| |
| void PaintLayerPainter::paintBackgroundForFragments(const PaintLayerFragments& layerFragments, |
| GraphicsContext& context, const LayoutRect& transparencyPaintDirtyRect, |
| const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) |
| { |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment : layerFragments) |
| paintFragmentWithPhase(PaintPhaseSelfBlockBackgroundOnly, fragment, context, fragment.backgroundRect, localPaintingInfo, paintFlags, HasNotClipped); |
| } |
| |
| void PaintLayerPainter::paintForegroundForFragments(const PaintLayerFragments& layerFragments, |
| GraphicsContext& context, const LayoutRect& transparencyPaintDirtyRect, |
| const PaintLayerPaintingInfo& localPaintingInfo, bool selectionOnly, PaintLayerFlags paintFlags) |
| { |
| DCHECK(!(paintFlags & PaintLayerPaintingRootBackgroundOnly)); |
| |
| // Optimize clipping for the single fragment case. |
| bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && !layerFragments[0].foregroundRect.isEmpty(); |
| ClipState clipState = HasNotClipped; |
| Optional<LayerClipRecorder> clipRecorder; |
| if (shouldClip && needsToClip(localPaintingInfo, layerFragments[0].foregroundRect)) { |
| clipRecorder.emplace(context, *m_paintLayer.layoutObject(), DisplayItem::kClipLayerForeground, layerFragments[0].foregroundRect, &localPaintingInfo, layerFragments[0].paginationOffset, paintFlags); |
| clipState = HasClipped; |
| } |
| |
| // We have to loop through every fragment multiple times, since we have to issue paint invalidations in each specific phase in order for |
| // interleaving of the fragments to work properly. |
| if (selectionOnly) { |
| paintForegroundForFragmentsWithPhase(PaintPhaseSelection, layerFragments, context, localPaintingInfo, paintFlags, clipState); |
| } else { |
| if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled() || m_paintLayer.needsPaintPhaseDescendantBlockBackgrounds()) { |
| size_t sizeBefore = context.getPaintController().newDisplayItemList().size(); |
| paintForegroundForFragmentsWithPhase(PaintPhaseDescendantBlockBackgroundsOnly, layerFragments, context, localPaintingInfo, paintFlags, clipState); |
| // Don't set the empty flag if we are not painting the whole background. |
| if (!(paintFlags & PaintLayerPaintingSkipRootBackground)) { |
| bool phaseIsEmpty = context.getPaintController().newDisplayItemList().size() == sizeBefore; |
| DCHECK(phaseIsEmpty || m_paintLayer.needsPaintPhaseDescendantBlockBackgrounds()); |
| m_paintLayer.setPreviousPaintPhaseDescendantBlockBackgroundsEmpty(phaseIsEmpty); |
| } |
| } |
| |
| if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled() || m_paintLayer.needsPaintPhaseFloat()) { |
| size_t sizeBefore = context.getPaintController().newDisplayItemList().size(); |
| paintForegroundForFragmentsWithPhase(PaintPhaseFloat, layerFragments, context, localPaintingInfo, paintFlags, clipState); |
| bool phaseIsEmpty = context.getPaintController().newDisplayItemList().size() == sizeBefore; |
| DCHECK(phaseIsEmpty || m_paintLayer.needsPaintPhaseFloat()); |
| m_paintLayer.setPreviousPaintPhaseFloatEmpty(phaseIsEmpty); |
| } |
| |
| paintForegroundForFragmentsWithPhase(PaintPhaseForeground, layerFragments, context, localPaintingInfo, paintFlags, clipState); |
| |
| if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled() || m_paintLayer.needsPaintPhaseDescendantOutlines()) { |
| size_t sizeBefore = context.getPaintController().newDisplayItemList().size(); |
| paintForegroundForFragmentsWithPhase(PaintPhaseDescendantOutlinesOnly, layerFragments, context, localPaintingInfo, paintFlags, clipState); |
| bool phaseIsEmpty = context.getPaintController().newDisplayItemList().size() == sizeBefore; |
| DCHECK(phaseIsEmpty || m_paintLayer.needsPaintPhaseDescendantOutlines()); |
| m_paintLayer.setPreviousPaintPhaseDescendantOutlinesEmpty(phaseIsEmpty); |
| } |
| } |
| } |
| |
| void PaintLayerPainter::paintForegroundForFragmentsWithPhase(PaintPhase phase, |
| const PaintLayerFragments& layerFragments, GraphicsContext& context, |
| const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags, ClipState clipState) |
| { |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment : layerFragments) { |
| if (!fragment.foregroundRect.isEmpty()) |
| paintFragmentWithPhase(phase, fragment, context, fragment.foregroundRect, localPaintingInfo, paintFlags, clipState); |
| } |
| } |
| |
| void PaintLayerPainter::paintSelfOutlineForFragments(const PaintLayerFragments& layerFragments, |
| GraphicsContext& context, const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) |
| { |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment : layerFragments) { |
| if (!fragment.backgroundRect.isEmpty()) |
| paintFragmentWithPhase(PaintPhaseSelfOutlineOnly, fragment, context, fragment.backgroundRect, localPaintingInfo, paintFlags, HasNotClipped); |
| } |
| } |
| |
| void PaintLayerPainter::paintMaskForFragments(const PaintLayerFragments& layerFragments, |
| GraphicsContext& context, const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) |
| { |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment : layerFragments) |
| paintFragmentWithPhase(PaintPhaseMask, fragment, context, fragment.backgroundRect, localPaintingInfo, paintFlags, HasNotClipped); |
| } |
| |
| void PaintLayerPainter::paintChildClippingMaskForFragments(const PaintLayerFragments& layerFragments, |
| GraphicsContext& context, const PaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) |
| { |
| Optional<DisplayItemCacheSkipper> cacheSkipper; |
| if (layerFragments.size() > 1) |
| cacheSkipper.emplace(context); |
| |
| for (auto& fragment: layerFragments) |
| paintFragmentWithPhase(PaintPhaseClippingMask, fragment, context, fragment.foregroundRect, localPaintingInfo, paintFlags, HasNotClipped); |
| } |
| |
| void PaintLayerPainter::paintOverlayScrollbars(GraphicsContext& context, const LayoutRect& damageRect, const GlobalPaintFlags paintFlags) |
| { |
| if (!m_paintLayer.containsDirtyOverlayScrollbars()) |
| return; |
| |
| PaintLayerPaintingInfo paintingInfo(&m_paintLayer, LayoutRect(enclosingIntRect(damageRect)), paintFlags, LayoutSize()); |
| paintLayer(context, paintingInfo, PaintLayerPaintingOverlayScrollbars); |
| |
| m_paintLayer.setContainsDirtyOverlayScrollbars(false); |
| } |
| |
| } // namespace blink |